286 lines
12 KiB
TypeScript
286 lines
12 KiB
TypeScript
// SPEC: PJ2026-010404 项目管理 draft-2026-06-25-p0-project-management-mdtodo; draft-2026-06-26-p1-mdtodo-web-operable.
|
|
// Responsibility: Cloud Web client for the public project-management same-origin API.
|
|
|
|
import { fetchJson } from "./client";
|
|
import type { ApiResult } from "@/types";
|
|
|
|
export interface ProjectManagementEnvelope {
|
|
contractVersion?: string;
|
|
serviceId?: string;
|
|
ok?: boolean;
|
|
valuesRedacted?: boolean;
|
|
}
|
|
|
|
export interface ProjectNavigationItem {
|
|
id: string;
|
|
label: string;
|
|
apiRoute?: string;
|
|
}
|
|
|
|
export interface ProjectNavigationCapabilities {
|
|
mdtodoProjection?: boolean;
|
|
workbenchLaunch?: boolean;
|
|
workbenchLinks?: boolean;
|
|
sourceOfTruth?: string;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
export interface ProjectNavigation {
|
|
id: string;
|
|
label: string;
|
|
actor?: { id?: string | null; role?: string | null; authMethod?: string | null } | null;
|
|
items?: ProjectNavigationItem[];
|
|
capabilities?: ProjectNavigationCapabilities;
|
|
}
|
|
|
|
export interface ProjectProjectionSummary {
|
|
sourceId?: string;
|
|
documentCount?: number;
|
|
taskCount?: number;
|
|
projectedAt?: string;
|
|
startedAt?: string;
|
|
}
|
|
|
|
export interface ProjectNavigationResponse extends ProjectManagementEnvelope {
|
|
navigation?: ProjectNavigation;
|
|
projection?: ProjectProjectionSummary | null;
|
|
}
|
|
|
|
export interface ProjectRecord {
|
|
projectId: string;
|
|
name?: string;
|
|
title?: string;
|
|
sourceIds?: string[];
|
|
sourceOfTruth?: string;
|
|
status?: string;
|
|
}
|
|
|
|
export interface ProjectSource {
|
|
sourceId: string;
|
|
kind?: string;
|
|
sourceKind?: string;
|
|
displayName?: string;
|
|
projectId?: string;
|
|
status?: string;
|
|
hwpodId?: string | null;
|
|
nodeId?: string | null;
|
|
workspaceRootRef?: string | null;
|
|
mdtodoRootRef?: string | null;
|
|
maxFiles?: number;
|
|
updatedAt?: string;
|
|
valuesRedacted?: boolean;
|
|
}
|
|
|
|
export interface ProjectSourceInput {
|
|
sourceId?: string;
|
|
sourceKind?: string;
|
|
displayName?: string;
|
|
projectId?: string;
|
|
hwpodId?: string;
|
|
nodeId?: string;
|
|
workspaceRootRef?: string;
|
|
mdtodoRootRef?: string;
|
|
maxFiles?: number;
|
|
}
|
|
|
|
export interface MdtodoFileRecord {
|
|
sourceId: string;
|
|
fileRef: string;
|
|
projectId?: string;
|
|
relativePath?: string;
|
|
title?: string;
|
|
fingerprint?: string;
|
|
taskCount?: number;
|
|
parseError?: string | null;
|
|
revision?: number;
|
|
lastIndexedAt?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface MdtodoTaskRecord {
|
|
taskRef: string;
|
|
projectId?: string;
|
|
sourceId?: string;
|
|
fileRef?: string;
|
|
taskId?: string;
|
|
rxxId?: string;
|
|
title?: string;
|
|
status?: string;
|
|
parentTaskRef?: string | null;
|
|
depth?: number;
|
|
lineNumber?: number;
|
|
ordinal?: number;
|
|
linkCount?: number;
|
|
sourceFingerprint?: string;
|
|
bodyPreview?: string;
|
|
parserMode?: string;
|
|
links?: MdtodoTaskLinkRecord[];
|
|
task?: {
|
|
bodyPreview?: string | null;
|
|
links?: MdtodoTaskLinkRecord[];
|
|
linkCount?: number | null;
|
|
title?: string | null;
|
|
[key: string]: unknown;
|
|
};
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface MdtodoTaskLinkRecord {
|
|
linkId: string;
|
|
label?: string;
|
|
href?: string;
|
|
kind?: string;
|
|
relativePath?: string | null;
|
|
ordinal?: number;
|
|
availability?: string;
|
|
message?: string;
|
|
valuesRedacted?: boolean;
|
|
}
|
|
|
|
export interface MdtodoTaskDetailRecord {
|
|
taskRef: string;
|
|
taskId?: string;
|
|
rxxId?: string;
|
|
projectId?: string;
|
|
sourceId?: string;
|
|
fileRef?: string;
|
|
relativePath?: string;
|
|
title?: string;
|
|
status?: string;
|
|
lineNumber?: number;
|
|
headingLevel?: number;
|
|
statusMarker?: string | null;
|
|
body?: string;
|
|
bodyPreview?: string;
|
|
links?: MdtodoTaskLinkRecord[];
|
|
valuesRedacted?: boolean;
|
|
}
|
|
|
|
export interface MdtodoExecutionContext {
|
|
sourceId?: string | null;
|
|
sourceKind?: string | null;
|
|
projectId?: string | null;
|
|
taskRef?: string | null;
|
|
fileRef?: string | null;
|
|
relativePath?: string | null;
|
|
taskId?: string | null;
|
|
hwpodId?: string | null;
|
|
nodeId?: string | null;
|
|
workspaceRootRef?: string | null;
|
|
workspaceRootHash?: string | null;
|
|
workspaceRootLabel?: string | null;
|
|
mdtodoRootRef?: string | null;
|
|
hwpodWorkspaceArgs?: string | null;
|
|
contextFingerprint?: string | null;
|
|
capabilities?: Record<string, unknown>;
|
|
valuesRedacted?: boolean;
|
|
}
|
|
|
|
export interface MdtodoReportPreviewRecord {
|
|
sourceId?: string;
|
|
relativePath?: string;
|
|
content?: string;
|
|
fingerprint?: string;
|
|
revision?: string;
|
|
byteCount?: number;
|
|
render?: string;
|
|
linkId?: string;
|
|
label?: string;
|
|
href?: string;
|
|
valuesRedacted?: boolean;
|
|
}
|
|
|
|
export interface ProjectWorkbenchLinkRecord {
|
|
linkId: string;
|
|
projectId?: string;
|
|
taskRef?: string | null;
|
|
sessionId?: string | null;
|
|
traceId?: string | null;
|
|
createdBy?: string | null;
|
|
role?: string;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface ProjectListResponse extends ProjectManagementEnvelope { projects?: ProjectRecord[] }
|
|
export interface ProjectSourceListResponse extends ProjectManagementEnvelope { sources?: ProjectSource[] }
|
|
export interface ProjectSourceMutationResponse extends ProjectManagementEnvelope { source?: ProjectSource }
|
|
export interface ProjectSourceProbeResponse extends ProjectManagementEnvelope { probe?: { ok?: boolean; status?: string; sourceId?: string; sourceKind?: string; entries?: number; valuesRedacted?: boolean } }
|
|
export interface ProjectSourceReindexResponse extends ProjectManagementEnvelope { projection?: ProjectProjectionSummary & { sourceKind?: string; rootRef?: string; valuesRedacted?: boolean } }
|
|
export interface MdtodoFileListResponse extends ProjectManagementEnvelope { files?: MdtodoFileRecord[] }
|
|
export interface MdtodoTaskPage {
|
|
offset?: number;
|
|
limit?: number;
|
|
maxLimit?: number;
|
|
total?: number;
|
|
hasMore?: boolean;
|
|
}
|
|
export interface MdtodoTaskListResponse extends ProjectManagementEnvelope { tasks?: MdtodoTaskRecord[]; page?: MdtodoTaskPage }
|
|
export interface MdtodoTaskDetailResponse extends ProjectManagementEnvelope { task?: MdtodoTaskRecord; detail?: MdtodoTaskDetailRecord; file?: MdtodoFileRecord }
|
|
export interface MdtodoLaunchContextResponse extends ProjectManagementEnvelope { task?: MdtodoTaskRecord; source?: Partial<ProjectSource>; executionContext?: MdtodoExecutionContext; launchContext?: Record<string, unknown> }
|
|
export interface MdtodoReportPreviewResponse extends ProjectManagementEnvelope { task?: MdtodoTaskRecord; link?: MdtodoTaskLinkRecord; report?: MdtodoReportPreviewRecord; file?: MdtodoFileRecord }
|
|
export interface MdtodoTaskMutationInput {
|
|
sourceId?: string;
|
|
fileRef?: string;
|
|
parentTaskRef?: string;
|
|
afterTaskRef?: string;
|
|
expectedFingerprint?: string;
|
|
revision?: string;
|
|
title?: string;
|
|
status?: string;
|
|
body?: string;
|
|
}
|
|
export interface MdtodoTaskMutationResponse extends ProjectManagementEnvelope {
|
|
task?: MdtodoTaskRecord | null;
|
|
changedTaskId?: string | null;
|
|
affectedTaskIds?: string[];
|
|
file?: MdtodoFileRecord & { parserMode?: string; valuesRedacted?: boolean };
|
|
projection?: ProjectProjectionSummary;
|
|
}
|
|
export interface ProjectWorkbenchLinkListResponse extends ProjectManagementEnvelope { links?: ProjectWorkbenchLinkRecord[] }
|
|
|
|
export interface MdtodoTaskQuery {
|
|
sourceId?: string | null;
|
|
fileRef?: string | null;
|
|
projectId?: string | null;
|
|
parentTaskRef?: string | null;
|
|
status?: string | null;
|
|
search?: string | null;
|
|
limit?: number | null;
|
|
offset?: number | null;
|
|
}
|
|
|
|
export interface ProjectWorkbenchLinkQuery {
|
|
taskRef?: string | null;
|
|
projectId?: string | null;
|
|
sessionId?: string | null;
|
|
}
|
|
|
|
export const projectManagementAPI = {
|
|
navigation: (): Promise<ApiResult<ProjectNavigationResponse>> => fetchJson("/v1/project-management/navigation", { timeoutMs: 12000, timeoutName: "project navigation" }),
|
|
projects: (): Promise<ApiResult<ProjectListResponse>> => fetchJson("/v1/project-management/projects", { timeoutMs: 12000, timeoutName: "project list" }),
|
|
sources: (): Promise<ApiResult<ProjectSourceListResponse>> => fetchJson("/v1/project-management/mdtodo/sources", { timeoutMs: 12000, timeoutName: "project mdtodo sources" }),
|
|
createSource: (input: ProjectSourceInput): Promise<ApiResult<ProjectSourceMutationResponse>> => fetchJson("/v1/project-management/mdtodo/sources", { method: "POST", body: JSON.stringify(input), timeoutMs: 12000, timeoutName: "project mdtodo source create" }),
|
|
updateSource: (sourceId: string, input: ProjectSourceInput): Promise<ApiResult<ProjectSourceMutationResponse>> => fetchJson(`/v1/project-management/mdtodo/sources/${encodeURIComponent(sourceId)}`, { method: "PATCH", body: JSON.stringify(input), timeoutMs: 12000, timeoutName: "project mdtodo source update" }),
|
|
probeSource: (sourceId: string): Promise<ApiResult<ProjectSourceProbeResponse>> => fetchJson(`/v1/project-management/mdtodo/sources/${encodeURIComponent(sourceId)}/probe`, { method: "POST", body: JSON.stringify({}), timeoutMs: 12000, timeoutName: "project mdtodo source probe" }),
|
|
reindexSource: (sourceId: string): Promise<ApiResult<ProjectSourceReindexResponse>> => fetchJson(`/v1/project-management/mdtodo/sources/${encodeURIComponent(sourceId)}/reindex`, { method: "POST", body: JSON.stringify({}), timeoutMs: 12000, timeoutName: "project mdtodo source reindex" }),
|
|
files: (sourceId?: string | null): Promise<ApiResult<MdtodoFileListResponse>> => fetchJson(`/v1/project-management/mdtodo/files${queryString({ sourceId })}`, { timeoutMs: 12000, timeoutName: "project mdtodo files" }),
|
|
tasks: (query: MdtodoTaskQuery = {}): Promise<ApiResult<MdtodoTaskListResponse>> => fetchJson(`/v1/project-management/mdtodo/tasks${queryString({ sourceId: query.sourceId, fileRef: query.fileRef, projectId: query.projectId, parentTaskRef: query.parentTaskRef, status: query.status, search: query.search, limit: query.limit, offset: query.offset })}`, { timeoutMs: 12000, timeoutName: "project mdtodo tasks" }),
|
|
taskDetail: (taskRef: string): Promise<ApiResult<MdtodoTaskDetailResponse>> => fetchJson(`/v1/project-management/mdtodo/task-detail${queryString({ taskRef })}`, { timeoutMs: 12000, timeoutName: "project mdtodo task detail" }),
|
|
launchContext: (taskRef: string): Promise<ApiResult<MdtodoLaunchContextResponse>> => fetchJson(`/v1/project-management/mdtodo/launch-context${queryString({ taskRef })}`, { timeoutMs: 12000, timeoutName: "project mdtodo launch context" }),
|
|
reportPreview: (taskRef: string, linkId: string): Promise<ApiResult<MdtodoReportPreviewResponse>> => fetchJson(`/v1/project-management/mdtodo/report-preview${queryString({ taskRef, linkId })}`, { timeoutMs: 12000, timeoutName: "project mdtodo report preview" }),
|
|
updateTask: (taskRef: string, input: MdtodoTaskMutationInput): Promise<ApiResult<MdtodoTaskMutationResponse>> => fetchJson(`/v1/project-management/mdtodo/tasks/${encodeURIComponent(taskRef)}`, { method: "PATCH", body: JSON.stringify(input), timeoutMs: 12000, timeoutName: "project mdtodo task update" }),
|
|
createTask: (input: MdtodoTaskMutationInput): Promise<ApiResult<MdtodoTaskMutationResponse>> => fetchJson("/v1/project-management/mdtodo/tasks", { method: "POST", body: JSON.stringify(input), timeoutMs: 12000, timeoutName: "project mdtodo task create" }),
|
|
deleteTask: (taskRef: string, input: MdtodoTaskMutationInput): Promise<ApiResult<MdtodoTaskMutationResponse>> => fetchJson(`/v1/project-management/mdtodo/tasks/${encodeURIComponent(taskRef)}`, { method: "DELETE", body: JSON.stringify(input), timeoutMs: 12000, timeoutName: "project mdtodo task delete" }),
|
|
workbenchLinks: (query: ProjectWorkbenchLinkQuery = {}): Promise<ApiResult<ProjectWorkbenchLinkListResponse>> => fetchJson(`/v1/project-management/workbench-links${queryString({ taskRef: query.taskRef, projectId: query.projectId, sessionId: query.sessionId })}`, { timeoutMs: 12000, timeoutName: "project workbench links" })
|
|
};
|
|
|
|
function queryString(values: Record<string, string | number | null | undefined>): string {
|
|
const params = new URLSearchParams();
|
|
for (const [key, value] of Object.entries(values)) {
|
|
if (value !== null && value !== undefined && value !== "") params.set(key, String(value));
|
|
}
|
|
const text = params.toString();
|
|
return text ? `?${text}` : "";
|
|
}
|