@cargo-ai/cdk 1.0.10 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/build/src/cli/commands/deploy.d.ts.map +1 -1
  2. package/build/src/cli/commands/deploy.js +105 -1
  3. package/build/src/cli/commands/inputTypes.d.ts +7 -0
  4. package/build/src/cli/commands/inputTypes.d.ts.map +1 -1
  5. package/build/src/cli/commands/inputTypes.js +23 -0
  6. package/build/src/cli/commands/types.d.ts.map +1 -1
  7. package/build/src/cli/commands/types.js +43 -2
  8. package/build/src/deploy/executors.live.d.ts.map +1 -1
  9. package/build/src/deploy/executors.live.js +56 -2
  10. package/build/src/deploy/import.d.ts +12 -0
  11. package/build/src/deploy/import.d.ts.map +1 -1
  12. package/build/src/deploy/import.js +79 -9
  13. package/build/src/deploy/index.d.ts +2 -0
  14. package/build/src/deploy/index.d.ts.map +1 -1
  15. package/build/src/deploy/index.js +1 -0
  16. package/build/src/deploy/pull/enumerate.d.ts +8 -0
  17. package/build/src/deploy/pull/enumerate.d.ts.map +1 -0
  18. package/build/src/deploy/pull/enumerate.js +258 -0
  19. package/build/src/deploy/pull/index.d.ts +28 -0
  20. package/build/src/deploy/pull/index.d.ts.map +1 -0
  21. package/build/src/deploy/pull/index.js +152 -0
  22. package/build/src/deploy/pull/ir.d.ts +45 -0
  23. package/build/src/deploy/pull/ir.d.ts.map +1 -0
  24. package/build/src/deploy/pull/ir.js +72 -0
  25. package/build/src/deploy/pull/mappers.d.ts +10 -0
  26. package/build/src/deploy/pull/mappers.d.ts.map +1 -0
  27. package/build/src/deploy/pull/mappers.js +689 -0
  28. package/build/src/deploy/pull/print.d.ts +9 -0
  29. package/build/src/deploy/pull/print.d.ts.map +1 -0
  30. package/build/src/deploy/pull/print.js +59 -0
  31. package/build/src/deploy/pull/resolve.d.ts +24 -0
  32. package/build/src/deploy/pull/resolve.d.ts.map +1 -0
  33. package/build/src/deploy/pull/resolve.js +113 -0
  34. package/build/src/deploy/pull/untar.d.ts +6 -0
  35. package/build/src/deploy/pull/untar.d.ts.map +1 -0
  36. package/build/src/deploy/pull/untar.js +84 -0
  37. package/build/src/deploy/readers.live.d.ts +3 -0
  38. package/build/src/deploy/readers.live.d.ts.map +1 -1
  39. package/build/src/deploy/readers.live.js +12 -6
  40. package/build/src/index.d.ts +3 -3
  41. package/build/src/index.d.ts.map +1 -1
  42. package/build/src/index.js +1 -1
  43. package/build/src/refs.d.ts +7 -1
  44. package/build/src/refs.d.ts.map +1 -1
  45. package/build/src/refs.js +10 -0
  46. package/build/src/resources/customIntegration.d.ts +3 -2
  47. package/build/src/resources/customIntegration.d.ts.map +1 -1
  48. package/build/src/resources/model.d.ts +67 -12
  49. package/build/src/resources/model.d.ts.map +1 -1
  50. package/build/src/resources/model.js +48 -1
  51. package/build/tsconfig.tsbuildinfo +1 -1
  52. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2CzC,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,GAChB,IAAI,CA2lBN"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+CzC,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,GAChB,IAAI,CAuuBN"}
@@ -1,4 +1,4 @@
1
- import { acquireLock, appendAudit, apply, compile, destroy, detectDrift, emptyState, formatPlan, importResource, liveExecutors, liveReaders, loadResources, newRunId, plan, readState, reconcileDrift, removeResources, restoreSnapshot, snapshotState, writeState, } from "../../deploy/index.js";
1
+ import { acquireLock, appendAudit, apply, compile, destroy, detectDrift, emptyState, formatPlan, importResource, liveExecutors, liveReaders, loadResources, newRunId, plan, pull, readState, reconcileDrift, removeResources, restoreSnapshot, SKIP_REASONS, snapshotState, SUPPORTED_KINDS, writeState, } from "../../deploy/index.js";
2
2
  import { getConfig } from "../auth.js";
3
3
  import { colors, confirm, ExitCodes, failWith, info, outputJson, startSpinner, success, } from "../io.js";
4
4
  // `plan / deploy / refresh / import / rollback / destroy` — the reconcile
@@ -301,6 +301,110 @@ export function registerDeployCommands(parent, getApi) {
301
301
  else
302
302
  success(`Imported ${id} → ${uuid} (${result.kind}).`);
303
303
  });
304
+ parent
305
+ .command("pull")
306
+ .description("Generate define* source from a live workspace and adopt it into " +
307
+ "cargo.state.json (so the next deploy is a no-op, not a duplicate-create).")
308
+ .option("--dir <path>", "Repo root to write into (default: cwd)")
309
+ .option("--types <kinds>", `Comma-separated kinds to pull (default: all supported — ${SUPPORTED_KINDS.join(", ")})`)
310
+ .option("--dry-run", "Print what would be generated without writing or adopting")
311
+ .option("--no-adopt", "Write files but skip state adoption (leaves a repo whose deploy would duplicate — advanced)")
312
+ .option("--force", "Steal the state lock held by another run")
313
+ .option("--json", "Output the result as JSON")
314
+ .action(async (opts) => {
315
+ if (opts.dir !== undefined)
316
+ process.chdir(opts.dir);
317
+ const root = ".";
318
+ let kinds;
319
+ if (opts.types !== undefined) {
320
+ const requested = opts.types.split(",").map((k) => k.trim());
321
+ const unknown = requested.filter((k) => !SUPPORTED_KINDS.includes(k));
322
+ if (unknown.length > 0) {
323
+ failWith(`Unsupported --types: ${unknown.join(", ")}. Supported: ${SUPPORTED_KINDS.join(", ")}.`, { code: ExitCodes.InvalidUsage });
324
+ }
325
+ kinds = requested;
326
+ }
327
+ const workspaceUuid = await resolveWorkspaceUuid(getApi);
328
+ const api = getApi();
329
+ const runId = newRunId();
330
+ if (opts.dryRun !== true) {
331
+ try {
332
+ acquireLock(root, "pull", opts.force === true);
333
+ }
334
+ catch (error) {
335
+ failWith(message(error), { code: ExitCodes.GenericError });
336
+ }
337
+ snapshotState(root); // enable `cdk rollback`
338
+ }
339
+ let result;
340
+ try {
341
+ result = await pull(root, api, workspaceUuid, {
342
+ kinds,
343
+ dryRun: opts.dryRun === true,
344
+ noAdopt: opts.adopt === false,
345
+ });
346
+ }
347
+ catch (error) {
348
+ appendAudit(root, {
349
+ runId,
350
+ at: new Date().toISOString(),
351
+ command: "pull",
352
+ workspaceUuid,
353
+ ok: false,
354
+ error: message(error),
355
+ });
356
+ failWith(`Pull failed: ${message(error)}`, {
357
+ code: ExitCodes.GenericError,
358
+ extra: { runId },
359
+ });
360
+ }
361
+ appendAudit(root, {
362
+ runId,
363
+ at: new Date().toISOString(),
364
+ command: "pull",
365
+ workspaceUuid,
366
+ ok: true,
367
+ summary: {
368
+ files: result.files.length,
369
+ adopted: result.adopted,
370
+ counts: result.counts,
371
+ skipped: result.skipped,
372
+ },
373
+ });
374
+ if (opts.json === true) {
375
+ // Binary assets would JSON.stringify as one numeric key per byte —
376
+ // report their path + size instead of the content.
377
+ outputJson({
378
+ ...result,
379
+ files: result.files.map((file) => "source" in file
380
+ ? file
381
+ : { path: file.path, byteLength: file.bytes.byteLength }),
382
+ });
383
+ return;
384
+ }
385
+ const summary = Object.entries(result.counts)
386
+ .map(([kind, n]) => `${String(n)} ${kind}`)
387
+ .join(", ");
388
+ const skipped = Object.entries(result.skipped)
389
+ .map(([kind, n]) => {
390
+ const reason = SKIP_REASONS[kind];
391
+ return `${String(n)} ${kind}${reason === undefined ? "" : ` (${reason})`}`;
392
+ })
393
+ .join(", ");
394
+ if (skipped.length > 0) {
395
+ info(`Skipped: ${skipped}.`);
396
+ }
397
+ if (result.envNames.length > 0) {
398
+ info(`${String(result.envNames.length)} env var(s) hold masked secrets` +
399
+ `${opts.dryRun === true ? "" : " (see .env.example)"}: ${result.envNames.join(", ")}.`);
400
+ }
401
+ if (opts.dryRun === true) {
402
+ success(`Would generate ${String(result.files.length)} file(s): ${summary || "nothing"}.`);
403
+ }
404
+ else {
405
+ success(`Generated ${String(result.files.length)} file(s) (${summary || "nothing"}); adopted ${String(result.adopted)} into state.`);
406
+ }
407
+ });
304
408
  parent
305
409
  .command("rollback")
306
410
  .description("Restore cargo.state.json from the snapshot taken before the last deploy/destroy/import.")
@@ -23,6 +23,13 @@ export declare function printJsonSchemaType(schema: unknown): string;
23
23
  * would not.
24
24
  */
25
25
  export declare function printJsonSchemaOutput(schema: unknown): string | undefined;
26
+ /**
27
+ * The top-level property names of an object JSON Schema — e.g. the column
28
+ * slugs an extractor's `columns.schema` declares. `allOf`/`anyOf`/`oneOf`
29
+ * members contribute the union of their keys; anything that isn't
30
+ * object-shaped contributes none.
31
+ */
32
+ export declare function jsonSchemaPropertyKeys(schema: unknown): string[];
26
33
  /**
27
34
  * Print a tool release's `formFields` as a TS input type. Returns
28
35
  * `undefined` when the fields can't be interpreted (caller falls back to
@@ -1 +1 @@
1
- {"version":3,"file":"inputTypes.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/inputTypes.ts"],"names":[],"mappings":"AAqCA;;;GAGG;AACH,eAAO,MAAM,oBAAoB,QAE+D,CAAC;AAEjG;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAM5D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAQ3D;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAQzE;AAmUD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAU5E"}
1
+ {"version":3,"file":"inputTypes.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/inputTypes.ts"],"names":[],"mappings":"AAqCA;;;GAGG;AACH,eAAO,MAAM,oBAAoB,QAE+D,CAAC;AAEjG;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAM5D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAQ3D;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAQzE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAGhE;AAgVD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAU5E"}
@@ -65,6 +65,29 @@ export function printJsonSchemaOutput(schema) {
65
65
  ? undefined
66
66
  : printed;
67
67
  }
68
+ /**
69
+ * The top-level property names of an object JSON Schema — e.g. the column
70
+ * slugs an extractor's `columns.schema` declares. `allOf`/`anyOf`/`oneOf`
71
+ * members contribute the union of their keys; anything that isn't
72
+ * object-shaped contributes none.
73
+ */
74
+ export function jsonSchemaPropertyKeys(schema) {
75
+ if (schema === null || typeof schema !== "object")
76
+ return [];
77
+ return [...collectPropertyKeys(schema)];
78
+ }
79
+ function collectPropertyKeys(schema) {
80
+ const keys = new Set(schema.properties === undefined ? [] : Object.keys(schema.properties));
81
+ for (const members of [schema.allOf, schema.anyOf, schema.oneOf]) {
82
+ if (members === undefined)
83
+ continue;
84
+ for (const member of members) {
85
+ for (const key of collectPropertyKeys(member))
86
+ keys.add(key);
87
+ }
88
+ }
89
+ return keys;
90
+ }
68
91
  // The platform's shared `encryption` schema definition: an object with a `type`
69
92
  // const/enum of "encryption" plus `isEncrypted` and `value`. In CDK config types
70
93
  // these positions become `EncryptionRef` (see printObject).
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/types.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsIzC,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,IAAI,CAkE7E"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/types.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoJzC,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,IAAI,CAoE7E"}
@@ -1,7 +1,7 @@
1
1
  import { mkdirSync, writeFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { handleApiCall, info, success } from "../io.js";
4
- import { printJsonSchemaInput, printJsonSchemaOutput, printJsonSchemaType, } from "./inputTypes.js";
4
+ import { jsonSchemaPropertyKeys, printJsonSchemaInput, printJsonSchemaOutput, printJsonSchemaType, } from "./inputTypes.js";
5
5
  // Composite natives surfaced via dedicated SDK syntax / scope helpers —
6
6
  // excluded so we don't double-register them. Mirrors the codegen exclusion
7
7
  // list in `packages/workflow-sdk/scripts/generateNativeIntegration.ts`
@@ -69,7 +69,7 @@ export function registerTypesCommand(parent, getApi) {
69
69
  ].join("\n"));
70
70
  info(``);
71
71
  const extractorCount = payload.extractorConfigs.reduce((n, e) => n + e.extractors.length, 0);
72
- info(`Typed ${String(payload.connectorConfigs.length)} connector config(s), ${String(extractorCount)} extractor config(s), ${String(payload.agentTriggerConfigs.length)} agent trigger config(s), ${String(payload.modelColumns.length)} model column set(s), ${String(payload.integrations.length)} integration(s), ${String(payload.native.length)} native action(s).`);
72
+ info(`Typed ${String(payload.connectorConfigs.length)} connector config(s), ${String(extractorCount)} extractor config(s), ${String(payload.agentTriggerConfigs.length)} agent trigger config(s), ${String(payload.modelColumns.length)} model column set(s), ${String(payload.extractorColumns.reduce((n, e) => n + e.extractors.length, 0))} extractor column set(s), ${String(payload.integrations.length)} integration(s), ${String(payload.native.length)} native action(s).`);
73
73
  });
74
74
  }
75
75
  async function fetchWorkspaceSurface(api) {
@@ -82,6 +82,7 @@ async function fetchWorkspaceSurface(api) {
82
82
  const connectorConfigs = collectConnectorConfigs(rawIntegrations);
83
83
  const extractorConfigs = collectExtractorConfigs(rawIntegrations);
84
84
  const extractorSlugs = collectExtractorSlugs(rawIntegrations);
85
+ const extractorColumns = collectExtractorColumns(rawIntegrations);
85
86
  const agentTriggerConfigs = collectAgentTriggerConfigs(rawIntegrations);
86
87
  const modelColumns = collectModelColumns(modelsResult.models);
87
88
  return {
@@ -90,6 +91,7 @@ async function fetchWorkspaceSurface(api) {
90
91
  connectorConfigs,
91
92
  extractorConfigs,
92
93
  extractorSlugs,
94
+ extractorColumns,
93
95
  agentTriggerConfigs,
94
96
  modelColumns,
95
97
  };
@@ -180,6 +182,33 @@ function collectExtractorSlugs(integrations) {
180
182
  out.sort((a, b) => a.integrationSlug.localeCompare(b.integrationSlug));
181
183
  return out;
182
184
  }
185
+ // integrationSlug → the column slugs each of its extractors statically
186
+ // declares (`extractor.columns.schema`, one property per column). Skips
187
+ // extractors without a declared schema — most compute their columns from the
188
+ // fetched records — and integrations left with none.
189
+ function collectExtractorColumns(integrations) {
190
+ return integrations
191
+ .map((integration) => ({
192
+ integrationSlug: integration.slug,
193
+ extractors: collectDeclaredColumns(integration),
194
+ }))
195
+ .filter((entry) => entry.extractors.length > 0)
196
+ .sort((a, b) => a.integrationSlug.localeCompare(b.integrationSlug));
197
+ }
198
+ // One `{ slug, columns }` entry per extractor that declares a non-empty
199
+ // `columns.schema`, extractors and columns both sorted.
200
+ function collectDeclaredColumns(integration) {
201
+ const { extractors = {} } = integration;
202
+ return Object.entries(extractors)
203
+ .map(([slug, { columns }]) => ({
204
+ slug,
205
+ columns: columns === undefined
206
+ ? []
207
+ : jsonSchemaPropertyKeys(columns.schema).sort((a, b) => a.localeCompare(b)),
208
+ }))
209
+ .filter((extractor) => extractor.columns.length > 0)
210
+ .sort((a, b) => a.slug.localeCompare(b.slug));
211
+ }
183
212
  // modelSlug → every column slug it exposes. Additional columns are addressed
184
213
  // with their kind as a storage prefix (`computed__score`, `custom__notes`, …) —
185
214
  // the same slugs `/storage/columns/list` returns — while original columns stay
@@ -298,6 +327,18 @@ function renderTypes(payload) {
298
327
  }
299
328
  cdkLines.push(` }`);
300
329
  }
330
+ if (payload.extractorColumns.length > 0) {
331
+ cdkLines.push(` interface ExtractorColumns {`);
332
+ for (const e of payload.extractorColumns) {
333
+ cdkLines.push(` ${jsonKey(e.integrationSlug)}: {`);
334
+ for (const x of e.extractors) {
335
+ const union = x.columns.map((c) => JSON.stringify(c)).join(" | ");
336
+ cdkLines.push(` ${jsonKey(x.slug)}: ${union};`);
337
+ }
338
+ cdkLines.push(` };`);
339
+ }
340
+ cdkLines.push(` }`);
341
+ }
301
342
  if (payload.modelColumns.length > 0) {
302
343
  cdkLines.push(` interface ModelColumns {`);
303
344
  for (const m of payload.modelColumns) {
@@ -1 +1 @@
1
- {"version":3,"file":"executors.live.d.ts","sourceRoot":"","sources":["../../../src/deploy/executors.live.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAIzC,OAAO,KAAK,EAAE,SAAS,EAAW,MAAM,YAAY,CAAC;AA4NrD,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,SAAS,CA8oBjD"}
1
+ {"version":3,"file":"executors.live.d.ts","sourceRoot":"","sources":["../../../src/deploy/executors.live.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAIzC,OAAO,KAAK,EAAE,SAAS,EAAW,MAAM,YAAY,CAAC;AAiPrD,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,SAAS,CAgsBjD"}
@@ -182,6 +182,22 @@ async function readContextFile(api, path) {
182
182
  return undefined; // new file — read 404s
183
183
  }
184
184
  }
185
+ // Does every field the code declares match the live value? Keys the code omits
186
+ // (server-filled defaults like `columnsUsed`) are not compared, mirroring the
187
+ // "include iff defined" rule — so an omitted optional never forces an update.
188
+ function declaredMatchesLive(declared, live) {
189
+ if (Array.isArray(declared)) {
190
+ return (Array.isArray(live) &&
191
+ declared.length === live.length &&
192
+ declared.every((item, i) => declaredMatchesLive(item, live[i])));
193
+ }
194
+ if (typeof declared === "object" && declared !== null) {
195
+ if (typeof live !== "object" || live === null)
196
+ return false;
197
+ return Object.entries(declared).every(([key, value]) => value === undefined || declaredMatchesLive(value, live[key]));
198
+ }
199
+ return declared === live;
200
+ }
185
201
  export function liveExecutors(api) {
186
202
  // Resolve the dataset uuid auto-created alongside a connector — needed when a
187
203
  // connector is ADOPTED (create returns it directly; list/adopt does not).
@@ -193,6 +209,31 @@ export function liveExecutors(api) {
193
209
  }
194
210
  return match.uuid;
195
211
  }
212
+ // Make a model's live additional columns match the declared list: create
213
+ // missing, update changed, remove undeclared. Only runs when the spec declares
214
+ // `additionalColumns` — an omitted field leaves live columns untouched.
215
+ // `liveColumns` skips the model fetch when the caller already knows them
216
+ // (a just-created model has none).
217
+ async function reconcileAdditionalColumns(modelUuid, declared, liveColumns) {
218
+ const live = liveColumns !== undefined
219
+ ? liveColumns
220
+ : (await api.storage.model.get(modelUuid)).model.additionalColumns;
221
+ const liveBySlug = new Map(live.map((column) => [column.slug, column]));
222
+ for (const column of declared) {
223
+ const live = liveBySlug.get(column.slug);
224
+ if (live === undefined) {
225
+ await api.storage.column.create({ modelUuid, column });
226
+ continue;
227
+ }
228
+ liveBySlug.delete(column.slug);
229
+ if (!declaredMatchesLive(column, live)) {
230
+ await api.storage.column.update({ modelUuid, column });
231
+ }
232
+ }
233
+ for (const slug of liveBySlug.keys()) {
234
+ await api.storage.column.remove({ modelUuid, columnSlug: slug });
235
+ }
236
+ }
196
237
  return {
197
238
  async connector(spec, prior) {
198
239
  const slug = str(spec["slug"]);
@@ -270,7 +311,13 @@ export function liveExecutors(api) {
270
311
  // `undefined` when the spec omits it, so the source's default (integration
271
312
  // mapping for connector models) is preserved.
272
313
  const unification = spec["unification"];
314
+ // Columns are reconciled BEFORE the unification update on every path, so a
315
+ // unification can reference a just-declared additional column.
316
+ const additionalColumns = spec["additionalColumns"];
273
317
  if (prior !== undefined) {
318
+ if (additionalColumns !== undefined) {
319
+ await reconcileAdditionalColumns(prior.uuid, additionalColumns);
320
+ }
274
321
  // No extractorSlug — a model's extractor is immutable post-create
275
322
  // (it's not part of UpdateModelPayload).
276
323
  await api.storage.model.update({
@@ -294,8 +341,12 @@ export function liveExecutors(api) {
294
341
  if (found === undefined) {
295
342
  throw new Error(`model "${slug}" reported to exist but was not found`);
296
343
  }
297
- // Adopted an existing model — apply the declared unification (create
298
- // skips it), then release on destroy rather than delete.
344
+ // Adopted an existing model — reconcile columns and apply the declared
345
+ // unification (create skips it), then release on destroy rather than
346
+ // delete.
347
+ if (additionalColumns !== undefined) {
348
+ await reconcileAdditionalColumns(found.uuid, additionalColumns);
349
+ }
299
350
  if (unification !== undefined) {
300
351
  await api.storage.model.update({ uuid: found.uuid, unification });
301
352
  }
@@ -311,6 +362,9 @@ export function liveExecutors(api) {
311
362
  schedule,
312
363
  folderUuid: optStr(spec["folderUuid"]),
313
364
  });
365
+ if (additionalColumns !== undefined) {
366
+ await reconcileAdditionalColumns(created.model.uuid, additionalColumns, []);
367
+ }
314
368
  // CreateModelPayload has no unification field, so a declared unification
315
369
  // is applied with a follow-up update once the model exists.
316
370
  if (unification !== undefined) {
@@ -10,4 +10,16 @@ export type ImportResult = {
10
10
  * the live resource is reachable, then writes/overwrites its state entry.
11
11
  */
12
12
  export declare function importResource(root: string, api: Api, workspaceUuid: string, id: string, uuid: string, nodes?: readonly ResourceNode[]): Promise<ImportResult>;
13
+ export type AdoptTarget = {
14
+ id: string;
15
+ uuid: string;
16
+ };
17
+ /**
18
+ * Batch import for `cdk pull` adoption: the same per-resource contract as
19
+ * importResource, but the graph is compiled once, the workspace listings
20
+ * importOutputs consults are fetched at most once, resources are processed
21
+ * concurrently, and the state file is written once at the end — so adoption is
22
+ * atomic (a mid-batch failure commits nothing) instead of N read/fsync cycles.
23
+ */
24
+ export declare function adoptResources(root: string, api: Api, workspaceUuid: string, targets: readonly AdoptTarget[], nodes: readonly ResourceNode[]): Promise<number>;
13
25
  //# sourceMappingURL=import.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../../src/deploy/import.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM7D,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AA2CF;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,GAAG,EACR,aAAa,EAAE,MAAM,EACrB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EAGZ,KAAK,CAAC,EAAE,SAAS,YAAY,EAAE,GAC9B,OAAO,CAAC,YAAY,CAAC,CAoCvB"}
1
+ {"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../../src/deploy/import.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM7D,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAmFF;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,GAAG,EACR,aAAa,EAAE,MAAM,EACrB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EAGZ,KAAK,CAAC,EAAE,SAAS,YAAY,EAAE,GAC9B,OAAO,CAAC,YAAY,CAAC,CAuBvB;AAsCD,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAiBvD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,GAAG,EACR,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,KAAK,EAAE,SAAS,YAAY,EAAE,GAC7B,OAAO,CAAC,MAAM,CAAC,CAyBjB"}
@@ -10,26 +10,53 @@ import { compile } from "./compile.js";
10
10
  import { loadResources } from "./load.js";
11
11
  import { liveReaders } from "./readers.live.js";
12
12
  import { emptyState, readState, writeState } from "./state.js";
13
+ function workspaceListings(api) {
14
+ const cache = {};
15
+ const lazy = (key, fetch) => {
16
+ return () => {
17
+ const hit = cache[key];
18
+ if (hit !== undefined)
19
+ return hit;
20
+ const miss = fetch();
21
+ cache[key] = miss;
22
+ return miss;
23
+ };
24
+ };
25
+ return {
26
+ datasets: lazy("datasets", async () => {
27
+ const { datasets } = await api.storage.dataset.all();
28
+ return datasets;
29
+ }),
30
+ plays: lazy("plays", async () => {
31
+ const { plays } = await api.orchestration.play.list();
32
+ return plays;
33
+ }),
34
+ tools: lazy("tools", async () => {
35
+ const { tools } = await api.orchestration.tool.all();
36
+ return tools;
37
+ }),
38
+ };
39
+ }
13
40
  // Reconstruct the outputs an apply would have produced for this kind, so tokens
14
41
  // its dependents reference (connector.datasetUuid, tool/play.workflowUuid,
15
42
  // worker/app.url) still resolve.
16
- async function importOutputs(api, kind, uuid) {
43
+ async function importOutputs(api, kind, uuid, listings) {
17
44
  const outputs = { uuid };
18
45
  if (kind === "connector") {
19
- const { datasets } = await api.storage.dataset.all();
46
+ const datasets = await listings.datasets();
20
47
  const match = datasets.find((d) => d["kind"] === "connector" && d["connectorUuid"] === uuid);
21
48
  if (match !== undefined)
22
49
  outputs["datasetUuid"] = match["uuid"];
23
50
  }
24
51
  else if (kind === "play") {
25
- const { plays } = await api.orchestration.play.list();
52
+ const plays = await listings.plays();
26
53
  const play = plays.find((p) => p["uuid"] === uuid);
27
54
  const wf = play === undefined ? undefined : play["workflowUuid"];
28
55
  if (typeof wf === "string")
29
56
  outputs["workflowUuid"] = wf;
30
57
  }
31
58
  else if (kind === "tool") {
32
- const { tools } = await api.orchestration.tool.all();
59
+ const tools = await listings.tools();
33
60
  const tool = tools.find((t) => t["uuid"] === uuid);
34
61
  const wf = tool === undefined ? undefined : tool["workflowUuid"];
35
62
  if (typeof wf === "string")
@@ -64,11 +91,20 @@ nodes) {
64
91
  throw new Error(`no resource "${id}" found in code — import targets a code resource id like "agent:sdr".`);
65
92
  }
66
93
  const kind = entry.kind;
67
- const outputs = await importOutputs(api, kind, uuid);
94
+ const stateEntry = await buildStateEntry(api, kind, uuid, entry.hash, workspaceListings(api));
95
+ const state = readValidatedState(root, workspaceUuid);
96
+ state.resources[id] = stateEntry;
97
+ writeState(root, state);
98
+ return { kind, outputs: stateEntry.outputs };
99
+ }
100
+ // Outputs + adopted marker + live existence check + drift baseline — the
101
+ // per-resource half of an import, shared by importResource and adoptResources.
102
+ async function buildStateEntry(api, kind, uuid, hash, listings) {
103
+ const outputs = await importOutputs(api, kind, uuid, listings);
68
104
  // Imported resources are treated as adopted: a later `destroy` releases them
69
105
  // (drops from state) rather than deleting infra the CDK didn't create.
70
106
  outputs["adopted"] = "true";
71
- const stateEntry = { hash: entry.hash, uuid, outputs };
107
+ const stateEntry = { hash, uuid, outputs };
72
108
  // Confirm it's actually there (and capture a drift baseline).
73
109
  const live = await liveReaders(api)[kind](stateEntry);
74
110
  if (live.exists === false) {
@@ -76,12 +112,46 @@ nodes) {
76
112
  }
77
113
  if (live.fingerprint !== undefined)
78
114
  stateEntry.live = live.fingerprint;
115
+ return stateEntry;
116
+ }
117
+ function readValidatedState(root, workspaceUuid) {
79
118
  const prior = readState(root);
80
- const state = prior !== undefined ? prior : emptyState(workspaceUuid);
81
119
  if (prior !== undefined && prior.workspaceUuid !== workspaceUuid) {
82
120
  throw new Error(`cargo.state.json belongs to workspace ${prior.workspaceUuid}, not the selected ${workspaceUuid}.`);
83
121
  }
84
- state.resources[id] = stateEntry;
122
+ return prior !== undefined ? prior : emptyState(workspaceUuid);
123
+ }
124
+ // Concurrency-capped map — adoption fans out 1-3 API calls per resource, so a
125
+ // full-workspace pull stays polite to the backend.
126
+ async function inChunks(items, size, fn) {
127
+ const out = [];
128
+ for (let index = 0; index < items.length; index += size) {
129
+ const chunk = items.slice(index, index + size);
130
+ out.push(...(await Promise.all(chunk.map(fn))));
131
+ }
132
+ return out;
133
+ }
134
+ /**
135
+ * Batch import for `cdk pull` adoption: the same per-resource contract as
136
+ * importResource, but the graph is compiled once, the workspace listings
137
+ * importOutputs consults are fetched at most once, resources are processed
138
+ * concurrently, and the state file is written once at the end — so adoption is
139
+ * atomic (a mid-batch failure commits nothing) instead of N read/fsync cycles.
140
+ */
141
+ export async function adoptResources(root, api, workspaceUuid, targets, nodes) {
142
+ const plan = new Map(compile({ nodes }).plan.map((entry) => [entry.id, entry]));
143
+ const listings = workspaceListings(api);
144
+ const entries = await inChunks(targets, 8, async ({ id, uuid }) => {
145
+ const entry = plan.get(id);
146
+ if (entry === undefined) {
147
+ throw new Error(`no resource "${id}" found in the generated code.`);
148
+ }
149
+ const stateEntry = await buildStateEntry(api, entry.kind, uuid, entry.hash, listings);
150
+ return [id, stateEntry];
151
+ });
152
+ const state = readValidatedState(root, workspaceUuid);
153
+ for (const [id, stateEntry] of entries)
154
+ state.resources[id] = stateEntry;
85
155
  writeState(root, state);
86
- return { kind, outputs };
156
+ return entries.length;
87
157
  }
@@ -15,6 +15,8 @@ export { loadResources } from "./load.js";
15
15
  export type { Lock } from "./lock.js";
16
16
  export { acquireLock } from "./lock.js";
17
17
  export { formatPlan } from "./plan.js";
18
+ export type { PullOptions, PullResult } from "./pull/index.js";
19
+ export { pull, SKIP_REASONS, SUPPORTED_KINDS } from "./pull/index.js";
18
20
  export { liveReaders } from "./readers.live.js";
19
21
  export type { DriftEntry, DriftStatus, LiveRead, LiveReader, LiveReaders, } from "./refresh.js";
20
22
  export { detectDrift, fingerprint, reconcileDrift } from "./refresh.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/deploy/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAW,KAAK,aAAa,EAAE,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAIvE,YAAY,EACV,aAAa,EACb,WAAW,EACX,SAAS,EACT,OAAO,GACR,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EACb,SAAS,EACT,KAAK,EACL,UAAU,EACV,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,YAAY,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EACV,UAAU,EACV,WAAW,EACX,QAAQ,EACR,UAAU,EACV,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,EACL,UAAU,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,UAAU,GACX,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAG/D;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,MAAM,CAC1B,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,YAAY,CAAC,CAyBvB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/deploy/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAW,KAAK,aAAa,EAAE,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAIvE,YAAY,EACV,aAAa,EACb,WAAW,EACX,SAAS,EACT,OAAO,GACR,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EACb,SAAS,EACT,KAAK,EACL,UAAU,EACV,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,YAAY,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EACV,UAAU,EACV,WAAW,EACX,QAAQ,EACR,UAAU,EACV,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,EACL,UAAU,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,UAAU,GACX,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAG/D;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,MAAM,CAC1B,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,YAAY,CAAC,CAyBvB"}
@@ -13,6 +13,7 @@ export { importResource } from "./import.js";
13
13
  export { loadResources } from "./load.js";
14
14
  export { acquireLock } from "./lock.js";
15
15
  export { formatPlan } from "./plan.js";
16
+ export { pull, SKIP_REASONS, SUPPORTED_KINDS } from "./pull/index.js";
16
17
  export { liveReaders } from "./readers.live.js";
17
18
  export { detectDrift, fingerprint, reconcileDrift } from "./refresh.js";
18
19
  export { emptyState, readState, restoreSnapshot, snapshotState, writeState, } from "./state.js";
@@ -0,0 +1,8 @@
1
+ import type { Api } from "@cargo-ai/api";
2
+ import type { ResourceKind } from "../../core.js";
3
+ export type EnumeratedRow = {
4
+ readonly kind: ResourceKind;
5
+ readonly row: Record<string, unknown>;
6
+ };
7
+ export declare function enumerateWorkspace(api: Api, kinds: readonly ResourceKind[]): Promise<EnumeratedRow[]>;
8
+ //# sourceMappingURL=enumerate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enumerate.d.ts","sourceRoot":"","sources":["../../../../src/deploy/pull/enumerate.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAKlD,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAI5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC,CAAC;AAqCF,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,SAAS,YAAY,EAAE,GAC7B,OAAO,CAAC,aAAa,EAAE,CAAC,CAkQ1B"}