deepline 0.1.32 → 0.1.33

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
@@ -67,19 +67,12 @@ var ConfigError = class extends DeeplineError {
67
67
  };
68
68
 
69
69
  // src/config.ts
70
+ var HOST_URL_ENV = "DEEPLINE_HOST_URL";
71
+ var API_KEY_ENV = "DEEPLINE_API_KEY";
70
72
  var PROD_URL = "https://code.deepline.com";
71
73
  var DEFAULT_TIMEOUT = 6e4;
72
74
  var DEFAULT_MAX_RETRIES = 3;
73
- var ACTIVE_DEEPLINE_ENV_FILE = ".env.deepline";
74
- function isProdBaseUrl(baseUrl) {
75
- return baseUrl.trim().replace(/\/$/, "") === PROD_URL;
76
- }
77
- function profileNameForBaseUrl(baseUrl) {
78
- return isProdBaseUrl(baseUrl) ? "prod" : "dev";
79
- }
80
- function projectEnvStartDir() {
81
- return process.env.DEEPLINE_PROJECT_ENV_DIR?.trim() || process.cwd();
82
- }
75
+ var PROJECT_DEEPLINE_ENV_FILE = ".env.deepline";
83
76
  function baseUrlSlug(baseUrl) {
84
77
  let url;
85
78
  try {
@@ -88,7 +81,7 @@ function baseUrlSlug(baseUrl) {
88
81
  return "unknown";
89
82
  }
90
83
  const host = url.hostname || "unknown";
91
- const port = url.port ? parseInt(url.port, 10) : null;
84
+ const port = url.port ? Number.parseInt(url.port, 10) : null;
92
85
  let slug = host.replace(/[^a-zA-Z0-9]/g, "-");
93
86
  if (port && port !== 80 && port !== 443) {
94
87
  slug = `${slug}-${port}`;
@@ -115,79 +108,52 @@ function parseEnvFile(filePath) {
115
108
  }
116
109
  return env;
117
110
  }
118
- function findNearestEnvFile(names, startDir = process.cwd()) {
111
+ function findNearestEnvFile(name, startDir = process.cwd()) {
119
112
  let current = (0, import_node_path.resolve)(startDir);
120
113
  while (true) {
121
- for (const name of names) {
122
- const filePath = (0, import_node_path.join)(current, name);
123
- if ((0, import_node_fs.existsSync)(filePath)) return filePath;
124
- }
114
+ const filePath = (0, import_node_path.join)(current, name);
115
+ if ((0, import_node_fs.existsSync)(filePath)) return filePath;
125
116
  const parent = (0, import_node_path.dirname)(current);
126
117
  if (parent === current) return null;
127
118
  current = parent;
128
119
  }
129
120
  }
130
- function findNearestEnv(names, startDir = process.cwd()) {
131
- const filePath = findNearestEnvFile(names, startDir);
121
+ function loadProjectDeeplineEnv(startDir = process.cwd()) {
122
+ const filePath = findNearestEnvFile(PROJECT_DEEPLINE_ENV_FILE, startDir);
132
123
  return filePath ? parseEnvFile(filePath) : {};
133
124
  }
134
- function findNearestWorktreeEnv(startDir = process.cwd()) {
135
- return findNearestEnv([".env.worktree"], startDir);
136
- }
137
- function resolveProfileEnvFileNames() {
138
- const explicitProfile = process.env.DEEPLINE_ENV_PROFILE?.trim() || process.env.DEEPLINE_PROFILE?.trim() || "";
139
- const names = [];
140
- if (explicitProfile) names.push(`.env.deepline.${explicitProfile}`);
141
- const nodeEnv = process.env.NODE_ENV?.trim();
142
- if (nodeEnv === "production") names.push(".env.deepline.prod");
143
- else if (nodeEnv === "staging") names.push(".env.deepline.staging");
144
- names.push(ACTIVE_DEEPLINE_ENV_FILE);
145
- return names;
146
- }
147
- function resolveProjectAppEnvFileNames() {
148
- const nodeEnv = process.env.NODE_ENV?.trim();
149
- const names = [];
150
- if (nodeEnv === "production") names.push(".env.prod");
151
- if (nodeEnv === "staging") names.push(".env.staging");
152
- names.push(".env.local", ".env");
153
- return names;
154
- }
155
- function resolveBaseUrlFromEnvValues(env) {
156
- return env.DEEPLINE_ORIGIN_URL?.trim() || env.DEEPLINE_API_BASE_URL?.trim() || "";
157
- }
158
- function loadProjectDeeplineEnv() {
159
- return findNearestEnv(resolveProfileEnvFileNames(), projectEnvStartDir());
160
- }
161
- function loadProjectAppEnv() {
162
- return findNearestEnv(resolveProjectAppEnvFileNames(), projectEnvStartDir());
163
- }
164
- function normalizeWorktreeBaseUrl(baseUrl, worktreeEnv = findNearestWorktreeEnv()) {
165
- const trimmed = baseUrl.trim().replace(/\/$/, "");
166
- if (!trimmed) return trimmed;
125
+ function normalizeBaseUrl(baseUrl) {
126
+ const trimmed = baseUrl.trim().replace(/\/+$/, "");
127
+ if (!trimmed) return "";
167
128
  try {
168
129
  const parsed = new URL(trimmed);
169
- if (parsed.hostname.endsWith(".localhost") && parsed.port === "1355") {
170
- const port = worktreeEnv.WORKTREE_APP_PORT || worktreeEnv.PORT;
171
- if (port) return `${parsed.protocol}//localhost:${port}`;
130
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
131
+ return "";
172
132
  }
133
+ return parsed.toString().replace(/\/+$/, "");
173
134
  } catch {
135
+ return "";
136
+ }
137
+ }
138
+ function firstNonEmpty(...values) {
139
+ for (const value of values) {
140
+ const trimmed = value?.trim();
141
+ if (trimmed) return trimmed;
174
142
  }
175
- return trimmed;
143
+ return "";
176
144
  }
177
- function resolveWorktreeBaseUrl() {
178
- const worktreeEnv = findNearestWorktreeEnv();
179
- const declared = worktreeEnv.DEEPLINE_API_BASE_URL || worktreeEnv.WORKTREE_PUBLIC_APP_URL || worktreeEnv.APP_URL || "";
180
- if (declared) return normalizeWorktreeBaseUrl(declared, worktreeEnv);
181
- const port = worktreeEnv.WORKTREE_APP_PORT || worktreeEnv.PORT || "";
182
- return port ? `http://localhost:${port}` : "";
145
+ function sdkCliConfigDir(baseUrl) {
146
+ const home = process.env.HOME?.trim() || (0, import_node_os.homedir)();
147
+ return (0, import_node_path.join)(home, ".local", "deepline", baseUrlSlug(baseUrl || PROD_URL));
183
148
  }
184
149
  function sdkCliEnvFilePath(baseUrl) {
185
- const home = process.env.HOME?.trim() || (0, import_node_os.homedir)();
186
- return (0, import_node_path.join)(home, ".local", "deepline", baseUrlSlug(baseUrl || PROD_URL), ".env");
150
+ return (0, import_node_path.join)(sdkCliConfigDir(baseUrl), ".env");
187
151
  }
188
152
  function loadCliEnv(baseUrl = PROD_URL) {
189
- const envPath = sdkCliEnvFilePath(baseUrl);
190
- return parseEnvFile(envPath);
153
+ return parseEnvFile(sdkCliEnvFilePath(baseUrl));
154
+ }
155
+ function hostConfigDirPath(baseUrl) {
156
+ return sdkCliConfigDir(baseUrl);
191
157
  }
192
158
  function hostEnvFilePath(baseUrl) {
193
159
  return sdkCliEnvFilePath(baseUrl);
@@ -198,9 +164,10 @@ function saveHostEnvValues(baseUrl, values) {
198
164
  if (!(0, import_node_fs.existsSync)(dir)) {
199
165
  (0, import_node_fs.mkdirSync)(dir, { recursive: true });
200
166
  }
201
- const existing = (0, import_node_fs.existsSync)(filePath) ? parseEnvFile(filePath) : {};
167
+ const existing = parseEnvFile(filePath);
202
168
  const merged = { ...existing, ...values };
203
- const lines = Object.entries(merged).filter(([, value]) => value !== "").map(([key, value]) => `${key}=${value}`);
169
+ const allowedKeys = /* @__PURE__ */ new Set([HOST_URL_ENV, API_KEY_ENV]);
170
+ const lines = Object.entries(merged).filter(([key, value]) => allowedKeys.has(key) && value !== "").map(([key, value]) => `${key}=${value}`);
204
171
  (0, import_node_fs.writeFileSync)(filePath, `${lines.join("\n")}
205
172
  `, "utf-8");
206
173
  }
@@ -208,31 +175,36 @@ function loadGlobalCliEnv() {
208
175
  return loadCliEnv(PROD_URL);
209
176
  }
210
177
  function autoDetectBaseUrl() {
211
- const envOrigin = process.env.DEEPLINE_ORIGIN_URL?.trim();
212
- if (envOrigin) return normalizeWorktreeBaseUrl(envOrigin);
213
- const envBase = process.env.DEEPLINE_API_BASE_URL?.trim();
214
- if (envBase) return normalizeWorktreeBaseUrl(envBase);
215
- const projectDeeplineBaseUrl = resolveBaseUrlFromEnvValues(loadProjectDeeplineEnv());
216
- if (projectDeeplineBaseUrl) return normalizeWorktreeBaseUrl(projectDeeplineBaseUrl);
217
- const projectAppBaseUrl = resolveBaseUrlFromEnvValues(loadProjectAppEnv());
218
- if (projectAppBaseUrl) return normalizeWorktreeBaseUrl(projectAppBaseUrl);
219
- const worktreeBaseUrl = resolveWorktreeBaseUrl();
220
- if (worktreeBaseUrl) return worktreeBaseUrl;
178
+ const projectEnv = loadProjectDeeplineEnv();
221
179
  const globalEnv = loadGlobalCliEnv();
222
- const globalOrigin = globalEnv.DEEPLINE_ORIGIN_URL?.trim();
223
- if (globalOrigin) return normalizeWorktreeBaseUrl(globalOrigin);
224
- return PROD_URL;
180
+ return normalizeBaseUrl(process.env[HOST_URL_ENV] ?? "") || normalizeBaseUrl(projectEnv[HOST_URL_ENV] ?? "") || normalizeBaseUrl(globalEnv[HOST_URL_ENV] ?? "") || PROD_URL;
181
+ }
182
+ function resolveApiKeyForBaseUrl(baseUrl, explicitApiKey) {
183
+ const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
184
+ const projectEnv = loadProjectDeeplineEnv();
185
+ const cliEnv = loadCliEnv(normalizedBaseUrl || baseUrl);
186
+ const projectBaseUrl = normalizeBaseUrl(projectEnv[HOST_URL_ENV] ?? "");
187
+ const projectKeyApplies = projectBaseUrl === normalizedBaseUrl;
188
+ return firstNonEmpty(
189
+ explicitApiKey,
190
+ process.env[API_KEY_ENV],
191
+ projectKeyApplies ? projectEnv[API_KEY_ENV] : "",
192
+ cliEnv[API_KEY_ENV]
193
+ );
225
194
  }
226
195
  function resolveConfig(options) {
227
- const requestedBaseUrl = options?.baseUrl?.trim() || autoDetectBaseUrl();
228
- const baseUrl = normalizeWorktreeBaseUrl(requestedBaseUrl);
229
- const cliEnv = loadCliEnv(baseUrl);
230
- const projectDeeplineEnv = loadProjectDeeplineEnv();
231
- const projectAppEnv = loadProjectAppEnv();
232
- const apiKey = options?.apiKey?.trim() || process.env.DEEPLINE_API_KEY?.trim() || projectDeeplineEnv.DEEPLINE_API_KEY || projectAppEnv.DEEPLINE_API_KEY || cliEnv.DEEPLINE_API_KEY || "";
196
+ const baseUrl = normalizeBaseUrl(
197
+ options?.baseUrl?.trim() || autoDetectBaseUrl()
198
+ );
199
+ if (!baseUrl) {
200
+ throw new ConfigError(
201
+ `Invalid ${HOST_URL_ENV}. Expected an http(s) URL such as https://code.deepline.com.`
202
+ );
203
+ }
204
+ const apiKey = resolveApiKeyForBaseUrl(baseUrl, options?.apiKey);
233
205
  if (!apiKey) {
234
206
  throw new ConfigError(
235
- `No API key found. Set DEEPLINE_API_KEY env var, pass apiKey option, or run: deepline auth register`
207
+ `No API key found. Set ${API_KEY_ENV}, add it to .env.deepline, or run: deepline auth register`
236
208
  );
237
209
  }
238
210
  return {
@@ -242,32 +214,10 @@ function resolveConfig(options) {
242
214
  maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES
243
215
  };
244
216
  }
245
- function mergeEnvFile(filePath, values) {
246
- const existing = (0, import_node_fs.existsSync)(filePath) ? parseEnvFile(filePath) : {};
247
- const merged = { ...existing, ...values };
248
- const dir = (0, import_node_path.dirname)(filePath);
249
- if (!(0, import_node_fs.existsSync)(dir)) (0, import_node_fs.mkdirSync)(dir, { recursive: true });
250
- const lines = Object.entries(merged).filter(([, value]) => value !== "").map(([key, value]) => `${key}=${value}`);
251
- (0, import_node_fs.writeFileSync)(filePath, `${lines.join("\n")}
252
- `, "utf-8");
253
- }
254
- function saveProjectDeeplineEnvValues(baseUrl, values, startDir = projectEnvStartDir()) {
255
- const root = (0, import_node_path.resolve)(startDir);
256
- const profile = profileNameForBaseUrl(baseUrl);
257
- const files = [
258
- (0, import_node_path.join)(root, ACTIVE_DEEPLINE_ENV_FILE),
259
- (0, import_node_path.join)(root, `.env.deepline.${profile}`)
260
- ];
261
- if (profile === "dev") files.push((0, import_node_path.join)(root, ".env"));
262
- for (const filePath of files) {
263
- mergeEnvFile(filePath, values);
264
- }
265
- return files;
266
- }
267
217
 
268
218
  // src/version.ts
269
- var SDK_VERSION = "0.1.32";
270
- var SDK_API_CONTRACT = "2026-05-generic-play-input-flags";
219
+ var SDK_VERSION = "0.1.33";
220
+ var SDK_API_CONTRACT = "2026-05-host-env-generic-play-input-flags";
271
221
 
272
222
  // ../shared_libs/play-runtime/coordinator-headers.ts
273
223
  var COORDINATOR_INTERNAL_TOKEN_HEADER = "x-deepline-internal-token";
@@ -1686,10 +1636,11 @@ var import_node_fs2 = require("fs");
1686
1636
  var import_promises = require("fs/promises");
1687
1637
  var import_node_os2 = require("os");
1688
1638
  var import_node_path2 = require("path");
1689
- var import_node_child_process = require("child_process");
1639
+ var childProcess = __toESM(require("child_process"));
1690
1640
  var import_sync = require("csv-parse/sync");
1691
1641
  var import_sync2 = require("csv-stringify/sync");
1692
1642
  var BROWSER_FOCUS_COOLDOWN_MS = 3e4;
1643
+ var defaultBrowserCommandRunner = childProcess;
1693
1644
  function getAuthedHttpClient() {
1694
1645
  const config = resolveConfig();
1695
1646
  return { config, http: new HttpClient(config) };
@@ -1758,9 +1709,9 @@ function browserAppNameFromBundleId(bundleId) {
1758
1709
  };
1759
1710
  return names[bundleId.toLowerCase()] ?? "";
1760
1711
  }
1761
- function readDefaultMacBrowserBundleId() {
1712
+ function readDefaultMacBrowserBundleId(runner = defaultBrowserCommandRunner) {
1762
1713
  try {
1763
- const output = (0, import_node_child_process.execFileSync)(
1714
+ const output = runner.execFileSync(
1764
1715
  "/usr/bin/defaults",
1765
1716
  [
1766
1717
  "read",
@@ -1796,8 +1747,8 @@ function browserStrategyForBundleId(bundleId) {
1796
1747
  }
1797
1748
  return normalized === "com.apple.safari" ? "safari" : "fallback";
1798
1749
  }
1799
- function runAppleScript(script, args) {
1800
- const result = (0, import_node_child_process.spawnSync)("osascript", ["-", ...args], {
1750
+ function runAppleScript(script, args, runner = defaultBrowserCommandRunner) {
1751
+ const result = runner.spawnSync("osascript", ["-", ...args], {
1801
1752
  input: script,
1802
1753
  encoding: "utf-8",
1803
1754
  stdio: ["pipe", "ignore", "ignore"],
@@ -1805,7 +1756,7 @@ function runAppleScript(script, args) {
1805
1756
  });
1806
1757
  return result.status === 0;
1807
1758
  }
1808
- function retargetChromiumMacos(appName, targetUrl, allowFocus) {
1759
+ function retargetChromiumMacos(appName, targetUrl, allowFocus, runner = defaultBrowserCommandRunner) {
1809
1760
  const host = extractUrlHost(targetUrl);
1810
1761
  if (!host) return false;
1811
1762
  const escapedAppName = appName.replace(/"/g, '\\"');
@@ -1837,9 +1788,9 @@ ${newTabBlock} end if
1837
1788
  end tell
1838
1789
  end run
1839
1790
  `;
1840
- return runAppleScript(script, [targetUrl, host]);
1791
+ return runAppleScript(script, [targetUrl, host], runner);
1841
1792
  }
1842
- function retargetSafariMacos(appName, targetUrl, allowFocus) {
1793
+ function retargetSafariMacos(appName, targetUrl, allowFocus, runner = defaultBrowserCommandRunner) {
1843
1794
  const host = extractUrlHost(targetUrl);
1844
1795
  if (!host) return false;
1845
1796
  const escapedAppName = appName.replace(/"/g, '\\"');
@@ -1871,30 +1822,38 @@ ${newTabBlock} end if
1871
1822
  end tell
1872
1823
  end run
1873
1824
  `;
1874
- return runAppleScript(script, [targetUrl, host]);
1825
+ return runAppleScript(script, [targetUrl, host], runner);
1875
1826
  }
1876
- function openUrlMacos(targetUrl, allowFocus) {
1877
- const defaultBundleId = readDefaultMacBrowserBundleId();
1827
+ function openUrlMacos(targetUrl, allowFocus, runner = defaultBrowserCommandRunner) {
1828
+ const defaultBundleId = readDefaultMacBrowserBundleId(runner);
1878
1829
  const appName = defaultBundleId ? browserAppNameFromBundleId(defaultBundleId) : "";
1879
1830
  const strategy = browserStrategyForBundleId(defaultBundleId);
1880
- if (appName && strategy === "chromium" && retargetChromiumMacos(appName, targetUrl, allowFocus)) {
1831
+ if (appName && strategy === "chromium" && retargetChromiumMacos(appName, targetUrl, allowFocus, runner)) {
1881
1832
  return true;
1882
1833
  }
1883
- if (appName && strategy === "safari" && retargetSafariMacos(appName, targetUrl, allowFocus)) {
1834
+ if (appName && strategy === "safari" && retargetSafariMacos(appName, targetUrl, allowFocus, runner)) {
1884
1835
  return true;
1885
1836
  }
1886
- if (!allowFocus) {
1887
- return false;
1888
- }
1889
1837
  try {
1890
- (0, import_node_child_process.execFileSync)("open", [targetUrl], { stdio: "ignore" });
1838
+ runner.execFileSync(
1839
+ "open",
1840
+ [...allowFocus ? [] : ["-g"], targetUrl],
1841
+ { stdio: "ignore" }
1842
+ );
1891
1843
  return true;
1892
1844
  } catch {
1893
1845
  return false;
1894
1846
  }
1895
1847
  }
1848
+ function browserOpeningDisabled() {
1849
+ const value = String(
1850
+ process.env.DEEPLINE_NO_BROWSER ?? process.env.PLAYGROUND_HEADLESS ?? ""
1851
+ ).trim().toLowerCase();
1852
+ return value === "1" || value === "true" || value === "yes" || value === "on";
1853
+ }
1896
1854
  function openInBrowser(url) {
1897
1855
  try {
1856
+ if (browserOpeningDisabled()) return;
1898
1857
  const targetUrl = String(url || "").trim();
1899
1858
  if (!targetUrl) return;
1900
1859
  const allowFocus = claimBrowserFocus();
@@ -1904,12 +1863,12 @@ function openInBrowser(url) {
1904
1863
  }
1905
1864
  if (!allowFocus) return;
1906
1865
  if (process.platform === "win32") {
1907
- (0, import_node_child_process.execFileSync)("cmd.exe", ["/c", "start", "", targetUrl], {
1866
+ childProcess.execFileSync("cmd.exe", ["/c", "start", "", targetUrl], {
1908
1867
  stdio: "ignore"
1909
1868
  });
1910
1869
  return;
1911
1870
  }
1912
- (0, import_node_child_process.execFileSync)("xdg-open", [targetUrl], { stdio: "ignore" });
1871
+ childProcess.execFileSync("xdg-open", [targetUrl], { stdio: "ignore" });
1913
1872
  } catch {
1914
1873
  }
1915
1874
  }
@@ -2023,17 +1982,39 @@ var EXIT_SERVER = 2;
2023
1982
  function envFilePath(baseUrl) {
2024
1983
  return hostEnvFilePath(baseUrl);
2025
1984
  }
2026
- function saveEnvValues(values, baseUrl) {
2027
- const filePath = envFilePath(baseUrl);
1985
+ function pendingClaimTokenPath(baseUrl) {
1986
+ return `${hostConfigDirPath(baseUrl)}/pending-claim-token`;
1987
+ }
1988
+ function savePendingClaimToken(baseUrl, claimToken) {
1989
+ const filePath = pendingClaimTokenPath(baseUrl);
2028
1990
  const dir = (0, import_node_path3.dirname)(filePath);
2029
1991
  if (!(0, import_node_fs3.existsSync)(dir)) {
2030
1992
  (0, import_node_fs3.mkdirSync)(dir, { recursive: true });
2031
1993
  }
2032
- const existing = (0, import_node_fs3.existsSync)(filePath) ? parseEnvFile(filePath) : {};
2033
- const merged = { ...existing, ...values };
2034
- const lines = Object.entries(merged).filter(([, v]) => v !== "").map(([k, v]) => `${k}=${v}`);
2035
- (0, import_node_fs3.writeFileSync)(filePath, lines.join("\n") + "\n", "utf-8");
2036
- saveProjectDeeplineEnvValues(baseUrl, values);
1994
+ (0, import_node_fs3.writeFileSync)(filePath, `${claimToken}
1995
+ `, "utf-8");
1996
+ }
1997
+ function readPendingClaimToken(baseUrl) {
1998
+ const filePath = pendingClaimTokenPath(baseUrl);
1999
+ if (!(0, import_node_fs3.existsSync)(filePath)) return "";
2000
+ try {
2001
+ return (0, import_node_fs3.readFileSync)(filePath, "utf-8").trim();
2002
+ } catch {
2003
+ return "";
2004
+ }
2005
+ }
2006
+ function clearPendingClaimToken(baseUrl) {
2007
+ try {
2008
+ (0, import_node_fs3.rmSync)(pendingClaimTokenPath(baseUrl), { force: true });
2009
+ } catch {
2010
+ }
2011
+ }
2012
+ function saveEnvValues(values, baseUrl) {
2013
+ const filtered = {
2014
+ ...values[HOST_URL_ENV] ? { [HOST_URL_ENV]: values[HOST_URL_ENV] } : {},
2015
+ ...values[API_KEY_ENV] ? { [API_KEY_ENV]: values[API_KEY_ENV] } : {}
2016
+ };
2017
+ saveHostEnvValues(baseUrl, filtered);
2037
2018
  }
2038
2019
  async function httpJson(method, url, apiKey, body) {
2039
2020
  const headers = { "Content-Type": "application/json" };
@@ -2139,9 +2120,9 @@ async function handleRegister(args) {
2139
2120
  const claimUrl = String(data.claim_url || "");
2140
2121
  const claimToken = String(data.claim_token || "");
2141
2122
  if (claimToken) {
2123
+ savePendingClaimToken(baseUrl, claimToken);
2142
2124
  saveEnvValues({
2143
- DEEPLINE_ORIGIN_URL: baseUrl,
2144
- DEEPLINE_CLAIM_TOKEN: claimToken
2125
+ [HOST_URL_ENV]: baseUrl
2145
2126
  }, baseUrl);
2146
2127
  }
2147
2128
  if (claimUrl) {
@@ -2165,6 +2146,7 @@ async function handleRegister(args) {
2165
2146
  { claim_token: claimToken, reveal: true }
2166
2147
  );
2167
2148
  if (s === 401 || s === 403) {
2149
+ clearPendingClaimToken(baseUrl);
2168
2150
  console.log("Status: unauthorized");
2169
2151
  return EXIT_AUTH;
2170
2152
  }
@@ -2181,15 +2163,16 @@ async function handleRegister(args) {
2181
2163
  const apiKey = String(statusData.api_key || "");
2182
2164
  if (apiKey) {
2183
2165
  saveEnvValues({
2184
- DEEPLINE_ORIGIN_URL: baseUrl,
2185
- DEEPLINE_API_KEY: apiKey,
2186
- DEEPLINE_CLAIM_TOKEN: ""
2166
+ [HOST_URL_ENV]: baseUrl,
2167
+ [API_KEY_ENV]: apiKey
2187
2168
  }, baseUrl);
2169
+ clearPendingClaimToken(baseUrl);
2188
2170
  printClaimSuccessBanner(statusData);
2189
2171
  return EXIT_OK;
2190
2172
  }
2191
2173
  }
2192
2174
  if (state === "expired") {
2175
+ clearPendingClaimToken(baseUrl);
2193
2176
  console.log("That approval link expired. Please run: deepline auth register");
2194
2177
  return EXIT_AUTH;
2195
2178
  }
@@ -2207,13 +2190,12 @@ async function handleWait(args) {
2207
2190
  }
2208
2191
  }
2209
2192
  }
2210
- const env = loadCliEnv(baseUrl);
2211
- if (env.DEEPLINE_API_KEY?.trim()) {
2212
- console.log("Already connected.");
2213
- return EXIT_OK;
2214
- }
2215
- const claimToken = env.DEEPLINE_CLAIM_TOKEN?.trim() || "";
2193
+ const claimToken = readPendingClaimToken(baseUrl);
2216
2194
  if (!claimToken) {
2195
+ if (resolveApiKeyForBaseUrl(baseUrl)) {
2196
+ console.log("Already connected.");
2197
+ return EXIT_OK;
2198
+ }
2217
2199
  console.error("No pending approval. Run: deepline auth register --no-wait");
2218
2200
  return EXIT_AUTH;
2219
2201
  }
@@ -2226,6 +2208,7 @@ async function handleWait(args) {
2226
2208
  { claim_token: claimToken, reveal: true }
2227
2209
  );
2228
2210
  if (status === 401 || status === 403) {
2211
+ clearPendingClaimToken(baseUrl);
2229
2212
  console.error("Claim is invalid. Run: deepline auth register");
2230
2213
  return EXIT_AUTH;
2231
2214
  }
@@ -2242,15 +2225,16 @@ async function handleWait(args) {
2242
2225
  const apiKey = String(data.api_key || "");
2243
2226
  if (apiKey) {
2244
2227
  saveEnvValues({
2245
- DEEPLINE_ORIGIN_URL: baseUrl,
2246
- DEEPLINE_API_KEY: apiKey,
2247
- DEEPLINE_CLAIM_TOKEN: ""
2228
+ [HOST_URL_ENV]: baseUrl,
2229
+ [API_KEY_ENV]: apiKey
2248
2230
  }, baseUrl);
2231
+ clearPendingClaimToken(baseUrl);
2249
2232
  printClaimSuccessBanner(data);
2250
2233
  return EXIT_OK;
2251
2234
  }
2252
2235
  }
2253
2236
  if (state === "expired") {
2237
+ clearPendingClaimToken(baseUrl);
2254
2238
  console.error("That approval link expired. Run: deepline auth register");
2255
2239
  return EXIT_AUTH;
2256
2240
  }
@@ -2285,10 +2269,9 @@ async function handleStatus(args) {
2285
2269
  };
2286
2270
  hostLines.push(`Host: ${baseUrl} (unreachable)`);
2287
2271
  }
2288
- const env = loadCliEnv(baseUrl);
2289
- const apiKey = process.env.DEEPLINE_API_KEY?.trim() || env.DEEPLINE_API_KEY || "";
2272
+ const apiKey = resolveApiKeyForBaseUrl(baseUrl);
2290
2273
  if (!apiKey) {
2291
- if (env.DEEPLINE_CLAIM_TOKEN?.trim()) {
2274
+ if (readPendingClaimToken(baseUrl)) {
2292
2275
  printCommandEnvelope({
2293
2276
  ...hostStatusPayload ?? { host: baseUrl },
2294
2277
  status: "pending",
@@ -2334,6 +2317,7 @@ async function handleStatus(args) {
2334
2317
  console.error(`Auth status error (status ${status}).`);
2335
2318
  return EXIT_SERVER;
2336
2319
  }
2320
+ clearPendingClaimToken(baseUrl);
2337
2321
  const payload = {
2338
2322
  ...hostStatusPayload ?? { host: baseUrl },
2339
2323
  status: data.status || "(unknown)",
@@ -2354,9 +2338,8 @@ async function handleStatus(args) {
2354
2338
  const apiKeyResp = String(data.api_key || apiKey);
2355
2339
  if (apiKeyResp) {
2356
2340
  saveEnvValues({
2357
- DEEPLINE_ORIGIN_URL: baseUrl,
2358
- DEEPLINE_API_KEY: apiKeyResp,
2359
- DEEPLINE_CLAIM_TOKEN: ""
2341
+ [HOST_URL_ENV]: baseUrl,
2342
+ [API_KEY_ENV]: apiKeyResp
2360
2343
  }, baseUrl);
2361
2344
  savedApiKeyPath = envFilePath(baseUrl);
2362
2345
  }
@@ -5843,10 +5826,6 @@ function openPlayDashboard(input) {
5843
5826
  }
5844
5827
  openInBrowser(input.dashboardUrl);
5845
5828
  }
5846
- function getDashboardUrlFromLiveEvent(event) {
5847
- const dashboardUrl = getEventPayload(event).dashboardUrl;
5848
- return typeof dashboardUrl === "string" && dashboardUrl.trim() ? dashboardUrl.trim() : null;
5849
- }
5850
5829
  function printPlayLogLines(input) {
5851
5830
  for (const line of input.lines) {
5852
5831
  if (input.emitLogs) {
@@ -5915,7 +5894,7 @@ async function waitForPlayCompletionByStream(input) {
5915
5894
  billing: false
5916
5895
  });
5917
5896
  if (TERMINAL_PLAY_STATUSES2.has(finalStatus.status)) {
5918
- return finalStatus;
5897
+ return input.dashboardUrl ? { ...finalStatus, dashboardUrl: input.dashboardUrl } : finalStatus;
5919
5898
  }
5920
5899
  }
5921
5900
  }
@@ -5943,6 +5922,10 @@ async function waitForPlayCompletionByStream(input) {
5943
5922
  }
5944
5923
  async function startAndWaitForPlayCompletionByStream(input) {
5945
5924
  const startedAt = Date.now();
5925
+ const dashboardUrl = buildPlayDashboardUrl(
5926
+ input.client.baseUrl,
5927
+ input.playName
5928
+ );
5946
5929
  const state = {
5947
5930
  lastLogIndex: 0,
5948
5931
  emittedRunnerStarted: false
@@ -5973,7 +5956,6 @@ async function startAndWaitForPlayCompletionByStream(input) {
5973
5956
  }
5974
5957
  const workflowId = lastKnownWorkflowId || "pending";
5975
5958
  if (workflowId !== "pending" && !emittedDashboardUrl) {
5976
- const dashboardUrl = getDashboardUrlFromLiveEvent(event) ?? buildPlayDashboardUrl(input.client.baseUrl, input.playName);
5977
5959
  openPlayDashboard({
5978
5960
  dashboardUrl,
5979
5961
  jsonOutput: input.jsonOutput,
@@ -6022,7 +6004,7 @@ async function startAndWaitForPlayCompletionByStream(input) {
6022
6004
  firstRunIdMs,
6023
6005
  lastPhase
6024
6006
  });
6025
- return finalStatus;
6007
+ return { ...finalStatus, dashboardUrl };
6026
6008
  }
6027
6009
  }
6028
6010
  } catch (error) {
@@ -6059,6 +6041,7 @@ async function startAndWaitForPlayCompletionByStream(input) {
6059
6041
  return waitForPlayCompletionByStream({
6060
6042
  client: input.client,
6061
6043
  workflowId: lastKnownWorkflowId,
6044
+ dashboardUrl,
6062
6045
  jsonOutput: input.jsonOutput,
6063
6046
  emitLogs: input.emitLogs,
6064
6047
  waitTimeoutMs: input.waitTimeoutMs,
@@ -6093,6 +6076,7 @@ async function startAndWaitForPlayCompletionByStream(input) {
6093
6076
  return waitForPlayCompletionByStream({
6094
6077
  client: input.client,
6095
6078
  workflowId: lastKnownWorkflowId,
6079
+ dashboardUrl,
6096
6080
  jsonOutput: input.jsonOutput,
6097
6081
  emitLogs: input.emitLogs,
6098
6082
  waitTimeoutMs: input.waitTimeoutMs,
@@ -6263,12 +6247,16 @@ function buildRunWarnings(status, rowsInfo) {
6263
6247
  }
6264
6248
  return [];
6265
6249
  }
6266
- function buildRunNextCommands(runId) {
6267
- return {
6250
+ function buildRunNextCommands(runId, dashboardUrl) {
6251
+ const commands = {
6268
6252
  get: `deepline runs get ${runId} --json`,
6269
6253
  stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
6270
6254
  logs: `deepline runs logs ${runId} --out run.log --json`
6271
6255
  };
6256
+ if (dashboardUrl) {
6257
+ commands.open = `Open ${dashboardUrl} to see results.`;
6258
+ }
6259
+ return commands;
6272
6260
  }
6273
6261
  var RUN_LOG_PREVIEW_LIMIT = 20;
6274
6262
  function getRecordField(value, key) {
@@ -6404,6 +6392,7 @@ function compactPlayStatus(status) {
6404
6392
  apiVersion: status.apiVersion ?? 1,
6405
6393
  ...typeof status.name === "string" ? { name: status.name } : {},
6406
6394
  ...typeof status.playName === "string" ? { playName: status.playName } : {},
6395
+ ...status.dashboardUrl ? { dashboardUrl: status.dashboardUrl } : {},
6407
6396
  status: status.status,
6408
6397
  run: normalizeRunStatusForEnvelope(status),
6409
6398
  progress: normalizeProgressForEnvelope(status, rowsInfo),
@@ -6416,7 +6405,7 @@ function compactPlayStatus(status) {
6416
6405
  ...status.resultView ? { resultView: status.resultView } : {},
6417
6406
  ...datasetStats ? { dataset_stats: datasetStats } : {},
6418
6407
  ...billing ? { billing } : {},
6419
- next: buildRunNextCommands(status.runId)
6408
+ next: buildRunNextCommands(status.runId, status.dashboardUrl)
6420
6409
  };
6421
6410
  }
6422
6411
  function enrichPlayStatusWithDatasetStats(status) {
@@ -6629,7 +6618,7 @@ async function exportPlayStatusRows(client, status, outPath, options = {}) {
6629
6618
  return null;
6630
6619
  }
6631
6620
  const availableRows = collectSerializedDatasetRowsInfos(status);
6632
- let rowsInfo = options.datasetPath ? availableRows.find((info) => info.source === options.datasetPath) ?? null : availableRows.length === 1 ? availableRows[0] : null;
6621
+ const rowsInfo = options.datasetPath ? availableRows.find((info) => info.source === options.datasetPath) ?? null : availableRows.length === 1 ? availableRows[0] : null;
6633
6622
  if (!rowsInfo && options.datasetPath) {
6634
6623
  const available = availableRows.map((info) => info.source).filter((source) => typeof source === "string");
6635
6624
  throw new DeeplineError(
@@ -7112,8 +7101,7 @@ async function handleFileBackedRun(options) {
7112
7101
  { targetKind: "file", playName },
7113
7102
  () => client.startPlayRun(startRequest)
7114
7103
  );
7115
- const dashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
7116
- const resolvedDashboardUrl = started.dashboardUrl ?? dashboardUrl;
7104
+ const resolvedDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
7117
7105
  openPlayDashboard({
7118
7106
  dashboardUrl: resolvedDashboardUrl,
7119
7107
  jsonOutput: options.jsonOutput,
@@ -7248,11 +7236,7 @@ async function handleNamedRun(options) {
7248
7236
  { targetKind: "name", playName },
7249
7237
  () => client.startPlayRun(startRequest)
7250
7238
  );
7251
- const dashboardUrl = buildPlayDashboardUrl(
7252
- client.baseUrl,
7253
- playName
7254
- );
7255
- const resolvedDashboardUrl = started.dashboardUrl ?? dashboardUrl;
7239
+ const resolvedDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
7256
7240
  openPlayDashboard({
7257
7241
  dashboardUrl: resolvedDashboardUrl,
7258
7242
  jsonOutput: options.jsonOutput,
@@ -9251,7 +9235,7 @@ async function executeTool(args) {
9251
9235
  }
9252
9236
 
9253
9237
  // src/cli/commands/update.ts
9254
- var import_node_child_process2 = require("child_process");
9238
+ var import_node_child_process = require("child_process");
9255
9239
  var import_node_fs10 = require("fs");
9256
9240
  var import_node_path12 = require("path");
9257
9241
  function posixShellQuote(value) {
@@ -9303,7 +9287,7 @@ function resolveUpdatePlan() {
9303
9287
  }
9304
9288
  function runCommand(command, args) {
9305
9289
  return new Promise((resolveExitCode) => {
9306
- const child = (0, import_node_child_process2.spawn)(command, args, {
9290
+ const child = (0, import_node_child_process.spawn)(command, args, {
9307
9291
  stdio: "inherit",
9308
9292
  shell: process.platform === "win32",
9309
9293
  env: process.env
@@ -9376,7 +9360,7 @@ Examples:
9376
9360
  }
9377
9361
 
9378
9362
  // src/cli/skills-sync.ts
9379
- var import_node_child_process3 = require("child_process");
9363
+ var import_node_child_process2 = require("child_process");
9380
9364
  var import_node_fs11 = require("fs");
9381
9365
  var import_node_os8 = require("os");
9382
9366
  var import_node_path13 = require("path");
@@ -9468,7 +9452,7 @@ function buildBunxSkillsInstallArgs(baseUrl) {
9468
9452
  ];
9469
9453
  }
9470
9454
  function hasCommand(command) {
9471
- const result = (0, import_node_child_process3.spawnSync)(command, ["--version"], {
9455
+ const result = (0, import_node_child_process2.spawnSync)(command, ["--version"], {
9472
9456
  stdio: "ignore",
9473
9457
  shell: process.platform === "win32"
9474
9458
  });
@@ -9499,7 +9483,7 @@ function resolveSkillsInstallCommands(baseUrl) {
9499
9483
  }
9500
9484
  function runOneSkillsInstall(install) {
9501
9485
  return new Promise((resolve10) => {
9502
- const child = (0, import_node_child_process3.spawn)(install.command, install.args, {
9486
+ const child = (0, import_node_child_process2.spawn)(install.command, install.args, {
9503
9487
  stdio: ["ignore", "ignore", "pipe"],
9504
9488
  env: process.env
9505
9489
  });