deepline 0.1.106 → 0.1.108

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.
package/dist/cli/index.js CHANGED
@@ -242,12 +242,24 @@ var SDK_RELEASE = {
242
242
  // 0.1.105 ships the billing catalog surface: billing plans, subscribe,
243
243
  // subscription status/cancel, invoices, and the client.billing namespace.
244
244
  // 0.1.106 ships play cell provenance metadata and v2 preview retry hardening.
245
- version: "0.1.106",
245
+ // 0.1.107 ships the v2 quickstart command, the deepline-plays-quickstart
246
+ // skill on the sdk sync surface, and the people-search-to-email prebuilt.
247
+ // 0.1.108 ships explicit dataset column/tool recompute policy and removes
248
+ // the SDK enrich generator's one-second stale policy.
249
+ version: "0.1.108",
246
250
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
247
251
  supportPolicy: {
248
- latest: "0.1.106",
252
+ latest: "0.1.108",
249
253
  minimumSupported: "0.1.53",
250
- deprecatedBelow: "0.1.53"
254
+ deprecatedBelow: "0.1.53",
255
+ commandMinimumSupported: [
256
+ {
257
+ command: "enrich",
258
+ minimumSupported: "0.1.108",
259
+ reason: "Older SDK CLI enrich generated stale play source for the current dataset API."
260
+ }
261
+ ],
262
+ autoUpdatePatchLag: 2
251
263
  }
252
264
  };
253
265
 
@@ -847,6 +859,8 @@ function normalizeStepProgress(value) {
847
859
  value.artifactTableNamespace
848
860
  )
849
861
  } : {},
862
+ ...finiteNumber(value.startedAt) !== null ? { startedAt: finiteNumber(value.startedAt) } : {},
863
+ ...finiteNumber(value.completedAt) !== null ? { completedAt: finiteNumber(value.completedAt) } : {},
850
864
  ...finiteNumber(value.updatedAt) !== null ? { updatedAt: finiteNumber(value.updatedAt) } : {}
851
865
  };
852
866
  }
@@ -3169,7 +3183,7 @@ function shouldSkipCompatibilityCheck() {
3169
3183
  const value = process.env.DEEPLINE_SKIP_SDK_COMPAT_CHECK?.trim().toLowerCase();
3170
3184
  return value === "1" || value === "true" || value === "yes";
3171
3185
  }
3172
- async function checkSdkCompatibility(baseUrl) {
3186
+ async function checkSdkCompatibility(baseUrl, options = {}) {
3173
3187
  if (shouldSkipCompatibilityCheck()) {
3174
3188
  return { response: null, error: null };
3175
3189
  }
@@ -3178,6 +3192,9 @@ async function checkSdkCompatibility(baseUrl) {
3178
3192
  try {
3179
3193
  const url = new URL("/api/v2/sdk/compat", baseUrl);
3180
3194
  url.searchParams.set("version", SDK_VERSION);
3195
+ if (options.command?.trim()) {
3196
+ url.searchParams.set("command", options.command.trim());
3197
+ }
3181
3198
  const response = await fetch(url, {
3182
3199
  method: "GET",
3183
3200
  headers: {
@@ -3198,9 +3215,8 @@ async function checkSdkCompatibility(baseUrl) {
3198
3215
  clearTimeout(timeout);
3199
3216
  }
3200
3217
  }
3201
- async function enforceSdkCompatibility(baseUrl) {
3202
- const { response, error } = await checkSdkCompatibility(baseUrl);
3203
- if (error || !response) {
3218
+ function enforceSdkCompatibilityResponse(response) {
3219
+ if (!response) {
3204
3220
  return;
3205
3221
  }
3206
3222
  if (response.update_required) {
@@ -15479,7 +15495,7 @@ function renderIdiomaticExecuteStep(command, options) {
15479
15495
  ` tool: ${tool},`,
15480
15496
  ` input: ${input2} as any,`,
15481
15497
  ` description: ${stringLiteral((command.description ?? "").trim() || `Run ${command.alias} via ${command.tool}.`)},`,
15482
- ...options.force ? [` staleAfterSeconds: 1,`] : [],
15498
+ ...options.force ? [` force: true,`] : [],
15483
15499
  ` });`,
15484
15500
  ` return ${extraction};`,
15485
15501
  `}`
@@ -15511,17 +15527,13 @@ function renderPlayStep(command, options) {
15511
15527
  ${indent(renderJavascriptBody(command.run_if_js), 6)}
15512
15528
  }` : "null";
15513
15529
  const runIfLines = command.run_if_js ? [` const __dlRunIf = ${runIfJs};`, ` if (!__dlRunIf(row)) return null;`] : [];
15514
- const playOptions = [
15515
- ` description: ${stringLiteral(command.description ?? command.alias)}`,
15516
- ...options.force ? [` staleAfterSeconds: 1`] : []
15517
- ].join(",\n");
15518
15530
  return [
15519
15531
  `async (row, stepCtx) => {`,
15520
15532
  ...options.precheck ? [` if (${options.precheck}) return null;`] : [],
15521
15533
  ...runIfLines,
15522
15534
  ` const payload = __dlTemplate(${payload}, row) as Record<string, unknown>;`,
15523
15535
  ` const result = await stepCtx.runPlay(${callId}, ${playRef}, payload, {`,
15524
- playOptions,
15536
+ ` description: ${stringLiteral(command.description ?? command.alias)}`,
15525
15537
  ` });`,
15526
15538
  ` return __dlPlayResultValue(${alias}, result);`,
15527
15539
  `}`
@@ -15538,12 +15550,17 @@ function renderJavascriptBody(source) {
15538
15550
  }
15539
15551
  return source;
15540
15552
  }
15541
- function renderColumnStep(alias, resolverSource, force) {
15553
+ function renderColumnStep(alias, resolverSource, options = {}) {
15542
15554
  const resolver = indent(resolverSource, 8);
15555
+ const optionFields = [
15556
+ ...options.recompute === true ? ["recompute: true"] : [],
15557
+ ...options.recomputeOnError === true ? ["recomputeOnError: true"] : []
15558
+ ];
15559
+ const optionSource = optionFields.length > 0 ? `{ ${optionFields.join(", ")} }` : null;
15543
15560
  return [
15544
15561
  ` .withColumn(${stringLiteral(alias)},`,
15545
- force ? `${resolver},` : resolver,
15546
- ...force ? [` { staleAfterSeconds: 1 }`] : [],
15562
+ `${resolver}${optionSource ? "," : ""}`,
15563
+ ...optionSource ? [` ${optionSource},`] : [],
15547
15564
  ` )`
15548
15565
  ].join("\n");
15549
15566
  }
@@ -15592,6 +15609,29 @@ function collectMetadataColumns(commands, waterfallGroupId) {
15592
15609
  }
15593
15610
  return columns;
15594
15611
  }
15612
+ function collectGeneratedAliases(commands) {
15613
+ const aliases = [];
15614
+ const addAlias = (alias) => {
15615
+ if (alias && !aliases.includes(alias)) {
15616
+ aliases.push(alias);
15617
+ }
15618
+ };
15619
+ for (const command of commands) {
15620
+ if (isWaterfall(command)) {
15621
+ const before = aliases.length;
15622
+ collectGeneratedAliases(command.commands).forEach(addAlias);
15623
+ if (aliases.length > before) {
15624
+ addAlias(command.with_waterfall);
15625
+ addAlias(`${command.with_waterfall}_source`);
15626
+ }
15627
+ continue;
15628
+ }
15629
+ if (!command.disabled) {
15630
+ addAlias(command.alias);
15631
+ }
15632
+ }
15633
+ return aliases;
15634
+ }
15595
15635
  function renderMetadataColumnStep(config) {
15596
15636
  const columns = collectMetadataColumns(config.commands);
15597
15637
  if (Object.keys(columns).length === 0) {
@@ -15599,8 +15639,8 @@ function renderMetadataColumnStep(config) {
15599
15639
  }
15600
15640
  return [
15601
15641
  ` .withColumn('_metadata',`,
15602
- ` (row) => __dlMergeMetadata(row._metadata, ${stableJson({ columns })}),`,
15603
- ` { staleAfterSeconds: 1 }`,
15642
+ ` (row) => __dlMergeMetadata(__dlMetadataFromRow(row), ${stableJson({ columns })}),`,
15643
+ ` { recompute: true },`,
15604
15644
  ` )`
15605
15645
  ].join("\n");
15606
15646
  }
@@ -15627,7 +15667,7 @@ function renderWaterfallColumns(command, forceAliases, inlineRunJavascript, idio
15627
15667
  inlineRunJavascript,
15628
15668
  idiomaticGetters
15629
15669
  }),
15630
- force
15670
+ { recompute: force, recomputeOnError: true }
15631
15671
  );
15632
15672
  }).filter((line) => line !== null);
15633
15673
  const aliases = activeChildren.map((nested) => nested.alias);
@@ -15643,15 +15683,13 @@ function renderWaterfallColumns(command, forceAliases, inlineRunJavascript, idio
15643
15683
  }
15644
15684
  return [
15645
15685
  ...columnSteps,
15646
- renderColumnStep(
15647
- command.with_waterfall,
15648
- `(row) => ${returnExpr}`,
15649
- forceParent
15650
- ),
15686
+ renderColumnStep(command.with_waterfall, `(row) => ${returnExpr}`, {
15687
+ recompute: forceParent
15688
+ }),
15651
15689
  renderColumnStep(
15652
15690
  `${command.with_waterfall}_source`,
15653
15691
  typeof command.min_results === "number" ? `(row) => __dlContributingAliases(row, ${stableJson(aliases)})` : `(row) => __dlFirstMeaningfulAlias(row, ${stableJson(aliases)})`,
15654
- forceParent
15692
+ { recompute: forceParent }
15655
15693
  )
15656
15694
  ];
15657
15695
  }
@@ -15688,18 +15726,20 @@ function compileEnrichConfigToPlaySource(config, options = {}) {
15688
15726
  inlineRunJavascript,
15689
15727
  idiomaticGetters
15690
15728
  }),
15691
- force
15729
+ { recompute: force, recomputeOnError: true }
15692
15730
  )
15693
15731
  );
15694
15732
  });
15695
15733
  const columnStepSource = columnSteps.length > 0 ? columnSteps.join("\n") : ` .withColumn('noop', () => null)`;
15696
15734
  const metadataColumnSource = renderMetadataColumnStep(config);
15735
+ const generatedAliases = collectGeneratedAliases(config.commands);
15697
15736
  const body = [
15698
15737
  `export default definePlay(${stringLiteral(playName)}, async (ctx, input: EnrichInput) => {`,
15699
15738
  ` const sourceRows = await ctx.csv<Record<string, unknown>>(input.file);`,
15700
15739
  ` const rowStart = __dlNonNegativeInteger(input.rowStart, 0);`,
15701
15740
  ` const rowEndExclusive = Number.isFinite(input.rowEnd) ? Math.max(rowStart, __dlWholeNumber(input.rowEnd, rowStart) + 1) : undefined;`,
15702
- ` const rows = sourceRows.slice(rowStart, rowEndExclusive, { key: 'deepline_enrich_selected_rows', sourceLabel: 'Selected enrich rows' });`,
15741
+ ` const selectedRows = sourceRows.slice(rowStart, rowEndExclusive, { key: 'deepline_enrich_selected_rows', sourceLabel: 'Selected enrich rows' });`,
15742
+ ` const rows = selectedRows.map((row) => __dlStripErroredGeneratedColumns(row, ${stableJson(generatedAliases)}), { key: 'deepline_enrich_repair_rows', sourceLabel: 'Selected enrich rows' });`,
15703
15743
  ` const enriched = await ctx`,
15704
15744
  ` .dataset(${stringLiteral(mapName)}, rows)`,
15705
15745
  columnStepSource,
@@ -15785,6 +15825,18 @@ function helperSource() {
15785
15825
  ` return null;`,
15786
15826
  `}`,
15787
15827
  ``,
15828
+ `function __dlMetadataFromRow(row: Record<string, unknown>): unknown {`,
15829
+ ` const direct = __dlParseMetadata(row._metadata);`,
15830
+ ` if (direct) return direct;`,
15831
+ ` const relocated = __dlParseMetadata(row.metadata);`,
15832
+ ` if (relocated) return relocated;`,
15833
+ ` const dotted = __dlParseMetadata(row['metadata.columns']);`,
15834
+ ` if (dotted) return { columns: dotted };`,
15835
+ ` const underscored = __dlParseMetadata(row.metadata__columns);`,
15836
+ ` if (underscored) return { columns: underscored };`,
15837
+ ` return row._metadata;`,
15838
+ `}`,
15839
+ ``,
15788
15840
  `function __dlMergeMetadata(existing: unknown, patch: Record<string, unknown>): Record<string, unknown> {`,
15789
15841
  ` const base = __dlParseMetadata(existing) ?? {};`,
15790
15842
  ` const baseColumns = __dlRecord(base.columns) ? base.columns : {};`,
@@ -15859,6 +15911,40 @@ function helperSource() {
15859
15911
  ` return status === 'error' || status === 'failed' || (typeof record.error === 'string' && record.error.trim() !== '') || (typeof resultError === 'string' && resultError.trim() !== '');`,
15860
15912
  `}`,
15861
15913
  ``,
15914
+ `function __dlGeneratedCellError(value: unknown): boolean {`,
15915
+ ` if (typeof value === 'string') {`,
15916
+ ` const lower = value.trim().toLowerCase();`,
15917
+ ` if (!lower) return false;`,
15918
+ ` if (lower.startsWith('error:') || lower.includes(': error:') || lower.includes('"error"')) return true;`,
15919
+ ` try {`,
15920
+ ` return __dlGeneratedCellError(JSON.parse(value));`,
15921
+ ` } catch {`,
15922
+ ` return false;`,
15923
+ ` }`,
15924
+ ` }`,
15925
+ ` if (!__dlRecord(value)) return false;`,
15926
+ ` const status = typeof value.status === 'string' ? value.status.toLowerCase() : '';`,
15927
+ ` if (status === 'error' || status === 'failed') return true;`,
15928
+ ` if (typeof value.error === 'string' && value.error.trim() !== '') return true;`,
15929
+ ` const raw = __dlGetByPath(value, 'toolResponse.raw') ?? __dlGetByPath(value, 'toolOutput.raw');`,
15930
+ ` if (raw !== undefined && raw !== value && __dlGeneratedCellError(raw)) return true;`,
15931
+ ` const result = value.result;`,
15932
+ ` return result !== undefined && result !== value && __dlGeneratedCellError(result);`,
15933
+ `}`,
15934
+ ``,
15935
+ `function __dlStripErroredGeneratedColumns(row: Record<string, unknown>, aliases: string[]): Record<string, unknown> {`,
15936
+ ` let next: Record<string, unknown> | null = null;`,
15937
+ ` for (const alias of aliases) {`,
15938
+ ` for (const key of __dlAliasCandidates(alias)) {`,
15939
+ ` if (key in row && __dlGeneratedCellError(row[key])) {`,
15940
+ ` if (next === null) next = { ...row };`,
15941
+ ` delete next[key];`,
15942
+ ` }`,
15943
+ ` }`,
15944
+ ` }`,
15945
+ ` return next ?? row;`,
15946
+ `}`,
15947
+ ``,
15862
15948
  `function __dlRawToolOutput(result: unknown): unknown {`,
15863
15949
  ` if (!result || typeof result !== 'object') return result;`,
15864
15950
  ` const record = result as Record<string, unknown>;`,
@@ -16269,7 +16355,7 @@ function helperSource() {
16269
16355
  ` tool: input.tool,`,
16270
16356
  ` input: payload,`,
16271
16357
  ` ...(input.description ? { description: input.description } : {}),`,
16272
- ` ...(input.force ? { staleAfterSeconds: 1 } : {}),`,
16358
+ ` ...(input.force ? { force: true } : {}),`,
16273
16359
  ` });`,
16274
16360
  ` return __dlExtract(input.alias, result, input.row, input.extract, Boolean(input.legacyEnvelope));`,
16275
16361
  `}`
@@ -17951,6 +18037,244 @@ Examples:
17951
18037
  ).option("--org-id <id>", "Switch using an explicit organization id").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgSwitch);
17952
18038
  }
17953
18039
 
18040
+ // src/cli/commands/quickstart.ts
18041
+ var import_node_child_process = require("child_process");
18042
+ var import_node_crypto4 = require("crypto");
18043
+ var import_node_http = require("http");
18044
+ var EXIT_OK2 = 0;
18045
+ var EXIT_AUTH2 = 1;
18046
+ var EXIT_SERVER3 = 2;
18047
+ var MAX_PROMPT_LENGTH = 8e3;
18048
+ var MAX_BODY_BYTES = 64 * 1024;
18049
+ function shellQuote(arg) {
18050
+ return `'${arg.replace(/'/g, `'\\''`)}'`;
18051
+ }
18052
+ function hasClaudeBinary() {
18053
+ try {
18054
+ const result = (0, import_node_child_process.spawnSync)("claude", ["--version"], {
18055
+ stdio: "ignore",
18056
+ shell: process.platform === "win32"
18057
+ });
18058
+ return result.status === 0;
18059
+ } catch {
18060
+ return false;
18061
+ }
18062
+ }
18063
+ function launchClaude(prompt) {
18064
+ return new Promise((resolve16) => {
18065
+ const child = (0, import_node_child_process.spawn)("claude", [prompt], {
18066
+ stdio: "inherit",
18067
+ shell: process.platform === "win32"
18068
+ });
18069
+ child.on("error", () => resolve16(EXIT_SERVER3));
18070
+ child.on("close", (status) => resolve16(status ?? EXIT_OK2));
18071
+ });
18072
+ }
18073
+ function readBody(req) {
18074
+ return new Promise((resolve16, reject) => {
18075
+ let raw = "";
18076
+ req.setEncoding("utf8");
18077
+ req.on("data", (chunk) => {
18078
+ raw += chunk;
18079
+ if (raw.length > MAX_BODY_BYTES) {
18080
+ reject(new Error("Request body too large."));
18081
+ req.destroy();
18082
+ }
18083
+ });
18084
+ req.on("end", () => resolve16(raw));
18085
+ req.on("error", reject);
18086
+ });
18087
+ }
18088
+ function writeJson(res, status, payload) {
18089
+ res.writeHead(status, { "content-type": "application/json" });
18090
+ res.end(JSON.stringify(payload));
18091
+ }
18092
+ function startCallbackServer(input2) {
18093
+ const server = (0, import_node_http.createServer)((req, res) => {
18094
+ res.setHeader("Access-Control-Allow-Origin", req.headers.origin ?? "*");
18095
+ res.setHeader("Vary", "Origin");
18096
+ res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
18097
+ res.setHeader("Access-Control-Allow-Headers", "content-type");
18098
+ res.setHeader("Access-Control-Allow-Private-Network", "true");
18099
+ res.setHeader("Access-Control-Max-Age", "600");
18100
+ if (req.method === "OPTIONS") {
18101
+ res.writeHead(204);
18102
+ res.end();
18103
+ return;
18104
+ }
18105
+ if (req.method !== "POST" || req.url !== "/submit") {
18106
+ writeJson(res, 404, { error: "Not found." });
18107
+ return;
18108
+ }
18109
+ void readBody(req).then((raw) => {
18110
+ let body = null;
18111
+ try {
18112
+ body = JSON.parse(raw);
18113
+ } catch {
18114
+ body = null;
18115
+ }
18116
+ const state = typeof body?.state === "string" ? body.state : "";
18117
+ const prompt = typeof body?.prompt === "string" ? body.prompt.trim() : "";
18118
+ const workflowId = typeof body?.workflow_id === "string" && body.workflow_id.trim() ? body.workflow_id.trim() : null;
18119
+ if (!state || state !== input2.state) {
18120
+ writeJson(res, 403, { error: "Invalid quickstart state token." });
18121
+ return;
18122
+ }
18123
+ if (!prompt) {
18124
+ writeJson(res, 400, { error: "prompt is required." });
18125
+ return;
18126
+ }
18127
+ if (prompt.length > MAX_PROMPT_LENGTH) {
18128
+ writeJson(res, 400, {
18129
+ error: `prompt exceeds ${MAX_PROMPT_LENGTH} characters.`
18130
+ });
18131
+ return;
18132
+ }
18133
+ writeJson(res, 200, { ok: true });
18134
+ setImmediate(() => input2.onSelection({ prompt, workflowId }));
18135
+ }).catch(() => {
18136
+ writeJson(res, 400, { error: "Invalid request body." });
18137
+ });
18138
+ });
18139
+ return new Promise((resolve16, reject) => {
18140
+ server.once("error", reject);
18141
+ server.listen(0, "127.0.0.1", () => {
18142
+ const address = server.address();
18143
+ if (!address || typeof address === "string") {
18144
+ reject(new Error("Failed to bind quickstart callback server."));
18145
+ return;
18146
+ }
18147
+ resolve16({ server, port: address.port });
18148
+ });
18149
+ });
18150
+ }
18151
+ async function handleQuickstart(options) {
18152
+ const jsonOutput = shouldEmitJson(options.json === true);
18153
+ const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
18154
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
18155
+ let apiKey = resolveApiKeyForBaseUrl(baseUrl);
18156
+ if (!apiKey) {
18157
+ if (interactive) {
18158
+ console.error("Not connected yet \u2014 registering this device first.");
18159
+ const registerExit = await handleRegister([]);
18160
+ if (registerExit !== EXIT_OK2) return registerExit;
18161
+ apiKey = resolveApiKeyForBaseUrl(baseUrl);
18162
+ }
18163
+ if (!apiKey) {
18164
+ console.error("Not connected. Run: deepline auth register");
18165
+ return EXIT_AUTH2;
18166
+ }
18167
+ }
18168
+ const state = (0, import_node_crypto4.randomBytes)(32).toString("hex");
18169
+ let resolveSelection;
18170
+ const selectionPromise = new Promise((resolve16) => {
18171
+ resolveSelection = resolve16;
18172
+ });
18173
+ let callback;
18174
+ try {
18175
+ callback = await startCallbackServer({
18176
+ state,
18177
+ onSelection: (selection) => resolveSelection(selection)
18178
+ });
18179
+ } catch (error) {
18180
+ console.error(
18181
+ `Could not start the local quickstart listener: ${error instanceof Error ? error.message : String(error)}`
18182
+ );
18183
+ return EXIT_SERVER3;
18184
+ }
18185
+ const quickstartUrl = `${baseUrl}/cli/quickstart?cb=${callback.port}&state=${state}`;
18186
+ console.error(" Opening the recipe picker in your browser.");
18187
+ console.error(` If it didn't open, cmd+click: ${quickstartUrl}`);
18188
+ if (options.open !== false) {
18189
+ openInBrowser(quickstartUrl);
18190
+ }
18191
+ const timeoutSeconds = Number.parseInt(options.timeout ?? "300", 10);
18192
+ const timeoutMs = (Number.isFinite(timeoutSeconds) && timeoutSeconds > 0 ? timeoutSeconds : 300) * 1e3;
18193
+ const timeoutHandle = setTimeout(
18194
+ () => resolveSelection("timeout"),
18195
+ timeoutMs
18196
+ );
18197
+ const progress = createCliProgress(!jsonOutput);
18198
+ progress.phase("waiting for your pick in the browser (Ctrl+C to skip)");
18199
+ const onSigint = () => resolveSelection("sigint");
18200
+ process.once("SIGINT", onSigint);
18201
+ const outcome = await selectionPromise;
18202
+ clearTimeout(timeoutHandle);
18203
+ process.removeListener("SIGINT", onSigint);
18204
+ callback.server.close();
18205
+ if (outcome === "sigint") {
18206
+ progress.fail();
18207
+ console.error("\nSkipped \u2014 run `deepline quickstart` anytime.");
18208
+ return EXIT_OK2;
18209
+ }
18210
+ if (outcome === "timeout") {
18211
+ progress.fail();
18212
+ console.error(
18213
+ "Timed out waiting for a selection. Run: deepline quickstart"
18214
+ );
18215
+ return EXIT_AUTH2;
18216
+ }
18217
+ progress.complete();
18218
+ const { prompt, workflowId } = outcome;
18219
+ const claudeCommand = `claude ${shellQuote(prompt)}`;
18220
+ const claudeAvailable = hasClaudeBinary();
18221
+ const willLaunch = options.launch !== false && !jsonOutput && interactive && claudeAvailable;
18222
+ printCommandEnvelope(
18223
+ {
18224
+ status: "submitted",
18225
+ workflowId,
18226
+ prompt,
18227
+ claudeCommand,
18228
+ url: quickstartUrl,
18229
+ launched: willLaunch,
18230
+ render: {
18231
+ sections: [
18232
+ {
18233
+ title: "quickstart",
18234
+ lines: [
18235
+ ...workflowId ? [`Recipe: ${workflowId}`] : [],
18236
+ willLaunch ? "Launching Claude Code with your recipe..." : claudeAvailable ? "Run the command below to start your recipe." : "Claude Code not found. Install it (https://code.claude.com/docs/en/overview), then run the command below."
18237
+ ]
18238
+ }
18239
+ ],
18240
+ actions: [{ label: "Run", command: claudeCommand }]
18241
+ }
18242
+ },
18243
+ { json: jsonOutput }
18244
+ );
18245
+ if (willLaunch) {
18246
+ return launchClaude(prompt);
18247
+ }
18248
+ return EXIT_OK2;
18249
+ }
18250
+ function registerQuickstartCommands(program) {
18251
+ program.command("quickstart").description(
18252
+ "Pick a starter recipe in the browser and launch it with Claude Code."
18253
+ ).addHelpText(
18254
+ "after",
18255
+ `
18256
+ Notes:
18257
+ Opens the hosted recipe picker in your browser. The picker sends your
18258
+ selection back to a temporary listener on 127.0.0.1, so the browser must run
18259
+ on the same machine as the CLI. Once a recipe arrives, the CLI prints the
18260
+ matching claude command and, when Claude Code is installed and the shell is
18261
+ interactive, launches it directly. Press Ctrl+C while waiting to skip.
18262
+
18263
+ Examples:
18264
+ deepline quickstart
18265
+ deepline quickstart --no-open
18266
+ deepline quickstart --no-launch --json
18267
+ deepline quickstart --timeout 120
18268
+ `
18269
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--no-open", "Do not open the browser; print the URL only").option("--no-launch", "Print the claude command instead of launching it").option(
18270
+ "--timeout <seconds>",
18271
+ "Maximum seconds to wait for a selection",
18272
+ "300"
18273
+ ).action(async (options) => {
18274
+ process.exitCode = await handleQuickstart(options);
18275
+ });
18276
+ }
18277
+
17954
18278
  // src/cli/commands/secrets.ts
17955
18279
  var import_node_process = require("process");
17956
18280
  var hiddenInputBuffer = "";
@@ -18168,7 +18492,7 @@ var import_node_fs11 = require("fs");
18168
18492
  var import_node_os8 = require("os");
18169
18493
  var import_node_path14 = require("path");
18170
18494
  var import_node_zlib = require("zlib");
18171
- var import_node_crypto4 = require("crypto");
18495
+ var import_node_crypto5 = require("crypto");
18172
18496
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
18173
18497
  var MAX_SESSION_UPLOAD_BYTES = 35e5;
18174
18498
  var MAX_DIRECT_SESSION_DECODED_BYTES = 50 * 1024 * 1024;
@@ -18431,7 +18755,7 @@ async function uploadPayload(path, payload) {
18431
18755
  return await http.post(path, payload);
18432
18756
  }
18433
18757
  async function uploadChunkedSessions(sessions, options) {
18434
- const uploadId = (0, import_node_crypto4.randomUUID)();
18758
+ const uploadId = (0, import_node_crypto5.randomUUID)();
18435
18759
  for (const session of sessions) {
18436
18760
  const bytes = Buffer.from(session.encodedContent, "base64");
18437
18761
  const chunks = [];
@@ -19955,7 +20279,7 @@ function parseExecuteOptions(args) {
19955
20279
  function safeFileStem(value) {
19956
20280
  return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
19957
20281
  }
19958
- function shellQuote(value) {
20282
+ function shellQuote2(value) {
19959
20283
  return `'${value.replace(/'/g, `'\\''`)}'`;
19960
20284
  }
19961
20285
  function powerShellQuote(value) {
@@ -20007,7 +20331,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
20007
20331
  path: scriptPath,
20008
20332
  sourceCode: script,
20009
20333
  projectDir,
20010
- macCopyCommand: `mkdir -p ${shellQuote(projectDir)} && cp ${shellQuote(scriptPath)} ${shellQuote(`${projectDir}/${fileName}`)}`,
20334
+ macCopyCommand: `mkdir -p ${shellQuote2(projectDir)} && cp ${shellQuote2(scriptPath)} ${shellQuote2(`${projectDir}/${fileName}`)}`,
20011
20335
  windowsCopyCommand: `New-Item -ItemType Directory -Force -Path ${powerShellQuote(projectDir.replace(/\//g, "\\"))} | Out-Null; Copy-Item -LiteralPath ${powerShellQuote(scriptPath)} -Destination ${powerShellQuote(`${projectDir.replace(/\//g, "\\")}\\${fileName}`)}`
20012
20336
  };
20013
20337
  }
@@ -20026,7 +20350,7 @@ function buildToolExecuteBaseEnvelope(input2) {
20026
20350
  summary: input2.summary
20027
20351
  };
20028
20352
  const envelopeHasCanonicalOutput = isRecord7(envelope.toolResponse) && Object.prototype.hasOwnProperty.call(envelope.toolResponse, "raw");
20029
- const inspectCommand = `deepline tools execute ${input2.toolId} --input ${shellQuote(JSON.stringify(input2.params))} --json`;
20353
+ const inspectCommand = `deepline tools execute ${input2.toolId} --input ${shellQuote2(JSON.stringify(input2.params))} --json`;
20030
20354
  const actions = input2.listConversion ? [
20031
20355
  {
20032
20356
  label: "next",
@@ -20260,7 +20584,7 @@ var import_promises6 = require("fs/promises");
20260
20584
  var import_node_path17 = require("path");
20261
20585
 
20262
20586
  // src/cli/workflow-to-play.ts
20263
- var import_node_crypto5 = require("crypto");
20587
+ var import_node_crypto6 = require("crypto");
20264
20588
  var HITL_WAIT_FOR_SIGNAL_TOOL = "deepline_workflow_wait_for_signal";
20265
20589
  var HITL_SLACK_TOOL = "slack_message_with_hitl";
20266
20590
  var SUB_WORKFLOW_TOOL_PREFIX = "deepline_workflow_";
@@ -20366,7 +20690,7 @@ function sanitizePlayNameSegment(value) {
20366
20690
  }
20367
20691
  function deriveWorkflowPlayName(workflowName) {
20368
20692
  const base = sanitizePlayNameSegment(workflowName) || "workflow";
20369
- const suffix = (0, import_node_crypto5.createHash)("sha256").update(workflowName).digest("hex").slice(0, 8);
20693
+ const suffix = (0, import_node_crypto6.createHash)("sha256").update(workflowName).digest("hex").slice(0, 8);
20370
20694
  const reserved = suffix.length + 1;
20371
20695
  const allowedBase = Math.max(1, MAX_PLAY_NAME_LENGTH - reserved);
20372
20696
  let name = `${base.slice(0, allowedBase)}_${suffix}`;
@@ -20745,7 +21069,7 @@ Notes:
20745
21069
  }
20746
21070
 
20747
21071
  // src/cli/commands/update.ts
20748
- var import_node_child_process = require("child_process");
21072
+ var import_node_child_process2 = require("child_process");
20749
21073
  var import_node_fs14 = require("fs");
20750
21074
  var import_node_path18 = require("path");
20751
21075
  function posixShellQuote(value) {
@@ -20754,14 +21078,14 @@ function posixShellQuote(value) {
20754
21078
  function windowsCmdQuote(value) {
20755
21079
  return `"${value.replace(/"/g, '""')}"`;
20756
21080
  }
20757
- function shellQuote2(value) {
21081
+ function shellQuote3(value) {
20758
21082
  if (process.platform === "win32") {
20759
21083
  return /^[A-Za-z0-9_./:@%+=,-]+$/.test(value) ? value : windowsCmdQuote(value);
20760
21084
  }
20761
21085
  return posixShellQuote(value);
20762
21086
  }
20763
21087
  function buildSourceUpdateCommand(sourceRoot) {
20764
- const quotedRoot = shellQuote2(sourceRoot);
21088
+ const quotedRoot = shellQuote3(sourceRoot);
20765
21089
  const cdCommand = process.platform === "win32" ? `cd /d ${quotedRoot}` : `cd ${quotedRoot}`;
20766
21090
  return `${cdCommand} && git fetch origin main --tags && git merge --ff-only origin/main`;
20767
21091
  }
@@ -20792,12 +21116,12 @@ function resolveUpdatePlan() {
20792
21116
  kind: "npm-global",
20793
21117
  command,
20794
21118
  args,
20795
- manualCommand: `${command} ${args.map(shellQuote2).join(" ")}`
21119
+ manualCommand: `${command} ${args.map(shellQuote3).join(" ")}`
20796
21120
  };
20797
21121
  }
20798
21122
  function runCommand(command, args) {
20799
21123
  return new Promise((resolveExitCode) => {
20800
- const child = (0, import_node_child_process.spawn)(command, args, {
21124
+ const child = (0, import_node_child_process2.spawn)(command, args, {
20801
21125
  stdio: "inherit",
20802
21126
  shell: process.platform === "win32",
20803
21127
  env: process.env
@@ -20812,6 +21136,12 @@ function runCommand(command, args) {
20812
21136
  });
20813
21137
  });
20814
21138
  }
21139
+ async function runNpmGlobalUpdatePlan(plan) {
21140
+ if (plan.kind !== "npm-global") {
21141
+ return 1;
21142
+ }
21143
+ return runCommand(plan.command, plan.args);
21144
+ }
20815
21145
  async function handleUpdate(options) {
20816
21146
  const plan = resolveUpdatePlan();
20817
21147
  const render = {
@@ -20958,16 +21288,27 @@ function skillsIndexUrl(baseUrl) {
20958
21288
  return `${normalizeBaseUrl2(baseUrl)}${INSTALL_COMMANDS.skills.index_path}`;
20959
21289
  }
20960
21290
  function buildSkillsAddArgs(baseUrl, skillName, options = {}) {
21291
+ const skillNames = Array.isArray(skillName) ? skillName : [skillName];
21292
+ const [firstSkillName, ...extraSkillNames] = skillNames;
20961
21293
  const values = {
20962
21294
  skills_index_url: skillsIndexUrl(baseUrl),
20963
21295
  agents: INSTALL_COMMANDS.skills.default_agents.join(" "),
20964
- skill_name: skillName
21296
+ skill_name: firstSkillName ?? ""
20965
21297
  };
20966
21298
  const rendered = INSTALL_COMMANDS.skills.npx_add_args_template.flatMap(
20967
21299
  (arg, index) => {
20968
21300
  const next = index === 0 && options.firstArg ? options.firstArg : arg;
20969
21301
  const value = renderTemplate(next, values);
20970
- return arg === "{agents}" ? INSTALL_COMMANDS.skills.default_agents : value;
21302
+ if (arg === "{agents}") {
21303
+ return INSTALL_COMMANDS.skills.default_agents;
21304
+ }
21305
+ if (arg === "{skill_name}") {
21306
+ return [
21307
+ value,
21308
+ ...extraSkillNames.flatMap((name) => ["--skill", name])
21309
+ ];
21310
+ }
21311
+ return value;
20971
21312
  }
20972
21313
  );
20973
21314
  return rendered;
@@ -21038,13 +21379,78 @@ function unknownCommandNameFromMessage(message) {
21038
21379
  return command ? command : null;
21039
21380
  }
21040
21381
 
21382
+ // src/cli/self-update.ts
21383
+ var import_node_child_process3 = require("child_process");
21384
+ function envTruthy(name) {
21385
+ const value = process.env[name]?.trim().toLowerCase();
21386
+ return value === "1" || value === "true" || value === "yes";
21387
+ }
21388
+ function isCi() {
21389
+ return envTruthy("CI") || envTruthy("GITHUB_ACTIONS");
21390
+ }
21391
+ function shouldSkipSelfUpdate() {
21392
+ return envTruthy("DEEPLINE_SKIP_SELF_UPDATE") || envTruthy("DEEPLINE_NO_AUTO_UPDATE") || envTruthy("DEEPLINE_SKIP_SDK_AUTO_UPDATE") || envTruthy("DEEPLINE_DISABLE_AUTO_UPDATE") || isCi();
21393
+ }
21394
+ function relaunchCurrentCommand() {
21395
+ return new Promise((resolve16) => {
21396
+ const child = (0, import_node_child_process3.spawn)(process.execPath, process.argv.slice(1), {
21397
+ stdio: "inherit",
21398
+ env: {
21399
+ ...process.env,
21400
+ DEEPLINE_NO_AUTO_UPDATE: "1"
21401
+ }
21402
+ });
21403
+ child.on("error", (error) => {
21404
+ process.stderr.write(
21405
+ `Deepline SDK/CLI updated, but relaunch failed: ${error.message}
21406
+ `
21407
+ );
21408
+ resolve16(1);
21409
+ });
21410
+ child.on("close", (code) => resolve16(code ?? 1));
21411
+ });
21412
+ }
21413
+ async function maybeAutoUpdateAndRelaunch(response) {
21414
+ const autoUpdate = response?.auto_update;
21415
+ if (!response || !autoUpdate?.should_auto_update || shouldSkipSelfUpdate()) {
21416
+ return false;
21417
+ }
21418
+ const plan = resolveUpdatePlan();
21419
+ if (plan.kind !== "npm-global") {
21420
+ return false;
21421
+ }
21422
+ const label = autoUpdate.required ? "requires an update" : "is more than the supported auto-update lag behind";
21423
+ process.stderr.write(
21424
+ `Deepline SDK/CLI ${label}; running ${plan.manualCommand}
21425
+ `
21426
+ );
21427
+ const updateExitCode = await runNpmGlobalUpdatePlan(plan);
21428
+ if (updateExitCode !== 0) {
21429
+ if (autoUpdate.required) {
21430
+ throw new Error(
21431
+ `Automatic Deepline SDK/CLI update failed with exit code ${updateExitCode}. ${response.message}`
21432
+ );
21433
+ }
21434
+ process.stderr.write(
21435
+ `Deepline SDK/CLI auto-update failed with exit code ${updateExitCode}; continuing with ${response.current ?? "current version"}.
21436
+ `
21437
+ );
21438
+ return false;
21439
+ }
21440
+ process.stderr.write("Deepline SDK/CLI updated; rerunning command.\n");
21441
+ const exitCode = await relaunchCurrentCommand();
21442
+ process.exit(exitCode);
21443
+ return true;
21444
+ }
21445
+
21041
21446
  // src/cli/skills-sync.ts
21042
- var import_node_child_process2 = require("child_process");
21447
+ var import_node_child_process4 = require("child_process");
21043
21448
  var import_node_fs15 = require("fs");
21044
21449
  var import_node_os11 = require("os");
21045
21450
  var import_node_path19 = require("path");
21046
21451
  var CHECK_TIMEOUT_MS2 = 3e3;
21047
21452
  var SDK_SKILL_NAME = "deepline-plays";
21453
+ var SDK_SKILL_NAMES = [SDK_SKILL_NAME, "deepline-plays-quickstart"];
21048
21454
  var attemptedSync = false;
21049
21455
  function shouldSkipSkillsSync() {
21050
21456
  const value = process.env.DEEPLINE_SKIP_SKILLS_SYNC?.trim().toLowerCase();
@@ -21161,19 +21567,19 @@ async function fetchSkillsUpdate(baseUrl, localVersion) {
21161
21567
  }
21162
21568
  }
21163
21569
  function buildSkillsInstallArgs(baseUrl) {
21164
- return buildSkillsAddArgs(baseUrl, SDK_SKILL_NAME);
21570
+ return buildSkillsAddArgs(baseUrl, SDK_SKILL_NAMES);
21165
21571
  }
21166
21572
  function buildBunxSkillsInstallArgs(baseUrl) {
21167
- return buildSkillsAddArgs(baseUrl, SDK_SKILL_NAME, { firstArg: "--bun" });
21573
+ return buildSkillsAddArgs(baseUrl, SDK_SKILL_NAMES, { firstArg: "--bun" });
21168
21574
  }
21169
21575
  function hasCommand(command) {
21170
- const result = (0, import_node_child_process2.spawnSync)(command, ["--version"], {
21576
+ const result = (0, import_node_child_process4.spawnSync)(command, ["--version"], {
21171
21577
  stdio: "ignore",
21172
21578
  shell: process.platform === "win32"
21173
21579
  });
21174
21580
  return result.status === 0;
21175
21581
  }
21176
- function shellQuote3(arg) {
21582
+ function shellQuote4(arg) {
21177
21583
  return `'${arg.replace(/'/g, `'\\''`)}'`;
21178
21584
  }
21179
21585
  function resolveSkillsInstallCommands(baseUrl) {
@@ -21181,7 +21587,7 @@ function resolveSkillsInstallCommands(baseUrl) {
21181
21587
  const npxInstall = {
21182
21588
  command: "npx",
21183
21589
  args: npxArgs,
21184
- manualCommand: `npx ${npxArgs.map(shellQuote3).join(" ")}`
21590
+ manualCommand: `npx ${npxArgs.map(shellQuote4).join(" ")}`
21185
21591
  };
21186
21592
  if (hasCommand("bunx")) {
21187
21593
  const bunxArgs = buildBunxSkillsInstallArgs(baseUrl);
@@ -21189,7 +21595,7 @@ function resolveSkillsInstallCommands(baseUrl) {
21189
21595
  {
21190
21596
  command: "bunx",
21191
21597
  args: bunxArgs,
21192
- manualCommand: `bunx ${bunxArgs.map(shellQuote3).join(" ")}`
21598
+ manualCommand: `bunx ${bunxArgs.map(shellQuote4).join(" ")}`
21193
21599
  },
21194
21600
  npxInstall
21195
21601
  ];
@@ -21198,7 +21604,7 @@ function resolveSkillsInstallCommands(baseUrl) {
21198
21604
  }
21199
21605
  function runOneSkillsInstall(install) {
21200
21606
  return new Promise((resolve16) => {
21201
- const child = (0, import_node_child_process2.spawn)(install.command, install.args, {
21607
+ const child = (0, import_node_child_process4.spawn)(install.command, install.args, {
21202
21608
  stdio: ["ignore", "ignore", "pipe"],
21203
21609
  env: process.env
21204
21610
  });
@@ -21266,7 +21672,7 @@ async function syncSdkSkillsIfNeeded(baseUrl) {
21266
21672
  if (!update?.needsUpdate && !hasStaleInstalledSkill || !update?.remoteVersion) {
21267
21673
  return;
21268
21674
  }
21269
- writeSdkSkillsStatusLine("SDK skills changed; syncing deepline-plays skill...");
21675
+ writeSdkSkillsStatusLine("SDK skills changed; syncing Deepline SDK skills...");
21270
21676
  const installed = await runSkillsInstall(baseUrl);
21271
21677
  if (!installed) return;
21272
21678
  if (installedSdkSkillHasStalePositionalExecuteExamples()) {
@@ -21489,7 +21895,9 @@ async function main() {
21489
21895
  progress?.phase("loading deepline cli");
21490
21896
  }
21491
21897
  const program = new import_commander3.Command();
21492
- program.name("deepline").description("Deepline CLI (TypeScript SDK)").version(SDK_VERSION, "-v, --version", "Show version").exitOverride().showHelpAfterError().showSuggestionAfterError(true).addHelpText(
21898
+ program.name("deepline").description(
21899
+ "Deepline CLI \u2014 GTM enrichment tools, plays, and runs from your terminal."
21900
+ ).version(SDK_VERSION, "-v, --version", "Show version").exitOverride().showHelpAfterError().showSuggestionAfterError(true).addHelpText(
21493
21901
  "after",
21494
21902
  `
21495
21903
  Common commands:
@@ -21530,11 +21938,23 @@ Exit codes:
21530
21938
  progress?.phase("checking sdk compatibility");
21531
21939
  }
21532
21940
  const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
21533
- await traceCliSpan(
21941
+ const compatibility = await traceCliSpan(
21534
21942
  "cli.sdk_compatibility",
21535
- { baseUrl },
21536
- () => enforceSdkCompatibility(baseUrl)
21943
+ { baseUrl, command: actionCommand.name() },
21944
+ () => checkSdkCompatibility(baseUrl, {
21945
+ command: actionCommand.name()
21946
+ })
21947
+ );
21948
+ if (compatibility.error) {
21949
+ return;
21950
+ }
21951
+ const relaunched = await maybeAutoUpdateAndRelaunch(
21952
+ compatibility.response
21537
21953
  );
21954
+ if (relaunched) {
21955
+ return;
21956
+ }
21957
+ enforceSdkCompatibilityResponse(compatibility.response);
21538
21958
  if (printStartupPhase) {
21539
21959
  progress?.phase("checking sdk skills");
21540
21960
  }
@@ -21559,6 +21979,7 @@ Exit codes:
21559
21979
  registerDbCommands(program);
21560
21980
  registerFeedbackCommands(program);
21561
21981
  registerUpdateCommand(program);
21982
+ registerQuickstartCommands(program);
21562
21983
  program.command("preflight").description("Run compact health, auth, and Deepline billing checks.").option("--json", "Force JSON output.").addHelpText(
21563
21984
  "after",
21564
21985
  `
@@ -21634,6 +22055,29 @@ Examples:
21634
22055
  process.stdout.write(`deepline ${SDK_VERSION}
21635
22056
  `);
21636
22057
  });
22058
+ if (process.argv.slice(2).length === 0) {
22059
+ if (!process.stdout.isTTY) {
22060
+ printJson({
22061
+ ok: false,
22062
+ exitCode: 2,
22063
+ code: "MISSING_COMMAND",
22064
+ message: "No command provided.",
22065
+ next: "deepline --help"
22066
+ });
22067
+ console.error("error: missing command (see: deepline --help)");
22068
+ } else {
22069
+ program.outputHelp({ error: true });
22070
+ console.error("\nerror: missing command (see usage above)");
22071
+ }
22072
+ recordCliTrace({
22073
+ phase: "cli.main_total",
22074
+ ms: Date.now() - mainStartedAt,
22075
+ ok: false,
22076
+ error: "missing command"
22077
+ });
22078
+ process.exitCode = 2;
22079
+ return;
22080
+ }
21637
22081
  try {
21638
22082
  await program.parseAsync(process.argv);
21639
22083
  recordCliTrace({