@cargo-ai/cdk 1.0.3 → 1.0.5

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 (63) hide show
  1. package/README.md +64 -35
  2. package/build/src/cli/auth.d.ts +9 -0
  3. package/build/src/cli/auth.d.ts.map +1 -0
  4. package/build/src/cli/auth.js +100 -0
  5. package/build/src/cli/bin.d.ts +3 -0
  6. package/build/src/cli/bin.d.ts.map +1 -0
  7. package/build/src/cli/bin.js +9 -0
  8. package/build/src/cli/commands/deploy.d.ts +4 -0
  9. package/build/src/cli/commands/deploy.d.ts.map +1 -0
  10. package/build/src/cli/commands/deploy.js +531 -0
  11. package/build/src/cli/commands/index.d.ts +4 -0
  12. package/build/src/cli/commands/index.d.ts.map +1 -0
  13. package/build/src/cli/commands/index.js +11 -0
  14. package/build/src/cli/commands/init.d.ts +3 -0
  15. package/build/src/cli/commands/init.d.ts.map +1 -0
  16. package/build/src/cli/commands/init.js +76 -0
  17. package/build/src/cli/commands/inputTypes.d.ts +24 -0
  18. package/build/src/cli/commands/inputTypes.d.ts.map +1 -0
  19. package/build/src/cli/commands/inputTypes.js +273 -0
  20. package/build/src/cli/commands/templates.d.ts +6 -0
  21. package/build/src/cli/commands/templates.d.ts.map +1 -0
  22. package/build/src/cli/commands/templates.js +33 -0
  23. package/build/src/cli/commands/types.d.ts +4 -0
  24. package/build/src/cli/commands/types.d.ts.map +1 -0
  25. package/build/src/cli/commands/types.js +385 -0
  26. package/build/src/cli/index.d.ts +4 -0
  27. package/build/src/cli/index.d.ts.map +1 -0
  28. package/build/src/cli/index.js +20 -0
  29. package/build/src/cli/io.d.ts +38 -0
  30. package/build/src/cli/io.d.ts.map +1 -0
  31. package/build/src/cli/io.js +226 -0
  32. package/build/src/cli/version.d.ts +2 -0
  33. package/build/src/cli/version.d.ts.map +1 -0
  34. package/build/src/cli/version.js +25 -0
  35. package/build/src/deploy/executors.live.d.ts.map +1 -1
  36. package/build/src/deploy/executors.live.js +15 -3
  37. package/build/src/resources/bundle.d.ts +4 -2
  38. package/build/src/resources/bundle.d.ts.map +1 -1
  39. package/build/src/resources/bundle.js +11 -2
  40. package/build/src/resources/connector.d.ts.map +1 -1
  41. package/build/src/resources/connector.js +3 -2
  42. package/build/src/resources/worker.d.ts.map +1 -1
  43. package/build/src/resources/worker.js +14 -7
  44. package/build/tsconfig.tsbuildinfo +1 -1
  45. package/package.json +14 -4
  46. package/templates/blank/package.json +3 -3
  47. package/templates/full/README.md +21 -20
  48. package/templates/full/apps/dashboard/index.html +1 -1
  49. package/templates/full/apps/dashboard/package.json +18 -3
  50. package/templates/full/apps/dashboard/src/App.tsx +57 -0
  51. package/templates/full/apps/dashboard/src/index.css +4 -0
  52. package/templates/full/apps/dashboard/src/main.tsx +13 -7
  53. package/templates/full/apps/dashboard/tailwind.config.ts +13 -0
  54. package/templates/full/apps/dashboard/tsconfig.json +10 -3
  55. package/templates/full/apps/dashboard/vite.config.ts +22 -0
  56. package/templates/full/package.json +3 -3
  57. package/templates/full/workers/webhook/package.json +12 -1
  58. package/templates/full/workers/webhook/src/index.ts +53 -0
  59. package/templates/full/workers/webhook/tsconfig.json +14 -0
  60. package/templates/full/workers/webhook.ts +9 -7
  61. package/templates/full/apps/dashboard/package-lock.json +0 -1080
  62. package/templates/full/workers/webhook/index.js +0 -12
  63. package/templates/full/workers/webhook/package-lock.json +0 -12
@@ -0,0 +1,385 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { handleApiCall, info, success } from "../io.js";
4
+ import { printJsonSchemaInput, printJsonSchemaType } from "./inputTypes.js";
5
+ // Composite natives surfaced via dedicated SDK syntax / scope helpers —
6
+ // excluded so we don't double-register them. Mirrors the codegen exclusion
7
+ // list in `packages/workflow-sdk/scripts/generateNativeIntegration.ts`
8
+ // (everything in the `logic` category gets dedicated syntax instead of
9
+ // going through `native.<slug>`).
10
+ const COMPOSITE_NATIVE = new Set([
11
+ "start",
12
+ "end",
13
+ "agent",
14
+ "balance",
15
+ "branch",
16
+ "delay",
17
+ "filter",
18
+ "group",
19
+ "humanReview",
20
+ "memory",
21
+ "split",
22
+ "switch",
23
+ "tool",
24
+ "variables",
25
+ ]);
26
+ // Native actions the SDK already ships typed declarations + registrations
27
+ // for (platform-level, identical in every workspace) — skipped so the
28
+ // synced `.d.ts` doesn't redeclare `Native` properties with conflicting
29
+ // (looser) types. Source of truth lives in
30
+ // `packages/workflow-sdk/src/native/manifest.generated.ts`.
31
+ const BUNDLED_NATIVE = new Set([
32
+ "note",
33
+ "python",
34
+ "scoring",
35
+ "script",
36
+ ]);
37
+ export function registerTypesCommand(parent, getApi) {
38
+ parent
39
+ .command("types")
40
+ .description("Generate per-workspace TypeScript types for the Cargo CDK — typed defineConnector/defineModel config + integration actions in workflow bodies")
41
+ .option("--dir <path>", "Repo root the generated files are written into (default: cwd)")
42
+ .option("--out <dir>", "Output subdirectory for generated files (default: .cargo-ai)", ".cargo-ai")
43
+ .action(async (opts) => {
44
+ const api = getApi();
45
+ // Match the other `cdk` commands: `--dir` selects the repo root, and the
46
+ // generated `.cargo-ai` lands there (not wherever the CLI happens to run).
47
+ if (opts.dir !== undefined)
48
+ process.chdir(opts.dir);
49
+ const baseDir = process.cwd();
50
+ const outDir = resolve(baseDir, opts.out);
51
+ info(`Fetching workspace surface…`);
52
+ const payload = await fetchWorkspaceSurface(api);
53
+ mkdirSync(outDir, { recursive: true });
54
+ const typesPath = resolve(outDir, "cargo-types.d.ts");
55
+ writeFileSync(typesPath, renderTypes(payload));
56
+ success(`Wrote ${relativeToBase(baseDir, typesPath)}`);
57
+ const eagerPath = resolve(outDir, "cargo-register.ts");
58
+ writeFileSync(eagerPath, renderEagerRegistration(payload));
59
+ success(`Wrote ${relativeToBase(baseDir, eagerPath)}`);
60
+ info(``);
61
+ info([
62
+ `Next steps:`,
63
+ ` 1. Add "${relativeToBase(baseDir, outDir)}/**/*.d.ts" to your tsconfig`,
64
+ ` include so the types are picked up (a bare dot-dir path is ignored by`,
65
+ ` TypeScript). Enables typed defineConnector/defineModel config.`,
66
+ ` 2. To also type integration actions inside workflow bodies, add`,
67
+ ` \`import "./${relativeToBase(baseDir, eagerPath).replace(/\.ts$/, ".js")}";\``,
68
+ ` to your entry file so the workspace's integrations register at runtime.`,
69
+ ].join("\n"));
70
+ info(``);
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).`);
73
+ });
74
+ }
75
+ async function fetchWorkspaceSurface(api) {
76
+ const integrationsResult = await handleApiCall(() => api.connection.integration.list({}));
77
+ const nativeIntegrationResult = await handleApiCall(() => api.connection.nativeIntegration.get());
78
+ const rawIntegrations = integrationsResult.integrations;
79
+ const integrations = collectIntegrations(rawIntegrations);
80
+ const native = collectNative(nativeIntegrationResult.nativeIntegration.actions);
81
+ const connectorConfigs = collectConnectorConfigs(rawIntegrations);
82
+ const extractorConfigs = collectExtractorConfigs(rawIntegrations);
83
+ return { integrations, native, connectorConfigs, extractorConfigs };
84
+ }
85
+ // integrationSlug → connector config type. Skips integrations whose schema
86
+ // prints as a bare record (no typing gained — the builder already falls back to
87
+ // that), keeping the generated file to connectors that actually add types.
88
+ function collectConnectorConfigs(integrations) {
89
+ const out = [];
90
+ for (const integration of integrations) {
91
+ const schema = integration.connector?.config?.schema;
92
+ if (schema === undefined)
93
+ continue;
94
+ const typeSrc = printJsonSchemaType(schema);
95
+ if (typeSrc === "Record<string, unknown>")
96
+ continue;
97
+ out.push({
98
+ slug: integration.slug,
99
+ name: trimOrUndefined(integration.name),
100
+ typeSrc,
101
+ });
102
+ }
103
+ out.sort((a, b) => a.slug.localeCompare(b.slug));
104
+ return out;
105
+ }
106
+ // integrationSlug → its extractors' config types. Skips extractors whose schema
107
+ // prints as a bare record, and integrations left with none.
108
+ function collectExtractorConfigs(integrations) {
109
+ const out = [];
110
+ for (const integration of integrations) {
111
+ const extractors = [];
112
+ for (const [slug, extractor] of Object.entries(integration.extractors ?? {}).sort(([a], [b]) => a.localeCompare(b))) {
113
+ const schema = extractor.config?.schema;
114
+ if (schema === undefined)
115
+ continue;
116
+ const typeSrc = printJsonSchemaType(schema);
117
+ if (typeSrc === "Record<string, unknown>")
118
+ continue;
119
+ extractors.push({ slug, typeSrc });
120
+ }
121
+ if (extractors.length === 0)
122
+ continue;
123
+ out.push({ integrationSlug: integration.slug, extractors });
124
+ }
125
+ out.sort((a, b) => a.integrationSlug.localeCompare(b.integrationSlug));
126
+ return out;
127
+ }
128
+ function collectIntegrations(integrations) {
129
+ const out = [];
130
+ for (const integration of integrations) {
131
+ const actionEntries = Object.entries(integration.actions ?? {}).sort(([a], [b]) => a.localeCompare(b));
132
+ if (actionEntries.length === 0)
133
+ continue;
134
+ const actions = actionEntries.map(([slug, raw]) => {
135
+ const meta = raw;
136
+ return {
137
+ slug,
138
+ name: trimOrUndefined(meta.name),
139
+ description: trimOrUndefined(meta.description),
140
+ inputTypeSrc: printJsonSchemaInput(meta.config?.schema),
141
+ };
142
+ });
143
+ out.push({
144
+ slug: integration.slug,
145
+ name: trimOrUndefined(integration.name),
146
+ description: trimOrUndefined(integration.description),
147
+ actions,
148
+ });
149
+ }
150
+ out.sort((a, b) => a.slug.localeCompare(b.slug));
151
+ return out;
152
+ }
153
+ function collectNative(actions) {
154
+ const out = [];
155
+ for (const [slug, meta] of Object.entries(actions)) {
156
+ if (COMPOSITE_NATIVE.has(slug))
157
+ continue;
158
+ if (BUNDLED_NATIVE.has(slug))
159
+ continue;
160
+ const name = meta.name.length > 0 ? meta.name : slug;
161
+ out.push({
162
+ slug,
163
+ name,
164
+ description: trimOrUndefined(meta.description),
165
+ });
166
+ }
167
+ out.sort((a, b) => a.slug.localeCompare(b.slug));
168
+ return out;
169
+ }
170
+ function trimOrUndefined(value) {
171
+ if (typeof value !== "string")
172
+ return undefined;
173
+ const trimmed = value.trim();
174
+ return trimmed.length > 0 ? trimmed : undefined;
175
+ }
176
+ const GENERATED_AT = new Date().toISOString();
177
+ const HEADER = `// THIS FILE IS GENERATED by \`cargo-ai cdk types\`. Do not edit by hand.
178
+ // Re-run the command to refresh after adding/removing workspace integrations
179
+ // or connectors.
180
+ // Last regenerated: ${GENERATED_AT}
181
+
182
+ `;
183
+ function renderTypes(payload) {
184
+ const blocks = [];
185
+ // ── @cargo-ai/cdk — typed define* config (connector + extractor schemas) ──
186
+ const cdkLines = [];
187
+ if (payload.connectorConfigs.length > 0) {
188
+ cdkLines.push(` interface ConnectorConfigs {`);
189
+ for (const c of payload.connectorConfigs) {
190
+ const doc = formatJsDoc({ name: c.name ?? c.slug, updatedAt: GENERATED_AT }, " ");
191
+ if (doc !== "")
192
+ cdkLines.push(doc.trimEnd());
193
+ cdkLines.push(` ${jsonKey(c.slug)}: ${c.typeSrc};`);
194
+ }
195
+ cdkLines.push(` }`);
196
+ }
197
+ if (payload.extractorConfigs.length > 0) {
198
+ cdkLines.push(` interface ExtractorConfigs {`);
199
+ for (const e of payload.extractorConfigs) {
200
+ cdkLines.push(` ${jsonKey(e.integrationSlug)}: {`);
201
+ for (const x of e.extractors) {
202
+ cdkLines.push(` ${jsonKey(x.slug)}: ${x.typeSrc};`);
203
+ }
204
+ cdkLines.push(` };`);
205
+ }
206
+ cdkLines.push(` }`);
207
+ }
208
+ if (cdkLines.length > 0) {
209
+ blocks.push([`declare module "@cargo-ai/cdk" {`, ...cdkLines, `}`].join("\n"));
210
+ }
211
+ // ── @cargo-ai/workflow-sdk — integration actions + natives (workflow bodies) ──
212
+ const wfLines = [];
213
+ if (payload.integrations.length > 0) {
214
+ wfLines.push(` interface Integrations {`);
215
+ for (const c of payload.integrations) {
216
+ const intDoc = formatJsDoc({
217
+ name: c.name ?? c.slug,
218
+ description: c.description,
219
+ updatedAt: GENERATED_AT,
220
+ }, " ");
221
+ if (intDoc !== "")
222
+ wfLines.push(intDoc.trimEnd());
223
+ wfLines.push(` ${jsonKey(c.slug)}: {`);
224
+ for (const action of c.actions) {
225
+ const actionDoc = formatJsDoc({
226
+ name: action.name,
227
+ description: action.description,
228
+ category: c.slug,
229
+ updatedAt: GENERATED_AT,
230
+ }, " ");
231
+ if (actionDoc !== "")
232
+ wfLines.push(actionDoc.trimEnd());
233
+ wfLines.push(` ${jsonKey(action.slug)}: IntegrationAction<${action.inputTypeSrc}, Record<string, unknown>>;`);
234
+ }
235
+ wfLines.push(` };`);
236
+ }
237
+ wfLines.push(` }`);
238
+ }
239
+ if (payload.native.length > 0) {
240
+ wfLines.push(` interface Native {`);
241
+ for (const n of payload.native) {
242
+ const doc = formatJsDoc({ name: n.name, description: n.description, updatedAt: GENERATED_AT }, " ");
243
+ if (doc !== "")
244
+ wfLines.push(doc.trimEnd());
245
+ wfLines.push(` ${jsonKey(n.slug)}: NativeAction<Record<string, unknown>, Record<string, unknown>>;`);
246
+ }
247
+ wfLines.push(` }`);
248
+ }
249
+ // The workflow-sdk augmentation needs its builder types imported; `Ref` only
250
+ // when a typed action input references it.
251
+ const preamble = [];
252
+ // The cdk augmentation references `EncryptionRef` at encryption-typed fields.
253
+ if (cdkLines.some((l) => l.includes("EncryptionRef"))) {
254
+ preamble.push(`import type { EncryptionRef } from "@cargo-ai/cdk";`);
255
+ }
256
+ if (wfLines.length > 0) {
257
+ const wfBody = wfLines.join("\n");
258
+ const imports = [
259
+ "IntegrationAction",
260
+ "NativeAction",
261
+ ...(wfBody.includes("Ref<") ? ["Ref"] : []),
262
+ ];
263
+ preamble.push([
264
+ `import type {`,
265
+ ...imports.map((name) => ` ${name},`),
266
+ `} from "@cargo-ai/workflow-sdk";`,
267
+ ].join("\n"));
268
+ blocks.push([`declare module "@cargo-ai/workflow-sdk" {`, wfBody, `}`].join("\n"));
269
+ }
270
+ // `declare module` only *augments* an existing module when this file is itself
271
+ // a module. The workflow-sdk import makes it one; when only the cdk block is
272
+ // emitted there's no import, so force module mode with an empty export.
273
+ if (preamble.length === 0)
274
+ preamble.push(`export {};`);
275
+ return `${HEADER.trim()}\n\n${[...preamble, ...blocks].join("\n\n")}\n`;
276
+ }
277
+ function renderEagerRegistration(payload) {
278
+ const lines = [];
279
+ 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
+ lines.push(``);
287
+ lines.push(`// Workspace surface is empty (no custom integrations / natives).`);
288
+ lines.push(``);
289
+ return lines.join("\n");
290
+ }
291
+ lines.push(`import { ${imports.join(", ")} } from "@cargo-ai/workflow-sdk";`);
292
+ 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
+ for (const n of payload.native) {
304
+ lines.push(`registerNative(${JSON.stringify(n.slug)}, ${JSON.stringify(n.name)});`);
305
+ }
306
+ lines.push(``);
307
+ return lines.join("\n");
308
+ }
309
+ function jsonKey(key) {
310
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
311
+ }
312
+ // Render a JSDoc block above a generated declaration so editors surface the
313
+ // entry's metadata on hover. Returns "" when there's nothing to document.
314
+ function formatJsDoc(doc, indent) {
315
+ const body = [];
316
+ if (doc.name !== undefined && doc.name.trim().length > 0) {
317
+ body.push(doc.name.trim());
318
+ }
319
+ if (doc.description !== undefined && doc.description.trim().length > 0) {
320
+ if (body.length > 0)
321
+ body.push("");
322
+ for (const line of doc.description.split("\n")) {
323
+ body.push(line.trimEnd());
324
+ }
325
+ }
326
+ const tags = [];
327
+ if (doc.category !== undefined) {
328
+ const cats = Array.isArray(doc.category)
329
+ ? doc.category.join(", ")
330
+ : String(doc.category);
331
+ if (cats.trim().length > 0) {
332
+ tags.push(`@category ${cats}`);
333
+ }
334
+ }
335
+ if (doc.author !== undefined && doc.author.trim().length > 0) {
336
+ tags.push(`@author ${doc.author.trim()}`);
337
+ }
338
+ if (doc.version !== undefined && String(doc.version).length > 0) {
339
+ tags.push(`@version ${String(doc.version)}`);
340
+ }
341
+ if (doc.updatedAt !== undefined && doc.updatedAt !== null) {
342
+ const date = doc.updatedAt instanceof Date ? doc.updatedAt : new Date(doc.updatedAt);
343
+ if (!Number.isNaN(date.getTime())) {
344
+ tags.push(`@updated ${date.toISOString().slice(0, 10)}`);
345
+ }
346
+ }
347
+ if (doc.see !== undefined && doc.see.trim().length > 0) {
348
+ tags.push(`@see ${doc.see.trim()}`);
349
+ }
350
+ if (body.length > 0 && tags.length > 0) {
351
+ body.push("");
352
+ }
353
+ body.push(...tags);
354
+ if (body.length === 0)
355
+ return "";
356
+ const out = [`${indent}/**`];
357
+ for (const line of body) {
358
+ const safe = line.replace(/\*\//g, "*\\/");
359
+ out.push(safe.length > 0 ? `${indent} * ${safe}` : `${indent} *`);
360
+ }
361
+ out.push(`${indent} */`);
362
+ return `${out.join("\n")}\n`;
363
+ }
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
+ function relativeToBase(base, path) {
380
+ if (path.startsWith(base)) {
381
+ const rel = path.slice(base.length);
382
+ return rel.startsWith("/") ? rel.slice(1) : rel;
383
+ }
384
+ return path;
385
+ }
@@ -0,0 +1,4 @@
1
+ export { getApi, getConfig } from "./auth.js";
2
+ export { registerCommands } from "./commands/index.js";
3
+ export declare function runCdkBin(argv?: string[]): Promise<void>;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAIvD,wBAAsB,SAAS,CAAC,IAAI,GAAE,MAAM,EAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAU5E"}
@@ -0,0 +1,20 @@
1
+ // Public entry for the Cargo CDK command layer: the standalone `cargo-cdk` bin
2
+ // (`runCdkBin`) and the `registerCommands` that @cargo-ai/cli reuses for its
3
+ // `cargo-ai cdk` alias.
4
+ import { Command } from "commander";
5
+ import { getApi as resolveApiFromCredentials } from "./auth.js";
6
+ import { registerCommands } from "./commands/index.js";
7
+ import { packageVersion } from "./version.js";
8
+ export { getApi, getConfig } from "./auth.js";
9
+ export { registerCommands } from "./commands/index.js";
10
+ // Attach the commands to the bin's root program (→ `cargo-cdk deploy`, not
11
+ // `cargo-cdk cdk deploy`). The caller (`bin.ts`) catches any rejection.
12
+ export async function runCdkBin(argv = process.argv) {
13
+ const program = new Command();
14
+ program
15
+ .name("cargo-cdk")
16
+ .description("Cargo CDK — define your Cargo workspace in code and deploy it (init/types/plan/deploy). Shares the Cargo CLI login.")
17
+ .version(packageVersion());
18
+ registerCommands(program, resolveApiFromCredentials);
19
+ await program.parseAsync(argv);
20
+ }
@@ -0,0 +1,38 @@
1
+ export declare const ExitCodes: {
2
+ readonly Success: 0;
3
+ readonly GenericError: 1;
4
+ readonly InvalidUsage: 2;
5
+ readonly NotAuthenticated: 3;
6
+ readonly PermissionDenied: 4;
7
+ readonly NotFound: 5;
8
+ };
9
+ export type ExitCode = (typeof ExitCodes)[keyof typeof ExitCodes];
10
+ export declare const colors: {
11
+ red: (s: string) => string;
12
+ green: (s: string) => string;
13
+ yellow: (s: string) => string;
14
+ cyan: (s: string) => string;
15
+ dim: (s: string) => string;
16
+ bold: (s: string) => string;
17
+ };
18
+ export declare function outputJson(value: unknown): void;
19
+ export declare function info(message: string): void;
20
+ export declare function success(message: string): void;
21
+ export declare function confirm(question: string): Promise<boolean>;
22
+ type FailWithOpts = {
23
+ code?: ExitCode;
24
+ extra?: Record<string, unknown>;
25
+ };
26
+ export declare function failWith(message: string, opts?: FailWithOpts): never;
27
+ export type Spinner = {
28
+ update: (message: string) => void;
29
+ log: (line: string) => void;
30
+ stop: () => void;
31
+ };
32
+ export declare function startSpinner(message: string): Spinner;
33
+ type HandleApiCallOpts = {
34
+ spinner?: string | false;
35
+ };
36
+ export declare function handleApiCall<T>(fn: () => Promise<T>, opts?: HandleApiCallOpts): Promise<T>;
37
+ export {};
38
+ //# sourceMappingURL=io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../../src/cli/io.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,SAAS;;;;;;;CAOZ,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAclE,eAAO,MAAM,MAAM;aACR,MAAM,KAAG,MAAM;eACb,MAAM,KAAG,MAAM;gBACd,MAAM,KAAG,MAAM;cACjB,MAAM,KAAG,MAAM;aAChB,MAAM,KAAG,MAAM;cACd,MAAM,KAAG,MAAM;CAC1B,CAAC;AAEF,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAM/C;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAID,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUhE;AAED,KAAK,YAAY,GAAG;IAClB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC,CAAC;AAEF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,CAyBpE;AAKD,MAAM,MAAM,OAAO,GAAG;IAEpB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAGlC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AAEF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CA6CrD;AAED,KAAK,iBAAiB,GAAG;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC1B,CAAC;AAOF,wBAAsB,aAAa,CAAC,CAAC,EACnC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,IAAI,CAAC,EAAE,iBAAiB,GACvB,OAAO,CAAC,CAAC,CAAC,CAiDZ"}
@@ -0,0 +1,226 @@
1
+ // Terminal I/O helpers (colors, info/success, confirm, failWith, a spinner, and
2
+ // API-error formatting) — a self-contained copy of the CLI's presentation helpers
3
+ // so the command layer ships with @cargo-ai/cdk and needn't reach into the CLI.
4
+ import { determineIfIsFetcherError } from "@cargo-ai/api";
5
+ export const ExitCodes = {
6
+ Success: 0,
7
+ GenericError: 1,
8
+ InvalidUsage: 2,
9
+ NotAuthenticated: 3,
10
+ PermissionDenied: 4,
11
+ NotFound: 5,
12
+ };
13
+ const useColor = process.stderr.isTTY === true &&
14
+ process.env["NO_COLOR"] === undefined &&
15
+ process.env["CI"] !== "true";
16
+ const colorize = (text, code) => {
17
+ if (useColor !== true) {
18
+ return text;
19
+ }
20
+ return `\x1B[${code}m${text}\x1B[0m`;
21
+ };
22
+ export const colors = {
23
+ red: (s) => colorize(s, "31"),
24
+ green: (s) => colorize(s, "32"),
25
+ yellow: (s) => colorize(s, "33"),
26
+ cyan: (s) => colorize(s, "36"),
27
+ dim: (s) => colorize(s, "2"),
28
+ bold: (s) => colorize(s, "1"),
29
+ };
30
+ export function outputJson(value) {
31
+ if (process.stdout.isTTY === true) {
32
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
33
+ return;
34
+ }
35
+ process.stdout.write(`${JSON.stringify(value)}\n`);
36
+ }
37
+ export function info(message) {
38
+ process.stderr.write(`${message}\n`);
39
+ }
40
+ export function success(message) {
41
+ process.stderr.write(`${colors.green("✓")} ${message}\n`);
42
+ }
43
+ // Ask a yes/no question on stderr (stdout stays reserved for JSON). Resolves
44
+ // false on a non-TTY stdin (callers should require an explicit --yes there).
45
+ export async function confirm(question) {
46
+ if (process.stdin.isTTY !== true)
47
+ return false;
48
+ const { createInterface } = await import("node:readline/promises");
49
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
50
+ try {
51
+ const answer = await rl.question(`${question} [y/N] `);
52
+ return /^y(es)?$/i.test(answer.trim());
53
+ }
54
+ finally {
55
+ rl.close();
56
+ }
57
+ }
58
+ export function failWith(message, opts) {
59
+ const code = opts !== undefined && opts.code !== undefined
60
+ ? opts.code
61
+ : ExitCodes.GenericError;
62
+ const extra = opts !== undefined ? opts.extra : undefined;
63
+ if (process.stderr.isTTY === true) {
64
+ process.stderr.write(`${colors.red("✗")} ${message}\n`);
65
+ if (extra !== undefined) {
66
+ for (const [key, value] of Object.entries(extra)) {
67
+ const valueStr = typeof value === "string" ? value : JSON.stringify(value);
68
+ process.stderr.write(` ${colors.dim(`${key}:`)} ${valueStr}\n`);
69
+ }
70
+ }
71
+ }
72
+ else {
73
+ const payload = { error: message };
74
+ if (extra !== undefined) {
75
+ Object.assign(payload, extra);
76
+ }
77
+ process.stderr.write(`${JSON.stringify(payload)}\n`);
78
+ }
79
+ process.exit(code);
80
+ }
81
+ const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
82
+ const SPINNER_INTERVAL_MS = 80;
83
+ export function startSpinner(message) {
84
+ if (process.stderr.isTTY !== true) {
85
+ // Non-TTY (CI, piped): no animation — just emit each line so progress is
86
+ // still visible in logs.
87
+ process.stderr.write(`${message}\n`);
88
+ return {
89
+ update: (m) => process.stderr.write(`${m}\n`),
90
+ log: (line) => process.stderr.write(`${line}\n`),
91
+ stop: () => undefined,
92
+ };
93
+ }
94
+ process.stderr.write("\x1B[?25l");
95
+ let current = message;
96
+ let frame = 0;
97
+ const render = () => {
98
+ const ch = SPINNER_FRAMES[frame % SPINNER_FRAMES.length];
99
+ if (ch !== undefined) {
100
+ // \x1B[K clears any stale characters when the message shrinks.
101
+ process.stderr.write(`\r\x1B[K${colors.cyan(ch)} ${colors.dim(current)}`);
102
+ }
103
+ frame += 1;
104
+ };
105
+ const id = setInterval(render, SPINNER_INTERVAL_MS);
106
+ let stopped = false;
107
+ return {
108
+ update: (m) => {
109
+ current = m;
110
+ },
111
+ log: (line) => {
112
+ // Wipe the spinner line, drop a permanent line, let the next frame redraw
113
+ // the spinner beneath it.
114
+ process.stderr.write(`\r\x1B[K${line}\n`);
115
+ },
116
+ stop: () => {
117
+ if (stopped) {
118
+ return;
119
+ }
120
+ stopped = true;
121
+ clearInterval(id);
122
+ process.stderr.write("\r\x1B[K\x1B[?25h");
123
+ },
124
+ };
125
+ }
126
+ const SPINNER_DELAY_MS = 200;
127
+ const DEFAULT_SPINNER_MESSAGE = "Loading...";
128
+ // Run an API call, surfacing a delayed spinner and turning FetcherErrors into a
129
+ // clean `failWith` with the right exit code. Used by `cdk types`.
130
+ export async function handleApiCall(fn, opts) {
131
+ const spinnerMessage = opts !== undefined && opts.spinner === false
132
+ ? undefined
133
+ : opts !== undefined && typeof opts.spinner === "string"
134
+ ? opts.spinner
135
+ : DEFAULT_SPINNER_MESSAGE;
136
+ let spinner;
137
+ let startTimer;
138
+ if (spinnerMessage !== undefined) {
139
+ startTimer = setTimeout(() => {
140
+ spinner = startSpinner(spinnerMessage);
141
+ }, SPINNER_DELAY_MS);
142
+ }
143
+ const stopAll = () => {
144
+ if (startTimer !== undefined) {
145
+ clearTimeout(startTimer);
146
+ }
147
+ if (spinner !== undefined) {
148
+ spinner.stop();
149
+ }
150
+ };
151
+ try {
152
+ const result = await fn();
153
+ stopAll();
154
+ return result;
155
+ }
156
+ catch (error) {
157
+ stopAll();
158
+ if (determineIfIsFetcherError(error)) {
159
+ const parsedError = error;
160
+ const status = parsedError.status;
161
+ const body = parsedError.body !== undefined ? parsedError.body : String(error);
162
+ const { message, code } = describeApiError(status, body);
163
+ failWith(message, {
164
+ code,
165
+ extra: { status: status !== undefined ? status : "unknown", body },
166
+ });
167
+ }
168
+ throw error;
169
+ }
170
+ }
171
+ function describeApiError(status, body) {
172
+ const detail = extractApiErrorDetail(body);
173
+ const detailSuffix = detail !== undefined ? `: ${detail}` : "";
174
+ if (status === 401) {
175
+ return {
176
+ message: `Not authenticated${detailSuffix}. Run \`cargo-ai login\` to refresh credentials.`,
177
+ code: ExitCodes.NotAuthenticated,
178
+ };
179
+ }
180
+ if (status === 403) {
181
+ return {
182
+ message: `Permission denied${detailSuffix}. Check your workspace permissions or selected workspace UUID.`,
183
+ code: ExitCodes.PermissionDenied,
184
+ };
185
+ }
186
+ if (status === 404) {
187
+ return {
188
+ message: `Not found${detailSuffix}. Verify the UUID(s) you passed.`,
189
+ code: ExitCodes.NotFound,
190
+ };
191
+ }
192
+ if (status === 429) {
193
+ return {
194
+ message: `Rate limited${detailSuffix}. Retry in a moment.`,
195
+ code: ExitCodes.GenericError,
196
+ };
197
+ }
198
+ if (status !== undefined && status >= 500) {
199
+ return {
200
+ message: `Server error (${String(status)})${detailSuffix}. Retry shortly or check status.getcargo.io.`,
201
+ code: ExitCodes.GenericError,
202
+ };
203
+ }
204
+ return {
205
+ message: `API error (${status !== undefined ? String(status) : "unknown"})${detailSuffix}`,
206
+ code: ExitCodes.GenericError,
207
+ };
208
+ }
209
+ function extractApiErrorDetail(body) {
210
+ if (typeof body === "string" && body.length > 0) {
211
+ return body;
212
+ }
213
+ if (body !== null && typeof body === "object") {
214
+ const record = body;
215
+ if (typeof record["message"] === "string") {
216
+ return record["message"];
217
+ }
218
+ if (typeof record["reason"] === "string") {
219
+ return record["reason"];
220
+ }
221
+ if (typeof record["error"] === "string") {
222
+ return record["error"];
223
+ }
224
+ }
225
+ return undefined;
226
+ }
@@ -0,0 +1,2 @@
1
+ export declare function packageVersion(): string;
2
+ //# sourceMappingURL=version.d.ts.map