@cargo-ai/cdk 1.0.5 → 1.0.6

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 (54) hide show
  1. package/README.md +55 -8
  2. package/build/src/cli/commands/types.js +11 -35
  3. package/build/src/core.d.ts +1 -1
  4. package/build/src/core.d.ts.map +1 -1
  5. package/build/src/deploy/apply.d.ts +6 -0
  6. package/build/src/deploy/apply.d.ts.map +1 -1
  7. package/build/src/deploy/apply.js +4 -0
  8. package/build/src/deploy/destroy.d.ts.map +1 -1
  9. package/build/src/deploy/destroy.js +33 -0
  10. package/build/src/deploy/executors.live.d.ts.map +1 -1
  11. package/build/src/deploy/executors.live.js +157 -1
  12. package/build/src/deploy/readers.live.d.ts.map +1 -1
  13. package/build/src/deploy/readers.live.js +34 -1
  14. package/build/src/index.d.ts +9 -5
  15. package/build/src/index.d.ts.map +1 -1
  16. package/build/src/index.js +4 -2
  17. package/build/src/refs.d.ts +22 -0
  18. package/build/src/refs.d.ts.map +1 -1
  19. package/build/src/refs.js +11 -2
  20. package/build/src/resources/actions.d.ts +39 -19
  21. package/build/src/resources/actions.d.ts.map +1 -1
  22. package/build/src/resources/actions.js +60 -33
  23. package/build/src/resources/agent.d.ts +10 -23
  24. package/build/src/resources/agent.d.ts.map +1 -1
  25. package/build/src/resources/agent.js +8 -25
  26. package/build/src/resources/app.d.ts +3 -1
  27. package/build/src/resources/app.d.ts.map +1 -1
  28. package/build/src/resources/app.js +2 -1
  29. package/build/src/resources/connector.d.ts +10 -0
  30. package/build/src/resources/connector.d.ts.map +1 -1
  31. package/build/src/resources/connector.js +18 -1
  32. package/build/src/resources/customIntegration.d.ts +12 -0
  33. package/build/src/resources/customIntegration.d.ts.map +1 -0
  34. package/build/src/resources/customIntegration.js +18 -0
  35. package/build/src/resources/mcpServer.d.ts +8 -10
  36. package/build/src/resources/mcpServer.d.ts.map +1 -1
  37. package/build/src/resources/mcpServer.js +4 -7
  38. package/build/src/resources/model.d.ts +36 -1
  39. package/build/src/resources/model.d.ts.map +1 -1
  40. package/build/src/resources/model.js +38 -1
  41. package/build/src/resources/relationship.d.ts +21 -0
  42. package/build/src/resources/relationship.d.ts.map +1 -0
  43. package/build/src/resources/relationship.js +39 -0
  44. package/build/src/resources/worker.d.ts +13 -1
  45. package/build/src/resources/worker.d.ts.map +1 -1
  46. package/build/src/resources/worker.js +3 -1
  47. package/build/tsconfig.tsbuildinfo +1 -1
  48. package/package.json +5 -5
  49. package/templates/blank/cargo.ts +17 -22
  50. package/templates/full/README.md +7 -4
  51. package/templates/full/agents/sdr.ts +12 -8
  52. package/templates/full/connectors/hunter.ts +10 -0
  53. package/templates/full/mcp/crm.ts +9 -6
  54. package/templates/full/tools/enrich.ts +28 -9
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Define your entire Cargo workspace in code — connectors, models, plays, tools,
4
4
  agents, MCP servers, context, segments, capacities, territories, folders, files, workers and apps — and deploy it
5
5
  declaratively, the same way you'd manage cloud infrastructure with Pulumi or the
6
- AWS CDK. For a complete overview, see the [Cargo CDK docs](https://docs.getcargo.ai/cdk/overview).
6
+ AWS CDK. For a complete overview, see the [Cargo CDK docs](https://docs.getcargo.ai/get-started/project-layout).
7
7
 
8
8
  ## Requirements
9
9
 
@@ -130,15 +130,19 @@ const flow = defineWorkflow("enrich", { input, output }, ({ input, ai }) => …)
130
130
  export const enrich = defineTool("enrich", { workflow: flow, emojiSlug: "mag" });
131
131
 
132
132
  export const sdr = defineAgent("sdr", {
133
- connector: openai, // handle (or connectorRef("…"))
133
+ connector: openai, // the LLM connector (or connectorRef("…"))
134
134
  languageModel: "gpt-4o",
135
135
  systemPrompt: "Qualify inbound leads.",
136
- models: [{ ref: contacts, readOnly: true }], // handle + options
137
- tools: [enrich], // bare handle
136
+ // everything the agent can call or read — one array, kind inferred per handle
137
+ uses: [
138
+ { ref: contacts, readOnly: true }, // a data model (handle + options)
139
+ enrich, // a tool (bare handle)
140
+ hunter.actions.findEmail, // an action off a connector handle
141
+ ],
138
142
  });
139
143
  ```
140
144
 
141
- See [Defining Resources](https://docs.getcargo.ai/cdk/resources) for every
145
+ See [Defining Resources](https://docs.getcargo.ai/connectors/overview) for every
142
146
  builder (connectors, models, plays, tools, agents, MCP servers, context, segments,
143
147
  capacities, territories, folders, files, workers, apps) and the slug rules.
144
148
 
@@ -161,6 +165,49 @@ There is one context repo per workspace, so this is a singleton. On deploy it
161
165
  writes each changed file into the repo via the runtime — **additive**: files
162
166
  added elsewhere (the UI, other tooling) are left in place.
163
167
 
168
+ ### Unification
169
+
170
+ A model can declare how it feeds Cargo's unified `Account` / `Contact` /
171
+ `AccountEvent` / `ContactEvent` models. A connector model defaults to its
172
+ integration's built-in mapping (`{ source: "integration" }`), so you only pass
173
+ `unification` to override it or to unify a model the integration doesn't map on
174
+ its own. A `custom` unification maps columns to shared references (`domain`,
175
+ `email`, `linkedinId`, …) and can link a child to its parent by pointing at
176
+ another model handle:
177
+
178
+ ```ts
179
+ import { defineModel } from "@cargo-ai/cdk";
180
+
181
+ export const accounts = defineModel("accounts", {
182
+ dataset: warehouse,
183
+ extractSlug: "runQuery",
184
+ unification: {
185
+ source: "custom",
186
+ type: "account",
187
+ uniqueColumns: [{ slug: "website", reference: "domain" }],
188
+ },
189
+ });
190
+
191
+ export const contacts = defineModel("contacts", {
192
+ dataset: warehouse,
193
+ extractSlug: "runQuery",
194
+ unification: {
195
+ source: "custom",
196
+ type: "contact",
197
+ uniqueColumns: [{ slug: "work_email", reference: "workEmail" }],
198
+ // link each contact to its unified account — pass the model handle, not a uuid
199
+ parent: { kind: "model", columnSlug: "account_id", parent: accounts },
200
+ },
201
+ });
202
+ ```
203
+
204
+ Unification isn't part of the create call, so the CDK applies it with a
205
+ follow-up update once the model exists — including models the deploy adopts.
206
+ Passing a parent handle makes the child depend on the parent, so the parent is
207
+ created first and its uuid is injected. See
208
+ [Unification](https://docs.getcargo.ai/models/unification) for the reference and
209
+ priority rules.
210
+
164
211
  ## Commands
165
212
 
166
213
  In a scaffolded project, the everyday commands are `npm run` scripts:
@@ -189,7 +236,7 @@ CLI installed — same engine, same login.
189
236
 
190
237
  Common flags: `--dir <path>`, `--yes` (skip the prompt — required in CI),
191
238
  `--json`, and `--force` (steal a stale state lock). See
192
- [Deploying](https://docs.getcargo.ai/cdk/deploying).
239
+ [Deploying](https://docs.getcargo.ai/deploy/deploying).
193
240
 
194
241
  ## State & drift
195
242
 
@@ -210,8 +257,8 @@ cargo.state.audit.jsonl
210
257
 
211
258
  `npx @cargo-ai/cdk refresh` reports resources changed or deleted outside the CDK
212
259
  (e.g. edited in the Cargo UI); `npx @cargo-ai/cdk deploy --refresh` re-applies your
213
- code over them. See [State & Drift](https://docs.getcargo.ai/cdk/state-and-drift).
260
+ code over them. See [State & Drift](https://docs.getcargo.ai/deploy/state-and-drift).
214
261
 
215
262
  ## Documentation
216
263
 
217
- Full documentation: [docs.getcargo.ai/cdk](https://docs.getcargo.ai/cdk/overview).
264
+ Full documentation: [docs.getcargo.ai/cdk](https://docs.getcargo.ai/get-started/project-layout).
@@ -230,7 +230,10 @@ function renderTypes(payload) {
230
230
  }, " ");
231
231
  if (actionDoc !== "")
232
232
  wfLines.push(actionDoc.trimEnd());
233
- wfLines.push(` ${jsonKey(action.slug)}: IntegrationAction<${action.inputTypeSrc}, Record<string, unknown>>;`);
233
+ // Action outputs aren't schema-typed, so `any` (not `Record<string,
234
+ // unknown>`, which lowers to an unusable nested `Ref`) — the result is
235
+ // then usable as a string, an object, or in any typed slot.
236
+ wfLines.push(` ${jsonKey(action.slug)}: IntegrationAction<${action.inputTypeSrc}, any>;`);
234
237
  }
235
238
  wfLines.push(` };`);
236
239
  }
@@ -242,7 +245,7 @@ function renderTypes(payload) {
242
245
  const doc = formatJsDoc({ name: n.name, description: n.description, updatedAt: GENERATED_AT }, " ");
243
246
  if (doc !== "")
244
247
  wfLines.push(doc.trimEnd());
245
- wfLines.push(` ${jsonKey(n.slug)}: NativeAction<Record<string, unknown>, Record<string, unknown>>;`);
248
+ wfLines.push(` ${jsonKey(n.slug)}: NativeAction<Record<string, unknown>, any>;`);
246
249
  }
247
250
  wfLines.push(` }`);
248
251
  }
@@ -274,32 +277,20 @@ function renderTypes(payload) {
274
277
  preamble.push(`export {};`);
275
278
  return `${HEADER.trim()}\n\n${[...preamble, ...blocks].join("\n\n")}\n`;
276
279
  }
280
+ // Runtime registrations. Only `native` needs one: integration actions are now
281
+ // referenced by handle via `defineWorkflow`'s `uses` (no `integrations.*` runtime
282
+ // registry), so integrations contribute types only (see `renderTypes`).
277
283
  function renderEagerRegistration(payload) {
278
284
  const lines = [];
279
285
  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) {
286
+ if (payload.native.length === 0) {
286
287
  lines.push(``);
287
- lines.push(`// Workspace surface is empty (no custom integrations / natives).`);
288
+ lines.push(`// No custom native actions in this workspace.`);
288
289
  lines.push(``);
289
290
  return lines.join("\n");
290
291
  }
291
- lines.push(`import { ${imports.join(", ")} } from "@cargo-ai/workflow-sdk";`);
292
+ lines.push(`import { registerNative } from "@cargo-ai/workflow-sdk";`);
292
293
  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
294
  for (const n of payload.native) {
304
295
  lines.push(`registerNative(${JSON.stringify(n.slug)}, ${JSON.stringify(n.name)});`);
305
296
  }
@@ -361,21 +352,6 @@ function formatJsDoc(doc, indent) {
361
352
  out.push(`${indent} */`);
362
353
  return `${out.join("\n")}\n`;
363
354
  }
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
355
  function relativeToBase(base, path) {
380
356
  if (path.startsWith(base)) {
381
357
  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
  }