deepline 0.1.143 → 0.1.144

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.
@@ -164,7 +164,15 @@ import { tmpdir as tmpdir4 } from "os";
164
164
  import { Command as Command3 } from "commander";
165
165
 
166
166
  // src/config.ts
167
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
167
+ import {
168
+ existsSync,
169
+ mkdirSync,
170
+ readdirSync,
171
+ realpathSync,
172
+ readFileSync,
173
+ statSync,
174
+ writeFileSync
175
+ } from "fs";
168
176
  import { homedir } from "os";
169
177
  import { dirname, join, resolve } from "path";
170
178
 
@@ -214,6 +222,22 @@ var PROD_URL = "https://code.deepline.com";
214
222
  var DEFAULT_TIMEOUT = 6e4;
215
223
  var DEFAULT_MAX_RETRIES = 3;
216
224
  var PROJECT_DEEPLINE_ENV_FILE = ".env.deepline";
225
+ var COWORK_IGNORED_WORKSPACE_DIRS = /* @__PURE__ */ new Set([
226
+ ".auto-memory",
227
+ ".claude",
228
+ ".remote-plugins",
229
+ "outputs",
230
+ "plugins",
231
+ "uploads"
232
+ ]);
233
+ var COWORK_PROJECT_MARKERS = [
234
+ ".deepline",
235
+ ".env.deepline",
236
+ ".git",
237
+ "AGENTS.md",
238
+ "package.json",
239
+ "pyproject.toml"
240
+ ];
217
241
  function baseUrlSlug(baseUrl) {
218
242
  let url;
219
243
  try {
@@ -259,9 +283,124 @@ function findNearestEnvFile(name, startDir = process.cwd()) {
259
283
  current = parent;
260
284
  }
261
285
  }
262
- function loadProjectDeeplineEnv(startDir = process.cwd()) {
263
- const filePath = findNearestEnvFile(PROJECT_DEEPLINE_ENV_FILE, startDir);
264
- return filePath ? parseEnvFile(filePath) : {};
286
+ function isDirectory(path) {
287
+ try {
288
+ return statSync(path).isDirectory();
289
+ } catch {
290
+ return false;
291
+ }
292
+ }
293
+ function canonicalPath(path) {
294
+ try {
295
+ return realpathSync(path);
296
+ } catch {
297
+ return resolve(path);
298
+ }
299
+ }
300
+ function isTruthy(value) {
301
+ return /^(1|true|yes|on)$/i.test(value?.trim() ?? "");
302
+ }
303
+ function sessionRootFromPath(path) {
304
+ const trimmed = path?.trim();
305
+ if (!trimmed) return null;
306
+ const match = /^\/sessions\/[^/]+(?=\/|$)/.exec(trimmed);
307
+ return match?.[0] ?? null;
308
+ }
309
+ function coworkSessionRoot() {
310
+ const home = process.env.HOME?.trim();
311
+ const homeSessionRoot = sessionRootFromPath(home);
312
+ if (homeSessionRoot && isDirectory(join(homeSessionRoot, "mnt"))) {
313
+ return homeSessionRoot;
314
+ }
315
+ const cwdSessionRoot = sessionRootFromPath(process.cwd());
316
+ if (cwdSessionRoot && isDirectory(join(cwdSessionRoot, "mnt"))) {
317
+ return cwdSessionRoot;
318
+ }
319
+ if (isTruthy(process.env.CLAUDE_CODE_REMOTE) && home) {
320
+ const mountedRoot = join(home, "mnt");
321
+ if (isDirectory(mountedRoot)) return resolve(home);
322
+ }
323
+ return null;
324
+ }
325
+ function isCoworkLikeSandbox() {
326
+ const home = process.env.HOME?.trim();
327
+ return isTruthy(process.env.CLAUDE_CODE_REMOTE) || sessionRootFromPath(home) !== null || sessionRootFromPath(process.cwd()) !== null;
328
+ }
329
+ function coworkProjectScore(path) {
330
+ let score = 0;
331
+ for (const marker of COWORK_PROJECT_MARKERS) {
332
+ if (existsSync(join(path, marker))) score += 1;
333
+ }
334
+ return score;
335
+ }
336
+ function listCoworkWorkspaceDirCandidates() {
337
+ if (!isCoworkLikeSandbox()) {
338
+ return [];
339
+ }
340
+ const explicitProjectDir = process.env.CLAUDE_PROJECT_DIR?.trim();
341
+ if (explicitProjectDir && isDirectory(explicitProjectDir)) {
342
+ return [resolve(explicitProjectDir)];
343
+ }
344
+ const sessionRoot = coworkSessionRoot();
345
+ if (!sessionRoot) return [];
346
+ const mountedRoot = join(sessionRoot, "mnt");
347
+ if (!isDirectory(mountedRoot)) return [];
348
+ let names;
349
+ try {
350
+ names = readdirSync(mountedRoot).sort();
351
+ } catch {
352
+ return [];
353
+ }
354
+ const candidates = [];
355
+ for (const name of names) {
356
+ if (name.startsWith(".") || COWORK_IGNORED_WORKSPACE_DIRS.has(name)) {
357
+ continue;
358
+ }
359
+ const candidate = join(mountedRoot, name);
360
+ if (isDirectory(candidate)) candidates.push(candidate);
361
+ }
362
+ if (candidates.length <= 1) return candidates;
363
+ const projectLike = candidates.filter(
364
+ (candidate) => coworkProjectScore(candidate) > 0
365
+ );
366
+ return projectLike.length > 0 ? projectLike : candidates;
367
+ }
368
+ function isInIgnoredCoworkMount(path) {
369
+ const sessionRoot = coworkSessionRoot();
370
+ if (!sessionRoot) return false;
371
+ const mountedRoot = canonicalPath(join(sessionRoot, "mnt"));
372
+ const resolvedPath = canonicalPath(path);
373
+ const prefix = `${mountedRoot}/`;
374
+ if (!resolvedPath.startsWith(prefix)) return false;
375
+ const relativePath = resolvedPath.slice(prefix.length);
376
+ const mountName = relativePath.split("/")[0];
377
+ return mountName.startsWith(".") || COWORK_IGNORED_WORKSPACE_DIRS.has(mountName);
378
+ }
379
+ function detectCoworkWorkspaceDir() {
380
+ const candidates = listCoworkWorkspaceDirCandidates();
381
+ return candidates.length === 1 ? candidates[0] : null;
382
+ }
383
+ function loadProjectEnvCandidates(startDir = process.cwd()) {
384
+ const filePaths = [];
385
+ const sources = /* @__PURE__ */ new Map();
386
+ const nearestFile = findNearestEnvFile(PROJECT_DEEPLINE_ENV_FILE, startDir);
387
+ if (nearestFile && !isInIgnoredCoworkMount(nearestFile)) {
388
+ filePaths.push(nearestFile);
389
+ sources.set(resolve(nearestFile), "nearest");
390
+ }
391
+ const coworkWorkspaceDir = detectCoworkWorkspaceDir();
392
+ if (coworkWorkspaceDir) {
393
+ const coworkFile = join(coworkWorkspaceDir, PROJECT_DEEPLINE_ENV_FILE);
394
+ if (existsSync(coworkFile) && !filePaths.some((filePath) => resolve(filePath) === resolve(coworkFile))) {
395
+ filePaths.push(coworkFile);
396
+ sources.set(resolve(coworkFile), "cowork");
397
+ }
398
+ }
399
+ return filePaths.map((filePath) => ({
400
+ filePath,
401
+ env: parseEnvFile(filePath),
402
+ source: sources.get(resolve(filePath)) ?? "nearest"
403
+ }));
265
404
  }
266
405
  function normalizeBaseUrl(baseUrl) {
267
406
  const trimmed = baseUrl.trim().replace(/\/+$/, "");
@@ -325,23 +464,35 @@ function loadGlobalCliEnv() {
325
464
  return loadCliEnv(PROD_URL);
326
465
  }
327
466
  function autoDetectBaseUrl() {
328
- const projectEnv = loadProjectDeeplineEnv();
467
+ const projectEnvs = loadProjectEnvCandidates();
329
468
  const globalEnv = loadGlobalCliEnv();
330
- return normalizeBaseUrl(process.env[HOST_URL_ENV] ?? "") || normalizeBaseUrl(projectEnv[HOST_URL_ENV] ?? "") || normalizeBaseUrl(globalEnv[HOST_URL_ENV] ?? "") || PROD_URL;
469
+ return normalizeBaseUrl(process.env[HOST_URL_ENV] ?? "") || firstNonEmpty(
470
+ ...projectEnvs.map(({ env }) => normalizeBaseUrl(env[HOST_URL_ENV]))
471
+ ) || normalizeBaseUrl(globalEnv[HOST_URL_ENV] ?? "") || PROD_URL;
331
472
  }
332
473
  function resolveApiKeyForBaseUrl(baseUrl, explicitApiKey) {
333
474
  const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
334
- const projectEnv = loadProjectDeeplineEnv();
475
+ const projectEnvs = loadProjectEnvCandidates();
335
476
  const cliEnv = loadCliEnv(normalizedBaseUrl || baseUrl);
336
- const projectBaseUrl = normalizeBaseUrl(projectEnv[HOST_URL_ENV] ?? "");
337
- const projectKeyApplies = projectBaseUrl === normalizedBaseUrl;
338
477
  return firstNonEmpty(
339
478
  explicitApiKey,
340
479
  process.env[API_KEY_ENV],
341
- projectKeyApplies ? projectEnv[API_KEY_ENV] : "",
480
+ ...projectEnvs.map(({ env }) => {
481
+ const projectBaseUrl = normalizeBaseUrl(env[HOST_URL_ENV] ?? "");
482
+ return projectBaseUrl === normalizedBaseUrl ? env[API_KEY_ENV] : "";
483
+ }),
342
484
  cliEnv[API_KEY_ENV]
343
485
  );
344
486
  }
487
+ function getResolvedProjectAuthSource(baseUrl, apiKey, startDir = process.cwd()) {
488
+ const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
489
+ const normalizedApiKey = apiKey.trim();
490
+ if (!normalizedBaseUrl || !normalizedApiKey) return null;
491
+ return loadProjectEnvCandidates(startDir).find(({ env }) => {
492
+ const projectBaseUrl = normalizeBaseUrl(env[HOST_URL_ENV] ?? "");
493
+ return projectBaseUrl === normalizedBaseUrl && (env[API_KEY_ENV] ?? "").trim() === normalizedApiKey;
494
+ }) ?? null;
495
+ }
345
496
  function resolveConfig(options) {
346
497
  const baseUrl = normalizeBaseUrl(
347
498
  options?.baseUrl?.trim() || autoDetectBaseUrl()
@@ -364,6 +515,69 @@ function resolveConfig(options) {
364
515
  maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES
365
516
  };
366
517
  }
518
+ function mergeProjectEnvFile(filePath, values) {
519
+ const existing = parseEnvFile(filePath);
520
+ const merged = { ...existing, ...values };
521
+ const dir = dirname(filePath);
522
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
523
+ ensureProjectEnvIsIgnored(dir);
524
+ const allowedKeys = /* @__PURE__ */ new Set([HOST_URL_ENV, API_KEY_ENV]);
525
+ const lines = Object.entries(merged).filter(([key, value]) => allowedKeys.has(key) && value !== "").map(([key, value]) => `${key}=${value}`);
526
+ writeFileSync(filePath, `${lines.join("\n")}
527
+ `, "utf-8");
528
+ }
529
+ function ensureProjectEnvIsIgnored(dir) {
530
+ const gitignorePath = join(dir, ".gitignore");
531
+ const entry = PROJECT_DEEPLINE_ENV_FILE;
532
+ const existing = existsSync(gitignorePath) ? readFileSync(gitignorePath, "utf-8") : "";
533
+ const alreadyIgnored = existing.split(/\r?\n/).map((line) => line.trim()).some((line) => line === entry || line === `/${entry}`);
534
+ if (alreadyIgnored) return;
535
+ const prefix = existing && !existing.endsWith("\n") ? "\n" : "";
536
+ writeFileSync(gitignorePath, `${existing}${prefix}${entry}
537
+ `, "utf-8");
538
+ }
539
+ function saveProjectDeeplineEnvValues(values, startDir = process.cwd()) {
540
+ const target = resolveProjectPinTarget(startDir);
541
+ if (!target.ok) {
542
+ throw new ConfigError(
543
+ `Cowork project folder is ambiguous. Candidate folders: ${target.candidates.join(
544
+ ", "
545
+ )}. Set CLAUDE_PROJECT_DIR or cd into the intended project folder before running this command.`
546
+ );
547
+ }
548
+ const filePath = join(target.dir, PROJECT_DEEPLINE_ENV_FILE);
549
+ mergeProjectEnvFile(filePath, values);
550
+ return [filePath];
551
+ }
552
+ function resolveProjectPinTarget(startDir = process.cwd()) {
553
+ const nearestFile = findNearestEnvFile(PROJECT_DEEPLINE_ENV_FILE, startDir);
554
+ if (nearestFile && !isInIgnoredCoworkMount(nearestFile)) {
555
+ return { ok: true, dir: dirname(nearestFile), source: "nearest" };
556
+ }
557
+ const coworkCandidates = listCoworkWorkspaceDirCandidates();
558
+ if (coworkCandidates.length === 1) {
559
+ return { ok: true, dir: coworkCandidates[0], source: "cowork" };
560
+ }
561
+ if (coworkCandidates.length > 1) {
562
+ const resolvedStartDir = canonicalPath(startDir);
563
+ const cwdCandidate = coworkCandidates.find((candidate) => {
564
+ const resolvedCandidate = canonicalPath(candidate);
565
+ return resolvedStartDir === resolvedCandidate || resolvedStartDir.startsWith(`${resolvedCandidate}/`);
566
+ });
567
+ if (cwdCandidate) {
568
+ return { ok: true, dir: cwdCandidate, source: "cowork" };
569
+ }
570
+ return {
571
+ ok: false,
572
+ reason: "ambiguous_cowork_project",
573
+ candidates: coworkCandidates
574
+ };
575
+ }
576
+ return { ok: true, dir: resolve(startDir), source: "cwd" };
577
+ }
578
+ function getActiveProjectAuthSource(startDir = process.cwd()) {
579
+ return loadProjectEnvCandidates(startDir)[0] ?? null;
580
+ }
367
581
 
368
582
  // src/http.ts
369
583
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
@@ -390,10 +604,10 @@ var SDK_RELEASE = {
390
604
  // 0.1.108 ships explicit dataset column/tool recompute policy and removes
391
605
  // the SDK enrich generator's one-second stale policy.
392
606
  // 0.1.110 ships authored V2 prebuilts and required top-level play descriptions.
393
- version: "0.1.143",
607
+ version: "0.1.144",
394
608
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
395
609
  supportPolicy: {
396
- latest: "0.1.143",
610
+ latest: "0.1.144",
397
611
  minimumSupported: "0.1.53",
398
612
  deprecatedBelow: "0.1.53",
399
613
  commandMinimumSupported: [
@@ -484,7 +698,7 @@ function normalizeAgentRuntime(value) {
484
698
  if (explicit === "gemini_cli") return "gemini";
485
699
  return EXPLICIT_AGENT_RUNTIMES.has(explicit) ? explicit : null;
486
700
  }
487
- function isCoworkLikeSandbox() {
701
+ function isCoworkLikeSandbox2() {
488
702
  const pluginMode = truthyEnv("DEEPLINE_PLUGIN_MODE");
489
703
  const claudeRemote = truthyEnv("CLAUDE_CODE_REMOTE");
490
704
  const projectDir = Boolean(process.env.CLAUDE_PROJECT_DIR?.trim());
@@ -497,7 +711,7 @@ function detectAgentRuntime(options = {}) {
497
711
  const explicit = normalizeAgentRuntime(process.env.DEEPLINE_AGENT_RUNTIME);
498
712
  if (explicit) return explicit;
499
713
  if (process.env.CODEX_THREAD_ID?.trim()) return "codex";
500
- if (options.detectCowork !== false && isCoworkLikeSandbox()) {
714
+ if (options.detectCowork !== false && isCoworkLikeSandbox2()) {
501
715
  return "claude_cowork";
502
716
  }
503
717
  if (process.env.CLAUDECODE?.trim() === "1") return "claude_code";
@@ -963,7 +1177,7 @@ function sleep(ms) {
963
1177
  return new Promise((resolve13) => setTimeout(resolve13, ms));
964
1178
  }
965
1179
  function withCoworkNetworkHint(message) {
966
- if (!isCoworkLikeSandbox() || message.includes(COWORK_NETWORK_HINT)) {
1180
+ if (!isCoworkLikeSandbox2() || message.includes(COWORK_NETWORK_HINT)) {
967
1181
  return message;
968
1182
  }
969
1183
  return `${message}
@@ -7017,9 +7231,9 @@ import { createHash as createHash2 } from "crypto";
7017
7231
  import {
7018
7232
  existsSync as existsSync6,
7019
7233
  readFileSync as readFileSync6,
7020
- readdirSync,
7021
- realpathSync,
7022
- statSync as statSync2,
7234
+ readdirSync as readdirSync2,
7235
+ realpathSync as realpathSync2,
7236
+ statSync as statSync3,
7023
7237
  writeFileSync as writeFileSync8
7024
7238
  } from "fs";
7025
7239
  import { basename, dirname as dirname6, join as join5, resolve as resolve7 } from "path";
@@ -7030,7 +7244,7 @@ import {
7030
7244
  closeSync,
7031
7245
  openSync,
7032
7246
  readSync,
7033
- statSync,
7247
+ statSync as statSync2,
7034
7248
  writeFileSync as writeFileSync7
7035
7249
  } from "fs";
7036
7250
  import { isAbsolute, relative, resolve as resolve6 } from "path";
@@ -7463,7 +7677,7 @@ function inferCsvColumnSpecs(headers, rows) {
7463
7677
  }
7464
7678
  function readCsvSample(csvPath) {
7465
7679
  const resolvedPath = resolve6(csvPath);
7466
- const size = statSync(resolvedPath).size;
7680
+ const size = statSync2(resolvedPath).size;
7467
7681
  const fd = openSync(resolvedPath, "r");
7468
7682
  const byteLength = Math.min(size, CSV_HEADER_SAMPLE_BYTES);
7469
7683
  const buffer = Buffer.alloc(byteLength);
@@ -9499,7 +9713,7 @@ function preflightLocalFileInputs(runtimeInput) {
9499
9713
  }
9500
9714
  let stat2;
9501
9715
  try {
9502
- stat2 = statSync2(absolutePath);
9716
+ stat2 = statSync3(absolutePath);
9503
9717
  } catch (error) {
9504
9718
  throw new DeeplineError(
9505
9719
  `Input ${ref.inputPath} references a local file that is not readable: ${ref.value} (${error instanceof Error ? error.message : String(error)}). No run was created.`,
@@ -9651,7 +9865,7 @@ function stageFile(logicalPath, absolutePath) {
9651
9865
  }
9652
9866
  function normalizePlayPath(filePath) {
9653
9867
  try {
9654
- return realpathSync.native(resolve7(filePath));
9868
+ return realpathSync2.native(resolve7(filePath));
9655
9869
  } catch {
9656
9870
  return resolve7(filePath);
9657
9871
  }
@@ -12913,7 +13127,7 @@ async function handlePlayRun(args) {
12913
13127
  if (existsSync6(dir)) {
12914
13128
  const base = basename(resolved);
12915
13129
  try {
12916
- const siblings = readdirSync(dir).filter(
13130
+ const siblings = readdirSync2(dir).filter(
12917
13131
  (f) => f.includes(base.replace(/\.(play\.)?ts$/, "")) || f.endsWith(".play.ts")
12918
13132
  );
12919
13133
  if (siblings.length > 0) {
@@ -17718,9 +17932,9 @@ Examples:
17718
17932
  import {
17719
17933
  existsSync as existsSync7,
17720
17934
  mkdirSync as mkdirSync5,
17721
- readdirSync as readdirSync2,
17935
+ readdirSync as readdirSync3,
17722
17936
  readFileSync as readFileSync7,
17723
- statSync as statSync3,
17937
+ statSync as statSync4,
17724
17938
  writeFileSync as writeFileSync9
17725
17939
  } from "fs";
17726
17940
  import { homedir as homedir7, platform } from "os";
@@ -17805,7 +18019,7 @@ function listSessionFiles(agent) {
17805
18019
  }
17806
18020
  function readDirectoryNames(dir) {
17807
18021
  try {
17808
- return readdirSync2(dir);
18022
+ return readdirSync3(dir);
17809
18023
  } catch {
17810
18024
  return [];
17811
18025
  }
@@ -17816,7 +18030,7 @@ function listJsonlFilesRecursive(root, maxDepth) {
17816
18030
  if (depth > maxDepth) return;
17817
18031
  let entries;
17818
18032
  try {
17819
- entries = readdirSync2(dir, { withFileTypes: true });
18033
+ entries = readdirSync3(dir, { withFileTypes: true });
17820
18034
  } catch {
17821
18035
  return;
17822
18036
  }
@@ -17834,7 +18048,7 @@ function listJsonlFilesRecursive(root, maxDepth) {
17834
18048
  }
17835
18049
  function statIfReadable(filePath) {
17836
18050
  try {
17837
- return statSync3(filePath);
18051
+ return statSync4(filePath);
17838
18052
  } catch {
17839
18053
  return null;
17840
18054
  }
@@ -18527,6 +18741,15 @@ Examples:
18527
18741
  async function fetchOrganizations(http, apiKey) {
18528
18742
  return http.post("/api/v2/auth/cli/organizations", { api_key: apiKey });
18529
18743
  }
18744
+ function normalizeAuthScope(value) {
18745
+ if (!value) return "auto";
18746
+ if (value === "auto" || value === "folder" || value === "global") {
18747
+ return value;
18748
+ }
18749
+ throw new Error(
18750
+ `Invalid --auth-scope "${value}". Expected one of: auto, folder, global.`
18751
+ );
18752
+ }
18530
18753
  function orgListLines(orgs) {
18531
18754
  return orgs.map((org, index) => {
18532
18755
  const current = org.is_current ? " (current)" : "";
@@ -18534,6 +18757,90 @@ function orgListLines(orgs) {
18534
18757
  return `${index + 1}. ${org.name}${role}${current}`;
18535
18758
  });
18536
18759
  }
18760
+ function redactApiKey(value) {
18761
+ if (!value) return null;
18762
+ if (value.length <= 10) return `${value.slice(0, 3)}...`;
18763
+ return `${value.slice(0, 8)}...${value.slice(-4)}`;
18764
+ }
18765
+ function processEnvValue(name) {
18766
+ return process.env[name]?.trim() ?? "";
18767
+ }
18768
+ function resolveOrgSwitchAuthTarget(scope, config) {
18769
+ const activeProject = getResolvedProjectAuthSource(
18770
+ config.baseUrl,
18771
+ config.apiKey
18772
+ );
18773
+ const folderTarget = resolveProjectPinTarget();
18774
+ const globalOverrideWarning = activeProject && scope === "global" ? [
18775
+ `Folder auth in ${activeProject.filePath} overrides global auth for commands run here.`
18776
+ ] : [];
18777
+ if (scope === "folder") {
18778
+ if (!folderTarget.ok) {
18779
+ throw new Error(
18780
+ `Cowork project folder is ambiguous. Candidate folders: ${folderTarget.candidates.join(
18781
+ ", "
18782
+ )}. Set CLAUDE_PROJECT_DIR or cd into the intended project folder before running this command.`
18783
+ );
18784
+ }
18785
+ return {
18786
+ kind: "folder",
18787
+ requested_scope: scope,
18788
+ effective_scope: "folder",
18789
+ reason: "explicit_folder",
18790
+ source: folderTarget.source,
18791
+ warnings: []
18792
+ };
18793
+ }
18794
+ if (scope === "global") {
18795
+ return {
18796
+ kind: "global",
18797
+ requested_scope: scope,
18798
+ effective_scope: "global",
18799
+ reason: "explicit_global",
18800
+ warnings: globalOverrideWarning
18801
+ };
18802
+ }
18803
+ if (activeProject) {
18804
+ return {
18805
+ kind: "folder",
18806
+ requested_scope: scope,
18807
+ effective_scope: "folder",
18808
+ reason: "existing_folder_auth",
18809
+ source: activeProject.source,
18810
+ warnings: []
18811
+ };
18812
+ }
18813
+ if (folderTarget.ok && folderTarget.source === "cowork") {
18814
+ return {
18815
+ kind: "folder",
18816
+ requested_scope: scope,
18817
+ effective_scope: "folder",
18818
+ reason: "detected_cowork_project",
18819
+ source: "cowork",
18820
+ warnings: []
18821
+ };
18822
+ }
18823
+ if (!folderTarget.ok) {
18824
+ return {
18825
+ kind: "global",
18826
+ requested_scope: scope,
18827
+ effective_scope: "global",
18828
+ reason: "ambiguous_cowork_fell_back_global",
18829
+ warnings: [
18830
+ `Cowork project folder is ambiguous, so global auth was updated. Candidate folders: ${folderTarget.candidates.join(
18831
+ ", "
18832
+ )}. Set CLAUDE_PROJECT_DIR or cd into the intended project folder to update folder auth.`
18833
+ ]
18834
+ };
18835
+ }
18836
+ return {
18837
+ kind: "global",
18838
+ requested_scope: scope,
18839
+ effective_scope: "global",
18840
+ reason: "default_global",
18841
+ warnings: []
18842
+ };
18843
+ }
18537
18844
  async function handleOrgList(options) {
18538
18845
  const config = resolveConfig();
18539
18846
  const http = new HttpClient(config);
@@ -18553,7 +18860,89 @@ async function handleOrgList(options) {
18553
18860
  { json: options.json }
18554
18861
  );
18555
18862
  }
18863
+ async function handleOrgStatus(options) {
18864
+ const config = resolveConfig();
18865
+ const http = new HttpClient(config);
18866
+ const payload = await fetchOrganizations(http, config.apiKey);
18867
+ const current = payload.organizations.find((org) => org.is_current) ?? payload.organizations.find((org) => org.org_id === payload.current_org_id) ?? null;
18868
+ const projectCandidate = getActiveProjectAuthSource();
18869
+ const activeProject = getResolvedProjectAuthSource(
18870
+ config.baseUrl,
18871
+ config.apiKey
18872
+ );
18873
+ const folderTarget = resolveProjectPinTarget();
18874
+ const hostPath = hostEnvFilePath(config.baseUrl);
18875
+ const envApiKey = processEnvValue(API_KEY_ENV);
18876
+ const envHostUrl = processEnvValue(HOST_URL_ENV);
18877
+ const authSource = envApiKey ? {
18878
+ scope: "env",
18879
+ source: "process",
18880
+ path: null,
18881
+ api_key: redactApiKey(envApiKey),
18882
+ host: envHostUrl || null,
18883
+ overrides_global: true,
18884
+ overrides_folder: Boolean(projectCandidate)
18885
+ } : activeProject ? {
18886
+ scope: "folder",
18887
+ source: activeProject.source,
18888
+ path: activeProject.filePath,
18889
+ api_key: redactApiKey(activeProject.env.DEEPLINE_API_KEY),
18890
+ overrides_global: true
18891
+ } : {
18892
+ scope: "global",
18893
+ source: "host",
18894
+ path: hostPath,
18895
+ api_key: redactApiKey(config.apiKey),
18896
+ overrides_global: false
18897
+ };
18898
+ const folderTargetPayload = folderTarget.ok ? {
18899
+ ok: true,
18900
+ source: folderTarget.source,
18901
+ dir: folderTarget.dir,
18902
+ env_path: `${folderTarget.dir}/.env.deepline`
18903
+ } : {
18904
+ ok: false,
18905
+ reason: folderTarget.reason,
18906
+ candidates: folderTarget.candidates
18907
+ };
18908
+ const lines = [
18909
+ `Organization: ${current?.name ?? "(unknown)"}`,
18910
+ `Host: ${config.baseUrl}`,
18911
+ `Auth scope: ${authSource.scope}`
18912
+ ];
18913
+ if (authSource.path) {
18914
+ lines.push(`Auth file: ${authSource.path}`);
18915
+ } else {
18916
+ lines.push(`Auth source: ${API_KEY_ENV} process environment`);
18917
+ }
18918
+ if (envApiKey) {
18919
+ lines.push(`${API_KEY_ENV} overrides saved auth for this process.`);
18920
+ } else if (activeProject) {
18921
+ lines.push("Global auth is overridden in this folder.");
18922
+ }
18923
+ printCommandEnvelope(
18924
+ {
18925
+ ok: true,
18926
+ host: config.baseUrl,
18927
+ organization: current,
18928
+ current_org_id: payload.current_org_id,
18929
+ auth_source: authSource,
18930
+ host_env_path: hostPath,
18931
+ folder_target: folderTargetPayload,
18932
+ next: {
18933
+ set_auto: "deepline org set <org>",
18934
+ set_folder: "deepline org set <org> --auth-scope folder",
18935
+ set_global: "deepline org set <org> --auth-scope global"
18936
+ },
18937
+ render: {
18938
+ sections: [{ title: "org status", lines }]
18939
+ }
18940
+ },
18941
+ { json: options.json }
18942
+ );
18943
+ }
18556
18944
  async function handleOrgSwitch(selection, options) {
18945
+ const authScope = normalizeAuthScope(options.authScope);
18557
18946
  const config = resolveConfig();
18558
18947
  const http = new HttpClient(config);
18559
18948
  const payload = await fetchOrganizations(http, config.apiKey);
@@ -18561,7 +18950,10 @@ async function handleOrgSwitch(selection, options) {
18561
18950
  printCommandEnvelope(
18562
18951
  {
18563
18952
  ...payload,
18564
- next: { switch: "deepline org switch <number>" },
18953
+ next: { set: "deepline org set <number>" },
18954
+ deprecated_command: options.deprecatedSwitch ? "org switch" : null,
18955
+ replacement_command: options.deprecatedSwitch ? "deepline org set" : null,
18956
+ warnings: options.deprecatedSwitch ? ["org switch is deprecated; use org set."] : [],
18565
18957
  render: {
18566
18958
  sections: [
18567
18959
  {
@@ -18569,7 +18961,7 @@ async function handleOrgSwitch(selection, options) {
18569
18961
  lines: orgListLines(payload.organizations)
18570
18962
  }
18571
18963
  ],
18572
- actions: [{ label: "Run", command: "deepline org switch <number>" }]
18964
+ actions: [{ label: "Run", command: "deepline org set <number>" }]
18573
18965
  }
18574
18966
  },
18575
18967
  { json: options.json }
@@ -18592,16 +18984,56 @@ async function handleOrgSwitch(selection, options) {
18592
18984
  if (!target) {
18593
18985
  throw new Error("Could not resolve the selected organization.");
18594
18986
  }
18987
+ const authTarget = resolveOrgSwitchAuthTarget(authScope, config);
18595
18988
  if (target.is_current) {
18989
+ let project_env_paths2 = [];
18990
+ if (authTarget.kind === "folder") {
18991
+ project_env_paths2 = saveProjectDeeplineEnvValues({
18992
+ DEEPLINE_HOST_URL: config.baseUrl,
18993
+ DEEPLINE_API_KEY: config.apiKey
18994
+ });
18995
+ } else {
18996
+ saveHostEnvValues(config.baseUrl, {
18997
+ DEEPLINE_HOST_URL: config.baseUrl,
18998
+ DEEPLINE_API_KEY: config.apiKey
18999
+ });
19000
+ }
19001
+ const renderLines2 = [`Already on ${target.name}.`];
19002
+ for (const projectPath of project_env_paths2) {
19003
+ renderLines2.push(`Saved folder auth in ${projectPath}`);
19004
+ }
19005
+ if (authTarget.kind === "global") {
19006
+ renderLines2.push(`Saved global auth in ${hostEnvFilePath(config.baseUrl)}`);
19007
+ }
19008
+ renderLines2.push(
19009
+ `Scope: ${authTarget.requested_scope} -> ${authTarget.effective_scope} (${authTarget.reason})`
19010
+ );
19011
+ if (options.deprecatedSwitch) {
19012
+ renderLines2.push("Warning: org switch is deprecated; use org set.");
19013
+ }
19014
+ for (const warning of authTarget.warnings) {
19015
+ renderLines2.push(`Warning: ${warning}`);
19016
+ }
19017
+ const warnings2 = [
19018
+ ...options.deprecatedSwitch ? ["org switch is deprecated; use org set."] : [],
19019
+ ...authTarget.warnings
19020
+ ];
18596
19021
  printCommandEnvelope(
18597
19022
  {
18598
19023
  ok: true,
18599
19024
  unchanged: true,
18600
19025
  organization: target,
19026
+ requested_auth_scope: authTarget.requested_scope,
19027
+ effective_auth_scope: authTarget.effective_scope,
19028
+ auth_scope_reason: authTarget.reason,
19029
+ auth_scope: authTarget.effective_scope,
19030
+ host_env_path: authTarget.kind === "global" ? hostEnvFilePath(config.baseUrl) : null,
19031
+ project_env_paths: project_env_paths2,
19032
+ deprecated_command: options.deprecatedSwitch ? "org switch" : null,
19033
+ replacement_command: options.deprecatedSwitch ? "deepline org set" : null,
19034
+ warnings: warnings2,
18601
19035
  render: {
18602
- sections: [
18603
- { title: "org switch", lines: [`Already on ${target.name}.`] }
18604
- ]
19036
+ sections: [{ title: "org set", lines: renderLines2 }]
18605
19037
  }
18606
19038
  },
18607
19039
  { json: options.json }
@@ -18612,26 +19044,61 @@ async function handleOrgSwitch(selection, options) {
18612
19044
  api_key: config.apiKey,
18613
19045
  org_id: target.org_id
18614
19046
  });
18615
- saveHostEnvValues(config.baseUrl, {
18616
- DEEPLINE_API_KEY: switched.api_key,
18617
- DEEPLINE_ACTIVE_ORG_ID: switched.org_id,
18618
- DEEPLINE_ACTIVE_ORG_NAME: switched.org_name
18619
- });
19047
+ let project_env_paths = [];
19048
+ if (authTarget.kind === "folder") {
19049
+ project_env_paths = saveProjectDeeplineEnvValues({
19050
+ DEEPLINE_HOST_URL: config.baseUrl,
19051
+ DEEPLINE_API_KEY: switched.api_key
19052
+ });
19053
+ } else {
19054
+ saveHostEnvValues(config.baseUrl, {
19055
+ DEEPLINE_HOST_URL: config.baseUrl,
19056
+ DEEPLINE_API_KEY: switched.api_key,
19057
+ DEEPLINE_ACTIVE_ORG_ID: switched.org_id,
19058
+ DEEPLINE_ACTIVE_ORG_NAME: switched.org_name
19059
+ });
19060
+ }
18620
19061
  const { api_key: _apiKey, ...publicSwitched } = switched;
19062
+ const renderLines = [`Switched to ${switched.org_name}.`];
19063
+ if (authTarget.kind === "folder") {
19064
+ for (const projectPath of project_env_paths) {
19065
+ renderLines.push(`Saved folder auth in ${projectPath}`);
19066
+ }
19067
+ } else {
19068
+ renderLines.push(`Saved global auth in ${hostEnvFilePath(config.baseUrl)}`);
19069
+ }
19070
+ renderLines.push(
19071
+ `Scope: ${authTarget.requested_scope} -> ${authTarget.effective_scope} (${authTarget.reason})`
19072
+ );
19073
+ if (options.deprecatedSwitch) {
19074
+ renderLines.push("Warning: org switch is deprecated; use org set.");
19075
+ }
19076
+ for (const warning of authTarget.warnings) {
19077
+ renderLines.push(`Warning: ${warning}`);
19078
+ }
19079
+ const warnings = [
19080
+ ...options.deprecatedSwitch ? ["org switch is deprecated; use org set."] : [],
19081
+ ...authTarget.warnings
19082
+ ];
18621
19083
  printCommandEnvelope(
18622
19084
  {
18623
19085
  ok: true,
18624
- host_env_path: hostEnvFilePath(config.baseUrl),
19086
+ host_env_path: authTarget.kind === "global" ? hostEnvFilePath(config.baseUrl) : null,
19087
+ project_env_paths,
18625
19088
  ...publicSwitched,
18626
19089
  api_key_saved: true,
19090
+ requested_auth_scope: authTarget.requested_scope,
19091
+ effective_auth_scope: authTarget.effective_scope,
19092
+ auth_scope_reason: authTarget.reason,
19093
+ auth_scope: authTarget.effective_scope,
19094
+ deprecated_command: options.deprecatedSwitch ? "org switch" : null,
19095
+ replacement_command: options.deprecatedSwitch ? "deepline org set" : null,
19096
+ warnings,
18627
19097
  render: {
18628
19098
  sections: [
18629
19099
  {
18630
- title: "org switch",
18631
- lines: [
18632
- `Switched to ${switched.org_name}.`,
18633
- `Saved host auth in ${hostEnvFilePath(config.baseUrl)}`
18634
- ]
19100
+ title: "org set",
19101
+ lines: renderLines
18635
19102
  }
18636
19103
  ]
18637
19104
  }
@@ -18647,6 +19114,7 @@ async function handleOrgCreate(name, options) {
18647
19114
  name
18648
19115
  });
18649
19116
  saveHostEnvValues(config.baseUrl, {
19117
+ DEEPLINE_HOST_URL: config.baseUrl,
18650
19118
  DEEPLINE_API_KEY: created.api_key,
18651
19119
  DEEPLINE_ACTIVE_ORG_ID: created.org_id,
18652
19120
  DEEPLINE_ACTIVE_ORG_NAME: created.org_name
@@ -18676,18 +19144,41 @@ async function handleOrgCreate(name, options) {
18676
19144
  );
18677
19145
  }
18678
19146
  function registerOrgCommands(program) {
18679
- const org = program.command("org").description("List, create, and switch organizations.").addHelpText(
19147
+ const org = program.command("org").description("List, create, and set organizations.").addHelpText(
18680
19148
  "after",
18681
19149
  `
18682
19150
  Notes:
18683
- Organizations are workspaces. Switching organizations mutates the saved host
18684
- auth file so later CLI commands target the selected workspace.
19151
+ Organizations are workspaces. Auth is stored as an API key scoped to one
19152
+ organization, so setting an org saves a key for the selected auth scope.
19153
+
19154
+ Auth scopes:
19155
+ auto Update the folder pin that already controls this command; in
19156
+ Cowork, write the mounted project folder; otherwise update global
19157
+ host auth.
19158
+ folder Write .env.deepline in the current project/Cowork project and make
19159
+ sure .env.deepline is gitignored. Commands below that folder use
19160
+ this org; sibling folders can pin different orgs.
19161
+ global Write ~/.local/deepline/<host>/.env. Commands outside folder pins
19162
+ use this org. Existing folder pins still override global auth.
19163
+
19164
+ Process env DEEPLINE_API_KEY overrides saved auth for that command only.
19165
+
19166
+ Agent loop:
19167
+ deepline org list --json
19168
+ deepline org set <number-or-org-id> --auth-scope folder --json
19169
+ deepline org status --json
19170
+ deepline auth status --json
18685
19171
 
18686
19172
  Examples:
18687
19173
  deepline org list --json
19174
+ deepline org status --json
18688
19175
  deepline org create Acme --json
18689
- deepline org switch 2
18690
- deepline org switch --org-id org_123 --json
19176
+ deepline org set 2
19177
+ deepline org set 2 --auth-scope folder
19178
+ deepline org set --org-id org_123 --json
19179
+ cd path/to/project-a && deepline org set Prove --auth-scope folder --json
19180
+ cd path/to/project-b && deepline org set Mixmax --auth-scope folder --json
19181
+ cd .. && deepline org status --json
18691
19182
  `
18692
19183
  );
18693
19184
  org.command("list").description("List your organizations.").addHelpText(
@@ -18714,21 +19205,88 @@ Examples:
18714
19205
  deepline org create "Acme Sales" --json
18715
19206
  `
18716
19207
  ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgCreate);
18717
- org.command("switch [selection]").description(
18718
- "Switch to another organization and save the new API key in the host auth file."
18719
- ).addHelpText(
19208
+ org.command("status").description("Show the current organization and auth source.").addHelpText(
18720
19209
  "after",
18721
19210
  `
18722
19211
  Notes:
18723
- Mutates the saved host auth file. Selection can be a list number, exact
18724
- organization name, or organization id. Without a selection, prints choices.
19212
+ Read-only. Shows the current org plus auth_source.scope, auth_source.path,
19213
+ folder_target, host_env_path, and next commands.
19214
+
19215
+ Scopes are env, folder, or global. env means DEEPLINE_API_KEY/
19216
+ DEEPLINE_HOST_URL in process env wins. folder means nearest/Cowork
19217
+ .env.deepline wins. global means host config wins.
19218
+
19219
+ Run before and after org set to verify what future commands will use.
19220
+
19221
+ Examples:
19222
+ deepline org status
19223
+ deepline org status --json
19224
+ `
19225
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgStatus);
19226
+ const addOrgSetOptions = (command) => command.option("--org-id <id>", "Set using an explicit organization id").option(
19227
+ "--auth-scope <scope>",
19228
+ "Where to save auth: auto, folder, or global",
19229
+ "auto"
19230
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped");
19231
+ addOrgSetOptions(
19232
+ org.command("set [selection]").description("Set the organization for the selected auth scope.").addHelpText(
19233
+ "after",
19234
+ `
19235
+ Notes:
19236
+ Selection can be a list number, exact organization name, or organization id.
19237
+ Without a selection, prints choices.
19238
+
19239
+ Mutates local auth state and asks Deepline for an org-scoped API key. It
19240
+ writes only the selected scope:
19241
+ auto Update the folder pin that already controls this command; in
19242
+ Cowork, write the mounted project folder; otherwise update global
19243
+ host auth.
19244
+ folder Write .env.deepline in the current project/Cowork project and make
19245
+ sure .env.deepline is gitignored.
19246
+ global Write ~/.local/deepline/<host>/.env.
19247
+
19248
+ Folder auth shadows global auth. If you set --auth-scope global from a pinned
19249
+ folder, commands in that folder still use the folder pin until it is changed
19250
+ or removed.
19251
+
19252
+ Use --json for stable fields including effective_auth_scope,
19253
+ auth_scope_reason, host_env_path, project_env_paths, warnings, org_id, and
19254
+ org_name.
19255
+
19256
+ Examples:
19257
+ deepline org set
19258
+ deepline org set 2
19259
+ deepline org set 2 --auth-scope folder
19260
+ deepline org set 2 --auth-scope global
19261
+ deepline org set --org-id org_123 --json
19262
+ deepline org status --json
19263
+ `
19264
+ )
19265
+ ).action(handleOrgSwitch);
19266
+ addOrgSetOptions(
19267
+ org.command("switch [selection]").description(
19268
+ "Deprecated alias for org set. Set the organization for the selected auth scope."
19269
+ ).addHelpText(
19270
+ "after",
19271
+ `
19272
+ Notes:
19273
+ Deprecated alias. Use deepline org set instead. The command still mutates
19274
+ local auth state with the same --auth-scope behavior as org set.
19275
+
19276
+ Run deepline org set --help for scope definitions and the agent verification
19277
+ loop.
18725
19278
 
18726
19279
  Examples:
18727
19280
  deepline org switch
18728
19281
  deepline org switch 2
19282
+ deepline org switch 2 --auth-scope folder
19283
+ deepline org switch 2 --auth-scope global
18729
19284
  deepline org switch --org-id org_123 --json
18730
19285
  `
18731
- ).option("--org-id <id>", "Switch using an explicit organization id").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgSwitch);
19286
+ )
19287
+ ).action(
19288
+ (selection, options) => handleOrgSwitch(selection, { ...options, deprecatedSwitch: true })
19289
+ );
18732
19290
  }
18733
19291
 
18734
19292
  // src/cli/commands/quickstart.ts
@@ -21595,7 +22153,7 @@ import { spawn as spawn3 } from "child_process";
21595
22153
  import {
21596
22154
  existsSync as existsSync11,
21597
22155
  mkdirSync as mkdirSync9,
21598
- realpathSync as realpathSync2,
22156
+ realpathSync as realpathSync3,
21599
22157
  readFileSync as readFileSync11,
21600
22158
  renameSync,
21601
22159
  rmSync as rmSync3,
@@ -22094,7 +22652,7 @@ function findRepoBackedSdkRoot(startPath) {
22094
22652
  function inferNpmGlobalPrefixFromEntrypoint(entrypoint) {
22095
22653
  const normalized = (() => {
22096
22654
  try {
22097
- return realpathSync2(entrypoint);
22655
+ return realpathSync3(entrypoint);
22098
22656
  } catch {
22099
22657
  return resolve12(entrypoint);
22100
22658
  }