feat(workspace-config): project grant-bound logical settings through WIP
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-companion-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:47064776569f7f208533f24d56ac80c0db6055d508ab29fb0a780c2c66387081)
|
||||
// Input contract: 0.1.0 (sha256:6001e9f7273288195fb5a9859e4a5932a521d2f2e7c3a9ecd8ec660fd6a37779)
|
||||
// Options: companion-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/companion-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_companion_api_types -- --check
|
||||
|
||||
@@ -269,6 +269,107 @@ export type ConfigCommitRequest = {
|
||||
entrypoints: Array<string>;
|
||||
};
|
||||
|
||||
export type WorkspaceConfigAccess = "read_only" | "read_write";
|
||||
|
||||
export type WorkspaceConfigNodeKind = "directory" | "file" | "missing";
|
||||
|
||||
export type WorkspaceConfigFailureClassification = "not_committed" | "unknown";
|
||||
|
||||
export type WorkspaceConfigAttachRequest = {
|
||||
alias: string | null;
|
||||
access: WorkspaceConfigAccess | null;
|
||||
};
|
||||
|
||||
export type WorkspaceConfigAttachment = {
|
||||
workspace_id: string;
|
||||
connection_id: string;
|
||||
alias: string;
|
||||
working_directory_id: string;
|
||||
access: WorkspaceConfigAccess;
|
||||
name: string;
|
||||
purpose: string;
|
||||
content_path: string;
|
||||
already_attached: boolean;
|
||||
};
|
||||
|
||||
export type WorkspaceConfigCurrentResponse = WorkspaceConfigAttachment | null;
|
||||
|
||||
export type WorkspaceConfigObserveRequest = {
|
||||
connection_id: string;
|
||||
paths: Array<string>;
|
||||
depth: number;
|
||||
};
|
||||
|
||||
export type WorkspaceConfigNode = {
|
||||
path: string;
|
||||
kind: WorkspaceConfigNodeKind;
|
||||
validator: string;
|
||||
digest: string | null;
|
||||
content_type: ConfigContentType | null;
|
||||
operations: Array<string>;
|
||||
};
|
||||
|
||||
export type WorkspaceConfigObserveResponse = {
|
||||
connection_id: string;
|
||||
validator: string;
|
||||
revision: number;
|
||||
digest: string;
|
||||
/**
|
||||
* Canonical commit entrypoints from this exact metadata snapshot.
|
||||
*/
|
||||
entrypoints: Array<string>;
|
||||
nodes: Array<WorkspaceConfigNode>;
|
||||
};
|
||||
|
||||
export type WorkspaceConfigReadRequest = {
|
||||
connection_id: string;
|
||||
path: string;
|
||||
validator: string;
|
||||
};
|
||||
|
||||
export type WorkspaceConfigReadResponse = {
|
||||
path: string;
|
||||
content: string;
|
||||
content_type: ConfigContentType;
|
||||
digest: string;
|
||||
validator: string;
|
||||
};
|
||||
|
||||
export type WorkspaceConfigCommitRequest = {
|
||||
connection_id: string;
|
||||
validator: string;
|
||||
request: ConfigCommitRequest;
|
||||
};
|
||||
|
||||
export type WorkspaceConfigCommitResponse = {
|
||||
validator: string;
|
||||
revision: number;
|
||||
digest: string;
|
||||
};
|
||||
|
||||
export type WorkspaceConfigApiError = {
|
||||
status: number;
|
||||
code: string;
|
||||
message: string;
|
||||
classification: WorkspaceConfigFailureClassification;
|
||||
};
|
||||
|
||||
export type WorkspaceConfigGrantCreateRequest = {
|
||||
runtime_id: string;
|
||||
worker_id: string;
|
||||
access: WorkspaceConfigAccess;
|
||||
};
|
||||
|
||||
export type WorkspaceConfigGrantResponse = {
|
||||
grant_id: string;
|
||||
workspace_id: string;
|
||||
runtime_id: string;
|
||||
worker_id: string;
|
||||
working_directory_id: string;
|
||||
access: WorkspaceConfigAccess;
|
||||
revoked: boolean;
|
||||
};
|
||||
|
||||
export type ProfileSettingsResponse = {
|
||||
workspace_id: string;
|
||||
registry_revision: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-repository-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:47064776569f7f208533f24d56ac80c0db6055d508ab29fb0a780c2c66387081)
|
||||
// Input contract: 0.1.0 (sha256:6001e9f7273288195fb5a9859e4a5932a521d2f2e7c3a9ecd8ec660fd6a37779)
|
||||
// Options: repository-operation-schema-closure,openapi-3.1,format:uint64=safe-number,strict-safe-integers,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/repository-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_repository_openapi_types -- --check
|
||||
@@ -79,12 +79,14 @@ export type WorkerWorkspaceSummary = { identity: string; visibility: string; wor
|
||||
|
||||
export type WorkingDirectoryCleanupTarget = { kind: string; repository_key: string; working_directory_id: string; };
|
||||
|
||||
export type WorkingDirectoryMaterializerKind = "runtime_git_clone" | "client_hosted_external";
|
||||
export type WorkingDirectoryMaterializerKind = "logical_workspace_config" | "runtime_git_clone" | "client_hosted_external";
|
||||
|
||||
export type WorkingDirectoryOccupancy = { display_name: string; linked_at: string; runtime_id: string; worker_id: string; };
|
||||
|
||||
export type WorkingDirectorySource = { kind: "repository"; repository_key: string; } | { grant_id: string; grant_permissions: ExternalWorkdirPermissions; kind: "external_grant"; };
|
||||
export type WorkingDirectorySource = { access: WorkspaceConfigAccess; content_path: string; kind: "workspace_config"; purpose: string; } | { kind: "repository"; repository_key: string; } | { grant_id: string; grant_permissions: ExternalWorkdirPermissions; kind: "external_grant"; };
|
||||
|
||||
export type WorkingDirectoryStatusKind = "active" | "cleanup_pending" | "corrupted" | "not_found" | "unknown";
|
||||
|
||||
export type WorkingDirectorySummary = { cleanliness?: string | null; cleanup_target?: WorkingDirectoryCleanupTarget | null; creation_ref?: string | null; creation_selector?: string | null; creation_tree?: string | null; current_ref?: string | null; current_selector?: string | null; current_tree?: string | null; display_name?: string | null; materializer_kind: WorkingDirectoryMaterializerKind; observed_at_epoch_seconds?: number | null; occupied_by?: WorkingDirectoryOccupancy | null; source: WorkingDirectorySource; status: WorkingDirectoryStatusKind; working_directory_id: string; };
|
||||
|
||||
export type WorkspaceConfigAccess = "read_only" | "read_write";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-runtime-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:47064776569f7f208533f24d56ac80c0db6055d508ab29fb0a780c2c66387081)
|
||||
// Input contract: 0.1.0 (sha256:6001e9f7273288195fb5a9859e4a5932a521d2f2e7c3a9ecd8ec660fd6a37779)
|
||||
// Options: runtime-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/runtime-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_runtime_api_types -- --check
|
||||
@@ -203,16 +203,18 @@ export type WorkerWorkspaceSummary = { identity: string; visibility: string; wor
|
||||
|
||||
export type WorkingDirectoryCleanupTarget = { kind: string; repository_key: string; working_directory_id: string; };
|
||||
|
||||
export type WorkingDirectoryMaterializerKind = "runtime_git_clone" | "client_hosted_external";
|
||||
export type WorkingDirectoryMaterializerKind = "logical_workspace_config" | "runtime_git_clone" | "client_hosted_external";
|
||||
|
||||
export type WorkingDirectoryOccupancy = { display_name: string; linked_at: string; runtime_id: string; worker_id: string; };
|
||||
|
||||
export type WorkingDirectorySource = { kind: "repository"; repository_key: string; } | { grant_id: string; grant_permissions: ExternalWorkdirPermissions; kind: "external_grant"; };
|
||||
export type WorkingDirectorySource = { access: WorkspaceConfigAccess; content_path: string; kind: "workspace_config"; purpose: string; } | { kind: "repository"; repository_key: string; } | { grant_id: string; grant_permissions: ExternalWorkdirPermissions; kind: "external_grant"; };
|
||||
|
||||
export type WorkingDirectoryStatusKind = "active" | "cleanup_pending" | "corrupted" | "not_found" | "unknown";
|
||||
|
||||
export type WorkingDirectorySummary = { cleanliness?: string | null; cleanup_target?: WorkingDirectoryCleanupTarget | null; creation_ref?: string | null; creation_selector?: string | null; creation_tree?: string | null; current_ref?: string | null; current_selector?: string | null; current_tree?: string | null; display_name?: string | null; materializer_kind: WorkingDirectoryMaterializerKind; observed_at_epoch_seconds?: number | null; occupied_by?: WorkingDirectoryOccupancy | null; source: WorkingDirectorySource; status: WorkingDirectoryStatusKind; working_directory_id: string; };
|
||||
|
||||
export type WorkspaceConfigAccess = "read_only" | "read_write";
|
||||
|
||||
export type WorkspaceRuntimeBindingState = "configured" | "verified" | "revoked";
|
||||
|
||||
export type WorkspaceRuntimeBindingSummary = { connection_state: RuntimeConnectionDisplayState; revision: number; state: WorkspaceRuntimeBindingState; verification?: RuntimeVerificationEvidenceSummary | null; workspace_key_generation?: number | null; workspace_key_id?: string | null; };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-ticket-browser-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:47064776569f7f208533f24d56ac80c0db6055d508ab29fb0a780c2c66387081)
|
||||
// Input contract: 0.1.0 (sha256:6001e9f7273288195fb5a9859e4a5932a521d2f2e7c3a9ecd8ec660fd6a37779)
|
||||
// Options: ticket-objective-merge-request-browser-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/ticket-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_ticket_api_types -- --check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-workdir-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:47064776569f7f208533f24d56ac80c0db6055d508ab29fb0a780c2c66387081)
|
||||
// Input contract: 0.1.0 (sha256:6001e9f7273288195fb5a9859e4a5932a521d2f2e7c3a9ecd8ec660fd6a37779)
|
||||
// Options: workdir-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/workdir-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_workdir_api_types -- --check
|
||||
@@ -27,7 +27,7 @@ export type WorkingDirectoryDetailResponse = { diagnostics: Array<Diagnostic>; i
|
||||
|
||||
export type WorkingDirectoryListResponse = { diagnostics: Array<Diagnostic>; items: Array<WorkingDirectorySummary>; workspace_id: string; };
|
||||
|
||||
export type WorkingDirectoryMaterializerKind = "runtime_git_clone" | "client_hosted_external";
|
||||
export type WorkingDirectoryMaterializerKind = "logical_workspace_config" | "runtime_git_clone" | "client_hosted_external";
|
||||
|
||||
export type WorkingDirectoryOccupancy = { display_name: string; linked_at: string; runtime_id: string; worker_id: string; };
|
||||
|
||||
@@ -37,8 +37,10 @@ export type WorkingDirectoryRemovalRequest = { reason: string; };
|
||||
|
||||
export type WorkingDirectoryRemovalResponse = { disposition: WorkingDirectoryRemovalDisposition; failure_category?: string | null; retryable: boolean; working_directory_id: string; };
|
||||
|
||||
export type WorkingDirectorySource = { kind: "repository"; repository_key: string; } | { grant_id: string; grant_permissions: ExternalWorkdirPermissions; kind: "external_grant"; };
|
||||
export type WorkingDirectorySource = { access: WorkspaceConfigAccess; content_path: string; kind: "workspace_config"; purpose: string; } | { kind: "repository"; repository_key: string; } | { grant_id: string; grant_permissions: ExternalWorkdirPermissions; kind: "external_grant"; };
|
||||
|
||||
export type WorkingDirectoryStatusKind = "active" | "cleanup_pending" | "corrupted" | "not_found" | "unknown";
|
||||
|
||||
export type WorkingDirectorySummary = { cleanliness?: string | null; cleanup_target?: WorkingDirectoryCleanupTarget | null; creation_ref?: string | null; creation_selector?: string | null; creation_tree?: string | null; current_ref?: string | null; current_selector?: string | null; current_tree?: string | null; display_name?: string | null; materializer_kind: WorkingDirectoryMaterializerKind; observed_at_epoch_seconds?: number | null; occupied_by?: WorkingDirectoryOccupancy | null; source: WorkingDirectorySource; status: WorkingDirectoryStatusKind; working_directory_id: string; };
|
||||
|
||||
export type WorkspaceConfigAccess = "read_only" | "read_write";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-worker-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:47064776569f7f208533f24d56ac80c0db6055d508ab29fb0a780c2c66387081)
|
||||
// Input contract: 0.1.0 (sha256:6001e9f7273288195fb5a9859e4a5932a521d2f2e7c3a9ecd8ec660fd6a37779)
|
||||
// Options: worker-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/worker-launch-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_worker_launch_api_types -- --check
|
||||
@@ -85,16 +85,18 @@ export type WorkerWorkspaceSummary = { identity: string; visibility: string; wor
|
||||
|
||||
export type WorkingDirectoryCleanupTarget = { kind: string; repository_key: string; working_directory_id: string; };
|
||||
|
||||
export type WorkingDirectoryMaterializerKind = "runtime_git_clone" | "client_hosted_external";
|
||||
export type WorkingDirectoryMaterializerKind = "logical_workspace_config" | "runtime_git_clone" | "client_hosted_external";
|
||||
|
||||
export type WorkingDirectoryOccupancy = { display_name: string; linked_at: string; runtime_id: string; worker_id: string; };
|
||||
|
||||
export type WorkingDirectoryRepositoryOption = { default_selector?: string | null; repository_key: string; };
|
||||
|
||||
export type WorkingDirectorySource = { kind: "repository"; repository_key: string; } | { grant_id: string; grant_permissions: ExternalWorkdirPermissions; kind: "external_grant"; };
|
||||
export type WorkingDirectorySource = { access: WorkspaceConfigAccess; content_path: string; kind: "workspace_config"; purpose: string; } | { kind: "repository"; repository_key: string; } | { grant_id: string; grant_permissions: ExternalWorkdirPermissions; kind: "external_grant"; };
|
||||
|
||||
export type WorkingDirectoryStatusKind = "active" | "cleanup_pending" | "corrupted" | "not_found" | "unknown";
|
||||
|
||||
export type WorkingDirectorySummary = { cleanliness?: string | null; cleanup_target?: WorkingDirectoryCleanupTarget | null; creation_ref?: string | null; creation_selector?: string | null; creation_tree?: string | null; current_ref?: string | null; current_selector?: string | null; current_tree?: string | null; display_name?: string | null; materializer_kind: WorkingDirectoryMaterializerKind; observed_at_epoch_seconds?: number | null; occupied_by?: WorkingDirectoryOccupancy | null; source: WorkingDirectorySource; status: WorkingDirectoryStatusKind; working_directory_id: string; };
|
||||
|
||||
export type WorkspaceConfigAccess = "read_only" | "read_write";
|
||||
|
||||
export type WorkspaceWorkerFeatureConnectionsRequest = { subjektiv?: SubjektivWorkerConnectionRequest | null; };
|
||||
|
||||
@@ -131,6 +131,7 @@ export function parseWorkingDirectorySummary(
|
||||
materializer_kind: enumField(record, "materializer_kind", [
|
||||
"runtime_git_clone",
|
||||
"client_hosted_external",
|
||||
"logical_workspace_config",
|
||||
]),
|
||||
status: enumField(record, "status", [
|
||||
"active",
|
||||
@@ -177,10 +178,27 @@ export function parseWorkingDirectorySummary(
|
||||
function parseWorkingDirectorySource(value: unknown): WorkingDirectorySource {
|
||||
const source = exactRecord(
|
||||
value,
|
||||
new Set(["kind", "repository_key", "grant_id", "grant_permissions"]),
|
||||
new Set(["kind", "repository_key", "grant_id", "grant_permissions", "access", "content_path", "purpose"]),
|
||||
"Workdir source",
|
||||
);
|
||||
const kind = stringField(source, "kind");
|
||||
if (kind === "workspace_config") {
|
||||
if (source.repository_key !== undefined || source.grant_id !== undefined || source.grant_permissions !== undefined) {
|
||||
throw new Error("Logical config source must not contain repository or External authority");
|
||||
}
|
||||
const access = stringField(source, "access");
|
||||
if (access !== "read_only" && access !== "read_write") {
|
||||
throw new Error("Logical config access has an unsupported value");
|
||||
}
|
||||
const content_path = stringField(source, "content_path");
|
||||
if (content_path !== "/workspace-config") {
|
||||
throw new Error("Logical config must reference its WIP content entrance");
|
||||
}
|
||||
return { kind, access, content_path, purpose: stringField(source, "purpose") };
|
||||
}
|
||||
if (source.access !== undefined || source.content_path !== undefined || source.purpose !== undefined) {
|
||||
throw new Error("Filesystem Workdir source must not contain logical config metadata");
|
||||
}
|
||||
if (kind === "repository") {
|
||||
if (
|
||||
source.grant_id !== undefined || source.grant_permissions !== undefined
|
||||
|
||||
@@ -363,6 +363,72 @@ test("restored invocation chips fetch declarative descriptors and stay editable
|
||||
expect(snapshot.segments[0].kind).toBe("feature_invoke");
|
||||
});
|
||||
|
||||
// Fixture of the Workspace config Feature declaration, consumed by the same
|
||||
// generic composer as other Features. Backend/WIP acceptance is tested in Rust;
|
||||
// this test proves the real input component's selection and typed-submit edge.
|
||||
const workspaceConfigDescriptor: FeatureInvocationDescriptor = {
|
||||
identity: "builtin:workspace-config/attach", name: "workspace-config", aliases: [],
|
||||
display_name: "Workspace config", description: "Select the logical Workspace config attachment",
|
||||
syntax: "parenthesized", arguments: [{ name: "access", position: 0, required: false,
|
||||
value_type: { kind: "enum", values: ["effective", "read_only", "read_write"] },
|
||||
completion: { kind: "none" } }],
|
||||
};
|
||||
|
||||
test("workspace-config completion prepares a typed invocation without consuming the request prose", async () => {
|
||||
const onsubmit = vi.fn();
|
||||
const resolveFeatureCompletions = vi.fn(async () => [
|
||||
{ value: workspaceConfigDescriptor.name, invocation: workspaceConfigDescriptor },
|
||||
]);
|
||||
const ui = render(ComposerInput, { historyScope: "workspace-config-selected", onsubmit,
|
||||
resolveFeatureCompletions });
|
||||
const cm = editor(ui.container);
|
||||
type(cm, "/workspace-con");
|
||||
await ui.findByRole("option");
|
||||
await fireEvent.keyDown(cm.contentDOM, { key: "Enter" });
|
||||
expect(onsubmit).not.toHaveBeenCalled();
|
||||
const suffix = ') このWorkspaceのモデル設定を変更して';
|
||||
cm.dispatch({ changes: { from: cm.state.doc.length, insert: suffix },
|
||||
selection: { anchor: cm.state.doc.length + suffix.length } });
|
||||
await waitFor(() => expect(ui.container.querySelectorAll(".composer-typed-chip")).toHaveLength(1));
|
||||
const snapshot = ui.component.snapshot();
|
||||
expect(snapshot.segments).toEqual([
|
||||
{ kind: "feature_invoke", invocation: {
|
||||
invocation_id: expect.any(String), identity: workspaceConfigDescriptor.identity,
|
||||
name: workspaceConfigDescriptor.name, arguments: [],
|
||||
} },
|
||||
{ kind: "text", content: " このWorkspaceのモデル設定を変更して" },
|
||||
]);
|
||||
await fireEvent.keyDown(cm.contentDOM, { key: "Enter", ctrlKey: true });
|
||||
expect(onsubmit).toHaveBeenCalledOnce();
|
||||
// Sending is not string scanning: identity and arguments survive intact.
|
||||
expect(ui.component.snapshot().segments).toEqual(snapshot.segments);
|
||||
});
|
||||
|
||||
test("workspace-config text alone is not invoked and a selected intent is removable", async () => {
|
||||
const ui = render(ComposerInput, { historyScope: "workspace-config-literal",
|
||||
resolveFeatureCompletions: async () => [{ value: workspaceConfigDescriptor.name, invocation: workspaceConfigDescriptor }] });
|
||||
const cm = editor(ui.container);
|
||||
const prose = "/workspace-config このWorkspaceのモデル設定を変更して";
|
||||
ui.component.restoreSegments([{ kind: "text", content: prose }]);
|
||||
expect(ui.component.snapshot().segments).toEqual([{ kind: "text", content: prose }]);
|
||||
ui.component.clear();
|
||||
type(cm, "/workspace-con");
|
||||
await ui.findByRole("option");
|
||||
await fireEvent.keyDown(cm.contentDOM, { key: "Enter" });
|
||||
cm.dispatch({ changes: { from: cm.state.doc.length, insert: '"read_only")' },
|
||||
selection: { anchor: cm.state.doc.length + '"read_only")'.length } });
|
||||
await waitFor(() => expect(ui.container.querySelectorAll(".composer-typed-chip")).toHaveLength(1));
|
||||
expect(ui.component.snapshot().segments[0]).toMatchObject({ kind: "feature_invoke", invocation: {
|
||||
arguments: [{ name: "access", value: { kind: "string", value: "read_only" } }],
|
||||
} });
|
||||
// happy-dom's focused DOM selection can reenter CodeMirror during range
|
||||
// replacement. Use the existing component suite's unfocused range fixture.
|
||||
cm.contentDOM.blur();
|
||||
cm.dispatch({ selection: { anchor: 0, head: cm.state.doc.length } });
|
||||
await fireEvent.keyDown(cm.contentDOM, { key: "Backspace" });
|
||||
expect(ui.component.snapshot().segments).toEqual([]);
|
||||
});
|
||||
|
||||
test("attachment adapter uses descriptor capability rather than a hardcoded slash name", async () => {
|
||||
const onclientadapter = vi.fn();
|
||||
const adapter = { ...invocationDescriptor, name: "send-file", arguments: [], client_adapter: "attachment" as const };
|
||||
|
||||
@@ -9,6 +9,18 @@ declare const Deno: {
|
||||
test(name: string, fn: () => Promise<void> | void): void;
|
||||
};
|
||||
|
||||
Deno.test("logical config attachments are never offered as process working directories", () => {
|
||||
const config = { working_directory_id: "config-1", display_name: "Workspace config",
|
||||
source: { kind: "workspace_config" as const, access: "read_write" as const, content_path: "/workspace-config", purpose: "Configuration" },
|
||||
materializer_kind: "logical_workspace_config" as const, status: "active" as const, cleanliness: "clean" };
|
||||
const form = defaultWorkerLaunchForm({ ...options, working_directories: [config] }, emptyForm({
|
||||
workdir_attachments: [{ alias: "config", working_directory_id: "config-1", relative_cwd: "" }],
|
||||
}));
|
||||
if (form.workdir_attachments.some((attachment) => attachment.working_directory_id === "config-1")) {
|
||||
throw new Error("logical config was converted to a process cwd attachment");
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("defaultWorkerLaunchForm uses the Backend-published defaults and initial Workdir", () => {
|
||||
const form = defaultWorkerLaunchForm(
|
||||
options,
|
||||
|
||||
@@ -41,6 +41,7 @@ export function defaultWorkerLaunchForm(
|
||||
const availableWorkingDirectories =
|
||||
options?.working_directories.filter((directory) =>
|
||||
directory.status === "active" &&
|
||||
directory.source.kind !== "workspace_config" &&
|
||||
(directory.source.kind === "external_grant" ||
|
||||
directory.cleanliness === "clean") &&
|
||||
directory.occupied_by == null
|
||||
|
||||
@@ -47,6 +47,18 @@ function attachment(
|
||||
};
|
||||
}
|
||||
|
||||
Deno.test("logical config sidebar label is not fabricated as a repository revision", () => {
|
||||
const meta = sidebarWorkdirMeta([attachment("workspace-config", {
|
||||
display_name: "Workspace configuration",
|
||||
source: { kind: "workspace_config", access: "read_write", content_path: "/workspace-config", purpose: "Configuration" },
|
||||
materializer_kind: "logical_workspace_config",
|
||||
})]);
|
||||
assertEquals(meta.text, "Workspace configuration");
|
||||
assert(!meta.text.includes("unknown-repo"), "config must not be labeled as a repository");
|
||||
assert(!meta.text.includes("editable"), "catalog grant ceiling must not claim effective connection editability");
|
||||
assert(meta.details.includes("workspace-config — Workspace configuration"), "logical alias remains visible");
|
||||
});
|
||||
|
||||
Deno.test("Worker sidebar Workdir label prefers the current branch selector", () => {
|
||||
const meta = sidebarWorkdirMeta([
|
||||
attachment("checkout", {
|
||||
|
||||
@@ -27,6 +27,11 @@ export function sidebarWorkdirMeta(
|
||||
|
||||
function attachmentLabel(attachment: SidebarWorkdirAttachment): string {
|
||||
const workdir = attachment.working_directory;
|
||||
if (workdir?.source.kind === "workspace_config") {
|
||||
// Catalog access is the grant ceiling, not this connection's effective
|
||||
// restriction. The invocation result reports effective attachment access.
|
||||
return clean(workdir.display_name) ?? "Workspace config";
|
||||
}
|
||||
if (workdir?.source.kind === "external_grant") {
|
||||
return clean(workdir.display_name) ?? "external";
|
||||
}
|
||||
|
||||
@@ -23,6 +23,26 @@ const summary = {
|
||||
},
|
||||
};
|
||||
|
||||
Deno.test("logical config Workdir source preserves WIP metadata without filesystem authority", () => {
|
||||
const source = { kind: "workspace_config", access: "read_only", content_path: "/workspace-config", purpose: "Workspace configuration" };
|
||||
const response = (source: unknown) => ({ workspace_id: "workspace-a", item: {
|
||||
working_directory_id: "config-1", source, materializer_kind: "logical_workspace_config", status: "active",
|
||||
}, diagnostics: [] });
|
||||
const detail = parseWorkingDirectoryDetailResponse(response(source));
|
||||
if (JSON.stringify(detail.item.source) !== JSON.stringify(source) || detail.runtime_id !== undefined) {
|
||||
throw new Error("logical config identity was lost or acquired a fake Runtime");
|
||||
}
|
||||
for (const invalid of [
|
||||
{ ...source, access: "command" }, { ...source, content_path: "/home/config" },
|
||||
{ ...source, grant_id: "external-grant" }, { ...source, repository_key: "main" },
|
||||
{ ...source, token: "secret" }, { kind: "repository", repository_key: "main", access: "read_write" },
|
||||
]) {
|
||||
let rejected = false;
|
||||
try { parseWorkingDirectoryDetailResponse(response(invalid)); } catch { rejected = true; }
|
||||
if (!rejected) throw new Error("mixed or unsafe source metadata was accepted");
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("Workdir REST validation accepts the generated list and create contracts", () => {
|
||||
const list = parseWorkingDirectoryListResponse({
|
||||
workspace_id: "workspace-a",
|
||||
|
||||
Reference in New Issue
Block a user