@cargo-ai/cdk 0.1.1

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 (122) hide show
  1. package/README.md +187 -0
  2. package/build/src/core.d.ts +83 -0
  3. package/build/src/core.d.ts.map +1 -0
  4. package/build/src/core.js +205 -0
  5. package/build/src/deploy/apply.d.ts +55 -0
  6. package/build/src/deploy/apply.d.ts.map +1 -0
  7. package/build/src/deploy/apply.js +219 -0
  8. package/build/src/deploy/audit.d.ts +14 -0
  9. package/build/src/deploy/audit.d.ts.map +1 -0
  10. package/build/src/deploy/audit.js +25 -0
  11. package/build/src/deploy/compile.d.ts +36 -0
  12. package/build/src/deploy/compile.d.ts.map +1 -0
  13. package/build/src/deploy/compile.js +99 -0
  14. package/build/src/deploy/destroy.d.ts +20 -0
  15. package/build/src/deploy/destroy.d.ts.map +1 -0
  16. package/build/src/deploy/destroy.js +153 -0
  17. package/build/src/deploy/executors.live.d.ts +4 -0
  18. package/build/src/deploy/executors.live.d.ts.map +1 -0
  19. package/build/src/deploy/executors.live.js +623 -0
  20. package/build/src/deploy/import.d.ts +13 -0
  21. package/build/src/deploy/import.d.ts.map +1 -0
  22. package/build/src/deploy/import.js +87 -0
  23. package/build/src/deploy/index.d.ts +39 -0
  24. package/build/src/deploy/index.d.ts.map +1 -0
  25. package/build/src/deploy/index.js +51 -0
  26. package/build/src/deploy/load.d.ts +7 -0
  27. package/build/src/deploy/load.d.ts.map +1 -0
  28. package/build/src/deploy/load.js +58 -0
  29. package/build/src/deploy/lock.d.ts +10 -0
  30. package/build/src/deploy/lock.d.ts.map +1 -0
  31. package/build/src/deploy/lock.js +101 -0
  32. package/build/src/deploy/plan.d.ts +3 -0
  33. package/build/src/deploy/plan.d.ts.map +1 -0
  34. package/build/src/deploy/plan.js +39 -0
  35. package/build/src/deploy/readers.live.d.ts +4 -0
  36. package/build/src/deploy/readers.live.d.ts.map +1 -0
  37. package/build/src/deploy/readers.live.js +273 -0
  38. package/build/src/deploy/refresh.d.ts +32 -0
  39. package/build/src/deploy/refresh.d.ts.map +1 -0
  40. package/build/src/deploy/refresh.js +76 -0
  41. package/build/src/deploy/state.d.ts +23 -0
  42. package/build/src/deploy/state.d.ts.map +1 -0
  43. package/build/src/deploy/state.js +107 -0
  44. package/build/src/index.d.ts +30 -0
  45. package/build/src/index.d.ts.map +1 -0
  46. package/build/src/index.js +30 -0
  47. package/build/src/refs.d.ts +28 -0
  48. package/build/src/refs.d.ts.map +1 -0
  49. package/build/src/refs.js +34 -0
  50. package/build/src/resources/actions.d.ts +31 -0
  51. package/build/src/resources/actions.d.ts.map +1 -0
  52. package/build/src/resources/actions.js +44 -0
  53. package/build/src/resources/agent.d.ts +71 -0
  54. package/build/src/resources/agent.d.ts.map +1 -0
  55. package/build/src/resources/agent.js +96 -0
  56. package/build/src/resources/app.d.ts +18 -0
  57. package/build/src/resources/app.d.ts.map +1 -0
  58. package/build/src/resources/app.js +33 -0
  59. package/build/src/resources/bundle.d.ts +15 -0
  60. package/build/src/resources/bundle.d.ts.map +1 -0
  61. package/build/src/resources/bundle.js +61 -0
  62. package/build/src/resources/connector.d.ts +30 -0
  63. package/build/src/resources/connector.d.ts.map +1 -0
  64. package/build/src/resources/connector.js +41 -0
  65. package/build/src/resources/context.d.ts +15 -0
  66. package/build/src/resources/context.d.ts.map +1 -0
  67. package/build/src/resources/context.js +58 -0
  68. package/build/src/resources/file.d.ts +15 -0
  69. package/build/src/resources/file.d.ts.map +1 -0
  70. package/build/src/resources/file.js +35 -0
  71. package/build/src/resources/folder.d.ts +20 -0
  72. package/build/src/resources/folder.d.ts.map +1 -0
  73. package/build/src/resources/folder.js +28 -0
  74. package/build/src/resources/mcpServer.d.ts +25 -0
  75. package/build/src/resources/mcpServer.d.ts.map +1 -0
  76. package/build/src/resources/mcpServer.js +34 -0
  77. package/build/src/resources/model.d.ts +34 -0
  78. package/build/src/resources/model.d.ts.map +1 -0
  79. package/build/src/resources/model.js +43 -0
  80. package/build/src/resources/play.d.ts +35 -0
  81. package/build/src/resources/play.d.ts.map +1 -0
  82. package/build/src/resources/play.js +49 -0
  83. package/build/src/resources/tool.d.ts +40 -0
  84. package/build/src/resources/tool.d.ts.map +1 -0
  85. package/build/src/resources/tool.js +36 -0
  86. package/build/src/resources/worker.d.ts +18 -0
  87. package/build/src/resources/worker.d.ts.map +1 -0
  88. package/build/src/resources/worker.js +40 -0
  89. package/build/tsconfig.tsbuildinfo +1 -0
  90. package/package.json +71 -0
  91. package/templates/blank/README.md +24 -0
  92. package/templates/blank/cargo.ts +44 -0
  93. package/templates/blank/package.json +17 -0
  94. package/templates/blank/tsconfig.json +14 -0
  95. package/templates/full/README.md +69 -0
  96. package/templates/full/agents/enricher.ts +13 -0
  97. package/templates/full/agents/sdr.ts +28 -0
  98. package/templates/full/apps/dashboard/index.html +12 -0
  99. package/templates/full/apps/dashboard/package-lock.json +1080 -0
  100. package/templates/full/apps/dashboard/package.json +15 -0
  101. package/templates/full/apps/dashboard/src/main.tsx +12 -0
  102. package/templates/full/apps/dashboard/tsconfig.json +13 -0
  103. package/templates/full/apps/dashboard.ts +9 -0
  104. package/templates/full/connectors/hubspot.ts +12 -0
  105. package/templates/full/connectors/openai.ts +9 -0
  106. package/templates/full/context/context.ts +7 -0
  107. package/templates/full/context/icp.md +22 -0
  108. package/templates/full/context/personas/head-of-growth.md +26 -0
  109. package/templates/full/files/playbook.md +19 -0
  110. package/templates/full/files/playbook.ts +8 -0
  111. package/templates/full/folders/crm.ts +14 -0
  112. package/templates/full/mcp/crm.ts +15 -0
  113. package/templates/full/models/contacts.ts +15 -0
  114. package/templates/full/package.json +17 -0
  115. package/templates/full/plays/onboarding.ts +41 -0
  116. package/templates/full/tools/enrich.ts +34 -0
  117. package/templates/full/tsconfig.json +17 -0
  118. package/templates/full/workers/webhook/index.js +12 -0
  119. package/templates/full/workers/webhook/manifest.json +1 -0
  120. package/templates/full/workers/webhook/package-lock.json +12 -0
  121. package/templates/full/workers/webhook/package.json +6 -0
  122. package/templates/full/workers/webhook.ts +13 -0
package/README.md ADDED
@@ -0,0 +1,187 @@
1
+ # Cargo CDK
2
+
3
+ Define your entire Cargo workspace in code — connectors, models, plays, tools,
4
+ agents, MCP servers, context, folders, files, workers and apps — and deploy it
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).
7
+
8
+ ## Requirements
9
+
10
+ - Node.js 22.x
11
+ - A Cargo API token (create in your workspace under **Settings → API**)
12
+
13
+ ## Installation
14
+
15
+ Install the CDK in your project (for the `define*` builders you import in your
16
+ resource files):
17
+
18
+ ```bash
19
+ npm install @cargo-ai/cdk
20
+ ```
21
+
22
+ The `cargo-ai cdk` commands ship with the Cargo CLI — install it globally:
23
+
24
+ ```bash
25
+ npm install -g @cargo-ai/cli
26
+ cargo-ai login --token <your-api-token>
27
+ ```
28
+
29
+ ## Quick start
30
+
31
+ Scaffold a starter project from a template:
32
+
33
+ ```bash
34
+ cargo-ai cdk init my-workspace # minimal 'blank' template (default)
35
+ cargo-ai cdk init my-workspace --template full # every resource type, wired up
36
+ cargo-ai cdk init --list-templates # see all templates
37
+ cd my-workspace && npm install
38
+ ```
39
+
40
+ Or add a resource file yourself — importing a file **is** registration, there's
41
+ no manifest to maintain:
42
+
43
+ ```ts
44
+ // my-workspace/contacts.ts
45
+ import { defineConnector, defineModel, secret } from "@cargo-ai/cdk";
46
+
47
+ // `secret()` reads the value at deploy time and keeps it out of state and the
48
+ // content hash — use it for credentials. The config shape is per-integration
49
+ // (HubSpot takes a Private App token or an OAuth refresh token); run
50
+ // `cargo-ai cdk types` to have it type-checked (see Typed config below).
51
+ export const hubspot = defineConnector("hubspot", {
52
+ integration: "hubspot",
53
+ config: { method: "privateApp", accessToken: secret("HUBSPOT_API_KEY") },
54
+ });
55
+
56
+ // Pass the connector handle as `dataset` — the model depends on it, so the CDK
57
+ // creates the connector first and injects its dataset uuid.
58
+ export const contacts = defineModel("contacts", {
59
+ dataset: hubspot,
60
+ extractSlug: "fetchRecords",
61
+ });
62
+ ```
63
+
64
+ ```bash
65
+ export HUBSPOT_API_KEY=... # secret() reads this at deploy time
66
+ cargo-ai cdk plan --dir my-workspace # offline diff — no API calls
67
+ cargo-ai cdk deploy --dir my-workspace # create everything, write cargo.state.json
68
+ ```
69
+
70
+ Re-running `deploy` only changes what changed; an unchanged workspace is a no-op.
71
+
72
+ ## Typed config
73
+
74
+ `defineConnector` and `defineModel` config are typed against your workspace's
75
+ actual integration schemas. Generate the types once:
76
+
77
+ ```bash
78
+ cargo-ai cdk types --dir my-workspace
79
+ ```
80
+
81
+ They land in `.cargo-ai/`. Add it to your tsconfig `include` with an explicit
82
+ glob — a bare `.cargo-ai` (a dot-dir) is ignored by TypeScript:
83
+
84
+ ```jsonc
85
+ // tsconfig.json
86
+ "include": ["*.ts", ".cargo-ai/**/*.d.ts"]
87
+ ```
88
+
89
+ After that, `config` is checked against the integration's schema — e.g. HubSpot
90
+ is a discriminated union, so the editor completes `method` and requires the
91
+ matching credential, and `secret()` is accepted wherever a credential is
92
+ expected. Re-run `cdk types` after adding or changing workspace integrations.
93
+
94
+ Typing is a bonus, never a gate: an integration you haven't synced (or a custom
95
+ one) falls back to a loose `Record<string, unknown>`, so `deploy` still works
96
+ without ever running `cdk types`.
97
+
98
+ ## Defining resources
99
+
100
+ Every Cargo resource has a `define*` builder that returns a **handle**. Wire
101
+ resources together by passing one handle to another — the dependency graph is
102
+ just your variable graph. Pass the **handle** directly (or `xxRef("uuid")` for a
103
+ resource you didn't define in code); no `.uuid` at the call site. Where a
104
+ reference takes per-call options, wrap it as `{ ref, …options }`.
105
+
106
+ ```ts
107
+ import { defineAgent, defineTool, defineWorkflow } from "@cargo-ai/cdk";
108
+
109
+ const flow = defineWorkflow("enrich", { input, output }, ({ input, ai }) => …);
110
+ export const enrich = defineTool("enrich", { workflow: flow, emojiSlug: "mag" });
111
+
112
+ export const sdr = defineAgent("sdr", {
113
+ connector: openai, // handle (or connectorRef("…"))
114
+ languageModel: "gpt-4o",
115
+ systemPrompt: "Qualify inbound leads.",
116
+ models: [{ ref: contacts, readOnly: true }], // handle + options
117
+ tools: [enrich], // bare handle
118
+ });
119
+ ```
120
+
121
+ See [Defining Resources](https://docs.getcargo.ai/cdk/resources) for every
122
+ builder (connectors, models, plays, tools, agents, MCP servers, context, folders,
123
+ files, workers, apps) and the slug rules.
124
+
125
+ ### Context
126
+
127
+ `defineContext` manages the workspace's context repository — its git-backed GTM
128
+ knowledge base of markdown/MDX — as code. Point it at a local folder and/or pass
129
+ inline files:
130
+
131
+ ```ts
132
+ import { defineContext } from "@cargo-ai/cdk";
133
+
134
+ export const context = defineContext({
135
+ dir: "./context", // sync a local folder of .md/.mdx
136
+ files: { "generated/icp.md": buildIcp() }, // + inline additions/overrides
137
+ });
138
+ ```
139
+
140
+ There is one context repo per workspace, so this is a singleton. On deploy it
141
+ writes each changed file into the repo via the runtime — **additive**: files
142
+ added elsewhere (the UI, other tooling) are left in place.
143
+
144
+ ## Commands
145
+
146
+ ```bash
147
+ cargo-ai cdk init <directory> # scaffold from a template (--template, --list-templates)
148
+ cargo-ai cdk types # generate per-workspace types for typed config
149
+ cargo-ai cdk plan # offline: diff the code against cargo.state.json
150
+ cargo-ai cdk deploy # create/update resources, write cargo.state.json
151
+ cargo-ai cdk deploy --prune # also delete resources removed from code
152
+ cargo-ai cdk deploy --refresh # re-read live resources, re-apply out-of-band changes
153
+ cargo-ai cdk refresh # read-only: report resources that drifted from code
154
+ cargo-ai cdk import <id> <uuid> # bind an existing live resource into state
155
+ cargo-ai cdk rollback # restore cargo.state.json from the pre-deploy snapshot
156
+ cargo-ai cdk destroy --target <id> # tear down one resource
157
+ cargo-ai cdk destroy --all # tear down everything in state
158
+ ```
159
+
160
+ Common flags: `--dir <path>`, `--yes` (skip the prompt — required in CI),
161
+ `--json`, and `--force` (steal a stale state lock). See
162
+ [Deploying](https://docs.getcargo.ai/cdk/deploying).
163
+
164
+ ## State & drift
165
+
166
+ `cargo-ai cdk deploy` writes `cargo.state.json` — the link from your code to the
167
+ resources Cargo created. **Commit it** (losing it orphans resources; recover a
168
+ link with `cdk import`). It records only uuids, hashes and outputs — never
169
+ secret values.
170
+
171
+ Git-ignore the generated types and the working files the CDK writes alongside it
172
+ (but commit `cargo.state.json`):
173
+
174
+ ```gitignore
175
+ .cargo-ai/
176
+ cargo.state.lock
177
+ cargo.state.bak.json
178
+ cargo.state.audit.jsonl
179
+ ```
180
+
181
+ `cargo-ai cdk refresh` reports resources changed or deleted outside the CDK
182
+ (e.g. edited in the Cargo UI); `cargo-ai cdk deploy --refresh` re-applies your
183
+ code over them. See [State & Drift](https://docs.getcargo.ai/cdk/state-and-drift).
184
+
185
+ ## Documentation
186
+
187
+ Full documentation: [docs.getcargo.ai/cdk](https://docs.getcargo.ai/cdk/overview).
@@ -0,0 +1,83 @@
1
+ export type Token = {
2
+ readonly __token: true;
3
+ readonly resourceId: string;
4
+ readonly field: string;
5
+ };
6
+ export declare function token(resourceId: string, field: string): Token;
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";
9
+ export type ResourceNode = {
10
+ readonly id: string;
11
+ readonly kind: ResourceKind;
12
+ readonly slug: string;
13
+ readonly spec: Record<string, unknown>;
14
+ };
15
+ /**
16
+ * Register a resource. Re-registering the SAME id with identical content is a
17
+ * no-op (the loader may evaluate a file more than once when it's imported both
18
+ * directly and transitively); re-registering an id with DIFFERENT content is a
19
+ * genuine duplicate slug and throws.
20
+ */
21
+ export declare function register(node: ResourceNode): void;
22
+ /**
23
+ * Deterministic JSON serialization for content hashing. Object keys are sorted
24
+ * recursively and `undefined`-valued keys are dropped, so the same logical spec
25
+ * always hashes the same regardless of how the author ordered keys or whether
26
+ * an optional field was passed as `undefined` vs omitted. Without this, the
27
+ * drift hash (and the register-dedup check) are sensitive to insertion order
28
+ * and produce spurious "update" diffs on every deploy. Arrays keep their order
29
+ * (it's semantically significant); Dates serialize to their ISO string.
30
+ */
31
+ export declare function stableStringify(value: unknown): string;
32
+ /** Every resource declared so far. */
33
+ export declare function resources(): ResourceNode[];
34
+ /** Clear the registry — the loader resets between runs; tests between cases. */
35
+ export declare function resetRegistry(): void;
36
+ /** Compose a resource's stable id from its kind + slug, validating the slug. */
37
+ export declare function resourceId(kind: ResourceKind, slug: string): string;
38
+ /** Collect the resource ids a spec depends on (every token reachable in it). */
39
+ export declare function dependenciesOf(value: unknown, acc?: Set<string>): Set<string>;
40
+ /** Deep-replace every token in a spec with the real value its producer returned. */
41
+ export declare function resolveTokens(value: unknown, outputs: Map<string, Record<string, string>>): unknown;
42
+ /**
43
+ * Read a config value from the environment at load time. Its value enters the
44
+ * spec — and therefore the content hash — so changing it shows as drift. Use for
45
+ * non-secret config you WANT to track; use `secret()` for rotating secrets.
46
+ * Returns a visible `${NAME}` placeholder when unset so it surfaces in a plan.
47
+ */
48
+ export declare function env(name: string): string;
49
+ /**
50
+ * A deferred reference to an environment secret, resolved at APPLY time and
51
+ * excluded from the content hash (the spec stores the reference, not the value).
52
+ * Rotating the secret therefore doesn't read as code drift — but a plain deploy
53
+ * won't push the new value either, since nothing in the hash changed; re-apply
54
+ * the resource (any other edit, or `--refresh`) to roll a rotated secret.
55
+ */
56
+ export type SecretRef = {
57
+ readonly __secret: true;
58
+ readonly name: string;
59
+ };
60
+ export declare function secret(name: string): SecretRef;
61
+ /**
62
+ * An encryption-typed connector/model config field. Server-side these fields are
63
+ * the `{ type: "encryption", isEncrypted, value }` envelope; in code you fill one
64
+ * with `secret("ENV")` (resolved to that envelope at deploy) — or, rarely, the
65
+ * raw envelope. `cargo-ai cdk types` prints `EncryptionRef` at exactly the schema
66
+ * positions typed as `encryption`, so `secret()` is accepted there and ONLY there.
67
+ */
68
+ export type EncryptionRef = SecretRef | {
69
+ type: "encryption";
70
+ isEncrypted: boolean;
71
+ value: string;
72
+ };
73
+ export declare function isSecret(value: unknown): value is SecretRef;
74
+ /**
75
+ * Deep-replace every `secret()` reference with its environment value, wrapped in
76
+ * Cargo's encryption envelope (`{ type: "encryption", isEncrypted: false, value }`).
77
+ * Every secret connector-config field (API keys, tokens, refresh tokens) is typed
78
+ * as this envelope server-side — the same shape the UI submits — so a bare string
79
+ * would be rejected as invalid config. The backend encrypts it at rest. Run at
80
+ * apply time (after token resolution). Throws if a referenced secret is unset.
81
+ */
82
+ export declare function resolveSecrets(value: unknown): unknown;
83
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +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,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"}
@@ -0,0 +1,205 @@
1
+ // @cargo-ai/cdk — core
2
+ //
3
+ // The runtime for Cargo's infrastructure-as-code graph (Pulumi/CDK-style).
4
+ // `define*` builders REGISTER a resource and return a HANDLE whose outputs
5
+ // (uuid, datasetUuid, …) are deferred TOKENS. You wire resources by passing one
6
+ // resource's token into another's spec (`defineModel("c", { datasetUuid:
7
+ // connector.datasetUuid })`) — the dependency graph is the variable graph. The
8
+ // deploy engine topologically creates resources and resolves each token to the
9
+ // real value its producer returned. No central manifest, no string slugs.
10
+ import { createHash } from "node:crypto";
11
+ export function token(resourceId, field) {
12
+ return { __token: true, resourceId, field };
13
+ }
14
+ export function isToken(value) {
15
+ return (typeof value === "object" &&
16
+ value !== null &&
17
+ value.__token === true);
18
+ }
19
+ // The registry is stored on globalThis under a global symbol, so that all copies
20
+ // of this module — including the separate instance the tsx loader creates when it
21
+ // transpiles a user's resource files — share ONE registry. Without this, define*
22
+ // (running in the loader's module graph) would populate a different Map than the
23
+ // host reads (the module-duplication / dual-package hazard).
24
+ const REGISTRY_KEY = Symbol.for("@cargo-ai/cdk/registry");
25
+ function registry() {
26
+ const holder = globalThis;
27
+ const existing = holder[REGISTRY_KEY];
28
+ if (existing !== undefined)
29
+ return existing;
30
+ const created = new Map();
31
+ holder[REGISTRY_KEY] = created;
32
+ return created;
33
+ }
34
+ /**
35
+ * Register a resource. Re-registering the SAME id with identical content is a
36
+ * no-op (the loader may evaluate a file more than once when it's imported both
37
+ * directly and transitively); re-registering an id with DIFFERENT content is a
38
+ * genuine duplicate slug and throws.
39
+ */
40
+ export function register(node) {
41
+ const store = registry();
42
+ const existing = store.get(node.id);
43
+ if (existing !== undefined) {
44
+ if (specHash(existing.spec) === specHash(node.spec))
45
+ return;
46
+ throw new Error(`Duplicate resource "${node.id}" — slugs must be unique per kind.`);
47
+ }
48
+ store.set(node.id, node);
49
+ }
50
+ function specHash(spec) {
51
+ return createHash("sha256").update(stableStringify(spec)).digest("hex");
52
+ }
53
+ /**
54
+ * Deterministic JSON serialization for content hashing. Object keys are sorted
55
+ * recursively and `undefined`-valued keys are dropped, so the same logical spec
56
+ * always hashes the same regardless of how the author ordered keys or whether
57
+ * an optional field was passed as `undefined` vs omitted. Without this, the
58
+ * drift hash (and the register-dedup check) are sensitive to insertion order
59
+ * and produce spurious "update" diffs on every deploy. Arrays keep their order
60
+ * (it's semantically significant); Dates serialize to their ISO string.
61
+ */
62
+ export function stableStringify(value) {
63
+ return JSON.stringify(canonicalize(value));
64
+ }
65
+ function canonicalize(value) {
66
+ if (value === null || typeof value !== "object")
67
+ return value;
68
+ if (Array.isArray(value))
69
+ return value.map(canonicalize);
70
+ if (value instanceof Date)
71
+ return value.toISOString();
72
+ const source = value;
73
+ const out = {};
74
+ for (const key of Object.keys(source).sort()) {
75
+ const v = source[key];
76
+ if (v === undefined)
77
+ continue;
78
+ out[key] = canonicalize(v);
79
+ }
80
+ return out;
81
+ }
82
+ /** Every resource declared so far. */
83
+ export function resources() {
84
+ return Array.from(registry().values());
85
+ }
86
+ /** Clear the registry — the loader resets between runs; tests between cases. */
87
+ export function resetRegistry() {
88
+ globalThis[REGISTRY_KEY] = new Map();
89
+ }
90
+ // Slug rules, matched to what each Cargo create endpoint actually enforces so a
91
+ // bad slug fails at define time (in `plan`) instead of mid-deploy. connector +
92
+ // model are snake_case; worker + app use the hosting kebab format; the rest have
93
+ // no API-side slug (uuid- or name-addressed), so they only get a sane base rule.
94
+ const SNAKE_CASE = /^[a-z0-9]+(_[a-z0-9]+)*$/;
95
+ const HOSTING_SLUG = /^[a-z][a-z0-9-]{2,30}$/;
96
+ const BASE_SLUG = /^[a-z0-9][a-z0-9_-]*$/;
97
+ function slugRule(kind) {
98
+ switch (kind) {
99
+ case "connector":
100
+ case "model":
101
+ return {
102
+ pattern: SNAKE_CASE,
103
+ help: 'snake_case — lowercase letters and digits joined by single underscores (e.g. "my_source")',
104
+ };
105
+ case "worker":
106
+ case "app":
107
+ return {
108
+ pattern: HOSTING_SLUG,
109
+ help: 'kebab-case — start with a letter, then 2-30 lowercase letters, digits or hyphens (e.g. "my-worker")',
110
+ };
111
+ default:
112
+ return {
113
+ pattern: BASE_SLUG,
114
+ help: 'lowercase letters, digits, hyphens and underscores, starting with a letter or digit (e.g. "my-thing")',
115
+ };
116
+ }
117
+ }
118
+ /** Compose a resource's stable id from its kind + slug, validating the slug. */
119
+ export function resourceId(kind, slug) {
120
+ const { pattern, help } = slugRule(kind);
121
+ if (!pattern.test(slug)) {
122
+ throw new Error(`invalid ${kind} slug "${slug}" — must be ${help}.`);
123
+ }
124
+ return `${kind}:${slug}`;
125
+ }
126
+ // ── graph helpers used by the deploy engine ──────────────────────────────────
127
+ /** Collect the resource ids a spec depends on (every token reachable in it). */
128
+ export function dependenciesOf(value, acc = new Set()) {
129
+ if (isToken(value)) {
130
+ acc.add(value.resourceId);
131
+ return acc;
132
+ }
133
+ if (Array.isArray(value)) {
134
+ for (const item of value)
135
+ dependenciesOf(item, acc);
136
+ return acc;
137
+ }
138
+ if (value !== null && typeof value === "object") {
139
+ for (const item of Object.values(value))
140
+ dependenciesOf(item, acc);
141
+ return acc;
142
+ }
143
+ return acc;
144
+ }
145
+ /** Deep-replace every token in a spec with the real value its producer returned. */
146
+ export function resolveTokens(value, outputs) {
147
+ if (isToken(value)) {
148
+ const produced = outputs.get(value.resourceId);
149
+ const resolved = produced === undefined ? undefined : produced[value.field];
150
+ if (resolved === undefined) {
151
+ throw new Error(`Unresolved token ${value.resourceId}.${value.field} — producer not applied yet.`);
152
+ }
153
+ return resolved;
154
+ }
155
+ if (Array.isArray(value)) {
156
+ return value.map((item) => resolveTokens(item, outputs));
157
+ }
158
+ if (value !== null && typeof value === "object") {
159
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, resolveTokens(v, outputs)]));
160
+ }
161
+ return value;
162
+ }
163
+ // ── secrets ──────────────────────────────────────────────────────────────────
164
+ /**
165
+ * Read a config value from the environment at load time. Its value enters the
166
+ * spec — and therefore the content hash — so changing it shows as drift. Use for
167
+ * non-secret config you WANT to track; use `secret()` for rotating secrets.
168
+ * Returns a visible `${NAME}` placeholder when unset so it surfaces in a plan.
169
+ */
170
+ export function env(name) {
171
+ const value = process.env[name];
172
+ return value === undefined ? `\${${name}}` : value;
173
+ }
174
+ export function secret(name) {
175
+ return { __secret: true, name };
176
+ }
177
+ export function isSecret(value) {
178
+ return (typeof value === "object" &&
179
+ value !== null &&
180
+ value.__secret === true);
181
+ }
182
+ /**
183
+ * Deep-replace every `secret()` reference with its environment value, wrapped in
184
+ * Cargo's encryption envelope (`{ type: "encryption", isEncrypted: false, value }`).
185
+ * Every secret connector-config field (API keys, tokens, refresh tokens) is typed
186
+ * as this envelope server-side — the same shape the UI submits — so a bare string
187
+ * would be rejected as invalid config. The backend encrypts it at rest. Run at
188
+ * apply time (after token resolution). Throws if a referenced secret is unset.
189
+ */
190
+ export function resolveSecrets(value) {
191
+ if (isSecret(value)) {
192
+ const resolved = process.env[value.name];
193
+ if (resolved === undefined) {
194
+ throw new Error(`secret "${value.name}" is not set — export ${value.name} before deploying.`);
195
+ }
196
+ return { type: "encryption", isEncrypted: false, value: resolved };
197
+ }
198
+ if (Array.isArray(value)) {
199
+ return value.map((item) => resolveSecrets(item));
200
+ }
201
+ if (value !== null && typeof value === "object") {
202
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, resolveSecrets(v)]));
203
+ }
204
+ return value;
205
+ }
@@ -0,0 +1,55 @@
1
+ import { type CompileInput, type PlanEntry, type State, type StateEntry } from "./compile.js";
2
+ import type { LiveReaders } from "./refresh.js";
3
+ export type Outputs = Record<string, string>;
4
+ export type Executors = {
5
+ connector(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
6
+ uuid: string;
7
+ datasetUuid: string;
8
+ }>;
9
+ model(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
10
+ uuid: string;
11
+ }>;
12
+ agent(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
13
+ uuid: string;
14
+ }>;
15
+ play(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
16
+ uuid: string;
17
+ }>;
18
+ tool(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
19
+ uuid: string;
20
+ }>;
21
+ mcpServer(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
22
+ uuid: string;
23
+ }>;
24
+ folder(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
25
+ uuid: string;
26
+ }>;
27
+ file(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
28
+ uuid: string;
29
+ }>;
30
+ worker(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
31
+ uuid: string;
32
+ url: string;
33
+ }>;
34
+ app(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
35
+ uuid: string;
36
+ url: string;
37
+ }>;
38
+ context(spec: Record<string, unknown>, prior: StateEntry | undefined): Promise<Outputs & {
39
+ uuid: string;
40
+ }>;
41
+ };
42
+ export type ApplyResult = {
43
+ state: State;
44
+ applied: PlanEntry[];
45
+ skipped: PlanEntry[];
46
+ };
47
+ export type ApplyProgress = {
48
+ phase: "start" | "done";
49
+ id: string;
50
+ change: "create" | "update";
51
+ index: number;
52
+ total: number;
53
+ };
54
+ export declare function apply(input: CompileInput, executors: Executors, persist?: (state: State) => void, readers?: LiveReaders, onProgress?: (event: ApplyProgress) => void): Promise<ApplyResult>;
55
+ //# sourceMappingURL=apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../src/deploy/apply.ts"],"names":[],"mappings":"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"}