deepline 0.1.136 → 0.1.138

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.136",
622
+ version: "0.1.138",
417
623
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
418
624
  supportPolicy: {
419
- latest: "0.1.136",
625
+ latest: "0.1.138",
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}
@@ -18465,6 +18671,15 @@ Examples:
18465
18671
  async function fetchOrganizations(http, apiKey) {
18466
18672
  return http.post("/api/v2/auth/cli/organizations", { api_key: apiKey });
18467
18673
  }
18674
+ function normalizeAuthScope(value) {
18675
+ if (!value) return "auto";
18676
+ if (value === "auto" || value === "folder" || value === "global") {
18677
+ return value;
18678
+ }
18679
+ throw new Error(
18680
+ `Invalid --auth-scope "${value}". Expected one of: auto, folder, global.`
18681
+ );
18682
+ }
18468
18683
  function orgListLines(orgs) {
18469
18684
  return orgs.map((org, index) => {
18470
18685
  const current = org.is_current ? " (current)" : "";
@@ -18472,6 +18687,90 @@ function orgListLines(orgs) {
18472
18687
  return `${index + 1}. ${org.name}${role}${current}`;
18473
18688
  });
18474
18689
  }
18690
+ function redactApiKey(value) {
18691
+ if (!value) return null;
18692
+ if (value.length <= 10) return `${value.slice(0, 3)}...`;
18693
+ return `${value.slice(0, 8)}...${value.slice(-4)}`;
18694
+ }
18695
+ function processEnvValue(name) {
18696
+ return process.env[name]?.trim() ?? "";
18697
+ }
18698
+ function resolveOrgSwitchAuthTarget(scope, config) {
18699
+ const activeProject = getResolvedProjectAuthSource(
18700
+ config.baseUrl,
18701
+ config.apiKey
18702
+ );
18703
+ const folderTarget = resolveProjectPinTarget();
18704
+ const globalOverrideWarning = activeProject && scope === "global" ? [
18705
+ `Folder auth in ${activeProject.filePath} overrides global auth for commands run here.`
18706
+ ] : [];
18707
+ if (scope === "folder") {
18708
+ if (!folderTarget.ok) {
18709
+ throw new Error(
18710
+ `Cowork project folder is ambiguous. Candidate folders: ${folderTarget.candidates.join(
18711
+ ", "
18712
+ )}. Set CLAUDE_PROJECT_DIR or cd into the intended project folder before running this command.`
18713
+ );
18714
+ }
18715
+ return {
18716
+ kind: "folder",
18717
+ requested_scope: scope,
18718
+ effective_scope: "folder",
18719
+ reason: "explicit_folder",
18720
+ source: folderTarget.source,
18721
+ warnings: []
18722
+ };
18723
+ }
18724
+ if (scope === "global") {
18725
+ return {
18726
+ kind: "global",
18727
+ requested_scope: scope,
18728
+ effective_scope: "global",
18729
+ reason: "explicit_global",
18730
+ warnings: globalOverrideWarning
18731
+ };
18732
+ }
18733
+ if (activeProject) {
18734
+ return {
18735
+ kind: "folder",
18736
+ requested_scope: scope,
18737
+ effective_scope: "folder",
18738
+ reason: "existing_folder_auth",
18739
+ source: activeProject.source,
18740
+ warnings: []
18741
+ };
18742
+ }
18743
+ if (folderTarget.ok && folderTarget.source === "cowork") {
18744
+ return {
18745
+ kind: "folder",
18746
+ requested_scope: scope,
18747
+ effective_scope: "folder",
18748
+ reason: "detected_cowork_project",
18749
+ source: "cowork",
18750
+ warnings: []
18751
+ };
18752
+ }
18753
+ if (!folderTarget.ok) {
18754
+ return {
18755
+ kind: "global",
18756
+ requested_scope: scope,
18757
+ effective_scope: "global",
18758
+ reason: "ambiguous_cowork_fell_back_global",
18759
+ warnings: [
18760
+ `Cowork project folder is ambiguous, so global auth was updated. Candidate folders: ${folderTarget.candidates.join(
18761
+ ", "
18762
+ )}. Set CLAUDE_PROJECT_DIR or cd into the intended project folder to update folder auth.`
18763
+ ]
18764
+ };
18765
+ }
18766
+ return {
18767
+ kind: "global",
18768
+ requested_scope: scope,
18769
+ effective_scope: "global",
18770
+ reason: "default_global",
18771
+ warnings: []
18772
+ };
18773
+ }
18475
18774
  async function handleOrgList(options) {
18476
18775
  const config = resolveConfig();
18477
18776
  const http = new HttpClient(config);
@@ -18491,7 +18790,89 @@ async function handleOrgList(options) {
18491
18790
  { json: options.json }
18492
18791
  );
18493
18792
  }
18793
+ async function handleOrgStatus(options) {
18794
+ const config = resolveConfig();
18795
+ const http = new HttpClient(config);
18796
+ const payload = await fetchOrganizations(http, config.apiKey);
18797
+ const current = payload.organizations.find((org) => org.is_current) ?? payload.organizations.find((org) => org.org_id === payload.current_org_id) ?? null;
18798
+ const projectCandidate = getActiveProjectAuthSource();
18799
+ const activeProject = getResolvedProjectAuthSource(
18800
+ config.baseUrl,
18801
+ config.apiKey
18802
+ );
18803
+ const folderTarget = resolveProjectPinTarget();
18804
+ const hostPath = hostEnvFilePath(config.baseUrl);
18805
+ const envApiKey = processEnvValue(API_KEY_ENV);
18806
+ const envHostUrl = processEnvValue(HOST_URL_ENV);
18807
+ const authSource = envApiKey ? {
18808
+ scope: "env",
18809
+ source: "process",
18810
+ path: null,
18811
+ api_key: redactApiKey(envApiKey),
18812
+ host: envHostUrl || null,
18813
+ overrides_global: true,
18814
+ overrides_folder: Boolean(projectCandidate)
18815
+ } : activeProject ? {
18816
+ scope: "folder",
18817
+ source: activeProject.source,
18818
+ path: activeProject.filePath,
18819
+ api_key: redactApiKey(activeProject.env.DEEPLINE_API_KEY),
18820
+ overrides_global: true
18821
+ } : {
18822
+ scope: "global",
18823
+ source: "host",
18824
+ path: hostPath,
18825
+ api_key: redactApiKey(config.apiKey),
18826
+ overrides_global: false
18827
+ };
18828
+ const folderTargetPayload = folderTarget.ok ? {
18829
+ ok: true,
18830
+ source: folderTarget.source,
18831
+ dir: folderTarget.dir,
18832
+ env_path: `${folderTarget.dir}/.env.deepline`
18833
+ } : {
18834
+ ok: false,
18835
+ reason: folderTarget.reason,
18836
+ candidates: folderTarget.candidates
18837
+ };
18838
+ const lines = [
18839
+ `Organization: ${current?.name ?? "(unknown)"}`,
18840
+ `Host: ${config.baseUrl}`,
18841
+ `Auth scope: ${authSource.scope}`
18842
+ ];
18843
+ if (authSource.path) {
18844
+ lines.push(`Auth file: ${authSource.path}`);
18845
+ } else {
18846
+ lines.push(`Auth source: ${API_KEY_ENV} process environment`);
18847
+ }
18848
+ if (envApiKey) {
18849
+ lines.push(`${API_KEY_ENV} overrides saved auth for this process.`);
18850
+ } else if (activeProject) {
18851
+ lines.push("Global auth is overridden in this folder.");
18852
+ }
18853
+ printCommandEnvelope(
18854
+ {
18855
+ ok: true,
18856
+ host: config.baseUrl,
18857
+ organization: current,
18858
+ current_org_id: payload.current_org_id,
18859
+ auth_source: authSource,
18860
+ host_env_path: hostPath,
18861
+ folder_target: folderTargetPayload,
18862
+ next: {
18863
+ set_auto: "deepline org set <org>",
18864
+ set_folder: "deepline org set <org> --auth-scope folder",
18865
+ set_global: "deepline org set <org> --auth-scope global"
18866
+ },
18867
+ render: {
18868
+ sections: [{ title: "org status", lines }]
18869
+ }
18870
+ },
18871
+ { json: options.json }
18872
+ );
18873
+ }
18494
18874
  async function handleOrgSwitch(selection, options) {
18875
+ const authScope = normalizeAuthScope(options.authScope);
18495
18876
  const config = resolveConfig();
18496
18877
  const http = new HttpClient(config);
18497
18878
  const payload = await fetchOrganizations(http, config.apiKey);
@@ -18499,7 +18880,10 @@ async function handleOrgSwitch(selection, options) {
18499
18880
  printCommandEnvelope(
18500
18881
  {
18501
18882
  ...payload,
18502
- next: { switch: "deepline org switch <number>" },
18883
+ next: { set: "deepline org set <number>" },
18884
+ deprecated_command: options.deprecatedSwitch ? "org switch" : null,
18885
+ replacement_command: options.deprecatedSwitch ? "deepline org set" : null,
18886
+ warnings: options.deprecatedSwitch ? ["org switch is deprecated; use org set."] : [],
18503
18887
  render: {
18504
18888
  sections: [
18505
18889
  {
@@ -18507,7 +18891,7 @@ async function handleOrgSwitch(selection, options) {
18507
18891
  lines: orgListLines(payload.organizations)
18508
18892
  }
18509
18893
  ],
18510
- actions: [{ label: "Run", command: "deepline org switch <number>" }]
18894
+ actions: [{ label: "Run", command: "deepline org set <number>" }]
18511
18895
  }
18512
18896
  },
18513
18897
  { json: options.json }
@@ -18530,16 +18914,56 @@ async function handleOrgSwitch(selection, options) {
18530
18914
  if (!target) {
18531
18915
  throw new Error("Could not resolve the selected organization.");
18532
18916
  }
18917
+ const authTarget = resolveOrgSwitchAuthTarget(authScope, config);
18533
18918
  if (target.is_current) {
18919
+ let project_env_paths2 = [];
18920
+ if (authTarget.kind === "folder") {
18921
+ project_env_paths2 = saveProjectDeeplineEnvValues({
18922
+ DEEPLINE_HOST_URL: config.baseUrl,
18923
+ DEEPLINE_API_KEY: config.apiKey
18924
+ });
18925
+ } else {
18926
+ saveHostEnvValues(config.baseUrl, {
18927
+ DEEPLINE_HOST_URL: config.baseUrl,
18928
+ DEEPLINE_API_KEY: config.apiKey
18929
+ });
18930
+ }
18931
+ const renderLines2 = [`Already on ${target.name}.`];
18932
+ for (const projectPath of project_env_paths2) {
18933
+ renderLines2.push(`Saved folder auth in ${projectPath}`);
18934
+ }
18935
+ if (authTarget.kind === "global") {
18936
+ renderLines2.push(`Saved global auth in ${hostEnvFilePath(config.baseUrl)}`);
18937
+ }
18938
+ renderLines2.push(
18939
+ `Scope: ${authTarget.requested_scope} -> ${authTarget.effective_scope} (${authTarget.reason})`
18940
+ );
18941
+ if (options.deprecatedSwitch) {
18942
+ renderLines2.push("Warning: org switch is deprecated; use org set.");
18943
+ }
18944
+ for (const warning of authTarget.warnings) {
18945
+ renderLines2.push(`Warning: ${warning}`);
18946
+ }
18947
+ const warnings2 = [
18948
+ ...options.deprecatedSwitch ? ["org switch is deprecated; use org set."] : [],
18949
+ ...authTarget.warnings
18950
+ ];
18534
18951
  printCommandEnvelope(
18535
18952
  {
18536
18953
  ok: true,
18537
18954
  unchanged: true,
18538
18955
  organization: target,
18956
+ requested_auth_scope: authTarget.requested_scope,
18957
+ effective_auth_scope: authTarget.effective_scope,
18958
+ auth_scope_reason: authTarget.reason,
18959
+ auth_scope: authTarget.effective_scope,
18960
+ host_env_path: authTarget.kind === "global" ? hostEnvFilePath(config.baseUrl) : null,
18961
+ project_env_paths: project_env_paths2,
18962
+ deprecated_command: options.deprecatedSwitch ? "org switch" : null,
18963
+ replacement_command: options.deprecatedSwitch ? "deepline org set" : null,
18964
+ warnings: warnings2,
18539
18965
  render: {
18540
- sections: [
18541
- { title: "org switch", lines: [`Already on ${target.name}.`] }
18542
- ]
18966
+ sections: [{ title: "org set", lines: renderLines2 }]
18543
18967
  }
18544
18968
  },
18545
18969
  { json: options.json }
@@ -18550,26 +18974,61 @@ async function handleOrgSwitch(selection, options) {
18550
18974
  api_key: config.apiKey,
18551
18975
  org_id: target.org_id
18552
18976
  });
18553
- saveHostEnvValues(config.baseUrl, {
18554
- DEEPLINE_API_KEY: switched.api_key,
18555
- DEEPLINE_ACTIVE_ORG_ID: switched.org_id,
18556
- DEEPLINE_ACTIVE_ORG_NAME: switched.org_name
18557
- });
18977
+ let project_env_paths = [];
18978
+ if (authTarget.kind === "folder") {
18979
+ project_env_paths = saveProjectDeeplineEnvValues({
18980
+ DEEPLINE_HOST_URL: config.baseUrl,
18981
+ DEEPLINE_API_KEY: switched.api_key
18982
+ });
18983
+ } else {
18984
+ saveHostEnvValues(config.baseUrl, {
18985
+ DEEPLINE_HOST_URL: config.baseUrl,
18986
+ DEEPLINE_API_KEY: switched.api_key,
18987
+ DEEPLINE_ACTIVE_ORG_ID: switched.org_id,
18988
+ DEEPLINE_ACTIVE_ORG_NAME: switched.org_name
18989
+ });
18990
+ }
18558
18991
  const { api_key: _apiKey, ...publicSwitched } = switched;
18992
+ const renderLines = [`Switched to ${switched.org_name}.`];
18993
+ if (authTarget.kind === "folder") {
18994
+ for (const projectPath of project_env_paths) {
18995
+ renderLines.push(`Saved folder auth in ${projectPath}`);
18996
+ }
18997
+ } else {
18998
+ renderLines.push(`Saved global auth in ${hostEnvFilePath(config.baseUrl)}`);
18999
+ }
19000
+ renderLines.push(
19001
+ `Scope: ${authTarget.requested_scope} -> ${authTarget.effective_scope} (${authTarget.reason})`
19002
+ );
19003
+ if (options.deprecatedSwitch) {
19004
+ renderLines.push("Warning: org switch is deprecated; use org set.");
19005
+ }
19006
+ for (const warning of authTarget.warnings) {
19007
+ renderLines.push(`Warning: ${warning}`);
19008
+ }
19009
+ const warnings = [
19010
+ ...options.deprecatedSwitch ? ["org switch is deprecated; use org set."] : [],
19011
+ ...authTarget.warnings
19012
+ ];
18559
19013
  printCommandEnvelope(
18560
19014
  {
18561
19015
  ok: true,
18562
- host_env_path: hostEnvFilePath(config.baseUrl),
19016
+ host_env_path: authTarget.kind === "global" ? hostEnvFilePath(config.baseUrl) : null,
19017
+ project_env_paths,
18563
19018
  ...publicSwitched,
18564
19019
  api_key_saved: true,
19020
+ requested_auth_scope: authTarget.requested_scope,
19021
+ effective_auth_scope: authTarget.effective_scope,
19022
+ auth_scope_reason: authTarget.reason,
19023
+ auth_scope: authTarget.effective_scope,
19024
+ deprecated_command: options.deprecatedSwitch ? "org switch" : null,
19025
+ replacement_command: options.deprecatedSwitch ? "deepline org set" : null,
19026
+ warnings,
18565
19027
  render: {
18566
19028
  sections: [
18567
19029
  {
18568
- title: "org switch",
18569
- lines: [
18570
- `Switched to ${switched.org_name}.`,
18571
- `Saved host auth in ${hostEnvFilePath(config.baseUrl)}`
18572
- ]
19030
+ title: "org set",
19031
+ lines: renderLines
18573
19032
  }
18574
19033
  ]
18575
19034
  }
@@ -18585,6 +19044,7 @@ async function handleOrgCreate(name, options) {
18585
19044
  name
18586
19045
  });
18587
19046
  saveHostEnvValues(config.baseUrl, {
19047
+ DEEPLINE_HOST_URL: config.baseUrl,
18588
19048
  DEEPLINE_API_KEY: created.api_key,
18589
19049
  DEEPLINE_ACTIVE_ORG_ID: created.org_id,
18590
19050
  DEEPLINE_ACTIVE_ORG_NAME: created.org_name
@@ -18614,18 +19074,20 @@ async function handleOrgCreate(name, options) {
18614
19074
  );
18615
19075
  }
18616
19076
  function registerOrgCommands(program) {
18617
- const org = program.command("org").description("List, create, and switch organizations.").addHelpText(
19077
+ const org = program.command("org").description("List, create, and set organizations.").addHelpText(
18618
19078
  "after",
18619
19079
  `
18620
19080
  Notes:
18621
- Organizations are workspaces. Switching organizations mutates the saved host
18622
- auth file so later CLI commands target the selected workspace.
19081
+ Organizations are workspaces. By default, switching updates the auth scope
19082
+ that will affect later commands from this folder.
18623
19083
 
18624
19084
  Examples:
18625
19085
  deepline org list --json
19086
+ deepline org status --json
18626
19087
  deepline org create Acme --json
18627
- deepline org switch 2
18628
- deepline org switch --org-id org_123 --json
19088
+ deepline org set 2
19089
+ deepline org set 2 --auth-scope folder
19090
+ deepline org set --org-id org_123 --json
18629
19091
  `
18630
19092
  );
18631
19093
  org.command("list").description("List your organizations.").addHelpText(
@@ -18652,21 +19114,65 @@ Examples:
18652
19114
  deepline org create "Acme Sales" --json
18653
19115
  `
18654
19116
  ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgCreate);
18655
- org.command("switch [selection]").description(
18656
- "Switch to another organization and save the new API key in the host auth file."
18657
- ).addHelpText(
19117
+ org.command("status").description("Show the current organization and auth source.").addHelpText(
18658
19118
  "after",
18659
19119
  `
18660
19120
  Notes:
18661
- Mutates the saved host auth file. Selection can be a list number, exact
18662
- organization name, or organization id. Without a selection, prints choices.
19121
+ Read-only. Shows whether commands resolve auth from process env, a folder
19122
+ .env.deepline file, or global host auth.
19123
+
19124
+ Examples:
19125
+ deepline org status
19126
+ deepline org status --json
19127
+ `
19128
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgStatus);
19129
+ const addOrgSetOptions = (command) => command.option("--org-id <id>", "Set using an explicit organization id").option(
19130
+ "--auth-scope <scope>",
19131
+ "Where to save auth: auto, folder, or global",
19132
+ "auto"
19133
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped");
19134
+ addOrgSetOptions(
19135
+ org.command("set [selection]").description("Set the organization for the selected auth scope.").addHelpText(
19136
+ "after",
19137
+ `
19138
+ Notes:
19139
+ Selection can be a list number, exact organization name, or organization id.
19140
+ Without a selection, prints choices. The default --auth-scope auto updates
19141
+ the folder auth file when one controls this command, updates the detected
19142
+ Cowork project folder when present, and otherwise updates global host auth.
19143
+
19144
+ Examples:
19145
+ deepline org set
19146
+ deepline org set 2
19147
+ deepline org set 2 --auth-scope folder
19148
+ deepline org set 2 --auth-scope global
19149
+ deepline org set --org-id org_123 --json
19150
+ `
19151
+ )
19152
+ ).action(handleOrgSwitch);
19153
+ addOrgSetOptions(
19154
+ org.command("switch [selection]").description(
19155
+ "Deprecated alias for org set. Set the organization for the selected auth scope."
19156
+ ).addHelpText(
19157
+ "after",
19158
+ `
19159
+ Notes:
19160
+ Deprecated alias. Use deepline org set instead. The default --auth-scope auto
19161
+ updates the folder auth file when one controls this command, updates the
19162
+ detected Cowork project folder when present, and otherwise updates global host
19163
+ auth.
18663
19164
 
18664
19165
  Examples:
18665
19166
  deepline org switch
18666
19167
  deepline org switch 2
19168
+ deepline org switch 2 --auth-scope folder
19169
+ deepline org switch 2 --auth-scope global
18667
19170
  deepline org switch --org-id org_123 --json
18668
19171
  `
18669
- ).option("--org-id <id>", "Switch using an explicit organization id").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgSwitch);
19172
+ )
19173
+ ).action(
19174
+ (selection, options) => handleOrgSwitch(selection, { ...options, deprecatedSwitch: true })
19175
+ );
18670
19176
  }
18671
19177
 
18672
19178
  // src/cli/commands/quickstart.ts
@@ -21619,34 +22125,6 @@ async function fetchV1SkillNames(baseUrl) {
21619
22125
  function buildSdkSkillNames(v1SkillNames) {
21620
22126
  return sortedUniqueSkillNames(v1SkillNames);
21621
22127
  }
21622
- async function fetchSkillsUpdate(baseUrl, localVersion) {
21623
- const controller = new AbortController();
21624
- const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS2);
21625
- try {
21626
- const response = await fetch(new URL("/api/v2/cli/update-check", baseUrl), {
21627
- method: "POST",
21628
- headers: { "Content-Type": "application/json" },
21629
- body: JSON.stringify({
21630
- skills: {
21631
- version: localVersion
21632
- }
21633
- }),
21634
- signal: controller.signal
21635
- });
21636
- if (!response.ok) return null;
21637
- const data = await response.json().catch(() => null);
21638
- const skills = data?.skills;
21639
- if (!skills) return null;
21640
- return {
21641
- needsUpdate: skills.needs_update === true,
21642
- remoteVersion: typeof skills.remote?.version === "string" ? skills.remote.version.trim() : ""
21643
- };
21644
- } catch {
21645
- return null;
21646
- } finally {
21647
- clearTimeout(timeout);
21648
- }
21649
- }
21650
22128
  function buildSkillsInstallArgs(baseUrl, skillNames = DEFAULT_SDK_SKILL_NAMES) {
21651
22129
  return buildSkillsAddArgs(baseUrl, sortedUniqueSkillNames(skillNames));
21652
22130
  }
@@ -21787,6 +22265,12 @@ function writeSdkSkillsStatusLine(line) {
21787
22265
  process.stderr.write(`${line}
21788
22266
  `);
21789
22267
  }
22268
+ function skillsStaleHintLine(update) {
22269
+ if (!update?.needs_update || !update.remote.version) {
22270
+ return null;
22271
+ }
22272
+ return "Deepline agent skills are out of date. Run `deepline update` to sync.";
22273
+ }
21790
22274
  async function syncSdkSkillsIfNeeded(baseUrl, options = {}) {
21791
22275
  if (attemptedSync || shouldSkipSkillsSync()) return;
21792
22276
  attemptedSync = true;
@@ -21794,14 +22278,11 @@ async function syncSdkSkillsIfNeeded(baseUrl, options = {}) {
21794
22278
  if (usingPluginSkills) {
21795
22279
  return;
21796
22280
  }
21797
- const localVersion = readSdkSkillsLocalVersion(baseUrl);
21798
- const update = options.update === void 0 ? await fetchSkillsUpdate(baseUrl, localVersion) : options.update ? {
21799
- needsUpdate: options.update.needs_update,
21800
- remoteVersion: options.update.remote.version
21801
- } : null;
21802
- if (!update?.needsUpdate || !update.remoteVersion) {
22281
+ const update = options.update ?? null;
22282
+ if (!update?.needs_update || !update.remote.version) {
21803
22283
  return;
21804
22284
  }
22285
+ const remoteVersion = update.remote.version;
21805
22286
  const remoteSkillNames = await fetchV1SkillNames(baseUrl);
21806
22287
  const skillNames = buildSdkSkillNames(
21807
22288
  remoteSkillNames.length > 0 ? remoteSkillNames : DEFAULT_SDK_SKILL_NAMES
@@ -21811,11 +22292,17 @@ async function syncSdkSkillsIfNeeded(baseUrl, options = {}) {
21811
22292
  const installed = await runSkillsInstall(baseUrl, skillNames);
21812
22293
  if (!installed) return;
21813
22294
  runLegacySkillsCleanup();
21814
- writeLocalSkillsVersion(baseUrl, update.remoteVersion);
22295
+ writeLocalSkillsVersion(baseUrl, remoteVersion);
21815
22296
  writeSdkSkillsStatusLine("Deepline agent skills are up to date.");
21816
22297
  }
21817
22298
 
21818
22299
  // src/cli/commands/update.ts
22300
+ async function resolveSkillsVerdictFromCompat(baseUrl) {
22301
+ const { response } = await checkSdkCompatibility(baseUrl, {
22302
+ skillsVersion: readSdkSkillsLocalVersion(baseUrl)
22303
+ });
22304
+ return response?.skills ?? null;
22305
+ }
21819
22306
  var NPM_SDK_INSTALL_COMMON_FLAGS = ["--no-audit", "--no-fund"];
21820
22307
  var NPM_SDK_GLOBAL_INSTALL_FLAGS = [
21821
22308
  "--no-audit",
@@ -22254,6 +22741,7 @@ async function runUpdateCommand(options, dependencies = {}) {
22254
22741
  const detectBaseUrl = dependencies.detectBaseUrl ?? autoDetectBaseUrl;
22255
22742
  const resolvePlan = dependencies.resolvePlan ?? resolveUpdatePlan;
22256
22743
  const runPlan = dependencies.runPlan ?? runUpdatePlan;
22744
+ const resolveSkillsVerdict = dependencies.resolveSkillsVerdict ?? resolveSkillsVerdictFromCompat;
22257
22745
  const syncSkills = dependencies.syncSkillsIfNeeded ?? syncSdkSkillsIfNeeded;
22258
22746
  const stderr = dependencies.stderr ?? process.stderr;
22259
22747
  const plan = resolvePlan();
@@ -22298,7 +22786,9 @@ async function runUpdateCommand(options, dependencies = {}) {
22298
22786
  if (updateExitCode !== 0) {
22299
22787
  return updateExitCode;
22300
22788
  }
22301
- await syncSkills(normalizeBaseUrl3(detectBaseUrl()));
22789
+ const updatedBaseUrl = normalizeBaseUrl3(detectBaseUrl());
22790
+ const skillsUpdate = await resolveSkillsVerdict(updatedBaseUrl);
22791
+ await syncSkills(updatedBaseUrl, { update: skillsUpdate });
22302
22792
  return 0;
22303
22793
  }
22304
22794
  function registerUpdateCommand(program) {
@@ -23015,7 +23505,7 @@ Exit codes:
23015
23505
  const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
23016
23506
  const compatibilityCommand = compatibilityCommandPath(actionCommand) || actionCommand.name();
23017
23507
  const shouldDeferSkillsSync = shouldDeferSkillsSyncForCommand();
23018
- const skillsVersion = shouldDeferSkillsSync ? void 0 : readSdkSkillsLocalVersion(baseUrl);
23508
+ const skillsVersion = readSdkSkillsLocalVersion(baseUrl);
23019
23509
  const compatibility = await traceCliSpan(
23020
23510
  "cli.sdk_compatibility",
23021
23511
  {
@@ -23051,8 +23541,14 @@ Exit codes:
23051
23541
  if (printStartupPhase) {
23052
23542
  progress?.phase("checking sdk skills");
23053
23543
  }
23054
- if (!shouldDeferSkillsSync) {
23055
- const skillsUpdate = compatibility.response && Object.prototype.hasOwnProperty.call(compatibility.response, "skills") ? compatibility.response.skills ?? null : void 0;
23544
+ const skillsUpdate = compatibility.response?.skills ?? null;
23545
+ if (shouldDeferSkillsSync) {
23546
+ const hint = skillsStaleHintLine(skillsUpdate);
23547
+ if (hint && !process.argv.includes("--json")) {
23548
+ process.stderr.write(`${hint}
23549
+ `);
23550
+ }
23551
+ } else {
23056
23552
  await traceCliSpan(
23057
23553
  "cli.sdk_skills_sync",
23058
23554
  { baseUrl },