deepline 0.1.142 → 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.
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.142",
622
+ version: "0.1.144",
417
623
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
418
624
  supportPolicy: {
419
- latest: "0.1.142",
625
+ latest: "0.1.144",
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}
@@ -18511,6 +18717,15 @@ Examples:
18511
18717
  async function fetchOrganizations(http, apiKey) {
18512
18718
  return http.post("/api/v2/auth/cli/organizations", { api_key: apiKey });
18513
18719
  }
18720
+ function normalizeAuthScope(value) {
18721
+ if (!value) return "auto";
18722
+ if (value === "auto" || value === "folder" || value === "global") {
18723
+ return value;
18724
+ }
18725
+ throw new Error(
18726
+ `Invalid --auth-scope "${value}". Expected one of: auto, folder, global.`
18727
+ );
18728
+ }
18514
18729
  function orgListLines(orgs) {
18515
18730
  return orgs.map((org, index) => {
18516
18731
  const current = org.is_current ? " (current)" : "";
@@ -18518,6 +18733,90 @@ function orgListLines(orgs) {
18518
18733
  return `${index + 1}. ${org.name}${role}${current}`;
18519
18734
  });
18520
18735
  }
18736
+ function redactApiKey(value) {
18737
+ if (!value) return null;
18738
+ if (value.length <= 10) return `${value.slice(0, 3)}...`;
18739
+ return `${value.slice(0, 8)}...${value.slice(-4)}`;
18740
+ }
18741
+ function processEnvValue(name) {
18742
+ return process.env[name]?.trim() ?? "";
18743
+ }
18744
+ function resolveOrgSwitchAuthTarget(scope, config) {
18745
+ const activeProject = getResolvedProjectAuthSource(
18746
+ config.baseUrl,
18747
+ config.apiKey
18748
+ );
18749
+ const folderTarget = resolveProjectPinTarget();
18750
+ const globalOverrideWarning = activeProject && scope === "global" ? [
18751
+ `Folder auth in ${activeProject.filePath} overrides global auth for commands run here.`
18752
+ ] : [];
18753
+ if (scope === "folder") {
18754
+ if (!folderTarget.ok) {
18755
+ throw new Error(
18756
+ `Cowork project folder is ambiguous. Candidate folders: ${folderTarget.candidates.join(
18757
+ ", "
18758
+ )}. Set CLAUDE_PROJECT_DIR or cd into the intended project folder before running this command.`
18759
+ );
18760
+ }
18761
+ return {
18762
+ kind: "folder",
18763
+ requested_scope: scope,
18764
+ effective_scope: "folder",
18765
+ reason: "explicit_folder",
18766
+ source: folderTarget.source,
18767
+ warnings: []
18768
+ };
18769
+ }
18770
+ if (scope === "global") {
18771
+ return {
18772
+ kind: "global",
18773
+ requested_scope: scope,
18774
+ effective_scope: "global",
18775
+ reason: "explicit_global",
18776
+ warnings: globalOverrideWarning
18777
+ };
18778
+ }
18779
+ if (activeProject) {
18780
+ return {
18781
+ kind: "folder",
18782
+ requested_scope: scope,
18783
+ effective_scope: "folder",
18784
+ reason: "existing_folder_auth",
18785
+ source: activeProject.source,
18786
+ warnings: []
18787
+ };
18788
+ }
18789
+ if (folderTarget.ok && folderTarget.source === "cowork") {
18790
+ return {
18791
+ kind: "folder",
18792
+ requested_scope: scope,
18793
+ effective_scope: "folder",
18794
+ reason: "detected_cowork_project",
18795
+ source: "cowork",
18796
+ warnings: []
18797
+ };
18798
+ }
18799
+ if (!folderTarget.ok) {
18800
+ return {
18801
+ kind: "global",
18802
+ requested_scope: scope,
18803
+ effective_scope: "global",
18804
+ reason: "ambiguous_cowork_fell_back_global",
18805
+ warnings: [
18806
+ `Cowork project folder is ambiguous, so global auth was updated. Candidate folders: ${folderTarget.candidates.join(
18807
+ ", "
18808
+ )}. Set CLAUDE_PROJECT_DIR or cd into the intended project folder to update folder auth.`
18809
+ ]
18810
+ };
18811
+ }
18812
+ return {
18813
+ kind: "global",
18814
+ requested_scope: scope,
18815
+ effective_scope: "global",
18816
+ reason: "default_global",
18817
+ warnings: []
18818
+ };
18819
+ }
18521
18820
  async function handleOrgList(options) {
18522
18821
  const config = resolveConfig();
18523
18822
  const http = new HttpClient(config);
@@ -18537,7 +18836,89 @@ async function handleOrgList(options) {
18537
18836
  { json: options.json }
18538
18837
  );
18539
18838
  }
18839
+ async function handleOrgStatus(options) {
18840
+ const config = resolveConfig();
18841
+ const http = new HttpClient(config);
18842
+ const payload = await fetchOrganizations(http, config.apiKey);
18843
+ const current = payload.organizations.find((org) => org.is_current) ?? payload.organizations.find((org) => org.org_id === payload.current_org_id) ?? null;
18844
+ const projectCandidate = getActiveProjectAuthSource();
18845
+ const activeProject = getResolvedProjectAuthSource(
18846
+ config.baseUrl,
18847
+ config.apiKey
18848
+ );
18849
+ const folderTarget = resolveProjectPinTarget();
18850
+ const hostPath = hostEnvFilePath(config.baseUrl);
18851
+ const envApiKey = processEnvValue(API_KEY_ENV);
18852
+ const envHostUrl = processEnvValue(HOST_URL_ENV);
18853
+ const authSource = envApiKey ? {
18854
+ scope: "env",
18855
+ source: "process",
18856
+ path: null,
18857
+ api_key: redactApiKey(envApiKey),
18858
+ host: envHostUrl || null,
18859
+ overrides_global: true,
18860
+ overrides_folder: Boolean(projectCandidate)
18861
+ } : activeProject ? {
18862
+ scope: "folder",
18863
+ source: activeProject.source,
18864
+ path: activeProject.filePath,
18865
+ api_key: redactApiKey(activeProject.env.DEEPLINE_API_KEY),
18866
+ overrides_global: true
18867
+ } : {
18868
+ scope: "global",
18869
+ source: "host",
18870
+ path: hostPath,
18871
+ api_key: redactApiKey(config.apiKey),
18872
+ overrides_global: false
18873
+ };
18874
+ const folderTargetPayload = folderTarget.ok ? {
18875
+ ok: true,
18876
+ source: folderTarget.source,
18877
+ dir: folderTarget.dir,
18878
+ env_path: `${folderTarget.dir}/.env.deepline`
18879
+ } : {
18880
+ ok: false,
18881
+ reason: folderTarget.reason,
18882
+ candidates: folderTarget.candidates
18883
+ };
18884
+ const lines = [
18885
+ `Organization: ${current?.name ?? "(unknown)"}`,
18886
+ `Host: ${config.baseUrl}`,
18887
+ `Auth scope: ${authSource.scope}`
18888
+ ];
18889
+ if (authSource.path) {
18890
+ lines.push(`Auth file: ${authSource.path}`);
18891
+ } else {
18892
+ lines.push(`Auth source: ${API_KEY_ENV} process environment`);
18893
+ }
18894
+ if (envApiKey) {
18895
+ lines.push(`${API_KEY_ENV} overrides saved auth for this process.`);
18896
+ } else if (activeProject) {
18897
+ lines.push("Global auth is overridden in this folder.");
18898
+ }
18899
+ printCommandEnvelope(
18900
+ {
18901
+ ok: true,
18902
+ host: config.baseUrl,
18903
+ organization: current,
18904
+ current_org_id: payload.current_org_id,
18905
+ auth_source: authSource,
18906
+ host_env_path: hostPath,
18907
+ folder_target: folderTargetPayload,
18908
+ next: {
18909
+ set_auto: "deepline org set <org>",
18910
+ set_folder: "deepline org set <org> --auth-scope folder",
18911
+ set_global: "deepline org set <org> --auth-scope global"
18912
+ },
18913
+ render: {
18914
+ sections: [{ title: "org status", lines }]
18915
+ }
18916
+ },
18917
+ { json: options.json }
18918
+ );
18919
+ }
18540
18920
  async function handleOrgSwitch(selection, options) {
18921
+ const authScope = normalizeAuthScope(options.authScope);
18541
18922
  const config = resolveConfig();
18542
18923
  const http = new HttpClient(config);
18543
18924
  const payload = await fetchOrganizations(http, config.apiKey);
@@ -18545,7 +18926,10 @@ async function handleOrgSwitch(selection, options) {
18545
18926
  printCommandEnvelope(
18546
18927
  {
18547
18928
  ...payload,
18548
- next: { switch: "deepline org switch <number>" },
18929
+ next: { set: "deepline org set <number>" },
18930
+ deprecated_command: options.deprecatedSwitch ? "org switch" : null,
18931
+ replacement_command: options.deprecatedSwitch ? "deepline org set" : null,
18932
+ warnings: options.deprecatedSwitch ? ["org switch is deprecated; use org set."] : [],
18549
18933
  render: {
18550
18934
  sections: [
18551
18935
  {
@@ -18553,7 +18937,7 @@ async function handleOrgSwitch(selection, options) {
18553
18937
  lines: orgListLines(payload.organizations)
18554
18938
  }
18555
18939
  ],
18556
- actions: [{ label: "Run", command: "deepline org switch <number>" }]
18940
+ actions: [{ label: "Run", command: "deepline org set <number>" }]
18557
18941
  }
18558
18942
  },
18559
18943
  { json: options.json }
@@ -18576,16 +18960,56 @@ async function handleOrgSwitch(selection, options) {
18576
18960
  if (!target) {
18577
18961
  throw new Error("Could not resolve the selected organization.");
18578
18962
  }
18963
+ const authTarget = resolveOrgSwitchAuthTarget(authScope, config);
18579
18964
  if (target.is_current) {
18965
+ let project_env_paths2 = [];
18966
+ if (authTarget.kind === "folder") {
18967
+ project_env_paths2 = saveProjectDeeplineEnvValues({
18968
+ DEEPLINE_HOST_URL: config.baseUrl,
18969
+ DEEPLINE_API_KEY: config.apiKey
18970
+ });
18971
+ } else {
18972
+ saveHostEnvValues(config.baseUrl, {
18973
+ DEEPLINE_HOST_URL: config.baseUrl,
18974
+ DEEPLINE_API_KEY: config.apiKey
18975
+ });
18976
+ }
18977
+ const renderLines2 = [`Already on ${target.name}.`];
18978
+ for (const projectPath of project_env_paths2) {
18979
+ renderLines2.push(`Saved folder auth in ${projectPath}`);
18980
+ }
18981
+ if (authTarget.kind === "global") {
18982
+ renderLines2.push(`Saved global auth in ${hostEnvFilePath(config.baseUrl)}`);
18983
+ }
18984
+ renderLines2.push(
18985
+ `Scope: ${authTarget.requested_scope} -> ${authTarget.effective_scope} (${authTarget.reason})`
18986
+ );
18987
+ if (options.deprecatedSwitch) {
18988
+ renderLines2.push("Warning: org switch is deprecated; use org set.");
18989
+ }
18990
+ for (const warning of authTarget.warnings) {
18991
+ renderLines2.push(`Warning: ${warning}`);
18992
+ }
18993
+ const warnings2 = [
18994
+ ...options.deprecatedSwitch ? ["org switch is deprecated; use org set."] : [],
18995
+ ...authTarget.warnings
18996
+ ];
18580
18997
  printCommandEnvelope(
18581
18998
  {
18582
18999
  ok: true,
18583
19000
  unchanged: true,
18584
19001
  organization: target,
19002
+ requested_auth_scope: authTarget.requested_scope,
19003
+ effective_auth_scope: authTarget.effective_scope,
19004
+ auth_scope_reason: authTarget.reason,
19005
+ auth_scope: authTarget.effective_scope,
19006
+ host_env_path: authTarget.kind === "global" ? hostEnvFilePath(config.baseUrl) : null,
19007
+ project_env_paths: project_env_paths2,
19008
+ deprecated_command: options.deprecatedSwitch ? "org switch" : null,
19009
+ replacement_command: options.deprecatedSwitch ? "deepline org set" : null,
19010
+ warnings: warnings2,
18585
19011
  render: {
18586
- sections: [
18587
- { title: "org switch", lines: [`Already on ${target.name}.`] }
18588
- ]
19012
+ sections: [{ title: "org set", lines: renderLines2 }]
18589
19013
  }
18590
19014
  },
18591
19015
  { json: options.json }
@@ -18596,26 +19020,61 @@ async function handleOrgSwitch(selection, options) {
18596
19020
  api_key: config.apiKey,
18597
19021
  org_id: target.org_id
18598
19022
  });
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
- });
19023
+ let project_env_paths = [];
19024
+ if (authTarget.kind === "folder") {
19025
+ project_env_paths = saveProjectDeeplineEnvValues({
19026
+ DEEPLINE_HOST_URL: config.baseUrl,
19027
+ DEEPLINE_API_KEY: switched.api_key
19028
+ });
19029
+ } else {
19030
+ saveHostEnvValues(config.baseUrl, {
19031
+ DEEPLINE_HOST_URL: config.baseUrl,
19032
+ DEEPLINE_API_KEY: switched.api_key,
19033
+ DEEPLINE_ACTIVE_ORG_ID: switched.org_id,
19034
+ DEEPLINE_ACTIVE_ORG_NAME: switched.org_name
19035
+ });
19036
+ }
18604
19037
  const { api_key: _apiKey, ...publicSwitched } = switched;
19038
+ const renderLines = [`Switched to ${switched.org_name}.`];
19039
+ if (authTarget.kind === "folder") {
19040
+ for (const projectPath of project_env_paths) {
19041
+ renderLines.push(`Saved folder auth in ${projectPath}`);
19042
+ }
19043
+ } else {
19044
+ renderLines.push(`Saved global auth in ${hostEnvFilePath(config.baseUrl)}`);
19045
+ }
19046
+ renderLines.push(
19047
+ `Scope: ${authTarget.requested_scope} -> ${authTarget.effective_scope} (${authTarget.reason})`
19048
+ );
19049
+ if (options.deprecatedSwitch) {
19050
+ renderLines.push("Warning: org switch is deprecated; use org set.");
19051
+ }
19052
+ for (const warning of authTarget.warnings) {
19053
+ renderLines.push(`Warning: ${warning}`);
19054
+ }
19055
+ const warnings = [
19056
+ ...options.deprecatedSwitch ? ["org switch is deprecated; use org set."] : [],
19057
+ ...authTarget.warnings
19058
+ ];
18605
19059
  printCommandEnvelope(
18606
19060
  {
18607
19061
  ok: true,
18608
- host_env_path: hostEnvFilePath(config.baseUrl),
19062
+ host_env_path: authTarget.kind === "global" ? hostEnvFilePath(config.baseUrl) : null,
19063
+ project_env_paths,
18609
19064
  ...publicSwitched,
18610
19065
  api_key_saved: true,
19066
+ requested_auth_scope: authTarget.requested_scope,
19067
+ effective_auth_scope: authTarget.effective_scope,
19068
+ auth_scope_reason: authTarget.reason,
19069
+ auth_scope: authTarget.effective_scope,
19070
+ deprecated_command: options.deprecatedSwitch ? "org switch" : null,
19071
+ replacement_command: options.deprecatedSwitch ? "deepline org set" : null,
19072
+ warnings,
18611
19073
  render: {
18612
19074
  sections: [
18613
19075
  {
18614
- title: "org switch",
18615
- lines: [
18616
- `Switched to ${switched.org_name}.`,
18617
- `Saved host auth in ${hostEnvFilePath(config.baseUrl)}`
18618
- ]
19076
+ title: "org set",
19077
+ lines: renderLines
18619
19078
  }
18620
19079
  ]
18621
19080
  }
@@ -18631,6 +19090,7 @@ async function handleOrgCreate(name, options) {
18631
19090
  name
18632
19091
  });
18633
19092
  saveHostEnvValues(config.baseUrl, {
19093
+ DEEPLINE_HOST_URL: config.baseUrl,
18634
19094
  DEEPLINE_API_KEY: created.api_key,
18635
19095
  DEEPLINE_ACTIVE_ORG_ID: created.org_id,
18636
19096
  DEEPLINE_ACTIVE_ORG_NAME: created.org_name
@@ -18660,18 +19120,41 @@ async function handleOrgCreate(name, options) {
18660
19120
  );
18661
19121
  }
18662
19122
  function registerOrgCommands(program) {
18663
- const org = program.command("org").description("List, create, and switch organizations.").addHelpText(
19123
+ const org = program.command("org").description("List, create, and set organizations.").addHelpText(
18664
19124
  "after",
18665
19125
  `
18666
19126
  Notes:
18667
- Organizations are workspaces. Switching organizations mutates the saved host
18668
- auth file so later CLI commands target the selected workspace.
19127
+ Organizations are workspaces. Auth is stored as an API key scoped to one
19128
+ organization, so setting an org saves a key for the selected auth scope.
19129
+
19130
+ Auth scopes:
19131
+ auto Update the folder pin that already controls this command; in
19132
+ Cowork, write the mounted project folder; otherwise update global
19133
+ host auth.
19134
+ folder Write .env.deepline in the current project/Cowork project and make
19135
+ sure .env.deepline is gitignored. Commands below that folder use
19136
+ this org; sibling folders can pin different orgs.
19137
+ global Write ~/.local/deepline/<host>/.env. Commands outside folder pins
19138
+ use this org. Existing folder pins still override global auth.
19139
+
19140
+ Process env DEEPLINE_API_KEY overrides saved auth for that command only.
19141
+
19142
+ Agent loop:
19143
+ deepline org list --json
19144
+ deepline org set <number-or-org-id> --auth-scope folder --json
19145
+ deepline org status --json
19146
+ deepline auth status --json
18669
19147
 
18670
19148
  Examples:
18671
19149
  deepline org list --json
19150
+ deepline org status --json
18672
19151
  deepline org create Acme --json
18673
- deepline org switch 2
18674
- deepline org switch --org-id org_123 --json
19152
+ deepline org set 2
19153
+ deepline org set 2 --auth-scope folder
19154
+ deepline org set --org-id org_123 --json
19155
+ cd path/to/project-a && deepline org set Prove --auth-scope folder --json
19156
+ cd path/to/project-b && deepline org set Mixmax --auth-scope folder --json
19157
+ cd .. && deepline org status --json
18675
19158
  `
18676
19159
  );
18677
19160
  org.command("list").description("List your organizations.").addHelpText(
@@ -18698,21 +19181,88 @@ Examples:
18698
19181
  deepline org create "Acme Sales" --json
18699
19182
  `
18700
19183
  ).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(
19184
+ org.command("status").description("Show the current organization and auth source.").addHelpText(
18704
19185
  "after",
18705
19186
  `
18706
19187
  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.
19188
+ Read-only. Shows the current org plus auth_source.scope, auth_source.path,
19189
+ folder_target, host_env_path, and next commands.
19190
+
19191
+ Scopes are env, folder, or global. env means DEEPLINE_API_KEY/
19192
+ DEEPLINE_HOST_URL in process env wins. folder means nearest/Cowork
19193
+ .env.deepline wins. global means host config wins.
19194
+
19195
+ Run before and after org set to verify what future commands will use.
19196
+
19197
+ Examples:
19198
+ deepline org status
19199
+ deepline org status --json
19200
+ `
19201
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgStatus);
19202
+ const addOrgSetOptions = (command) => command.option("--org-id <id>", "Set using an explicit organization id").option(
19203
+ "--auth-scope <scope>",
19204
+ "Where to save auth: auto, folder, or global",
19205
+ "auto"
19206
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped");
19207
+ addOrgSetOptions(
19208
+ org.command("set [selection]").description("Set the organization for the selected auth scope.").addHelpText(
19209
+ "after",
19210
+ `
19211
+ Notes:
19212
+ Selection can be a list number, exact organization name, or organization id.
19213
+ Without a selection, prints choices.
19214
+
19215
+ Mutates local auth state and asks Deepline for an org-scoped API key. It
19216
+ writes only the selected scope:
19217
+ auto Update the folder pin that already controls this command; in
19218
+ Cowork, write the mounted project folder; otherwise update global
19219
+ host auth.
19220
+ folder Write .env.deepline in the current project/Cowork project and make
19221
+ sure .env.deepline is gitignored.
19222
+ global Write ~/.local/deepline/<host>/.env.
19223
+
19224
+ Folder auth shadows global auth. If you set --auth-scope global from a pinned
19225
+ folder, commands in that folder still use the folder pin until it is changed
19226
+ or removed.
19227
+
19228
+ Use --json for stable fields including effective_auth_scope,
19229
+ auth_scope_reason, host_env_path, project_env_paths, warnings, org_id, and
19230
+ org_name.
19231
+
19232
+ Examples:
19233
+ deepline org set
19234
+ deepline org set 2
19235
+ deepline org set 2 --auth-scope folder
19236
+ deepline org set 2 --auth-scope global
19237
+ deepline org set --org-id org_123 --json
19238
+ deepline org status --json
19239
+ `
19240
+ )
19241
+ ).action(handleOrgSwitch);
19242
+ addOrgSetOptions(
19243
+ org.command("switch [selection]").description(
19244
+ "Deprecated alias for org set. Set the organization for the selected auth scope."
19245
+ ).addHelpText(
19246
+ "after",
19247
+ `
19248
+ Notes:
19249
+ Deprecated alias. Use deepline org set instead. The command still mutates
19250
+ local auth state with the same --auth-scope behavior as org set.
19251
+
19252
+ Run deepline org set --help for scope definitions and the agent verification
19253
+ loop.
18709
19254
 
18710
19255
  Examples:
18711
19256
  deepline org switch
18712
19257
  deepline org switch 2
19258
+ deepline org switch 2 --auth-scope folder
19259
+ deepline org switch 2 --auth-scope global
18713
19260
  deepline org switch --org-id org_123 --json
18714
19261
  `
18715
- ).option("--org-id <id>", "Switch using an explicit organization id").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgSwitch);
19262
+ )
19263
+ ).action(
19264
+ (selection, options) => handleOrgSwitch(selection, { ...options, deprecatedSwitch: true })
19265
+ );
18716
19266
  }
18717
19267
 
18718
19268
  // src/cli/commands/quickstart.ts
@@ -20799,6 +21349,10 @@ function buildToolExecuteBaseEnvelope(input2) {
20799
21349
  summary: input2.summary
20800
21350
  };
20801
21351
  const envelopeHasCanonicalOutput = isRecord8(envelope.toolResponse) && Object.prototype.hasOwnProperty.call(envelope.toolResponse, "raw");
21352
+ const envelopeHasDeclaredOutput = Object.prototype.hasOwnProperty.call(
21353
+ envelope,
21354
+ "output"
21355
+ );
20802
21356
  const inspectCommand = `deepline tools execute ${input2.toolId} --input ${shellQuote2(JSON.stringify(input2.params))} --json`;
20803
21357
  const actions = input2.listConversion ? [
20804
21358
  {
@@ -20808,7 +21362,7 @@ function buildToolExecuteBaseEnvelope(input2) {
20808
21362
  ] : [];
20809
21363
  return {
20810
21364
  ...envelope,
20811
- ...envelopeHasCanonicalOutput ? { output_preview: outputPreview } : { output: outputPreview },
21365
+ ...envelopeHasCanonicalOutput || envelopeHasDeclaredOutput ? { output_preview: outputPreview } : { output: outputPreview },
20812
21366
  ...summaryEntries.length > 0 ? { summary: input2.summary } : {},
20813
21367
  next: {
20814
21368
  inspect: inspectCommand,