deepline 0.3.59 → 0.3.60

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.
@@ -1010,6 +1010,51 @@ function resolveProjectPinTarget(startDir = process.cwd()) {
1010
1010
  function getActiveProjectAuthSource(startDir = process.cwd()) {
1011
1011
  return loadProjectEnvCandidates(startDir)[0] ?? null;
1012
1012
  }
1013
+ function findNearestProjectMarkerDir(startDir) {
1014
+ let current = resolve(startDir);
1015
+ while (true) {
1016
+ if (COWORK_PROJECT_MARKERS.some((marker) => existsSync(join(current, marker)))) {
1017
+ return current;
1018
+ }
1019
+ const parent = dirname(current);
1020
+ if (parent === current) return null;
1021
+ current = parent;
1022
+ }
1023
+ }
1024
+ function resolveCliAuthProvenance(config, startDir = process.cwd()) {
1025
+ const envApiKey = process.env[API_KEY_ENV]?.trim();
1026
+ const folderAuth = getResolvedProjectAuthSource(
1027
+ config.baseUrl,
1028
+ config.apiKey,
1029
+ startDir
1030
+ );
1031
+ const pinTarget = resolveProjectPinTarget(startDir);
1032
+ const markerDir = findNearestProjectMarkerDir(startDir);
1033
+ const project = !pinTarget.ok ? {
1034
+ state: "ambiguous_cowork_project",
1035
+ candidates: pinTarget.candidates
1036
+ } : folderAuth ? {
1037
+ state: "project",
1038
+ dir: dirname(folderAuth.filePath),
1039
+ pinPath: folderAuth.filePath,
1040
+ source: "folder"
1041
+ } : pinTarget.source === "cowork" ? {
1042
+ state: "project",
1043
+ dir: pinTarget.dir,
1044
+ pinPath: join(pinTarget.dir, PROJECT_DEEPLINE_ENV_FILE),
1045
+ source: "cowork"
1046
+ } : markerDir ? {
1047
+ state: "project",
1048
+ dir: markerDir,
1049
+ pinPath: join(markerDir, PROJECT_DEEPLINE_ENV_FILE),
1050
+ source: "marker"
1051
+ } : { state: "not_project" };
1052
+ return {
1053
+ scope: envApiKey ? "env" : folderAuth ? "folder" : "global",
1054
+ project,
1055
+ folderAuthPath: folderAuth?.filePath ?? null
1056
+ };
1057
+ }
1013
1058
 
1014
1059
  // ../plays/artifact-contract-version.ts
1015
1060
  var CURRENT_PLAY_ARTIFACT_CONTRACT_VERSION = 2;
@@ -1060,7 +1105,7 @@ var SDK_RELEASE = {
1060
1105
  // available at toolResponse.rawV2 while toolResponse.raw and all declared
1061
1106
  // getters keep their established compatibility behavior.
1062
1107
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
1063
- version: "0.3.59",
1108
+ version: "0.3.60",
1064
1109
  updateSummary: "Automatic CLI updates are now enabled by default. To opt out, run `deepline settings autoupdate off`; use `deepline settings autoupdate on` to re-enable updates or `deepline settings autoupdate pin <version>` to hold an exact release. This release also adds raw-v2 tool responses at toolResponse.rawV2 while preserving existing toolResponse.raw and declared getters.",
1065
1110
  packageCapabilities: {
1066
1111
  updatePreferences: 1
@@ -4864,6 +4909,7 @@ var DeeplineClient = class {
4864
4909
  ...this.summarizePlayListItem(play, options),
4865
4910
  currentPublishedVersion: play.currentPublishedVersion ?? play.liveRevision?.version ?? null,
4866
4911
  latestRunId: play.latestRunId ?? detail.latestRuns[0]?.workflowId ?? null,
4912
+ ...play.triggerMetadata ? { triggerMetadata: play.triggerMetadata } : {},
4867
4913
  ...play.runtimeLimit ? { runtimeLimit: play.runtimeLimit } : {},
4868
4914
  ...play.activeScheduledPlays ? { activeScheduledPlays: play.activeScheduledPlays } : {}
4869
4915
  };
@@ -13675,6 +13721,39 @@ var PLAY_SQL_LISTENER_TOOL_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]*\.[a-zA-Z][a-zA-Z0-
13675
13721
  var PLAY_SQL_LISTENER_WHERE_OPERATOR_SET = new Set(
13676
13722
  PLAY_SQL_LISTENER_WHERE_OPERATORS
13677
13723
  );
13724
+ var PLAY_AUTHORING_BINDING_FIELDS = {
13725
+ root: [
13726
+ "billing",
13727
+ "compatibility",
13728
+ "cron",
13729
+ "inline",
13730
+ "runtime",
13731
+ "secrets",
13732
+ "sqlListeners",
13733
+ "webhook"
13734
+ ],
13735
+ cron: ["schedule", "timezone", "input"],
13736
+ webhook: ["auth", "hmac"],
13737
+ sqlListener: [
13738
+ "id",
13739
+ "tool",
13740
+ "stream",
13741
+ "where",
13742
+ "table",
13743
+ "monitor",
13744
+ "output",
13745
+ "operations"
13746
+ ]
13747
+ };
13748
+ var PLAY_AUTHORING_DEFINE_PLAY_OPTION_FIELDS = [
13749
+ "id",
13750
+ "name",
13751
+ "description",
13752
+ "input",
13753
+ "run",
13754
+ "bindings",
13755
+ ...PLAY_AUTHORING_BINDING_FIELDS.root
13756
+ ];
13678
13757
  var PLAY_AUTHORING_DOCUMENTATION = {
13679
13758
  fetchBatching: {
13680
13759
  warning: "A static ctx.fetch key inside a loop is a warning because every iteration must still have distinct method, URL, body, or safe headers. One durable receipt must never stand in for every request.",
@@ -13936,6 +14015,22 @@ var PLAY_AUTHORING_FIELD_REGISTRY = {
13936
14015
  description: "IANA timezone. Omitted means UTC.",
13937
14016
  errorMessage: "bindings.cron.timezone must be a valid non-empty IANA timezone string."
13938
14017
  },
14018
+ "bindings.cron.input": {
14019
+ schema: Type.Object({}, { additionalProperties: true }),
14020
+ fixtures: {
14021
+ valid: { apply: true },
14022
+ invalid: ["apply"],
14023
+ absent: void 0,
14024
+ unresolved: ["cronInput"],
14025
+ edition1: void 0
14026
+ },
14027
+ referenceType: "Record<string, unknown>",
14028
+ required: false,
14029
+ resolution: "static-required",
14030
+ issueCode: "play_authoring_binding_invalid",
14031
+ description: "Static JSON object passed to every run created by this cron binding.",
14032
+ errorMessage: "bindings.cron.input must be a static JSON object (no variables, spreads, or functions)."
14033
+ },
13939
14034
  "bindings.sqlListeners": {
13940
14035
  schema: Type.Array(Type.Object({}, { additionalProperties: true })),
13941
14036
  fixtures: {
@@ -14939,14 +15034,15 @@ var PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
14939
15034
  " readonly timeoutMs?: PlayRuntimeTimeoutMs;",
14940
15035
  " readonly receiptWaitMs?: PlayReceiptWaitMs;",
14941
15036
  "};",
14942
- "export type PlayBindings = {",
15037
+ "export type PlayCronInput<TInput> = TInput extends readonly unknown[] ? never : TInput extends object ? TInput : never;",
15038
+ "export type PlayBindings<TInput = Record<string, unknown>> = {",
14943
15039
  ` description?: ${cloudReferenceType("description")};`,
14944
15040
  ` compatibility?: { toolErrorSchemaVersion?: ${cloudReferenceType("compatibility.toolErrorSchemaVersion")}; toolResponseReceiptRevision?: ${cloudReferenceType("compatibility.toolResponseReceiptRevision")} };`,
14945
15041
  ` inline?: ${cloudReferenceType("inline")};`,
14946
15042
  ` billing?: { maxCreditsPerRun?: ${cloudReferenceType("billing.maxCreditsPerRun")} };`,
14947
15043
  ` runtime?: { timeout?: ${cloudReferenceType("runtime.timeout")}; size?: ${cloudReferenceType("runtime.size")} };`,
14948
15044
  ` webhook?: { hmac?: { algorithm?: ${cloudReferenceType("bindings.webhook.hmac.algorithm")}; header?: ${cloudReferenceType("bindings.webhook.hmac.header")}; secretEnv: ${cloudReferenceType("bindings.webhook.hmac.secretEnv")} }; auth?: { type: ${cloudReferenceType("bindings.webhook.auth.type")}; headerFamily: ${cloudReferenceType("bindings.webhook.auth.headerFamily")}; signingSecrets: readonly ${cloudReferenceType("bindings.webhook.auth.signingSecrets[]")}[]; toleranceSeconds?: ${cloudReferenceType("bindings.webhook.auth.toleranceSeconds")} } };`,
14949
- ` cron?: { schedule: ${cloudReferenceType("bindings.cron.schedule")}; timezone?: ${cloudReferenceType("bindings.cron.timezone")} };`,
15045
+ ` cron?: { schedule: ${cloudReferenceType("bindings.cron.schedule")}; timezone?: ${cloudReferenceType("bindings.cron.timezone")}; input?: PlayCronInput<TInput> };`,
14950
15046
  " sqlListeners?: readonly SqlListenerDeclaration[];",
14951
15047
  ` secrets?: readonly ${cloudReferenceType("bindings.secrets[]")}[];`,
14952
15048
  "};",
@@ -15012,8 +15108,10 @@ var PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
15012
15108
  " log(message: string): void;",
15013
15109
  ` sleep(ms: ${cloudReferenceType("ctx.sleep.ms")}): Promise<void>;`,
15014
15110
  "}",
15015
- "export type DefinePlayConfig<TInput, TOutput extends PlayReturnObject> = { id: string; description?: string; input: PlayInputContract<TInput>; run: (ctx: DeeplinePlayRuntimeContext, input: TInput) => Promise<TOutput>; bindings?: PlayBindings; billing?: PlayBindings['billing']; runtime?: PlayBindings['runtime']; compatibility?: PlayBindings['compatibility'] };",
15016
- "export type DefinedPlay<TInput, TOutput extends PlayReturnObject> = ((ctx: DeeplinePlayRuntimeContext, input: TInput) => Promise<TOutput>) & { readonly name: string; readonly __inputType?: TInput; readonly __outputType?: TOutput; readonly runtime?: PlayBindings['runtime']; readonly compatibility?: PlayBindings['compatibility'] };"
15111
+ "export type DefinePlayConfig<TInput, TOutput extends PlayReturnObject> = { id: string; description?: string; input: PlayInputContract<TInput>; run: (ctx: DeeplinePlayRuntimeContext, input: TInput) => Promise<TOutput>; bindings?: PlayBindings<TInput>; billing?: PlayBindings<TInput>['billing']; runtime?: PlayBindings<TInput>['runtime']; compatibility?: PlayBindings<TInput>['compatibility'] };",
15112
+ "export type DefinedPlay<TInput, TOutput extends PlayReturnObject> = ((ctx: DeeplinePlayRuntimeContext, input: TInput) => Promise<TOutput>) & { readonly name: string; readonly __inputType?: TInput; readonly __outputType?: TOutput; readonly bindings?: PlayBindings<TInput>; readonly runtime?: PlayBindings<TInput>['runtime']; readonly compatibility?: PlayBindings<TInput>['compatibility'] };",
15113
+ "export type PlayHandlerInput<THandler> = THandler extends (context: DeeplinePlayRuntimeContext, input: infer TInput) => Promise<PlayReturnObject> ? TInput : never;",
15114
+ "export type PlayHandlerOutput<THandler> = THandler extends (context: DeeplinePlayRuntimeContext, input: unknown) => Promise<infer TOutput extends PlayReturnObject> ? TOutput : never;"
15017
15115
  ];
15018
15116
 
15019
15117
  // ../plays/ts-ast.ts
@@ -20832,6 +20930,11 @@ function writeStartedPlayRun(input2) {
20832
20930
  name: input2.playName,
20833
20931
  status: input2.status ?? "started",
20834
20932
  dashboardUrl: input2.dashboardUrl,
20933
+ execution: {
20934
+ revision: input2.revisionLabel ?? "resolved by Deepline",
20935
+ input_field_count: input2.inputFieldCount ?? 0,
20936
+ cache_policy: input2.forceToolRefresh ? "completed provider receipts may be refreshed" : input2.force ? "fresh run graph; completed provider receipts may still be reused" : "completed provider receipts may be reused"
20937
+ },
20835
20938
  next: {
20836
20939
  inspect: `deepline runs get ${input2.runId} --json`,
20837
20940
  full: `deepline runs get ${input2.runId} --full --json`,
@@ -20843,6 +20946,9 @@ function writeStartedPlayRun(input2) {
20843
20946
  const lines = [
20844
20947
  `Started ${input2.playName}`,
20845
20948
  ` run id: ${input2.runId}`,
20949
+ ` revision: ${input2.revisionLabel ?? "resolved by Deepline"}`,
20950
+ ` input: ${input2.inputFieldCount ?? 0} top-level field(s) supplied`,
20951
+ ` cache: ${input2.forceToolRefresh ? "completed provider receipts may be refreshed" : input2.force ? "fresh run graph; completed provider receipts may still be reused" : "completed provider receipts may be reused"}`,
20846
20952
  ` inspect: deepline runs get ${input2.runId} --json`,
20847
20953
  ` full debug: deepline runs get ${input2.runId} --full --json`,
20848
20954
  ` logs: deepline runs logs ${input2.runId} --json`,
@@ -21380,7 +21486,7 @@ function printPlayTriggers(triggers) {
21380
21486
  if (!triggers) return;
21381
21487
  const hasAny = (triggers.sqlListeners?.length ?? 0) > 0 || Boolean(triggers.cron) || triggers.webhook === true;
21382
21488
  if (!hasAny) return;
21383
- console.log(" triggers:");
21489
+ console.log(" declared triggers (source only):");
21384
21490
  for (const listener of triggers.sqlListeners ?? []) {
21385
21491
  const target = listener.tool && listener.stream ? `${listener.tool}/${listener.stream}` : listener.tool ?? listener.stream ?? listener.id;
21386
21492
  const operations = listener.operations.length ? listener.operations.join(", ") : "INSERT, UPDATE";
@@ -21395,10 +21501,77 @@ function printPlayTriggers(triggers) {
21395
21501
  if (triggers.cron) {
21396
21502
  const timezone = triggers.cron.timezone ? ` (${triggers.cron.timezone})` : "";
21397
21503
  console.log(` cron \u2192 ${triggers.cron.schedule}${timezone}`);
21504
+ if (triggers.cron.input && Object.keys(triggers.cron.input).length > 0) {
21505
+ console.log(` input \u2192 ${JSON.stringify(triggers.cron.input)}`);
21506
+ }
21398
21507
  }
21399
21508
  if (triggers.webhook === true) {
21400
21509
  console.log(" webhook \u2192 enabled");
21401
21510
  }
21511
+ if (triggers.cron) {
21512
+ console.log(
21513
+ " This check validates the declaration only. Publish this revision before a cron can be armed."
21514
+ );
21515
+ }
21516
+ }
21517
+ function cronExecutionLabel(status) {
21518
+ if (!status) return "not configured";
21519
+ if (status === "active") return "armed";
21520
+ if (status === "blocked") return "blocked";
21521
+ return status;
21522
+ }
21523
+ function readLiveCronMetadata(play) {
21524
+ const record2 = readRecord(play);
21525
+ const metadata = readRecord(record2?.triggerMetadata);
21526
+ return {
21527
+ schedule: typeof metadata?.cronSchedule === "string" ? metadata.cronSchedule : null,
21528
+ timezone: typeof metadata?.cronTimezone === "string" ? metadata.cronTimezone : null,
21529
+ nextScheduledAt: typeof metadata?.nextScheduledAt === "number" ? metadata.nextScheduledAt : null
21530
+ };
21531
+ }
21532
+ function liveTriggerStatusLines(status, play) {
21533
+ const cron = readLiveCronMetadata(play);
21534
+ if (!status?.cron && !cron.schedule && !status?.webhook) return [];
21535
+ const lines = [];
21536
+ if (status?.cron || cron.schedule) {
21537
+ const timezone = cron.timezone ? ` (${cron.timezone})` : "";
21538
+ lines.push(
21539
+ ` Scheduled execution: ${cronExecutionLabel(status?.cron ?? null)}${cron.schedule ? ` \u2014 ${cron.schedule}${timezone}` : ""}`
21540
+ );
21541
+ if (cron.nextScheduledAt !== null && status?.cron === "active") {
21542
+ lines.push(
21543
+ ` Next scheduled run: ${formatTimestamp(cron.nextScheduledAt)}`
21544
+ );
21545
+ }
21546
+ if (status?.blockedReason) {
21547
+ lines.push(` Schedule issue: ${status.blockedReason}`);
21548
+ }
21549
+ }
21550
+ if (status?.webhook) {
21551
+ lines.push(` Webhook: ${status.webhook}`);
21552
+ }
21553
+ return lines;
21554
+ }
21555
+ function triggerStatusFromMetadata(value) {
21556
+ const metadata = readRecord(value);
21557
+ return {
21558
+ cron: typeof metadata?.cronStatus === "string" ? metadata.cronStatus : null,
21559
+ webhook: typeof metadata?.webhookStatus === "string" ? metadata.webhookStatus : null,
21560
+ blockedReason: typeof metadata?.blockedReason === "string" ? metadata.blockedReason : null
21561
+ };
21562
+ }
21563
+ function printPublishReceipt(input2) {
21564
+ console.log(`\u2713 Published ${input2.name} as v${input2.liveVersion ?? "?"}`);
21565
+ if (input2.previousLiveVersion !== void 0 && input2.previousLiveVersion !== null) {
21566
+ console.log(` previous live: v${input2.previousLiveVersion}`);
21567
+ }
21568
+ for (const line of liveTriggerStatusLines(
21569
+ triggerStatusFromMetadata(input2.triggerMetadata),
21570
+ { triggerMetadata: input2.triggerMetadata }
21571
+ )) {
21572
+ console.log(line);
21573
+ }
21574
+ console.log(` revisions: deepline plays versions --name ${input2.name}`);
21402
21575
  }
21403
21576
  function printRecognizedSummary(recognized) {
21404
21577
  if (!recognized) return;
@@ -21974,7 +22147,11 @@ async function handleFileBackedRun(options, hooks) {
21974
22147
  dashboardUrl: resolvedDashboardUrl,
21975
22148
  package: options.fullJson ? void 0 : started.package,
21976
22149
  jsonOutput: options.jsonOutput,
21977
- progress
22150
+ progress,
22151
+ inputFieldCount: Object.keys(options.input ?? {}).length,
22152
+ revisionLabel: "local file revision",
22153
+ force: options.force,
22154
+ forceToolRefresh: options.forceToolRefresh
21978
22155
  });
21979
22156
  return 0;
21980
22157
  }
@@ -22157,7 +22334,11 @@ async function handleNamedRun(options, hooks) {
22157
22334
  dashboardUrl: resolvedDashboardUrl,
22158
22335
  package: options.fullJson ? void 0 : started.package,
22159
22336
  jsonOutput: options.jsonOutput,
22160
- progress
22337
+ progress,
22338
+ inputFieldCount: Object.keys(options.input ?? {}).length,
22339
+ revisionLabel: selectedRevisionId ? `pinned revision ${selectedRevisionId}` : "live revision",
22340
+ force: options.force,
22341
+ forceToolRefresh: options.forceToolRefresh
22161
22342
  });
22162
22343
  return 0;
22163
22344
  }
@@ -22823,7 +23004,17 @@ async function handlePlayGet(args) {
22823
23004
  );
22824
23005
  console.log(`Live version: ${detail.play.liveRevision?.version ?? "\u2014"}`);
22825
23006
  console.log(`Draft dirty: ${detail.play.isDraftDirty ? "yes" : "no"}`);
22826
- console.log(`Runs: ${detail.latestRuns.length}`);
23007
+ if (detail.play.isDraftDirty && detail.play.liveRevision?.version) {
23008
+ console.log(
23009
+ ` Scheduled and named runs continue to use live v${detail.play.liveRevision.version}.`
23010
+ );
23011
+ }
23012
+ for (const line of liveTriggerStatusLines(
23013
+ detail.play.triggerStatus,
23014
+ detail.play
23015
+ )) {
23016
+ console.log(line);
23017
+ }
22827
23018
  console.log(`Updated: ${formatTimestamp(detail.play.updatedAt)}`);
22828
23019
  console.log(`Sheet rows: ${detail.sheetSummary?.stats?.total ?? 0}`);
22829
23020
  if (detail.customerDbUrl) {
@@ -22908,19 +23099,23 @@ async function handlePlayList(args) {
22908
23099
  if (play.inputSchema || play.hasInputSchema) {
22909
23100
  process.stdout.write(" inputSchema: yes\n");
22910
23101
  }
22911
- const configuredTriggers = [
22912
- play.triggerStatus?.cron ? `cron=${play.triggerStatus.cron}` : null,
22913
- play.triggerStatus?.webhook ? `webhook=${play.triggerStatus.webhook}` : null
22914
- ].filter(Boolean);
22915
- if (configuredTriggers.length > 0) {
22916
- process.stdout.write(` triggers: ${configuredTriggers.join(", ")}
22917
- `);
22918
- if (play.triggerStatus?.blockedReason) {
23102
+ if (play.triggerStatus?.cron) {
23103
+ process.stdout.write(
23104
+ ` scheduled execution: ${cronExecutionLabel(play.triggerStatus.cron)}
23105
+ `
23106
+ );
23107
+ if (play.triggerStatus.blockedReason) {
22919
23108
  process.stdout.write(
22920
- ` trigger issue: ${play.triggerStatus.blockedReason}
23109
+ ` schedule issue: ${play.triggerStatus.blockedReason}
22921
23110
  `
22922
23111
  );
22923
23112
  }
23113
+ } else {
23114
+ process.stdout.write(" scheduled execution: not configured\n");
23115
+ }
23116
+ if (play.triggerStatus?.webhook) {
23117
+ process.stdout.write(` webhook: ${play.triggerStatus.webhook}
23118
+ `);
22924
23119
  }
22925
23120
  process.stdout.write(` run: deepline plays run ${reference} --watch
22926
23121
  `);
@@ -23028,15 +23223,13 @@ function printPlayDescription(play) {
23028
23223
  console.log(` ${line}`);
23029
23224
  }
23030
23225
  }
23031
- const configuredTriggers = [
23032
- play.triggerStatus?.cron ? `cron=${play.triggerStatus.cron}` : null,
23033
- play.triggerStatus?.webhook ? `webhook=${play.triggerStatus.webhook}` : null
23034
- ].filter(Boolean);
23035
- if (configuredTriggers.length > 0) {
23036
- console.log(` Triggers: ${configuredTriggers.join(", ")}`);
23226
+ for (const line of liveTriggerStatusLines(play.triggerStatus, play)) {
23227
+ console.log(line);
23037
23228
  }
23038
- if (play.triggerStatus?.blockedReason) {
23039
- console.log(` Trigger issue: ${play.triggerStatus.blockedReason}`);
23229
+ if (play.isDraftDirty && play.liveVersion) {
23230
+ console.log(
23231
+ ` Draft note: scheduled runs continue to use live v${play.liveVersion}.`
23232
+ );
23040
23233
  }
23041
23234
  const runtimeLimit = formatPlayRuntimeLimit(play.runtimeLimit);
23042
23235
  if (runtimeLimit) {
@@ -23462,7 +23655,9 @@ async function handlePlaySave(args) {
23462
23655
  console.log(
23463
23656
  `\u2713 Saved ${playName} working draft as v${result.version ?? "?"}`
23464
23657
  );
23465
- console.log(" Live revision unchanged.");
23658
+ console.log(
23659
+ " Live revision and any armed schedule are unchanged; scheduled runs do not use this draft yet."
23660
+ );
23466
23661
  console.log(` publish: ${result.next.publish}`);
23467
23662
  }
23468
23663
  return 0;
@@ -23609,24 +23804,19 @@ async function handlePlayPublish(args) {
23609
23804
  process.stdout.write(`${JSON.stringify(result2)}
23610
23805
  `);
23611
23806
  } else {
23612
- console.log(
23613
- `\u2713 Published ${rootPlayName} as v${result2.liveVersion ?? "?"}`
23614
- );
23807
+ printPublishReceipt({
23808
+ name: rootPlayName,
23809
+ liveVersion: result2.liveVersion,
23810
+ previousLiveVersion: result2.previousLiveVersion,
23811
+ triggerMetadata: result2.triggerMetadata
23812
+ });
23615
23813
  console.log(` source: ${result2.sourceHash.slice(0, 12)}`);
23616
23814
  console.log(` artifact: ${result2.artifactHash.slice(0, 12)}`);
23617
- if (result2.previousLiveVersion !== null) {
23618
- console.log(
23619
- ` previous live: v${result2.previousLiveVersion} (${result2.previousArtifactHash?.slice(0, 12) ?? "unknown artifact"})`
23620
- );
23621
- }
23622
23815
  if (result2.sourceBytes.before !== null) {
23623
23816
  console.log(
23624
23817
  ` source bytes: ${result2.sourceBytes.before.toLocaleString()} \u2192 ${result2.sourceBytes.after.toLocaleString()}`
23625
23818
  );
23626
23819
  }
23627
- console.log(
23628
- ` revisions: deepline plays versions --name ${rootPlayName}`
23629
- );
23630
23820
  }
23631
23821
  return 0;
23632
23822
  }
@@ -23667,8 +23857,16 @@ async function handlePlayPublish(args) {
23667
23857
  resolvedName,
23668
23858
  revisionId ? { revisionId } : {}
23669
23859
  );
23670
- process.stdout.write(`${JSON.stringify(result)}
23860
+ if (options.jsonOutput) {
23861
+ process.stdout.write(`${JSON.stringify(result)}
23671
23862
  `);
23863
+ } else {
23864
+ printPublishReceipt({
23865
+ name: result.name,
23866
+ liveVersion: result.liveVersion,
23867
+ triggerMetadata: result.triggerMetadata
23868
+ });
23869
+ }
23672
23870
  return result.success ? 0 : 1;
23673
23871
  }
23674
23872
  async function handlePlayDelete(args) {
@@ -31457,7 +31655,7 @@ function parseJsonObjectArg(raw, argLabel) {
31457
31655
  return parsed;
31458
31656
  }
31459
31657
  function resolveMonitorJsonBody(input2) {
31460
- const readFile5 = input2.readFile ?? ((path) => readFileSync10(path, "utf-8"));
31658
+ const readFile6 = input2.readFile ?? ((path) => readFileSync10(path, "utf-8"));
31461
31659
  const readStdin = input2.readStdin ?? (() => readFileSync10(0, "utf-8"));
31462
31660
  if (input2.positional !== void 0 && input2.file !== void 0) {
31463
31661
  throw new MonitorsUsageError(
@@ -31473,7 +31671,7 @@ function resolveMonitorJsonBody(input2) {
31473
31671
  }
31474
31672
  let raw;
31475
31673
  try {
31476
- raw = readFile5(input2.file);
31674
+ raw = readFile6(input2.file);
31477
31675
  } catch (error) {
31478
31676
  throw new MonitorsUsageError(
31479
31677
  `Could not read --file ${input2.file}: ${error instanceof Error ? error.message : String(error)}`
@@ -33550,6 +33748,7 @@ async function handleOrgStatus(options) {
33550
33748
  config.apiKey
33551
33749
  );
33552
33750
  const folderTarget = resolveProjectPinTarget();
33751
+ const provenance = resolveCliAuthProvenance(config);
33553
33752
  const hostPath = hostEnvFilePath(config.baseUrl);
33554
33753
  const envApiKey = processEnvValue(API_KEY_ENV);
33555
33754
  const envHostUrl = processEnvValue(HOST_URL_ENV);
@@ -33598,6 +33797,9 @@ async function handleOrgStatus(options) {
33598
33797
  lines.push(`${API_KEY_ENV} overrides saved auth for this process.`);
33599
33798
  } else if (activeProject) {
33600
33799
  lines.push("Global auth is overridden in this folder.");
33800
+ } else if (provenance.scope === "global" && provenance.project.state === "project") {
33801
+ lines.push("Cloud mutations are blocked until this project is pinned.");
33802
+ lines.push("Pin it: deepline org set --auth-scope folder");
33601
33803
  }
33602
33804
  printCommandEnvelope(
33603
33805
  {
@@ -33608,6 +33810,15 @@ async function handleOrgStatus(options) {
33608
33810
  auth_source: authSource,
33609
33811
  host_env_path: hostPath,
33610
33812
  folder_target: folderTargetPayload,
33813
+ project_pin: provenance.scope === "global" && provenance.project.state === "project" ? {
33814
+ required: true,
33815
+ path: provenance.project.pinPath,
33816
+ cloud_mutations_blocked: true,
33817
+ command: "deepline org set --auth-scope folder"
33818
+ } : {
33819
+ required: false,
33820
+ cloud_mutations_blocked: false
33821
+ },
33611
33822
  next: {
33612
33823
  set_auto: "deepline org set <org>",
33613
33824
  set_folder: "deepline org set <org> --auth-scope folder",
@@ -33623,6 +33834,41 @@ async function handleOrgStatus(options) {
33623
33834
  async function handleOrgSwitch(selection, options) {
33624
33835
  const authScope = normalizeAuthScope2(options.authScope);
33625
33836
  const config = resolveConfig();
33837
+ const provenance = resolveCliAuthProvenance(config);
33838
+ if (!selection && !options.orgId && authScope === "folder" && provenance.scope === "global" && provenance.project.state === "project") {
33839
+ const project_env_paths2 = saveProjectDeeplineEnvValues(
33840
+ {
33841
+ [HOST_URL_ENV]: config.baseUrl,
33842
+ [API_KEY_ENV]: config.apiKey
33843
+ },
33844
+ provenance.project.dir
33845
+ );
33846
+ printCommandEnvelope(
33847
+ {
33848
+ ok: true,
33849
+ unchanged: true,
33850
+ requested_auth_scope: "folder",
33851
+ effective_auth_scope: "folder",
33852
+ auth_scope: "folder",
33853
+ project_env_paths: project_env_paths2,
33854
+ next: { status: "deepline org status --json" },
33855
+ render: {
33856
+ sections: [
33857
+ {
33858
+ title: "org set",
33859
+ lines: [
33860
+ "\u2713 Pinned this project to the current target.",
33861
+ `Saved folder auth in ${project_env_paths2[0]}.`,
33862
+ "Global host auth was not changed."
33863
+ ]
33864
+ }
33865
+ ]
33866
+ }
33867
+ },
33868
+ { json: options.json }
33869
+ );
33870
+ return;
33871
+ }
33626
33872
  const http = new HttpClient(config);
33627
33873
  const payload = await fetchOrganizations(http, config.apiKey);
33628
33874
  if (!selection && !options.orgId) {
@@ -33844,6 +34090,9 @@ Notes:
33844
34090
  use this org. Existing folder pins still override global auth.
33845
34091
 
33846
34092
  Process env DEEPLINE_API_KEY overrides saved auth for that command only.
34093
+ For projects, agents, CI, and multiple customer workspaces, prefer folder
34094
+ scope or a per-command DEEPLINE_API_KEY. Global scope is machine-wide and
34095
+ can make an unrelated terminal target the last-selected organization.
33847
34096
 
33848
34097
  Agent loop:
33849
34098
  deepline org list --json
@@ -33919,7 +34168,9 @@ Examples:
33919
34168
  `
33920
34169
  Notes:
33921
34170
  Selection can be a list number, exact organization name, or organization id.
33922
- Without a selection, prints choices.
34171
+ Without a selection, prints choices. With --auth-scope folder while this
34172
+ project is using global host auth, it pins the current target directly; this
34173
+ is the fastest safe repair for a blocked cloud mutation.
33923
34174
 
33924
34175
  Mutates local auth state and asks Deepline for an org-scoped API key. It
33925
34176
  writes only the selected scope:
@@ -33934,6 +34185,11 @@ Notes:
33934
34185
  folder, commands in that folder still use the folder pin until it is changed
33935
34186
  or removed.
33936
34187
 
34188
+ Prefer --auth-scope folder for a repository or customer workspace. Use
34189
+ --auth-scope global only for deliberate host-wide interactive defaults; it
34190
+ is shared by every unpinned CLI on this machine. For automation, pass
34191
+ DEEPLINE_API_KEY only to that invocation and confirm with org status --json.
34192
+
33937
34193
  Use --json for stable fields including effective_auth_scope,
33938
34194
  auth_scope_reason, host_env_path, project_env_paths, warnings, org_id, and
33939
34195
  org_name.
@@ -34057,6 +34313,7 @@ Deprecated:
34057
34313
 
34058
34314
  // src/cli/commands/secrets.ts
34059
34315
  import { stdin as input, stdout as output } from "process";
34316
+ import { readFile as readFile4 } from "fs/promises";
34060
34317
  var hiddenInputBuffer = "";
34061
34318
  function normalizeSecretName(value) {
34062
34319
  const normalized = value.trim().toUpperCase();
@@ -34069,7 +34326,10 @@ function normalizeSecretName(value) {
34069
34326
  }
34070
34327
  function renderSecret(secret) {
34071
34328
  const scope = secret.scope === "play" && secret.playName ? `play:${secret.playName}` : secret.scope;
34072
- return `${secret.name} (${scope}) - ${secret.status}${secret.hasValue ? ", set" : ", empty"}`;
34329
+ const added = new Date(secret.createdAt).toISOString();
34330
+ const updated = new Date(secret.updatedAt).toISOString();
34331
+ const lastUsed = secret.lastUsedAt ? `, last used ${new Date(secret.lastUsedAt).toISOString()}` : ", never used";
34332
+ return `${secret.name} (${scope}) - ${secret.status}${secret.hasValue ? ", set" : ", empty"}, added ${added}${updated !== added ? `, updated ${updated}` : ""}${lastUsed}`;
34073
34333
  }
34074
34334
  async function readHiddenLine(prompt, streams = {}) {
34075
34335
  const inputStream = streams.input ?? input;
@@ -34161,6 +34421,48 @@ async function readSecretValue() {
34161
34421
  }
34162
34422
  return first;
34163
34423
  }
34424
+ function trimOneTerminalNewline(value) {
34425
+ return value.endsWith("\r\n") ? value.slice(0, -2) : value.endsWith("\n") ? value.slice(0, -1) : value;
34426
+ }
34427
+ async function readSecretValueFromOptions(options) {
34428
+ const sources = [
34429
+ options.valueStdin ? "--value-stdin" : null,
34430
+ options.fromEnv ? "--from-env" : null,
34431
+ options.fromFile ? "--from-file" : null
34432
+ ].filter((source) => Boolean(source));
34433
+ if (sources.length > 1) {
34434
+ throw new Error(
34435
+ "Use exactly one secret source: --value-stdin, --from-env, or --from-file."
34436
+ );
34437
+ }
34438
+ if (sources.length === 0) return readSecretValue();
34439
+ let value;
34440
+ if (options.valueStdin) {
34441
+ if (input.isTTY) {
34442
+ throw new Error(
34443
+ "--value-stdin requires piped stdin. Omit it to use the hidden interactive prompt."
34444
+ );
34445
+ }
34446
+ const chunks = [];
34447
+ for await (const chunk of input) {
34448
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
34449
+ }
34450
+ value = trimOneTerminalNewline(Buffer.concat(chunks).toString("utf8"));
34451
+ } else if (options.fromEnv) {
34452
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(options.fromEnv)) {
34453
+ throw new Error("--from-env must name a valid environment variable.");
34454
+ }
34455
+ const envValue = process.env[options.fromEnv];
34456
+ if (envValue === void 0) {
34457
+ throw new Error(`Environment variable ${options.fromEnv} is not set.`);
34458
+ }
34459
+ value = envValue;
34460
+ } else {
34461
+ value = trimOneTerminalNewline(await readFile4(options.fromFile, "utf8"));
34462
+ }
34463
+ if (!value) throw new Error("Secret value is required.");
34464
+ return value;
34465
+ }
34164
34466
  function preventShellHistoryLeak(forbidden) {
34165
34467
  if (forbidden.length > 0) {
34166
34468
  throw new Error(
@@ -34201,7 +34503,7 @@ async function handleCheck(nameInput, options) {
34201
34503
  {
34202
34504
  title: "secret check",
34203
34505
  lines: [
34204
- secret ? `${name}: active` : `${name}: missing, disabled, or empty`
34506
+ secret ? `${name}: active \u2014 ${renderSecret(secret)}` : `${name}: missing, disabled, or empty`
34205
34507
  ]
34206
34508
  }
34207
34509
  ]
@@ -34219,7 +34521,7 @@ async function handleSet(nameInput, forbidden, options) {
34219
34521
  if (scope === "play" && !playName) {
34220
34522
  throw new Error("--play <name> is required when --scope play is used.");
34221
34523
  }
34222
- const value = await readSecretValue();
34524
+ const value = await readSecretValueFromOptions(options);
34223
34525
  const { http } = getAuthedHttpClient();
34224
34526
  const response = await http.post(
34225
34527
  "/api/v2/secrets",
@@ -34239,7 +34541,10 @@ async function handleSet(nameInput, forbidden, options) {
34239
34541
  sections: [
34240
34542
  {
34241
34543
  title: "secret saved",
34242
- lines: [`${secret.name}: saved (${secret.scope})`]
34544
+ lines: [
34545
+ `${secret.name}: saved (${secret.scope}); secret value was not displayed.`,
34546
+ renderSecret(secret)
34547
+ ]
34243
34548
  }
34244
34549
  ]
34245
34550
  }
@@ -34252,14 +34557,18 @@ function registerSecretsCommands(program) {
34252
34557
  "after",
34253
34558
  `
34254
34559
  Notes:
34255
- Secret values are never accepted as command arguments, stdin pipes, env vars,
34256
- or files. Use deepline secrets set NAME and type the value at the hidden TTY
34257
- prompt. Agents can list/check metadata but should not enter secret values.
34560
+ Secret values are never accepted as command arguments. Use the hidden prompt,
34561
+ or one explicit non-argv source: --value-stdin, --from-env NAME, or
34562
+ --from-file PATH. Values are never printed. secrets list shows only status,
34563
+ added, updated, and last-used metadata.
34258
34564
 
34259
34565
  Examples:
34260
34566
  deepline secrets list
34261
34567
  deepline secrets check HUBSPOT_TOKEN
34262
34568
  deepline secrets set HUBSPOT_TOKEN
34569
+ printf %s "$HUBSPOT_TOKEN" | deepline secrets set HUBSPOT_TOKEN --value-stdin
34570
+ deepline secrets set HUBSPOT_TOKEN --from-env HUBSPOT_TOKEN
34571
+ deepline secrets set HUBSPOT_TOKEN --from-file ./hubspot-token.txt
34263
34572
  `
34264
34573
  );
34265
34574
  secrets.command("list").description("List secret metadata only.").option("--json", "Emit JSON output").action(async (options) => {
@@ -34268,7 +34577,7 @@ Examples:
34268
34577
  secrets.command("check").description("Check whether a secret exists and is active.").argument("<name>", "Secret name").option("--json", "Emit JSON output").action(async (name, options) => {
34269
34578
  await handleCheck(name, options);
34270
34579
  });
34271
- secrets.command("set").description("Set or rotate a secret through a hidden interactive prompt.").argument("<name>", "Secret name").argument("[forbidden...]", "Do not pass secret values here").option("--json", "Emit JSON output").option("--scope <scope>", "Secret scope: org or play", "org").option("--play <name>", "Play name for play-scoped secrets").action(
34580
+ secrets.command("set").description("Set or rotate a secret without exposing its value in argv.").argument("<name>", "Secret name").argument("[forbidden...]", "Do not pass secret values here").option("--json", "Emit JSON output").option("--scope <scope>", "Secret scope: org or play", "org").option("--play <name>", "Play name for play-scoped secrets").option("--value-stdin", "Read the value from piped stdin").option("--from-env <name>", "Read the value from an environment variable").option("--from-file <path>", "Read the value from a file").action(
34272
34581
  async (name, forbidden, options) => {
34273
34582
  await handleSet(name, forbidden ?? [], options);
34274
34583
  }
@@ -40858,7 +41167,7 @@ Examples:
40858
41167
  }
40859
41168
 
40860
41169
  // src/cli/commands/workflow.ts
40861
- import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
41170
+ import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
40862
41171
  import { dirname as dirname20, join as join22, resolve as resolve20 } from "path";
40863
41172
 
40864
41173
  // src/cli/workflow-to-play.ts
@@ -41076,7 +41385,7 @@ function readStatus(payload) {
41076
41385
  }
41077
41386
  async function readJsonOption(payload, file) {
41078
41387
  if (file) {
41079
- const raw = await readFile4(resolve20(file), "utf8");
41388
+ const raw = await readFile5(resolve20(file), "utf8");
41080
41389
  return JSON.parse(raw);
41081
41390
  }
41082
41391
  if (payload) {
@@ -41842,6 +42151,97 @@ async function maybeReportSdkCliFailure(input2) {
41842
42151
 
41843
42152
  // src/cli/index.ts
41844
42153
  var PREFLIGHT_TIMEOUT_MS = 3e3;
42154
+ var ProjectOrgPinRequiredError = class extends Error {
42155
+ code = "PROJECT_ORG_NOT_PINNED";
42156
+ exitCode = 3;
42157
+ payload;
42158
+ constructor(input2) {
42159
+ super("Cloud work is blocked because this project uses global host auth.");
42160
+ this.name = "ProjectOrgPinRequiredError";
42161
+ this.payload = {
42162
+ ok: false,
42163
+ exitCode: this.exitCode,
42164
+ code: this.code,
42165
+ message: this.message,
42166
+ command: input2.command,
42167
+ auth: { scope: "global", source: "host", projectPinned: false },
42168
+ project: { dir: input2.projectDir, pinPath: input2.pinPath },
42169
+ next: "deepline org set --auth-scope folder"
42170
+ };
42171
+ }
42172
+ };
42173
+ function commandUsesOnlyReadOrLocalOperations(command) {
42174
+ const [group, action] = command.split(" ");
42175
+ if ([
42176
+ "auth",
42177
+ "org",
42178
+ "health",
42179
+ "preflight",
42180
+ "version",
42181
+ "update",
42182
+ "skills",
42183
+ "doctor",
42184
+ "quickstart"
42185
+ ].includes(group ?? "")) {
42186
+ return true;
42187
+ }
42188
+ if (group === "plays") {
42189
+ return [
42190
+ "check",
42191
+ "get",
42192
+ "list",
42193
+ "search",
42194
+ "grep",
42195
+ "describe",
42196
+ "versions"
42197
+ ].includes(action ?? "");
42198
+ }
42199
+ if (group === "runs") {
42200
+ return ["get", "list", "tail", "logs", "export"].includes(action ?? "");
42201
+ }
42202
+ if (group === "tools") {
42203
+ return ["list", "search", "get", "describe"].includes(action ?? "");
42204
+ }
42205
+ if (group === "providers") return ["list", "get"].includes(action ?? "");
42206
+ if (group === "secrets") return ["list", "check"].includes(action ?? "");
42207
+ if (group === "billing")
42208
+ return ["balance", "usage", "history"].includes(action ?? "");
42209
+ return false;
42210
+ }
42211
+ function assertProjectCloudWorkIsPinned(command) {
42212
+ if (process.argv.includes("--dry-run") || commandUsesOnlyReadOrLocalOperations(command)) {
42213
+ return;
42214
+ }
42215
+ const config = resolveConfig();
42216
+ const provenance = resolveCliAuthProvenance(config);
42217
+ if (provenance.scope !== "global" || provenance.project.state !== "project") {
42218
+ return;
42219
+ }
42220
+ throw new ProjectOrgPinRequiredError({
42221
+ command,
42222
+ projectDir: provenance.project.dir,
42223
+ pinPath: provenance.project.pinPath
42224
+ });
42225
+ }
42226
+ function printProjectOrgPinRequiredError(error) {
42227
+ if (shouldEmitJson(process.argv.includes("--json"))) {
42228
+ printJson(error.payload);
42229
+ return;
42230
+ }
42231
+ const project = error.payload.project;
42232
+ process.stderr.write(
42233
+ [
42234
+ "\u2717 Cloud work is blocked in this project",
42235
+ "",
42236
+ " Auth: global host default",
42237
+ " This project is not pinned. Global auth is shared by every unpinned CLI on this machine.",
42238
+ "",
42239
+ " Pin this folder to the current target:",
42240
+ " deepline org set --auth-scope folder",
42241
+ ` This writes: ${project.pinPath}`
42242
+ ].join("\n") + "\n"
42243
+ );
42244
+ }
41845
42245
  function asCommanderError(error) {
41846
42246
  if (!(error instanceof Error) || !("code" in error)) {
41847
42247
  return null;
@@ -42151,11 +42551,12 @@ Exit codes:
42151
42551
  if (actionCommand.name() === "version" || actionCommand.name() === "update" || actionCommand.name() === "setup" || actionCommand.name() === "skills" || actionCommand.name() === "doctor" || isAutoUpdateSettingsInvocation() || isDeprecatedCommandInvocation()) {
42152
42552
  return;
42153
42553
  }
42554
+ const compatibilityCommand = compatibilityCommandPath(actionCommand) || actionCommand.name();
42555
+ assertProjectCloudWorkIsPinned(compatibilityCommand);
42154
42556
  if (printStartupPhase) {
42155
42557
  progress?.phase("checking sdk compatibility");
42156
42558
  }
42157
42559
  const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
42158
- const compatibilityCommand = compatibilityCommandPath(actionCommand) || actionCommand.name();
42159
42560
  const shouldDeferSkillsSync = shouldDeferSkillsSyncForCommand();
42160
42561
  const skillsVersion = shouldDeferSkillsSync ? void 0 : readSdkSkillsLocalVersion(baseUrl);
42161
42562
  const compatibility = await traceCliSpan(
@@ -42332,7 +42733,10 @@ Examples:
42332
42733
  });
42333
42734
  progress?.fail();
42334
42735
  const wantsJson = process.argv.includes("--json");
42335
- if (commanderError) {
42736
+ if (error instanceof ProjectOrgPinRequiredError) {
42737
+ printProjectOrgPinRequiredError(error);
42738
+ process.exitCode = error.exitCode;
42739
+ } else if (commanderError) {
42336
42740
  if (commanderError.code === "commander.unknownCommand") {
42337
42741
  }
42338
42742
  process.exitCode = commanderError.code === "commander.unknownCommand" && !wantsJson ? 2 : commanderError.exitCode ?? 1;