feat(settings): import existing project setup

This commit is contained in:
Mohamed Boudra
2026-07-17 11:15:10 +02:00
parent a8ebd390fa
commit 8fe11e3bb0
43 changed files with 3464 additions and 48 deletions
+90
View File
@@ -3109,6 +3109,96 @@ test("writes project config via correlated RPC and returns inline failures", asy
});
});
test("previews and applies project config import via correlated RPC", async () => {
const logger = createMockLogger();
const mock = createMockTransport();
const client = new DaemonClient({
url: "ws://test",
clientId: "clsk_unit_test",
logger,
reconnect: { enabled: false },
transportFactory: () => mock.transport,
});
clients.push(client);
const connectPromise = client.connect();
mock.triggerOpen();
await connectPromise;
const previewPromise = client.getProjectConfigImport({
requestId: "get-import-1",
repoRoot: "/repo/app",
source: { kind: "conductor" },
});
expect(parseSentFrame(mock.sent[0])).toEqual({
type: "project.config.get_import.request",
requestId: "get-import-1",
repoRoot: "/repo/app",
source: { kind: "conductor" },
});
mock.triggerMessage(
wrapSessionMessage({
type: "project.config.get_import.response",
payload: {
requestId: "get-import-1",
repoRoot: "/repo/app",
source: { kind: "conductor" },
ok: true,
status: "available",
sourceRevision: "source-1",
paseoRevision: null,
inputs: [{ role: "shared", relativePath: ".conductor/settings.toml" }],
items: [{ key: "worktree.setup", label: "Setup", outcome: "import" }],
preview: { worktree: { setup: "npm ci" } },
},
}),
);
await expect(previewPromise).resolves.toMatchObject({
ok: true,
sourceRevision: "source-1",
preview: { worktree: { setup: "npm ci" } },
});
const applyPromise = client.applyProjectConfigImport({
requestId: "apply-import-1",
repoRoot: "/repo/app",
source: { kind: "conductor" },
expectedSourceRevision: "source-1",
expectedPaseoRevision: null,
});
expect(parseSentFrame(mock.sent[1])).toEqual({
type: "project.config.apply_import.request",
requestId: "apply-import-1",
repoRoot: "/repo/app",
source: { kind: "conductor" },
expectedSourceRevision: "source-1",
expectedPaseoRevision: null,
});
mock.triggerMessage(
wrapSessionMessage({
type: "project.config.apply_import.response",
payload: {
requestId: "apply-import-1",
repoRoot: "/repo/app",
ok: false,
error: { code: "stale_source_config", source: { kind: "conductor" } },
},
}),
);
await expect(applyPromise).resolves.toEqual({
requestId: "apply-import-1",
repoRoot: "/repo/app",
ok: false,
error: { code: "stale_source_config", source: { kind: "conductor" } },
});
});
test("requests directory suggestions via RPC", async () => {
const logger = createMockLogger();
const mock = createMockTransport();
+51
View File
@@ -94,6 +94,7 @@ import type {
SendAgentMessageRequest,
PaseoConfigRaw,
PaseoConfigRevision,
ProjectConfigImportSource,
WorkspaceCreateRequest,
WorkspaceRecoveryState,
} from "@getpaseo/protocol/messages";
@@ -430,6 +431,14 @@ type WriteProjectConfigPayload = Extract<
SessionOutboundMessage,
{ type: "write_project_config_response" }
>["payload"];
type GetProjectConfigImportPayload = Extract<
SessionOutboundMessage,
{ type: "project.config.get_import.response" }
>["payload"];
type ApplyProjectConfigImportPayload = Extract<
SessionOutboundMessage,
{ type: "project.config.apply_import.response" }
>["payload"];
type ListCommandsPayload = ListCommandsResponse["payload"];
type ListCommandsDraftConfig = Pick<
@@ -442,6 +451,18 @@ export interface WriteProjectConfigInput {
expectedRevision: PaseoConfigRevision | null;
requestId?: string;
}
export interface GetProjectConfigImportInput {
repoRoot: string;
source: ProjectConfigImportSource;
requestId?: string;
}
export interface ApplyProjectConfigImportInput {
repoRoot: string;
source: ProjectConfigImportSource;
expectedSourceRevision: string;
expectedPaseoRevision: PaseoConfigRevision | null;
requestId?: string;
}
interface ListCommandsOptions {
agentId: string;
requestId?: string;
@@ -4266,6 +4287,36 @@ export class DaemonClient {
});
}
async getProjectConfigImport(
input: GetProjectConfigImportInput,
): Promise<GetProjectConfigImportPayload> {
return this.sendCorrelatedSessionRequest({
requestId: input.requestId,
message: {
type: "project.config.get_import.request",
repoRoot: input.repoRoot,
source: input.source,
},
responseType: "project.config.get_import.response",
});
}
async applyProjectConfigImport(
input: ApplyProjectConfigImportInput,
): Promise<ApplyProjectConfigImportPayload> {
return this.sendCorrelatedSessionRequest({
requestId: input.requestId,
message: {
type: "project.config.apply_import.request",
repoRoot: input.repoRoot,
source: input.source,
expectedSourceRevision: input.expectedSourceRevision,
expectedPaseoRevision: input.expectedPaseoRevision,
},
responseType: "project.config.apply_import.response",
});
}
async refreshProvidersSnapshot(options?: {
cwd?: string;
providers?: AgentProvider[];