deepline 0.1.143 → 0.1.145

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
@@ -237,6 +237,22 @@ var PROD_URL = "https://code.deepline.com";
237
237
  var DEFAULT_TIMEOUT = 6e4;
238
238
  var DEFAULT_MAX_RETRIES = 3;
239
239
  var PROJECT_DEEPLINE_ENV_FILE = ".env.deepline";
240
+ var COWORK_IGNORED_WORKSPACE_DIRS = /* @__PURE__ */ new Set([
241
+ ".auto-memory",
242
+ ".claude",
243
+ ".remote-plugins",
244
+ "outputs",
245
+ "plugins",
246
+ "uploads"
247
+ ]);
248
+ var COWORK_PROJECT_MARKERS = [
249
+ ".deepline",
250
+ ".env.deepline",
251
+ ".git",
252
+ "AGENTS.md",
253
+ "package.json",
254
+ "pyproject.toml"
255
+ ];
240
256
  function baseUrlSlug(baseUrl) {
241
257
  let url;
242
258
  try {
@@ -282,9 +298,124 @@ function findNearestEnvFile(name, startDir = process.cwd()) {
282
298
  current = parent;
283
299
  }
284
300
  }
285
- function loadProjectDeeplineEnv(startDir = process.cwd()) {
286
- const filePath = findNearestEnvFile(PROJECT_DEEPLINE_ENV_FILE, startDir);
287
- return filePath ? parseEnvFile(filePath) : {};
301
+ function isDirectory(path) {
302
+ try {
303
+ return (0, import_node_fs.statSync)(path).isDirectory();
304
+ } catch {
305
+ return false;
306
+ }
307
+ }
308
+ function canonicalPath(path) {
309
+ try {
310
+ return (0, import_node_fs.realpathSync)(path);
311
+ } catch {
312
+ return (0, import_node_path.resolve)(path);
313
+ }
314
+ }
315
+ function isTruthy(value) {
316
+ return /^(1|true|yes|on)$/i.test(value?.trim() ?? "");
317
+ }
318
+ function sessionRootFromPath(path) {
319
+ const trimmed = path?.trim();
320
+ if (!trimmed) return null;
321
+ const match = /^\/sessions\/[^/]+(?=\/|$)/.exec(trimmed);
322
+ return match?.[0] ?? null;
323
+ }
324
+ function coworkSessionRoot() {
325
+ const home = process.env.HOME?.trim();
326
+ const homeSessionRoot = sessionRootFromPath(home);
327
+ if (homeSessionRoot && isDirectory((0, import_node_path.join)(homeSessionRoot, "mnt"))) {
328
+ return homeSessionRoot;
329
+ }
330
+ const cwdSessionRoot = sessionRootFromPath(process.cwd());
331
+ if (cwdSessionRoot && isDirectory((0, import_node_path.join)(cwdSessionRoot, "mnt"))) {
332
+ return cwdSessionRoot;
333
+ }
334
+ if (isTruthy(process.env.CLAUDE_CODE_REMOTE) && home) {
335
+ const mountedRoot = (0, import_node_path.join)(home, "mnt");
336
+ if (isDirectory(mountedRoot)) return (0, import_node_path.resolve)(home);
337
+ }
338
+ return null;
339
+ }
340
+ function isCoworkLikeSandbox() {
341
+ const home = process.env.HOME?.trim();
342
+ return isTruthy(process.env.CLAUDE_CODE_REMOTE) || sessionRootFromPath(home) !== null || sessionRootFromPath(process.cwd()) !== null;
343
+ }
344
+ function coworkProjectScore(path) {
345
+ let score = 0;
346
+ for (const marker of COWORK_PROJECT_MARKERS) {
347
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(path, marker))) score += 1;
348
+ }
349
+ return score;
350
+ }
351
+ function listCoworkWorkspaceDirCandidates() {
352
+ if (!isCoworkLikeSandbox()) {
353
+ return [];
354
+ }
355
+ const explicitProjectDir = process.env.CLAUDE_PROJECT_DIR?.trim();
356
+ if (explicitProjectDir && isDirectory(explicitProjectDir)) {
357
+ return [(0, import_node_path.resolve)(explicitProjectDir)];
358
+ }
359
+ const sessionRoot = coworkSessionRoot();
360
+ if (!sessionRoot) return [];
361
+ const mountedRoot = (0, import_node_path.join)(sessionRoot, "mnt");
362
+ if (!isDirectory(mountedRoot)) return [];
363
+ let names;
364
+ try {
365
+ names = (0, import_node_fs.readdirSync)(mountedRoot).sort();
366
+ } catch {
367
+ return [];
368
+ }
369
+ const candidates = [];
370
+ for (const name of names) {
371
+ if (name.startsWith(".") || COWORK_IGNORED_WORKSPACE_DIRS.has(name)) {
372
+ continue;
373
+ }
374
+ const candidate = (0, import_node_path.join)(mountedRoot, name);
375
+ if (isDirectory(candidate)) candidates.push(candidate);
376
+ }
377
+ if (candidates.length <= 1) return candidates;
378
+ const projectLike = candidates.filter(
379
+ (candidate) => coworkProjectScore(candidate) > 0
380
+ );
381
+ return projectLike.length > 0 ? projectLike : candidates;
382
+ }
383
+ function isInIgnoredCoworkMount(path) {
384
+ const sessionRoot = coworkSessionRoot();
385
+ if (!sessionRoot) return false;
386
+ const mountedRoot = canonicalPath((0, import_node_path.join)(sessionRoot, "mnt"));
387
+ const resolvedPath = canonicalPath(path);
388
+ const prefix = `${mountedRoot}/`;
389
+ if (!resolvedPath.startsWith(prefix)) return false;
390
+ const relativePath = resolvedPath.slice(prefix.length);
391
+ const mountName = relativePath.split("/")[0];
392
+ return mountName.startsWith(".") || COWORK_IGNORED_WORKSPACE_DIRS.has(mountName);
393
+ }
394
+ function detectCoworkWorkspaceDir() {
395
+ const candidates = listCoworkWorkspaceDirCandidates();
396
+ return candidates.length === 1 ? candidates[0] : null;
397
+ }
398
+ function loadProjectEnvCandidates(startDir = process.cwd()) {
399
+ const filePaths = [];
400
+ const sources = /* @__PURE__ */ new Map();
401
+ const nearestFile = findNearestEnvFile(PROJECT_DEEPLINE_ENV_FILE, startDir);
402
+ if (nearestFile && !isInIgnoredCoworkMount(nearestFile)) {
403
+ filePaths.push(nearestFile);
404
+ sources.set((0, import_node_path.resolve)(nearestFile), "nearest");
405
+ }
406
+ const coworkWorkspaceDir = detectCoworkWorkspaceDir();
407
+ if (coworkWorkspaceDir) {
408
+ const coworkFile = (0, import_node_path.join)(coworkWorkspaceDir, PROJECT_DEEPLINE_ENV_FILE);
409
+ if ((0, import_node_fs.existsSync)(coworkFile) && !filePaths.some((filePath) => (0, import_node_path.resolve)(filePath) === (0, import_node_path.resolve)(coworkFile))) {
410
+ filePaths.push(coworkFile);
411
+ sources.set((0, import_node_path.resolve)(coworkFile), "cowork");
412
+ }
413
+ }
414
+ return filePaths.map((filePath) => ({
415
+ filePath,
416
+ env: parseEnvFile(filePath),
417
+ source: sources.get((0, import_node_path.resolve)(filePath)) ?? "nearest"
418
+ }));
288
419
  }
289
420
  function normalizeBaseUrl(baseUrl) {
290
421
  const trimmed = baseUrl.trim().replace(/\/+$/, "");
@@ -348,23 +479,35 @@ function loadGlobalCliEnv() {
348
479
  return loadCliEnv(PROD_URL);
349
480
  }
350
481
  function autoDetectBaseUrl() {
351
- const projectEnv = loadProjectDeeplineEnv();
482
+ const projectEnvs = loadProjectEnvCandidates();
352
483
  const globalEnv = loadGlobalCliEnv();
353
- return normalizeBaseUrl(process.env[HOST_URL_ENV] ?? "") || normalizeBaseUrl(projectEnv[HOST_URL_ENV] ?? "") || normalizeBaseUrl(globalEnv[HOST_URL_ENV] ?? "") || PROD_URL;
484
+ return normalizeBaseUrl(process.env[HOST_URL_ENV] ?? "") || firstNonEmpty(
485
+ ...projectEnvs.map(({ env }) => normalizeBaseUrl(env[HOST_URL_ENV]))
486
+ ) || normalizeBaseUrl(globalEnv[HOST_URL_ENV] ?? "") || PROD_URL;
354
487
  }
355
488
  function resolveApiKeyForBaseUrl(baseUrl, explicitApiKey) {
356
489
  const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
357
- const projectEnv = loadProjectDeeplineEnv();
490
+ const projectEnvs = loadProjectEnvCandidates();
358
491
  const cliEnv = loadCliEnv(normalizedBaseUrl || baseUrl);
359
- const projectBaseUrl = normalizeBaseUrl(projectEnv[HOST_URL_ENV] ?? "");
360
- const projectKeyApplies = projectBaseUrl === normalizedBaseUrl;
361
492
  return firstNonEmpty(
362
493
  explicitApiKey,
363
494
  process.env[API_KEY_ENV],
364
- projectKeyApplies ? projectEnv[API_KEY_ENV] : "",
495
+ ...projectEnvs.map(({ env }) => {
496
+ const projectBaseUrl = normalizeBaseUrl(env[HOST_URL_ENV] ?? "");
497
+ return projectBaseUrl === normalizedBaseUrl ? env[API_KEY_ENV] : "";
498
+ }),
365
499
  cliEnv[API_KEY_ENV]
366
500
  );
367
501
  }
502
+ function getResolvedProjectAuthSource(baseUrl, apiKey, startDir = process.cwd()) {
503
+ const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
504
+ const normalizedApiKey = apiKey.trim();
505
+ if (!normalizedBaseUrl || !normalizedApiKey) return null;
506
+ return loadProjectEnvCandidates(startDir).find(({ env }) => {
507
+ const projectBaseUrl = normalizeBaseUrl(env[HOST_URL_ENV] ?? "");
508
+ return projectBaseUrl === normalizedBaseUrl && (env[API_KEY_ENV] ?? "").trim() === normalizedApiKey;
509
+ }) ?? null;
510
+ }
368
511
  function resolveConfig(options) {
369
512
  const baseUrl = normalizeBaseUrl(
370
513
  options?.baseUrl?.trim() || autoDetectBaseUrl()
@@ -387,6 +530,69 @@ function resolveConfig(options) {
387
530
  maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES
388
531
  };
389
532
  }
533
+ function mergeProjectEnvFile(filePath, values) {
534
+ const existing = parseEnvFile(filePath);
535
+ const merged = { ...existing, ...values };
536
+ const dir = (0, import_node_path.dirname)(filePath);
537
+ if (!(0, import_node_fs.existsSync)(dir)) (0, import_node_fs.mkdirSync)(dir, { recursive: true });
538
+ ensureProjectEnvIsIgnored(dir);
539
+ const allowedKeys = /* @__PURE__ */ new Set([HOST_URL_ENV, API_KEY_ENV]);
540
+ const lines = Object.entries(merged).filter(([key, value]) => allowedKeys.has(key) && value !== "").map(([key, value]) => `${key}=${value}`);
541
+ (0, import_node_fs.writeFileSync)(filePath, `${lines.join("\n")}
542
+ `, "utf-8");
543
+ }
544
+ function ensureProjectEnvIsIgnored(dir) {
545
+ const gitignorePath = (0, import_node_path.join)(dir, ".gitignore");
546
+ const entry = PROJECT_DEEPLINE_ENV_FILE;
547
+ const existing = (0, import_node_fs.existsSync)(gitignorePath) ? (0, import_node_fs.readFileSync)(gitignorePath, "utf-8") : "";
548
+ const alreadyIgnored = existing.split(/\r?\n/).map((line) => line.trim()).some((line) => line === entry || line === `/${entry}`);
549
+ if (alreadyIgnored) return;
550
+ const prefix = existing && !existing.endsWith("\n") ? "\n" : "";
551
+ (0, import_node_fs.writeFileSync)(gitignorePath, `${existing}${prefix}${entry}
552
+ `, "utf-8");
553
+ }
554
+ function saveProjectDeeplineEnvValues(values, startDir = process.cwd()) {
555
+ const target = resolveProjectPinTarget(startDir);
556
+ if (!target.ok) {
557
+ throw new ConfigError(
558
+ `Cowork project folder is ambiguous. Candidate folders: ${target.candidates.join(
559
+ ", "
560
+ )}. Set CLAUDE_PROJECT_DIR or cd into the intended project folder before running this command.`
561
+ );
562
+ }
563
+ const filePath = (0, import_node_path.join)(target.dir, PROJECT_DEEPLINE_ENV_FILE);
564
+ mergeProjectEnvFile(filePath, values);
565
+ return [filePath];
566
+ }
567
+ function resolveProjectPinTarget(startDir = process.cwd()) {
568
+ const nearestFile = findNearestEnvFile(PROJECT_DEEPLINE_ENV_FILE, startDir);
569
+ if (nearestFile && !isInIgnoredCoworkMount(nearestFile)) {
570
+ return { ok: true, dir: (0, import_node_path.dirname)(nearestFile), source: "nearest" };
571
+ }
572
+ const coworkCandidates = listCoworkWorkspaceDirCandidates();
573
+ if (coworkCandidates.length === 1) {
574
+ return { ok: true, dir: coworkCandidates[0], source: "cowork" };
575
+ }
576
+ if (coworkCandidates.length > 1) {
577
+ const resolvedStartDir = canonicalPath(startDir);
578
+ const cwdCandidate = coworkCandidates.find((candidate) => {
579
+ const resolvedCandidate = canonicalPath(candidate);
580
+ return resolvedStartDir === resolvedCandidate || resolvedStartDir.startsWith(`${resolvedCandidate}/`);
581
+ });
582
+ if (cwdCandidate) {
583
+ return { ok: true, dir: cwdCandidate, source: "cowork" };
584
+ }
585
+ return {
586
+ ok: false,
587
+ reason: "ambiguous_cowork_project",
588
+ candidates: coworkCandidates
589
+ };
590
+ }
591
+ return { ok: true, dir: (0, import_node_path.resolve)(startDir), source: "cwd" };
592
+ }
593
+ function getActiveProjectAuthSource(startDir = process.cwd()) {
594
+ return loadProjectEnvCandidates(startDir)[0] ?? null;
595
+ }
390
596
 
391
597
  // src/http.ts
392
598
  var import_node_fs2 = require("fs");
@@ -413,10 +619,10 @@ var SDK_RELEASE = {
413
619
  // 0.1.108 ships explicit dataset column/tool recompute policy and removes
414
620
  // the SDK enrich generator's one-second stale policy.
415
621
  // 0.1.110 ships authored V2 prebuilts and required top-level play descriptions.
416
- version: "0.1.143",
622
+ version: "0.1.145",
417
623
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
418
624
  supportPolicy: {
419
- latest: "0.1.143",
625
+ latest: "0.1.145",
420
626
  minimumSupported: "0.1.53",
421
627
  deprecatedBelow: "0.1.53",
422
628
  commandMinimumSupported: [
@@ -507,7 +713,7 @@ function normalizeAgentRuntime(value) {
507
713
  if (explicit === "gemini_cli") return "gemini";
508
714
  return EXPLICIT_AGENT_RUNTIMES.has(explicit) ? explicit : null;
509
715
  }
510
- function isCoworkLikeSandbox() {
716
+ function isCoworkLikeSandbox2() {
511
717
  const pluginMode = truthyEnv("DEEPLINE_PLUGIN_MODE");
512
718
  const claudeRemote = truthyEnv("CLAUDE_CODE_REMOTE");
513
719
  const projectDir = Boolean(process.env.CLAUDE_PROJECT_DIR?.trim());
@@ -520,7 +726,7 @@ function detectAgentRuntime(options = {}) {
520
726
  const explicit = normalizeAgentRuntime(process.env.DEEPLINE_AGENT_RUNTIME);
521
727
  if (explicit) return explicit;
522
728
  if (process.env.CODEX_THREAD_ID?.trim()) return "codex";
523
- if (options.detectCowork !== false && isCoworkLikeSandbox()) {
729
+ if (options.detectCowork !== false && isCoworkLikeSandbox2()) {
524
730
  return "claude_cowork";
525
731
  }
526
732
  if (process.env.CLAUDECODE?.trim() === "1") return "claude_code";
@@ -986,7 +1192,7 @@ function sleep(ms) {
986
1192
  return new Promise((resolve13) => setTimeout(resolve13, ms));
987
1193
  }
988
1194
  function withCoworkNetworkHint(message) {
989
- if (!isCoworkLikeSandbox() || message.includes(COWORK_NETWORK_HINT)) {
1195
+ if (!isCoworkLikeSandbox2() || message.includes(COWORK_NETWORK_HINT)) {
990
1196
  return message;
991
1197
  }
992
1198
  return `${message}
@@ -3293,6 +3499,9 @@ var DeeplineClient = class {
3293
3499
  if (input2.runId?.trim()) {
3294
3500
  params.set("runId", input2.runId.trim());
3295
3501
  }
3502
+ if (input2.rowMode === "all") {
3503
+ params.set("rowMode", "all");
3504
+ }
3296
3505
  return await this.http.get(
3297
3506
  `/api/v2/plays/${encodeURIComponent(input2.playName)}/sheet?${params.toString()}`
3298
3507
  );
@@ -11740,7 +11949,8 @@ async function fetchBackingDatasetRows(input2) {
11740
11949
  tableNamespace,
11741
11950
  runId: input2.status.runId,
11742
11951
  limit: RUN_EXPORT_PAGE_SIZE,
11743
- offset
11952
+ offset,
11953
+ rowMode: "all"
11744
11954
  });
11745
11955
  sheetRows.push(...page.rows);
11746
11956
  const summaryTotal = page.summary?.stats?.total;
@@ -16080,8 +16290,10 @@ function helperSource() {
16080
16290
 
16081
16291
  // src/cli/commands/enrich.ts
16082
16292
  var ENRICH_EXPORT_PAGE_SIZE = 5e3;
16293
+ var ENRICH_AUTO_BATCH_ROWS = 250;
16083
16294
  var EXIT_SERVER2 = 5;
16084
16295
  var ENRICH_DEBUG_T0 = Date.now();
16296
+ var GENERATED_ENRICH_ROWS_TABLE_NAMESPACE = "deepline_enrich_rows";
16085
16297
  var PLAN_SHAPING_OPTION_NAMES = [
16086
16298
  "with",
16087
16299
  "withWaterfall",
@@ -16745,7 +16957,12 @@ async function runGeneratedEnrichPlay(runArgs, options = {}) {
16745
16957
  async function writeOutputCsv(outputPath, status, options) {
16746
16958
  let rowsInfo = extractCanonicalRowsInfo(status);
16747
16959
  if (!rowsInfo) {
16748
- throw new Error("The generated play did not return row-shaped output.");
16960
+ rowsInfo = fallbackRowsInfoForGeneratedEnrichExport(status, options);
16961
+ }
16962
+ if (!rowsInfo) {
16963
+ throw new Error(
16964
+ "The generated play did not return row-shaped output, and no durable enrich rows were available to export."
16965
+ );
16749
16966
  }
16750
16967
  if (options?.client) {
16751
16968
  rowsInfo = await fetchBackingRowsForCsvExport({
@@ -16808,6 +17025,98 @@ function extractPlayName2(status) {
16808
17025
  }
16809
17026
  return null;
16810
17027
  }
17028
+ function selectedSourceCsvRange(sourceCsvPath, rows) {
17029
+ const sourceRows = readCsvRows(sourceCsvPath).length;
17030
+ if (sourceRows === 0) {
17031
+ return { start: 0, end: -1, count: 0, sourceRows };
17032
+ }
17033
+ const start = Math.max(0, rows?.rowStart ?? 0);
17034
+ if (start >= sourceRows) {
17035
+ return { start, end: start - 1, count: 0, sourceRows };
17036
+ }
17037
+ const maxEnd = sourceRows - 1;
17038
+ const end = rows?.rowEnd === null || rows?.rowEnd === void 0 ? maxEnd : Math.min(maxEnd, rows.rowEnd);
17039
+ return {
17040
+ start,
17041
+ end,
17042
+ count: Math.max(0, end - start + 1),
17043
+ sourceRows
17044
+ };
17045
+ }
17046
+ function selectedSourceCsvRowCount(options) {
17047
+ if (!options?.sourceCsvPath) {
17048
+ return null;
17049
+ }
17050
+ return selectedSourceCsvRange(options.sourceCsvPath, options.rows).count;
17051
+ }
17052
+ function collectStringFields(value, key, output2, depth = 0) {
17053
+ if (depth > 12 || !value || typeof value !== "object") {
17054
+ return;
17055
+ }
17056
+ if (Array.isArray(value)) {
17057
+ for (const item of value.slice(0, 50)) {
17058
+ collectStringFields(item, key, output2, depth + 1);
17059
+ }
17060
+ return;
17061
+ }
17062
+ const record = value;
17063
+ const direct = record[key];
17064
+ if (typeof direct === "string" && direct.trim()) {
17065
+ output2.add(direct.trim());
17066
+ }
17067
+ for (const child of Object.values(record)) {
17068
+ collectStringFields(child, key, output2, depth + 1);
17069
+ }
17070
+ }
17071
+ function fallbackRowsInfoForGeneratedEnrichExport(status, options) {
17072
+ if (!extractRunId(status) || !extractPlayName2(status)) {
17073
+ return null;
17074
+ }
17075
+ const totalRows = selectedSourceCsvRowCount(options);
17076
+ if (totalRows === null) {
17077
+ return null;
17078
+ }
17079
+ const tableNamespaces = /* @__PURE__ */ new Set();
17080
+ collectStringFields(status, "tableNamespace", tableNamespaces);
17081
+ collectStringFields(status, "artifactTableNamespace", tableNamespaces);
17082
+ const tableNamespace = tableNamespaces.values().next().value ?? GENERATED_ENRICH_ROWS_TABLE_NAMESPACE;
17083
+ return {
17084
+ rows: [],
17085
+ totalRows,
17086
+ columns: [],
17087
+ columnsExplicit: false,
17088
+ complete: false,
17089
+ source: tableNamespace,
17090
+ tableNamespace
17091
+ };
17092
+ }
17093
+ function firstCollectedStringField(value, key) {
17094
+ const fields = /* @__PURE__ */ new Set();
17095
+ collectStringFields(value, key, fields);
17096
+ return fields.values().next().value ?? null;
17097
+ }
17098
+ function emitPlainBatchRunFailure(input2) {
17099
+ process.stderr.write(
17100
+ `Batch ${input2.chunkIndex + 1}/${input2.chunkCount} failed for rows ${input2.rows.rowStart}:${input2.rows.rowEnd}.
17101
+ `
17102
+ );
17103
+ const runId = extractRunId(input2.status);
17104
+ const error = firstCollectedStringField(input2.status, "error") ?? firstCollectedStringField(input2.status, "message");
17105
+ if (runId) {
17106
+ process.stderr.write(`Run id: ${runId}
17107
+ `);
17108
+ }
17109
+ if (error) {
17110
+ process.stderr.write(`Error: ${error}
17111
+ `);
17112
+ }
17113
+ if (runId) {
17114
+ process.stderr.write(
17115
+ `Inspect run output: deepline runs get ${runId} --full --json
17116
+ `
17117
+ );
17118
+ }
17119
+ }
16811
17120
  function exportableSheetRow2(row) {
16812
17121
  if (!row || typeof row !== "object" || Array.isArray(row)) {
16813
17122
  return null;
@@ -17175,7 +17484,8 @@ async function fetchBackingRowsForCsvExport(input2) {
17175
17484
  tableNamespace,
17176
17485
  runId,
17177
17486
  limit: ENRICH_EXPORT_PAGE_SIZE,
17178
- offset
17487
+ offset,
17488
+ rowMode: "all"
17179
17489
  });
17180
17490
  sheetRows.push(...page.rows);
17181
17491
  const summaryTotal = page.summary?.stats?.total;
@@ -17543,41 +17853,177 @@ function registerEnrichCommand(program) {
17543
17853
  const tempPlay = (0, import_node_path11.join)(tempDir, "deepline-enrich.play.ts");
17544
17854
  try {
17545
17855
  await (0, import_promises3.writeFile)(tempPlay, playSource, "utf8");
17546
- const runtimeInput = {
17547
- file: (0, import_node_path11.resolve)(sourceCsvPath),
17548
- ...rows.rowStart !== null ? { rowStart: rows.rowStart } : {},
17549
- ...rows.rowEnd !== null ? { rowEnd: rows.rowEnd } : {}
17856
+ const runOne = async (input2) => {
17857
+ const runtimeInput = {
17858
+ file: (0, import_node_path11.resolve)(input2.sourceCsvPath),
17859
+ ...input2.rows.rowStart !== null ? { rowStart: input2.rows.rowStart } : {},
17860
+ ...input2.rows.rowEnd !== null ? { rowEnd: input2.rows.rowEnd } : {}
17861
+ };
17862
+ const runArgs = [
17863
+ "--file",
17864
+ tempPlay,
17865
+ "--input",
17866
+ JSON.stringify(runtimeInput),
17867
+ "--watch"
17868
+ ];
17869
+ if (options.profile) {
17870
+ runArgs.push("--profile", options.profile);
17871
+ }
17872
+ if (options.noOpen || input2.suppressOpen) {
17873
+ runArgs.push("--no-open");
17874
+ }
17875
+ if (input2.json) {
17876
+ runArgs.push("--json");
17877
+ } else {
17878
+ runArgs.push("--logs");
17879
+ }
17880
+ const timeoutSeconds = options.timeout ? Number.parseInt(options.timeout, 10) : null;
17881
+ if (timeoutSeconds !== null && Number.isFinite(timeoutSeconds) && timeoutSeconds > 0) {
17882
+ runArgs.push("--tail-timeout-ms", String(timeoutSeconds * 1e3));
17883
+ }
17884
+ const captured2 = await runGeneratedEnrichPlay(runArgs, {
17885
+ passthroughStdout: input2.passthroughStdout
17886
+ });
17887
+ const status2 = input2.json ? parseJsonOutput(captured2.stdout) : await resolveWatchedGeneratedPlayStatus({
17888
+ client: client2,
17889
+ stdout: captured2.stdout,
17890
+ exitCode: captured2.result
17891
+ });
17892
+ const exportResult2 = captured2.result === 0 && outputPath ? await writeOutputCsv(outputPath, status2, {
17893
+ client: client2,
17894
+ config,
17895
+ sourceCsvPath: input2.sourceCsvPath,
17896
+ rows: input2.rows,
17897
+ inPlace: Boolean(options.inPlace)
17898
+ }) : null;
17899
+ return { captured: captured2, status: status2, exportResult: exportResult2 };
17550
17900
  };
17551
- const runArgs = [
17552
- "--file",
17553
- tempPlay,
17554
- "--input",
17555
- JSON.stringify(runtimeInput),
17556
- "--watch"
17557
- ];
17558
- if (options.profile) {
17559
- runArgs.push("--profile", options.profile);
17560
- }
17561
- if (options.noOpen) {
17562
- runArgs.push("--no-open");
17563
- }
17564
- if (options.json) {
17565
- runArgs.push("--json");
17566
- } else {
17567
- runArgs.push("--logs");
17568
- }
17569
- const timeoutSeconds = options.timeout ? Number.parseInt(options.timeout, 10) : null;
17570
- if (timeoutSeconds !== null && Number.isFinite(timeoutSeconds) && timeoutSeconds > 0) {
17571
- runArgs.push("--tail-timeout-ms", String(timeoutSeconds * 1e3));
17901
+ const selectedRange = selectedSourceCsvRange(sourceCsvPath, rows);
17902
+ if (outputPath && selectedRange.count > ENRICH_AUTO_BATCH_ROWS) {
17903
+ const chunkCount = Math.ceil(
17904
+ selectedRange.count / ENRICH_AUTO_BATCH_ROWS
17905
+ );
17906
+ if (!options.json) {
17907
+ process.stderr.write(
17908
+ `Large enrich input selected ${selectedRange.count.toLocaleString()} rows. Running ${chunkCount.toLocaleString()} sequential batch runs of up to ${ENRICH_AUTO_BATCH_ROWS.toLocaleString()} rows and merging ${(0, import_node_path11.resolve)(outputPath)}.
17909
+ `
17910
+ );
17911
+ }
17912
+ let workingSourceCsvPath = sourceCsvPath;
17913
+ let lastStatus = null;
17914
+ let finalExportResult = null;
17915
+ let totalEnrichedRows = 0;
17916
+ const batchFailureRows = [];
17917
+ for (let chunkStart = selectedRange.start, chunkIndex = 0; chunkStart <= selectedRange.end; chunkStart += ENRICH_AUTO_BATCH_ROWS, chunkIndex += 1) {
17918
+ const chunkRows = {
17919
+ rowStart: chunkStart,
17920
+ rowEnd: Math.min(
17921
+ selectedRange.end,
17922
+ chunkStart + ENRICH_AUTO_BATCH_ROWS - 1
17923
+ )
17924
+ };
17925
+ if (!options.json) {
17926
+ process.stderr.write(
17927
+ `Batch ${chunkIndex + 1}/${chunkCount}: rows ${chunkRows.rowStart}:${chunkRows.rowEnd}
17928
+ `
17929
+ );
17930
+ }
17931
+ const chunk = await runOne({
17932
+ sourceCsvPath: workingSourceCsvPath,
17933
+ rows: chunkRows,
17934
+ json: true,
17935
+ passthroughStdout: false,
17936
+ suppressOpen: true
17937
+ });
17938
+ lastStatus = chunk.status;
17939
+ if (chunk.captured.result !== 0) {
17940
+ if (options.json) {
17941
+ printJson({
17942
+ ok: false,
17943
+ batch: {
17944
+ chunk: chunkIndex + 1,
17945
+ chunks: chunkCount,
17946
+ rows: chunkRows
17947
+ },
17948
+ result: chunk.status
17949
+ });
17950
+ } else {
17951
+ emitPlainBatchRunFailure({
17952
+ chunkIndex,
17953
+ chunkCount,
17954
+ rows: chunkRows,
17955
+ status: chunk.status
17956
+ });
17957
+ }
17958
+ process.exitCode = chunk.captured.result;
17959
+ return;
17960
+ }
17961
+ if (chunk.exportResult) {
17962
+ finalExportResult = chunk.exportResult;
17963
+ totalEnrichedRows += chunk.exportResult.enrichedRows;
17964
+ batchFailureRows.push(...chunk.exportResult.enrichedDataRows);
17965
+ workingSourceCsvPath = outputPath;
17966
+ }
17967
+ }
17968
+ const outputRows = readCsvRows(outputPath);
17969
+ const failureReport2 = await maybeEmitEnrichFailureReport({
17970
+ config,
17971
+ rows: batchFailureRows,
17972
+ rowRange: {
17973
+ rowStart: selectedRange.start,
17974
+ rowEnd: selectedRange.end
17975
+ },
17976
+ client: client2,
17977
+ outputPath
17978
+ });
17979
+ if (options.json) {
17980
+ const run = rewriteEnrichJsonStatus({
17981
+ status: lastStatus,
17982
+ config,
17983
+ forceAliases,
17984
+ output: finalExportResult ? {
17985
+ ...finalExportResult,
17986
+ sourceCsvRows: selectedRange.sourceRows,
17987
+ selectedRows: selectedRange.count,
17988
+ enrichedRows: totalEnrichedRows,
17989
+ rows: outputRows.length,
17990
+ enrichedDataRows: outputRows
17991
+ } : null,
17992
+ failureReport: failureReport2
17993
+ });
17994
+ printJson({
17995
+ ok: !failureReport2,
17996
+ run,
17997
+ batch: {
17998
+ chunks: chunkCount,
17999
+ chunkRows: ENRICH_AUTO_BATCH_ROWS,
18000
+ selectedRows: selectedRange.count
18001
+ },
18002
+ output: finalExportResult ? {
18003
+ sourceCsvRows: selectedRange.sourceRows,
18004
+ selectedRows: selectedRange.count,
18005
+ enrichedRows: totalEnrichedRows,
18006
+ path: finalExportResult.path
18007
+ } : null,
18008
+ ...failureReport2 ? {
18009
+ failure_report: {
18010
+ path: failureReport2.path,
18011
+ jobs: failureReport2.jobs.length
18012
+ }
18013
+ } : {}
18014
+ });
18015
+ }
18016
+ if (failureReport2) {
18017
+ process.exitCode = EXIT_SERVER2;
18018
+ }
18019
+ return;
17572
18020
  }
17573
- const captured = await runGeneratedEnrichPlay(runArgs, {
18021
+ const { captured, status, exportResult } = await runOne({
18022
+ sourceCsvPath,
18023
+ rows,
18024
+ json: Boolean(options.json),
17574
18025
  passthroughStdout: !options.json
17575
18026
  });
17576
- const status = options.json ? parseJsonOutput(captured.stdout) : await resolveWatchedGeneratedPlayStatus({
17577
- client: client2,
17578
- stdout: captured.stdout,
17579
- exitCode: captured.result
17580
- });
17581
18027
  if (captured.result !== 0) {
17582
18028
  if (options.json) {
17583
18029
  printJson({
@@ -17587,13 +18033,6 @@ function registerEnrichCommand(program) {
17587
18033
  process.exitCode = captured.result;
17588
18034
  return;
17589
18035
  }
17590
- const exportResult = outputPath ? await writeOutputCsv(outputPath, status, {
17591
- client: client2,
17592
- config,
17593
- sourceCsvPath,
17594
- rows,
17595
- inPlace: Boolean(options.inPlace)
17596
- }) : null;
17597
18036
  const rowsForFailureReport = exportResult?.enrichedDataRows ?? extractCanonicalRowsInfo(status)?.rows ?? [];
17598
18037
  if (options.json) {
17599
18038
  const failureReport2 = await maybeEmitEnrichFailureReport({
@@ -18511,6 +18950,15 @@ Examples:
18511
18950
  async function fetchOrganizations(http, apiKey) {
18512
18951
  return http.post("/api/v2/auth/cli/organizations", { api_key: apiKey });
18513
18952
  }
18953
+ function normalizeAuthScope(value) {
18954
+ if (!value) return "auto";
18955
+ if (value === "auto" || value === "folder" || value === "global") {
18956
+ return value;
18957
+ }
18958
+ throw new Error(
18959
+ `Invalid --auth-scope "${value}". Expected one of: auto, folder, global.`
18960
+ );
18961
+ }
18514
18962
  function orgListLines(orgs) {
18515
18963
  return orgs.map((org, index) => {
18516
18964
  const current = org.is_current ? " (current)" : "";
@@ -18518,6 +18966,90 @@ function orgListLines(orgs) {
18518
18966
  return `${index + 1}. ${org.name}${role}${current}`;
18519
18967
  });
18520
18968
  }
18969
+ function redactApiKey(value) {
18970
+ if (!value) return null;
18971
+ if (value.length <= 10) return `${value.slice(0, 3)}...`;
18972
+ return `${value.slice(0, 8)}...${value.slice(-4)}`;
18973
+ }
18974
+ function processEnvValue(name) {
18975
+ return process.env[name]?.trim() ?? "";
18976
+ }
18977
+ function resolveOrgSwitchAuthTarget(scope, config) {
18978
+ const activeProject = getResolvedProjectAuthSource(
18979
+ config.baseUrl,
18980
+ config.apiKey
18981
+ );
18982
+ const folderTarget = resolveProjectPinTarget();
18983
+ const globalOverrideWarning = activeProject && scope === "global" ? [
18984
+ `Folder auth in ${activeProject.filePath} overrides global auth for commands run here.`
18985
+ ] : [];
18986
+ if (scope === "folder") {
18987
+ if (!folderTarget.ok) {
18988
+ throw new Error(
18989
+ `Cowork project folder is ambiguous. Candidate folders: ${folderTarget.candidates.join(
18990
+ ", "
18991
+ )}. Set CLAUDE_PROJECT_DIR or cd into the intended project folder before running this command.`
18992
+ );
18993
+ }
18994
+ return {
18995
+ kind: "folder",
18996
+ requested_scope: scope,
18997
+ effective_scope: "folder",
18998
+ reason: "explicit_folder",
18999
+ source: folderTarget.source,
19000
+ warnings: []
19001
+ };
19002
+ }
19003
+ if (scope === "global") {
19004
+ return {
19005
+ kind: "global",
19006
+ requested_scope: scope,
19007
+ effective_scope: "global",
19008
+ reason: "explicit_global",
19009
+ warnings: globalOverrideWarning
19010
+ };
19011
+ }
19012
+ if (activeProject) {
19013
+ return {
19014
+ kind: "folder",
19015
+ requested_scope: scope,
19016
+ effective_scope: "folder",
19017
+ reason: "existing_folder_auth",
19018
+ source: activeProject.source,
19019
+ warnings: []
19020
+ };
19021
+ }
19022
+ if (folderTarget.ok && folderTarget.source === "cowork") {
19023
+ return {
19024
+ kind: "folder",
19025
+ requested_scope: scope,
19026
+ effective_scope: "folder",
19027
+ reason: "detected_cowork_project",
19028
+ source: "cowork",
19029
+ warnings: []
19030
+ };
19031
+ }
19032
+ if (!folderTarget.ok) {
19033
+ return {
19034
+ kind: "global",
19035
+ requested_scope: scope,
19036
+ effective_scope: "global",
19037
+ reason: "ambiguous_cowork_fell_back_global",
19038
+ warnings: [
19039
+ `Cowork project folder is ambiguous, so global auth was updated. Candidate folders: ${folderTarget.candidates.join(
19040
+ ", "
19041
+ )}. Set CLAUDE_PROJECT_DIR or cd into the intended project folder to update folder auth.`
19042
+ ]
19043
+ };
19044
+ }
19045
+ return {
19046
+ kind: "global",
19047
+ requested_scope: scope,
19048
+ effective_scope: "global",
19049
+ reason: "default_global",
19050
+ warnings: []
19051
+ };
19052
+ }
18521
19053
  async function handleOrgList(options) {
18522
19054
  const config = resolveConfig();
18523
19055
  const http = new HttpClient(config);
@@ -18537,7 +19069,89 @@ async function handleOrgList(options) {
18537
19069
  { json: options.json }
18538
19070
  );
18539
19071
  }
19072
+ async function handleOrgStatus(options) {
19073
+ const config = resolveConfig();
19074
+ const http = new HttpClient(config);
19075
+ const payload = await fetchOrganizations(http, config.apiKey);
19076
+ const current = payload.organizations.find((org) => org.is_current) ?? payload.organizations.find((org) => org.org_id === payload.current_org_id) ?? null;
19077
+ const projectCandidate = getActiveProjectAuthSource();
19078
+ const activeProject = getResolvedProjectAuthSource(
19079
+ config.baseUrl,
19080
+ config.apiKey
19081
+ );
19082
+ const folderTarget = resolveProjectPinTarget();
19083
+ const hostPath = hostEnvFilePath(config.baseUrl);
19084
+ const envApiKey = processEnvValue(API_KEY_ENV);
19085
+ const envHostUrl = processEnvValue(HOST_URL_ENV);
19086
+ const authSource = envApiKey ? {
19087
+ scope: "env",
19088
+ source: "process",
19089
+ path: null,
19090
+ api_key: redactApiKey(envApiKey),
19091
+ host: envHostUrl || null,
19092
+ overrides_global: true,
19093
+ overrides_folder: Boolean(projectCandidate)
19094
+ } : activeProject ? {
19095
+ scope: "folder",
19096
+ source: activeProject.source,
19097
+ path: activeProject.filePath,
19098
+ api_key: redactApiKey(activeProject.env.DEEPLINE_API_KEY),
19099
+ overrides_global: true
19100
+ } : {
19101
+ scope: "global",
19102
+ source: "host",
19103
+ path: hostPath,
19104
+ api_key: redactApiKey(config.apiKey),
19105
+ overrides_global: false
19106
+ };
19107
+ const folderTargetPayload = folderTarget.ok ? {
19108
+ ok: true,
19109
+ source: folderTarget.source,
19110
+ dir: folderTarget.dir,
19111
+ env_path: `${folderTarget.dir}/.env.deepline`
19112
+ } : {
19113
+ ok: false,
19114
+ reason: folderTarget.reason,
19115
+ candidates: folderTarget.candidates
19116
+ };
19117
+ const lines = [
19118
+ `Organization: ${current?.name ?? "(unknown)"}`,
19119
+ `Host: ${config.baseUrl}`,
19120
+ `Auth scope: ${authSource.scope}`
19121
+ ];
19122
+ if (authSource.path) {
19123
+ lines.push(`Auth file: ${authSource.path}`);
19124
+ } else {
19125
+ lines.push(`Auth source: ${API_KEY_ENV} process environment`);
19126
+ }
19127
+ if (envApiKey) {
19128
+ lines.push(`${API_KEY_ENV} overrides saved auth for this process.`);
19129
+ } else if (activeProject) {
19130
+ lines.push("Global auth is overridden in this folder.");
19131
+ }
19132
+ printCommandEnvelope(
19133
+ {
19134
+ ok: true,
19135
+ host: config.baseUrl,
19136
+ organization: current,
19137
+ current_org_id: payload.current_org_id,
19138
+ auth_source: authSource,
19139
+ host_env_path: hostPath,
19140
+ folder_target: folderTargetPayload,
19141
+ next: {
19142
+ set_auto: "deepline org set <org>",
19143
+ set_folder: "deepline org set <org> --auth-scope folder",
19144
+ set_global: "deepline org set <org> --auth-scope global"
19145
+ },
19146
+ render: {
19147
+ sections: [{ title: "org status", lines }]
19148
+ }
19149
+ },
19150
+ { json: options.json }
19151
+ );
19152
+ }
18540
19153
  async function handleOrgSwitch(selection, options) {
19154
+ const authScope = normalizeAuthScope(options.authScope);
18541
19155
  const config = resolveConfig();
18542
19156
  const http = new HttpClient(config);
18543
19157
  const payload = await fetchOrganizations(http, config.apiKey);
@@ -18545,7 +19159,10 @@ async function handleOrgSwitch(selection, options) {
18545
19159
  printCommandEnvelope(
18546
19160
  {
18547
19161
  ...payload,
18548
- next: { switch: "deepline org switch <number>" },
19162
+ next: { set: "deepline org set <number>" },
19163
+ deprecated_command: options.deprecatedSwitch ? "org switch" : null,
19164
+ replacement_command: options.deprecatedSwitch ? "deepline org set" : null,
19165
+ warnings: options.deprecatedSwitch ? ["org switch is deprecated; use org set."] : [],
18549
19166
  render: {
18550
19167
  sections: [
18551
19168
  {
@@ -18553,7 +19170,7 @@ async function handleOrgSwitch(selection, options) {
18553
19170
  lines: orgListLines(payload.organizations)
18554
19171
  }
18555
19172
  ],
18556
- actions: [{ label: "Run", command: "deepline org switch <number>" }]
19173
+ actions: [{ label: "Run", command: "deepline org set <number>" }]
18557
19174
  }
18558
19175
  },
18559
19176
  { json: options.json }
@@ -18576,16 +19193,56 @@ async function handleOrgSwitch(selection, options) {
18576
19193
  if (!target) {
18577
19194
  throw new Error("Could not resolve the selected organization.");
18578
19195
  }
19196
+ const authTarget = resolveOrgSwitchAuthTarget(authScope, config);
18579
19197
  if (target.is_current) {
19198
+ let project_env_paths2 = [];
19199
+ if (authTarget.kind === "folder") {
19200
+ project_env_paths2 = saveProjectDeeplineEnvValues({
19201
+ DEEPLINE_HOST_URL: config.baseUrl,
19202
+ DEEPLINE_API_KEY: config.apiKey
19203
+ });
19204
+ } else {
19205
+ saveHostEnvValues(config.baseUrl, {
19206
+ DEEPLINE_HOST_URL: config.baseUrl,
19207
+ DEEPLINE_API_KEY: config.apiKey
19208
+ });
19209
+ }
19210
+ const renderLines2 = [`Already on ${target.name}.`];
19211
+ for (const projectPath of project_env_paths2) {
19212
+ renderLines2.push(`Saved folder auth in ${projectPath}`);
19213
+ }
19214
+ if (authTarget.kind === "global") {
19215
+ renderLines2.push(`Saved global auth in ${hostEnvFilePath(config.baseUrl)}`);
19216
+ }
19217
+ renderLines2.push(
19218
+ `Scope: ${authTarget.requested_scope} -> ${authTarget.effective_scope} (${authTarget.reason})`
19219
+ );
19220
+ if (options.deprecatedSwitch) {
19221
+ renderLines2.push("Warning: org switch is deprecated; use org set.");
19222
+ }
19223
+ for (const warning of authTarget.warnings) {
19224
+ renderLines2.push(`Warning: ${warning}`);
19225
+ }
19226
+ const warnings2 = [
19227
+ ...options.deprecatedSwitch ? ["org switch is deprecated; use org set."] : [],
19228
+ ...authTarget.warnings
19229
+ ];
18580
19230
  printCommandEnvelope(
18581
19231
  {
18582
19232
  ok: true,
18583
19233
  unchanged: true,
18584
19234
  organization: target,
19235
+ requested_auth_scope: authTarget.requested_scope,
19236
+ effective_auth_scope: authTarget.effective_scope,
19237
+ auth_scope_reason: authTarget.reason,
19238
+ auth_scope: authTarget.effective_scope,
19239
+ host_env_path: authTarget.kind === "global" ? hostEnvFilePath(config.baseUrl) : null,
19240
+ project_env_paths: project_env_paths2,
19241
+ deprecated_command: options.deprecatedSwitch ? "org switch" : null,
19242
+ replacement_command: options.deprecatedSwitch ? "deepline org set" : null,
19243
+ warnings: warnings2,
18585
19244
  render: {
18586
- sections: [
18587
- { title: "org switch", lines: [`Already on ${target.name}.`] }
18588
- ]
19245
+ sections: [{ title: "org set", lines: renderLines2 }]
18589
19246
  }
18590
19247
  },
18591
19248
  { json: options.json }
@@ -18596,26 +19253,61 @@ async function handleOrgSwitch(selection, options) {
18596
19253
  api_key: config.apiKey,
18597
19254
  org_id: target.org_id
18598
19255
  });
18599
- saveHostEnvValues(config.baseUrl, {
18600
- DEEPLINE_API_KEY: switched.api_key,
18601
- DEEPLINE_ACTIVE_ORG_ID: switched.org_id,
18602
- DEEPLINE_ACTIVE_ORG_NAME: switched.org_name
18603
- });
19256
+ let project_env_paths = [];
19257
+ if (authTarget.kind === "folder") {
19258
+ project_env_paths = saveProjectDeeplineEnvValues({
19259
+ DEEPLINE_HOST_URL: config.baseUrl,
19260
+ DEEPLINE_API_KEY: switched.api_key
19261
+ });
19262
+ } else {
19263
+ saveHostEnvValues(config.baseUrl, {
19264
+ DEEPLINE_HOST_URL: config.baseUrl,
19265
+ DEEPLINE_API_KEY: switched.api_key,
19266
+ DEEPLINE_ACTIVE_ORG_ID: switched.org_id,
19267
+ DEEPLINE_ACTIVE_ORG_NAME: switched.org_name
19268
+ });
19269
+ }
18604
19270
  const { api_key: _apiKey, ...publicSwitched } = switched;
19271
+ const renderLines = [`Switched to ${switched.org_name}.`];
19272
+ if (authTarget.kind === "folder") {
19273
+ for (const projectPath of project_env_paths) {
19274
+ renderLines.push(`Saved folder auth in ${projectPath}`);
19275
+ }
19276
+ } else {
19277
+ renderLines.push(`Saved global auth in ${hostEnvFilePath(config.baseUrl)}`);
19278
+ }
19279
+ renderLines.push(
19280
+ `Scope: ${authTarget.requested_scope} -> ${authTarget.effective_scope} (${authTarget.reason})`
19281
+ );
19282
+ if (options.deprecatedSwitch) {
19283
+ renderLines.push("Warning: org switch is deprecated; use org set.");
19284
+ }
19285
+ for (const warning of authTarget.warnings) {
19286
+ renderLines.push(`Warning: ${warning}`);
19287
+ }
19288
+ const warnings = [
19289
+ ...options.deprecatedSwitch ? ["org switch is deprecated; use org set."] : [],
19290
+ ...authTarget.warnings
19291
+ ];
18605
19292
  printCommandEnvelope(
18606
19293
  {
18607
19294
  ok: true,
18608
- host_env_path: hostEnvFilePath(config.baseUrl),
19295
+ host_env_path: authTarget.kind === "global" ? hostEnvFilePath(config.baseUrl) : null,
19296
+ project_env_paths,
18609
19297
  ...publicSwitched,
18610
19298
  api_key_saved: true,
19299
+ requested_auth_scope: authTarget.requested_scope,
19300
+ effective_auth_scope: authTarget.effective_scope,
19301
+ auth_scope_reason: authTarget.reason,
19302
+ auth_scope: authTarget.effective_scope,
19303
+ deprecated_command: options.deprecatedSwitch ? "org switch" : null,
19304
+ replacement_command: options.deprecatedSwitch ? "deepline org set" : null,
19305
+ warnings,
18611
19306
  render: {
18612
19307
  sections: [
18613
19308
  {
18614
- title: "org switch",
18615
- lines: [
18616
- `Switched to ${switched.org_name}.`,
18617
- `Saved host auth in ${hostEnvFilePath(config.baseUrl)}`
18618
- ]
19309
+ title: "org set",
19310
+ lines: renderLines
18619
19311
  }
18620
19312
  ]
18621
19313
  }
@@ -18631,6 +19323,7 @@ async function handleOrgCreate(name, options) {
18631
19323
  name
18632
19324
  });
18633
19325
  saveHostEnvValues(config.baseUrl, {
19326
+ DEEPLINE_HOST_URL: config.baseUrl,
18634
19327
  DEEPLINE_API_KEY: created.api_key,
18635
19328
  DEEPLINE_ACTIVE_ORG_ID: created.org_id,
18636
19329
  DEEPLINE_ACTIVE_ORG_NAME: created.org_name
@@ -18660,18 +19353,41 @@ async function handleOrgCreate(name, options) {
18660
19353
  );
18661
19354
  }
18662
19355
  function registerOrgCommands(program) {
18663
- const org = program.command("org").description("List, create, and switch organizations.").addHelpText(
19356
+ const org = program.command("org").description("List, create, and set organizations.").addHelpText(
18664
19357
  "after",
18665
19358
  `
18666
19359
  Notes:
18667
- Organizations are workspaces. Switching organizations mutates the saved host
18668
- auth file so later CLI commands target the selected workspace.
19360
+ Organizations are workspaces. Auth is stored as an API key scoped to one
19361
+ organization, so setting an org saves a key for the selected auth scope.
19362
+
19363
+ Auth scopes:
19364
+ auto Update the folder pin that already controls this command; in
19365
+ Cowork, write the mounted project folder; otherwise update global
19366
+ host auth.
19367
+ folder Write .env.deepline in the current project/Cowork project and make
19368
+ sure .env.deepline is gitignored. Commands below that folder use
19369
+ this org; sibling folders can pin different orgs.
19370
+ global Write ~/.local/deepline/<host>/.env. Commands outside folder pins
19371
+ use this org. Existing folder pins still override global auth.
19372
+
19373
+ Process env DEEPLINE_API_KEY overrides saved auth for that command only.
19374
+
19375
+ Agent loop:
19376
+ deepline org list --json
19377
+ deepline org set <number-or-org-id> --auth-scope folder --json
19378
+ deepline org status --json
19379
+ deepline auth status --json
18669
19380
 
18670
19381
  Examples:
18671
19382
  deepline org list --json
19383
+ deepline org status --json
18672
19384
  deepline org create Acme --json
18673
- deepline org switch 2
18674
- deepline org switch --org-id org_123 --json
19385
+ deepline org set 2
19386
+ deepline org set 2 --auth-scope folder
19387
+ deepline org set --org-id org_123 --json
19388
+ cd path/to/project-a && deepline org set Prove --auth-scope folder --json
19389
+ cd path/to/project-b && deepline org set Mixmax --auth-scope folder --json
19390
+ cd .. && deepline org status --json
18675
19391
  `
18676
19392
  );
18677
19393
  org.command("list").description("List your organizations.").addHelpText(
@@ -18698,21 +19414,88 @@ Examples:
18698
19414
  deepline org create "Acme Sales" --json
18699
19415
  `
18700
19416
  ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgCreate);
18701
- org.command("switch [selection]").description(
18702
- "Switch to another organization and save the new API key in the host auth file."
18703
- ).addHelpText(
19417
+ org.command("status").description("Show the current organization and auth source.").addHelpText(
18704
19418
  "after",
18705
19419
  `
18706
19420
  Notes:
18707
- Mutates the saved host auth file. Selection can be a list number, exact
18708
- organization name, or organization id. Without a selection, prints choices.
19421
+ Read-only. Shows the current org plus auth_source.scope, auth_source.path,
19422
+ folder_target, host_env_path, and next commands.
19423
+
19424
+ Scopes are env, folder, or global. env means DEEPLINE_API_KEY/
19425
+ DEEPLINE_HOST_URL in process env wins. folder means nearest/Cowork
19426
+ .env.deepline wins. global means host config wins.
19427
+
19428
+ Run before and after org set to verify what future commands will use.
19429
+
19430
+ Examples:
19431
+ deepline org status
19432
+ deepline org status --json
19433
+ `
19434
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgStatus);
19435
+ const addOrgSetOptions = (command) => command.option("--org-id <id>", "Set using an explicit organization id").option(
19436
+ "--auth-scope <scope>",
19437
+ "Where to save auth: auto, folder, or global",
19438
+ "auto"
19439
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped");
19440
+ addOrgSetOptions(
19441
+ org.command("set [selection]").description("Set the organization for the selected auth scope.").addHelpText(
19442
+ "after",
19443
+ `
19444
+ Notes:
19445
+ Selection can be a list number, exact organization name, or organization id.
19446
+ Without a selection, prints choices.
19447
+
19448
+ Mutates local auth state and asks Deepline for an org-scoped API key. It
19449
+ writes only the selected scope:
19450
+ auto Update the folder pin that already controls this command; in
19451
+ Cowork, write the mounted project folder; otherwise update global
19452
+ host auth.
19453
+ folder Write .env.deepline in the current project/Cowork project and make
19454
+ sure .env.deepline is gitignored.
19455
+ global Write ~/.local/deepline/<host>/.env.
19456
+
19457
+ Folder auth shadows global auth. If you set --auth-scope global from a pinned
19458
+ folder, commands in that folder still use the folder pin until it is changed
19459
+ or removed.
19460
+
19461
+ Use --json for stable fields including effective_auth_scope,
19462
+ auth_scope_reason, host_env_path, project_env_paths, warnings, org_id, and
19463
+ org_name.
19464
+
19465
+ Examples:
19466
+ deepline org set
19467
+ deepline org set 2
19468
+ deepline org set 2 --auth-scope folder
19469
+ deepline org set 2 --auth-scope global
19470
+ deepline org set --org-id org_123 --json
19471
+ deepline org status --json
19472
+ `
19473
+ )
19474
+ ).action(handleOrgSwitch);
19475
+ addOrgSetOptions(
19476
+ org.command("switch [selection]").description(
19477
+ "Deprecated alias for org set. Set the organization for the selected auth scope."
19478
+ ).addHelpText(
19479
+ "after",
19480
+ `
19481
+ Notes:
19482
+ Deprecated alias. Use deepline org set instead. The command still mutates
19483
+ local auth state with the same --auth-scope behavior as org set.
19484
+
19485
+ Run deepline org set --help for scope definitions and the agent verification
19486
+ loop.
18709
19487
 
18710
19488
  Examples:
18711
19489
  deepline org switch
18712
19490
  deepline org switch 2
19491
+ deepline org switch 2 --auth-scope folder
19492
+ deepline org switch 2 --auth-scope global
18713
19493
  deepline org switch --org-id org_123 --json
18714
19494
  `
18715
- ).option("--org-id <id>", "Switch using an explicit organization id").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgSwitch);
19495
+ )
19496
+ ).action(
19497
+ (selection, options) => handleOrgSwitch(selection, { ...options, deprecatedSwitch: true })
19498
+ );
18716
19499
  }
18717
19500
 
18718
19501
  // src/cli/commands/quickstart.ts