@cargo-ai/cdk 1.0.5 → 1.0.7

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 (64) hide show
  1. package/README.md +59 -9
  2. package/build/src/cli/commands/from.d.ts +65 -0
  3. package/build/src/cli/commands/from.d.ts.map +1 -0
  4. package/build/src/cli/commands/from.js +275 -0
  5. package/build/src/cli/commands/init.d.ts +1 -1
  6. package/build/src/cli/commands/init.d.ts.map +1 -1
  7. package/build/src/cli/commands/init.js +32 -13
  8. package/build/src/cli/commands/types.d.ts.map +1 -1
  9. package/build/src/cli/commands/types.js +75 -37
  10. package/build/src/core.d.ts +1 -1
  11. package/build/src/core.d.ts.map +1 -1
  12. package/build/src/deploy/apply.d.ts +6 -0
  13. package/build/src/deploy/apply.d.ts.map +1 -1
  14. package/build/src/deploy/apply.js +4 -0
  15. package/build/src/deploy/destroy.d.ts.map +1 -1
  16. package/build/src/deploy/destroy.js +33 -0
  17. package/build/src/deploy/executors.live.d.ts.map +1 -1
  18. package/build/src/deploy/executors.live.js +157 -1
  19. package/build/src/deploy/readers.live.d.ts.map +1 -1
  20. package/build/src/deploy/readers.live.js +34 -1
  21. package/build/src/index.d.ts +11 -7
  22. package/build/src/index.d.ts.map +1 -1
  23. package/build/src/index.js +6 -4
  24. package/build/src/refs.d.ts +22 -0
  25. package/build/src/refs.d.ts.map +1 -1
  26. package/build/src/refs.js +11 -2
  27. package/build/src/resources/actions.d.ts +39 -19
  28. package/build/src/resources/actions.d.ts.map +1 -1
  29. package/build/src/resources/actions.js +60 -33
  30. package/build/src/resources/agent.d.ts +33 -23
  31. package/build/src/resources/agent.d.ts.map +1 -1
  32. package/build/src/resources/agent.js +24 -24
  33. package/build/src/resources/app.d.ts +3 -1
  34. package/build/src/resources/app.d.ts.map +1 -1
  35. package/build/src/resources/app.js +2 -1
  36. package/build/src/resources/capacity.d.ts +11 -0
  37. package/build/src/resources/capacity.d.ts.map +1 -1
  38. package/build/src/resources/capacity.js +81 -0
  39. package/build/src/resources/connector.d.ts +10 -0
  40. package/build/src/resources/connector.d.ts.map +1 -1
  41. package/build/src/resources/connector.js +18 -1
  42. package/build/src/resources/customIntegration.d.ts +12 -0
  43. package/build/src/resources/customIntegration.d.ts.map +1 -0
  44. package/build/src/resources/customIntegration.js +18 -0
  45. package/build/src/resources/mcpServer.d.ts +8 -10
  46. package/build/src/resources/mcpServer.d.ts.map +1 -1
  47. package/build/src/resources/mcpServer.js +4 -7
  48. package/build/src/resources/model.d.ts +47 -4
  49. package/build/src/resources/model.d.ts.map +1 -1
  50. package/build/src/resources/model.js +38 -1
  51. package/build/src/resources/relationship.d.ts +21 -0
  52. package/build/src/resources/relationship.d.ts.map +1 -0
  53. package/build/src/resources/relationship.js +39 -0
  54. package/build/src/resources/worker.d.ts +13 -1
  55. package/build/src/resources/worker.d.ts.map +1 -1
  56. package/build/src/resources/worker.js +3 -1
  57. package/build/tsconfig.tsbuildinfo +1 -1
  58. package/package.json +5 -5
  59. package/templates/blank/cargo.ts +17 -22
  60. package/templates/full/README.md +7 -4
  61. package/templates/full/agents/sdr.ts +12 -8
  62. package/templates/full/connectors/hunter.ts +10 -0
  63. package/templates/full/mcp/crm.ts +9 -6
  64. package/templates/full/tools/enrich.ts +28 -9
@@ -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.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.integrations.length)} integration(s), ${String(payload.native.length)} native action(s).`);
73
73
  });
74
74
  }
75
75
  async function fetchWorkspaceSurface(api) {
@@ -80,7 +80,16 @@ async function fetchWorkspaceSurface(api) {
80
80
  const native = collectNative(nativeIntegrationResult.nativeIntegration.actions);
81
81
  const connectorConfigs = collectConnectorConfigs(rawIntegrations);
82
82
  const extractorConfigs = collectExtractorConfigs(rawIntegrations);
83
- return { integrations, native, connectorConfigs, extractorConfigs };
83
+ const extractorSlugs = collectExtractorSlugs(rawIntegrations);
84
+ const agentTriggerConfigs = collectAgentTriggerConfigs(rawIntegrations);
85
+ return {
86
+ integrations,
87
+ native,
88
+ connectorConfigs,
89
+ extractorConfigs,
90
+ extractorSlugs,
91
+ agentTriggerConfigs,
92
+ };
84
93
  }
85
94
  // integrationSlug → connector config type. Skips integrations whose schema
86
95
  // prints as a bare record (no typing gained — the builder already falls back to
@@ -103,6 +112,27 @@ function collectConnectorConfigs(integrations) {
103
112
  out.sort((a, b) => a.slug.localeCompare(b.slug));
104
113
  return out;
105
114
  }
115
+ // integrationSlug → its agent connector-trigger config type. Only a few
116
+ // integrations expose an agent trigger (e.g. slack, http); the rest are skipped,
117
+ // as are those whose schema prints as a bare record.
118
+ function collectAgentTriggerConfigs(integrations) {
119
+ const out = [];
120
+ for (const integration of integrations) {
121
+ const schema = integration.agent?.trigger?.config?.schema;
122
+ if (schema === undefined)
123
+ continue;
124
+ const typeSrc = printJsonSchemaType(schema);
125
+ if (typeSrc === "Record<string, unknown>")
126
+ continue;
127
+ out.push({
128
+ slug: integration.slug,
129
+ name: trimOrUndefined(integration.name),
130
+ typeSrc,
131
+ });
132
+ }
133
+ out.sort((a, b) => a.slug.localeCompare(b.slug));
134
+ return out;
135
+ }
106
136
  // integrationSlug → its extractors' config types. Skips extractors whose schema
107
137
  // prints as a bare record, and integrations left with none.
108
138
  function collectExtractorConfigs(integrations) {
@@ -125,6 +155,20 @@ function collectExtractorConfigs(integrations) {
125
155
  out.sort((a, b) => a.integrationSlug.localeCompare(b.integrationSlug));
126
156
  return out;
127
157
  }
158
+ // integrationSlug → every extractor slug it exposes (the keys of `extractors`),
159
+ // so `defineModel`'s `extractSlug` autocompletes the full set, not just the ones
160
+ // that happen to add a typed config.
161
+ function collectExtractorSlugs(integrations) {
162
+ const out = [];
163
+ for (const integration of integrations) {
164
+ const slugs = Object.keys(integration.extractors ?? {}).sort((a, b) => a.localeCompare(b));
165
+ if (slugs.length === 0)
166
+ continue;
167
+ out.push({ integrationSlug: integration.slug, slugs });
168
+ }
169
+ out.sort((a, b) => a.integrationSlug.localeCompare(b.integrationSlug));
170
+ return out;
171
+ }
128
172
  function collectIntegrations(integrations) {
129
173
  const out = [];
130
174
  for (const integration of integrations) {
@@ -205,6 +249,24 @@ function renderTypes(payload) {
205
249
  }
206
250
  cdkLines.push(` }`);
207
251
  }
252
+ if (payload.extractorSlugs.length > 0) {
253
+ cdkLines.push(` interface ExtractorSlugs {`);
254
+ for (const e of payload.extractorSlugs) {
255
+ const union = e.slugs.map((s) => JSON.stringify(s)).join(" | ");
256
+ cdkLines.push(` ${jsonKey(e.integrationSlug)}: ${union};`);
257
+ }
258
+ cdkLines.push(` }`);
259
+ }
260
+ if (payload.agentTriggerConfigs.length > 0) {
261
+ cdkLines.push(` interface AgentTriggerConfigs {`);
262
+ for (const t of payload.agentTriggerConfigs) {
263
+ const doc = formatJsDoc({ name: t.name ?? t.slug, updatedAt: GENERATED_AT }, " ");
264
+ if (doc !== "")
265
+ cdkLines.push(doc.trimEnd());
266
+ cdkLines.push(` ${jsonKey(t.slug)}: ${t.typeSrc};`);
267
+ }
268
+ cdkLines.push(` }`);
269
+ }
208
270
  if (cdkLines.length > 0) {
209
271
  blocks.push([`declare module "@cargo-ai/cdk" {`, ...cdkLines, `}`].join("\n"));
210
272
  }
@@ -230,7 +292,10 @@ function renderTypes(payload) {
230
292
  }, " ");
231
293
  if (actionDoc !== "")
232
294
  wfLines.push(actionDoc.trimEnd());
233
- wfLines.push(` ${jsonKey(action.slug)}: IntegrationAction<${action.inputTypeSrc}, Record<string, unknown>>;`);
295
+ // Action outputs aren't schema-typed, so `any` (not `Record<string,
296
+ // unknown>`, which lowers to an unusable nested `Ref`) — the result is
297
+ // then usable as a string, an object, or in any typed slot.
298
+ wfLines.push(` ${jsonKey(action.slug)}: IntegrationAction<${action.inputTypeSrc}, any>;`);
234
299
  }
235
300
  wfLines.push(` };`);
236
301
  }
@@ -242,7 +307,7 @@ function renderTypes(payload) {
242
307
  const doc = formatJsDoc({ name: n.name, description: n.description, updatedAt: GENERATED_AT }, " ");
243
308
  if (doc !== "")
244
309
  wfLines.push(doc.trimEnd());
245
- wfLines.push(` ${jsonKey(n.slug)}: NativeAction<Record<string, unknown>, Record<string, unknown>>;`);
310
+ wfLines.push(` ${jsonKey(n.slug)}: NativeAction<Record<string, unknown>, any>;`);
246
311
  }
247
312
  wfLines.push(` }`);
248
313
  }
@@ -274,32 +339,20 @@ function renderTypes(payload) {
274
339
  preamble.push(`export {};`);
275
340
  return `${HEADER.trim()}\n\n${[...preamble, ...blocks].join("\n\n")}\n`;
276
341
  }
342
+ // Runtime registrations. Only `native` needs one: integration actions are now
343
+ // referenced by handle via `defineWorkflow`'s `uses` (no `integrations.*` runtime
344
+ // registry), so integrations contribute types only (see `renderTypes`).
277
345
  function renderEagerRegistration(payload) {
278
346
  const lines = [];
279
347
  lines.push(HEADER.trim());
280
- const imports = [];
281
- if (payload.integrations.length > 0)
282
- imports.push("registerIntegration");
283
- if (payload.native.length > 0)
284
- imports.push("registerNative");
285
- if (imports.length === 0) {
348
+ if (payload.native.length === 0) {
286
349
  lines.push(``);
287
- lines.push(`// Workspace surface is empty (no custom integrations / natives).`);
350
+ lines.push(`// No custom native actions in this workspace.`);
288
351
  lines.push(``);
289
352
  return lines.join("\n");
290
353
  }
291
- lines.push(`import { ${imports.join(", ")} } from "@cargo-ai/workflow-sdk";`);
354
+ lines.push(`import { registerNative } from "@cargo-ai/workflow-sdk";`);
292
355
  lines.push(``);
293
- for (const c of payload.integrations) {
294
- lines.push(`registerIntegration(${JSON.stringify(c.slug)}, {`);
295
- for (const action of c.actions) {
296
- const displayName = action.name !== undefined && action.name.length > 0
297
- ? action.name
298
- : `${capitalize(c.slug)} ${humanize(action.slug)}`;
299
- lines.push(` ${jsonKey(action.slug)}: { name: ${JSON.stringify(displayName)} },`);
300
- }
301
- lines.push(`});`);
302
- }
303
356
  for (const n of payload.native) {
304
357
  lines.push(`registerNative(${JSON.stringify(n.slug)}, ${JSON.stringify(n.name)});`);
305
358
  }
@@ -361,21 +414,6 @@ function formatJsDoc(doc, indent) {
361
414
  out.push(`${indent} */`);
362
415
  return `${out.join("\n")}\n`;
363
416
  }
364
- function capitalize(s) {
365
- if (s.length === 0)
366
- return s;
367
- const first = s[0];
368
- if (first === undefined)
369
- return s;
370
- return first.toUpperCase() + s.slice(1);
371
- }
372
- function humanize(slug) {
373
- return slug
374
- .replace(/([A-Z])/g, " $1")
375
- .replace(/[_-]+/g, " ")
376
- .trim()
377
- .toLowerCase();
378
- }
379
417
  function relativeToBase(base, path) {
380
418
  if (path.startsWith(base)) {
381
419
  const rel = path.slice(base.length);
@@ -5,7 +5,7 @@ export type Token = {
5
5
  };
6
6
  export declare function token(resourceId: string, field: string): Token;
7
7
  export declare function isToken(value: unknown): value is Token;
8
- export type ResourceKind = "connector" | "model" | "agent" | "play" | "tool" | "mcpServer" | "folder" | "file" | "worker" | "app" | "context" | "capacity" | "territory" | "segment";
8
+ export type ResourceKind = "connector" | "customIntegration" | "model" | "agent" | "play" | "tool" | "mcpServer" | "folder" | "file" | "worker" | "app" | "context" | "capacity" | "territory" | "segment" | "relationship";
9
9
  export type ResourceNode = {
10
10
  readonly id: string;
11
11
  readonly kind: ResourceKind;
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/core.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAE9D;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,CAMtD;AAGD,MAAM,MAAM,YAAY,GACpB,WAAW,GACX,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,WAAW,GACX,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,KAAK,GACL,SAAS,GACT,UAAU,GACV,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC,CAAC;AAoBF;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAUjD;AAMD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEtD;AAgBD,sCAAsC;AACtC,wBAAgB,SAAS,IAAI,YAAY,EAAE,CAE1C;AAED,gFAAgF;AAChF,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAgCD,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAMnE;AAGD,gFAAgF;AAChF,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EACd,GAAG,GAAE,GAAG,CAAC,MAAM,CAAa,GAC3B,GAAG,CAAC,MAAM,CAAC,CAcb;AAED,oFAAoF;AACpF,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC3C,OAAO,CAoBT;AAGD;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGxC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAE9C;AAED;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GACrB,SAAS,GACT;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAM3D;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAmBtD"}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/core.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAE9D;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,CAMtD;AAGD,MAAM,MAAM,YAAY,GACpB,WAAW,GACX,mBAAmB,GACnB,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,WAAW,GACX,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,KAAK,GACL,SAAS,GACT,UAAU,GACV,WAAW,GACX,SAAS,GACT,cAAc,CAAC;AAEnB,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC,CAAC;AAoBF;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAUjD;AAMD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEtD;AAgBD,sCAAsC;AACtC,wBAAgB,SAAS,IAAI,YAAY,EAAE,CAE1C;AAED,gFAAgF;AAChF,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAgCD,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAMnE;AAGD,gFAAgF;AAChF,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EACd,GAAG,GAAE,GAAG,CAAC,MAAM,CAAa,GAC3B,GAAG,CAAC,MAAM,CAAC,CAcb;AAED,oFAAoF;AACpF,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC3C,OAAO,CAoBT;AAGD;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGxC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAE9C;AAED;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GACrB,SAAS,GACT;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAM3D;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAmBtD"}
@@ -27,6 +27,9 @@ export type Executors = {
27
27
  file(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
28
28
  uuid: string;
29
29
  }>;
30
+ customIntegration(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
31
+ uuid: string;
32
+ }>;
30
33
  worker(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
31
34
  uuid: string;
32
35
  url: string;
@@ -47,6 +50,9 @@ export type Executors = {
47
50
  segment(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
48
51
  uuid: string;
49
52
  }>;
53
+ relationship(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
54
+ uuid: string;
55
+ }>;
50
56
  };
51
57
  export type ApplyResult = {
52
58
  state: State;
@@ -1 +1 @@
1
- {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../src/deploy/apply.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../src/deploy/apply.ts"],"names":[],"mappings":"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"}
@@ -209,6 +209,8 @@ function runExecutor(node, resolved, prior, executors) {
209
209
  return executors.folder(resolved, prior);
210
210
  case "file":
211
211
  return executors.file(resolved, prior);
212
+ case "customIntegration":
213
+ return executors.customIntegration(resolved, prior);
212
214
  case "worker":
213
215
  return executors.worker(resolved, prior);
214
216
  case "app":
@@ -221,5 +223,7 @@ function runExecutor(node, resolved, prior, executors) {
221
223
  return executors.territory(resolved, prior);
222
224
  case "segment":
223
225
  return executors.segment(resolved, prior);
226
+ case "relationship":
227
+ return executors.relationship(resolved, prior);
224
228
  }
225
229
  }
@@ -1 +1 @@
1
- {"version":3,"file":"destroy.d.ts","sourceRoot":"","sources":["../../../src/deploy/destroy.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAMzC,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AA8GtE;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,MAAM,EAAE,GACZ,OAAO,CAAC,aAAa,CAAC,CA4BxB;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAC3B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,GAAG,EACR,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7B,OAAO,CAAC,aAAa,CAAC,CA4BxB"}
1
+ {"version":3,"file":"destroy.d.ts","sourceRoot":"","sources":["../../../src/deploy/destroy.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAMzC,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AA8ItE;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,MAAM,EAAE,GACZ,OAAO,CAAC,aAAa,CAAC,CA4BxB;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAC3B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,GAAG,EACR,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7B,OAAO,CAAC,aAAa,CAAC,CA4BxB"}
@@ -12,11 +12,13 @@ const REMOVE_ORDER = [
12
12
  "context",
13
13
  "capacity",
14
14
  "territory",
15
+ "relationship",
15
16
  "segment",
16
17
  "play",
17
18
  "agent",
18
19
  "mcpServer",
19
20
  "tool",
21
+ "customIntegration",
20
22
  "worker",
21
23
  "app",
22
24
  "file",
@@ -68,6 +70,8 @@ async function remove(api, kind, uuid) {
68
70
  switch (kind) {
69
71
  case "connector":
70
72
  return api.connection.connector.remove(uuid);
73
+ case "customIntegration":
74
+ return api.connection.customIntegration.remove(uuid);
71
75
  case "model":
72
76
  await api.storage.model.remove(uuid);
73
77
  return;
@@ -101,6 +105,35 @@ async function remove(api, kind, uuid) {
101
105
  // the error is surfaced (destroy prunes dependents first, so a CDK-managed
102
106
  // play is already gone by the time its segment is removed).
103
107
  return api.segmentation.segment.remove(uuid);
108
+ case "relationship": {
109
+ // No per-uuid delete: the backend `set` endpoint replaces a dataset's
110
+ // relationships. Find this row to learn its dataset, then re-set that
111
+ // dataset's relationships MINUS this one (preserving the rest verbatim).
112
+ const { relationships } = await api.storage.relationship.all();
113
+ const target = relationships.find((r) => r.uuid === uuid);
114
+ if (target === undefined)
115
+ return; // already gone
116
+ const datasetUuid = target.fromDatasetUuid;
117
+ const remaining = relationships
118
+ .filter((r) => r.fromDatasetUuid === datasetUuid && r.uuid !== uuid)
119
+ .map((r) => ({
120
+ uuid: r.uuid,
121
+ fromModelUuid: r.fromModelUuid,
122
+ fromColumnSlug: r.fromColumnSlug,
123
+ fromPropertySlug: r.fromPropertySlug ?? null,
124
+ toModelUuid: r.toModelUuid,
125
+ toColumnSlug: r.toColumnSlug,
126
+ toPropertySlug: r.toPropertySlug ?? null,
127
+ fromSelectedColumnSlugs: r.fromSelectedColumnSlugs ?? null,
128
+ toSelectedColumnSlugs: r.toSelectedColumnSlugs ?? null,
129
+ relation: r.relation,
130
+ }));
131
+ await api.storage.relationship.set({
132
+ datasetUuid,
133
+ relationships: remaining,
134
+ });
135
+ return;
136
+ }
104
137
  }
105
138
  }
106
139
  /**
@@ -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;AA4BrD,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,SAAS,CA4qBjD"}
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;AA4BrD,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,SAAS,CAkxBjD"}
@@ -108,6 +108,11 @@ export function liveExecutors(api) {
108
108
  const slug = str(spec["slug"]);
109
109
  const datasetUuid = str(spec["datasetUuid"]);
110
110
  const schedule = spec["schedule"];
111
+ // Unification is not part of CreateModelPayload — it's set through an
112
+ // update, both when the model is first created and on later syncs. Left
113
+ // `undefined` when the spec omits it, so the source's default (integration
114
+ // mapping for connector models) is preserved.
115
+ const unification = spec["unification"];
111
116
  if (prior !== undefined) {
112
117
  // No extractorSlug — a model's extractor is immutable post-create
113
118
  // (it's not part of UpdateModelPayload).
@@ -117,6 +122,7 @@ export function liveExecutors(api) {
117
122
  description: optStr(spec["description"]) ?? null,
118
123
  config: spec["config"] ?? {},
119
124
  schedule: schedule ?? null,
125
+ unification,
120
126
  });
121
127
  const out = { uuid: prior.uuid };
122
128
  if (prior.outputs["adopted"] !== undefined) {
@@ -131,7 +137,11 @@ export function liveExecutors(api) {
131
137
  if (found === undefined) {
132
138
  throw new Error(`model "${slug}" reported to exist but was not found`);
133
139
  }
134
- // Adopted an existing model — release on destroy, don't delete.
140
+ // Adopted an existing model — apply the declared unification (create
141
+ // skips it), then release on destroy rather than delete.
142
+ if (unification !== undefined) {
143
+ await api.storage.model.update({ uuid: found.uuid, unification });
144
+ }
135
145
  return { uuid: found.uuid, adopted: "true" };
136
146
  }
137
147
  const created = await api.storage.model.create({
@@ -144,6 +154,14 @@ export function liveExecutors(api) {
144
154
  schedule,
145
155
  folderUuid: optStr(spec["folderUuid"]),
146
156
  });
157
+ // CreateModelPayload has no unification field, so a declared unification
158
+ // is applied with a follow-up update once the model exists.
159
+ if (unification !== undefined) {
160
+ await api.storage.model.update({
161
+ uuid: created.model.uuid,
162
+ unification,
163
+ });
164
+ }
147
165
  return { uuid: created.model.uuid };
148
166
  },
149
167
  async play(spec, prior) {
@@ -348,13 +366,30 @@ export function liveExecutors(api) {
348
366
  const created = await api.ai.mcpServer.create(base);
349
367
  return { uuid: created.mcpServer.uuid };
350
368
  },
369
+ async customIntegration(spec, prior) {
370
+ const workerUuid = str(spec["workerUuid"]);
371
+ if (prior !== undefined) {
372
+ await api.connection.customIntegration.update({
373
+ uuid: prior.uuid,
374
+ workerUuid,
375
+ });
376
+ return { uuid: prior.uuid };
377
+ }
378
+ const created = await api.connection.customIntegration.create({
379
+ kind: "worker",
380
+ workerUuid,
381
+ });
382
+ return { uuid: created.customIntegration.uuid };
383
+ },
351
384
  async worker(spec, prior) {
352
385
  const files = readBundle(str(spec["path"]));
353
386
  const slug = str(spec["slug"]);
387
+ const triggers = mapWorkerTriggers(spec["triggers"]);
354
388
  let uuid;
355
389
  let adopted = false;
356
390
  if (prior !== undefined) {
357
391
  uuid = prior.uuid;
392
+ await api.hosting.worker.update({ uuid, triggers });
358
393
  }
359
394
  else {
360
395
  // Recover our own slot if a prior deploy created it but crashed before
@@ -367,6 +402,7 @@ export function liveExecutors(api) {
367
402
  if (existing !== undefined) {
368
403
  uuid = existing.uuid;
369
404
  adopted = true;
405
+ await api.hosting.worker.update({ uuid, triggers });
370
406
  }
371
407
  else {
372
408
  uuid = (await api.hosting.worker.create({
@@ -374,9 +410,11 @@ export function liveExecutors(api) {
374
410
  slug,
375
411
  description: optStr(spec["description"]) ?? null,
376
412
  folderUuid: optStr(spec["folderUuid"]),
413
+ triggers,
377
414
  })).worker.uuid;
378
415
  }
379
416
  }
417
+ await syncEnvVars(api, { kind: "worker", workerUuid: uuid }, spec["env"]);
380
418
  const { deployment } = await api.hosting.deployment.create({
381
419
  kind: "worker",
382
420
  workerUuid: uuid,
@@ -424,6 +462,7 @@ export function liveExecutors(api) {
424
462
  })).app.uuid;
425
463
  }
426
464
  }
465
+ await syncEnvVars(api, { kind: "app", appUuid: uuid }, spec["env"]);
427
466
  const { deployment } = await api.hosting.deployment.create({
428
467
  kind: "app",
429
468
  appUuid: uuid,
@@ -619,6 +658,72 @@ export function liveExecutors(api) {
619
658
  });
620
659
  return { uuid: created.segment.uuid };
621
660
  },
661
+ // A relationship joins two models within one dataset. The backend `set`
662
+ // endpoint is a per-dataset declarative REPLACE (rows absent from the
663
+ // payload are deleted), so this executor read-merges: it lists the dataset's
664
+ // live relationships, preserves every OTHER row (by uuid), and upserts this
665
+ // one. No server slug → identity is prior.uuid; a fresh row is reconciled by
666
+ // matching its join fields in the response. The dataset is derived from the
667
+ // `from` model. Sequential apply means a later relationship on the same
668
+ // dataset sees the rows earlier ones just created.
669
+ async relationship(spec, prior) {
670
+ const fromModelUuid = str(spec["fromModelUuid"]);
671
+ const { model } = await api.storage.model.get(fromModelUuid);
672
+ const datasetUuid = model.datasetUuid;
673
+ const row = {
674
+ fromModelUuid,
675
+ fromColumnSlug: str(spec["fromColumnSlug"]),
676
+ fromPropertySlug: optStr(spec["fromPropertySlug"]) ?? null,
677
+ toModelUuid: str(spec["toModelUuid"]),
678
+ toColumnSlug: str(spec["toColumnSlug"]),
679
+ toPropertySlug: optStr(spec["toPropertySlug"]) ?? null,
680
+ fromSelectedColumnSlugs: null,
681
+ toSelectedColumnSlugs: null,
682
+ relation: spec["relation"],
683
+ };
684
+ const { relationships } = await api.storage.relationship.all();
685
+ // Every row in this dataset except the one we manage — kept verbatim (with
686
+ // its uuid) so `set` updates it in place rather than deleting it.
687
+ const others = relationships
688
+ .filter((r) => r.fromDatasetUuid === datasetUuid && r.uuid !== prior?.uuid)
689
+ .map(toSetInput);
690
+ const ours = prior === undefined ? row : { ...row, uuid: prior.uuid };
691
+ const { relationships: after } = await api.storage.relationship.set({
692
+ datasetUuid,
693
+ relationships: [...others, ours],
694
+ });
695
+ if (prior !== undefined)
696
+ return { uuid: prior.uuid };
697
+ const created = after.find((r) => r.fromModelUuid === row.fromModelUuid &&
698
+ r.toModelUuid === row.toModelUuid &&
699
+ r.fromColumnSlug === row.fromColumnSlug &&
700
+ r.toColumnSlug === row.toColumnSlug &&
701
+ r.fromPropertySlug === row.fromPropertySlug &&
702
+ r.toPropertySlug === row.toPropertySlug &&
703
+ r.relation === row.relation);
704
+ if (created === undefined) {
705
+ throw new Error(`relationship was set but not found in the dataset's relationships`);
706
+ }
707
+ return { uuid: created.uuid };
708
+ },
709
+ };
710
+ }
711
+ // Project a live relationship into a `set` payload entry: drop the
712
+ // dataset-scoped fields (derived server-side, not author-set) but KEEP its uuid
713
+ // so `set` updates it in place instead of deleting it. `all()` returns nullable
714
+ // fields as `undefined` while `set()` expects `null`, so normalize each here.
715
+ function toSetInput(relationship) {
716
+ return {
717
+ uuid: relationship.uuid,
718
+ fromModelUuid: relationship.fromModelUuid,
719
+ fromColumnSlug: relationship.fromColumnSlug,
720
+ fromPropertySlug: relationship.fromPropertySlug ?? null,
721
+ toModelUuid: relationship.toModelUuid,
722
+ toColumnSlug: relationship.toColumnSlug,
723
+ toPropertySlug: relationship.toPropertySlug ?? null,
724
+ fromSelectedColumnSlugs: relationship.fromSelectedColumnSlugs ?? null,
725
+ toSelectedColumnSlugs: relationship.toSelectedColumnSlugs ?? null,
726
+ relation: relationship.relation,
622
727
  };
623
728
  }
624
729
  // Carry the "adopted" marker forward across an update so destroy keeps releasing
@@ -707,6 +812,57 @@ function mapToolTriggers(value) {
707
812
  data: t.data === undefined ? {} : t.data,
708
813
  }));
709
814
  }
815
+ // Upsert the spec's env vars before the build (values are baked into the
816
+ // bundle at build time). `secret("X")` values arrive as the encryption
817
+ // envelope after resolveSecrets and become secret env vars. Keys removed
818
+ // from the spec are left in place; secret values read back as null, so
819
+ // they are re-sent on every apply.
820
+ async function syncEnvVars(api, subject, value) {
821
+ const env = value;
822
+ const entries = Object.entries(env ?? {});
823
+ if (entries.length === 0)
824
+ return;
825
+ const { envVars } = await api.hosting.envVars.list(subject.kind === "worker"
826
+ ? { workerUuid: subject.workerUuid }
827
+ : { appUuid: subject.appUuid });
828
+ for (const [key, raw] of entries) {
829
+ const isSecret = typeof raw !== "string";
830
+ const varValue = typeof raw === "string" ? raw : raw.value;
831
+ const existing = envVars.find((envVar) => envVar.key === key);
832
+ if (existing === undefined) {
833
+ await api.hosting.envVars.create({
834
+ ...subject,
835
+ key,
836
+ value: varValue,
837
+ isSecret,
838
+ });
839
+ }
840
+ else if (existing.value !== varValue || existing.isSecret !== isSecret) {
841
+ await api.hosting.envVars.update({
842
+ uuid: existing.uuid,
843
+ value: varValue,
844
+ isSecret,
845
+ });
846
+ }
847
+ }
848
+ }
849
+ // Always an array (unlike tools): sending [] on update clears triggers
850
+ // removed from the spec.
851
+ function mapWorkerTriggers(value) {
852
+ const triggers = value;
853
+ if (triggers === undefined)
854
+ return [];
855
+ return triggers.map((t) => ({
856
+ type: "cron",
857
+ name: t.name === undefined ? t.path : t.name,
858
+ description: t.description,
859
+ cron: t.cron,
860
+ path: t.path,
861
+ method: t.method === undefined ? "POST" : t.method,
862
+ data: t.data,
863
+ headers: t.headers,
864
+ }));
865
+ }
710
866
  function mapAgentTriggers(value) {
711
867
  // triggers are optional on both agent.create and agent.update — omit the key
712
868
  // entirely when there are none rather than sending an empty array.
@@ -1 +1 @@
1
- {"version":3,"file":"readers.live.d.ts","sourceRoot":"","sources":["../../../src/deploy/readers.live.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAA8B,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAoD5E,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,WAAW,CAwRjD"}
1
+ {"version":3,"file":"readers.live.d.ts","sourceRoot":"","sources":["../../../src/deploy/readers.live.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAA8B,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAoD5E,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,WAAW,CA2TjD"}
@@ -228,6 +228,17 @@ export function liveReaders(api) {
228
228
  return goneOrThrow(error);
229
229
  }
230
230
  },
231
+ async customIntegration(entry) {
232
+ try {
233
+ const { connector } = await api.connection.customIntegration.get({
234
+ uuid: entry.uuid,
235
+ });
236
+ return present(pick(connector, ["kind", "workerUuid"]));
237
+ }
238
+ catch (error) {
239
+ return goneOrThrow(error);
240
+ }
241
+ },
231
242
  async worker(entry) {
232
243
  try {
233
244
  const { worker } = await api.hosting.worker.get(entry.uuid);
@@ -237,7 +248,7 @@ export function liveReaders(api) {
237
248
  ? undefined
238
249
  : promoted["meta"];
239
250
  return present({
240
- ...pick(w, ["name", "description", "slug", "folderUuid"]),
251
+ ...pick(w, ["name", "description", "slug", "folderUuid", "triggers"]),
241
252
  bundle: meta === undefined ? undefined : meta["bundleSha256"],
242
253
  outboundAllowlist: meta === undefined ? undefined : meta["outboundAllowlist"],
243
254
  });
@@ -314,5 +325,27 @@ export function liveReaders(api) {
314
325
  return goneOrThrow(error);
315
326
  }
316
327
  },
328
+ // No per-uuid get — list all and find by the state uuid. Fingerprint only
329
+ // the join fields the executor reconciles.
330
+ async relationship(entry) {
331
+ try {
332
+ const { relationships } = await api.storage.relationship.all();
333
+ const found = relationships.find((r) => r.uuid === entry.uuid);
334
+ if (found === undefined)
335
+ return GONE;
336
+ return present(pick(found, [
337
+ "fromModelUuid",
338
+ "fromColumnSlug",
339
+ "fromPropertySlug",
340
+ "toModelUuid",
341
+ "toColumnSlug",
342
+ "toPropertySlug",
343
+ "relation",
344
+ ]));
345
+ }
346
+ catch (error) {
347
+ return goneOrThrow(error);
348
+ }
349
+ },
317
350
  };
318
351
  }