skydive-cli 0.5.0 → 0.6.0-beta.3

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/js/bin.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import { c as version, s as name, t as createRestClient } from "./rest-B9__Zsuk.mjs";
3
- import { B as resolveSession$1, C as deleteConfig, D as getPreference, E as getLastSeenVersion, G as setLastSeenVersion, H as saveConfig, I as resolveAppUrl, L as resolveChatAuth, M as getStoredApiKeyId, N as getStoredApiKeyWorkspaceName, O as getPromptHistoryPath, P as getUpdateCheckDisabled, R as resolveConfig, S as PREFERENCES, T as getDefaultAgent, U as saveSession, _ as API_KEYS_URL, b as DEFAULT_API_URL, i as resolveAgent$1, j as getShareMachineDefault, v as API_KEY_FAMILY_PREFIX, w as getConfigPath, y as API_KEY_PREFIX, z as resolveManagementAuth } from "./print-D_UEjdSw.mjs";
4
- import { A as installAgentAlias, B as resolveWorkspaceId, D as machineOsFromPlatform, E as buildImportSeedPrompt, F as detectShell, I as ensureActiveOrganization, L as getActiveWorkspaceId, M as takenAliasNames, N as SUPPORTED_SHELLS, O as aliasActivationHint, P as defaultInstallEnv, R as getSessionIdentity, V as setActiveWorkspace, c as getAutomaticLogsDir, j as slugifyAliasName, k as dedupeAliasName, l as maybeStartProfiling, o as brandHelpArt, t as installCrashHandler, u as profileDir, w as themes, z as listWorkspaces } from "./install-CtAVvERm.mjs";
2
+ import { c as version, s as name, t as createRestClient } from "./rest-ChXt8YGA.mjs";
3
+ import { B as resolveSession$1, C as deleteConfig, D as getPreference, E as getLastSeenVersion, G as setLastSeenVersion, H as saveConfig, I as resolveAppUrl, L as resolveChatAuth, M as getStoredApiKeyId, N as getStoredApiKeyWorkspaceName, O as getPromptHistoryPath, P as getUpdateCheckDisabled, R as resolveConfig, S as PREFERENCES, T as getDefaultAgent, U as saveSession, _ as API_KEYS_URL, b as DEFAULT_API_URL, i as resolveAgent$1, j as getShareMachineDefault, v as API_KEY_FAMILY_PREFIX, w as getConfigPath, y as API_KEY_PREFIX, z as resolveManagementAuth } from "./print-B3yhPm0N.mjs";
4
+ import { A as installAgentAlias, B as resolveWorkspaceId, D as machineOsFromPlatform, E as buildImportSeedPrompt, F as detectShell, I as ensureActiveOrganization, L as getActiveWorkspaceId, M as takenAliasNames, N as SUPPORTED_SHELLS, O as aliasActivationHint, P as defaultInstallEnv, R as getSessionIdentity, V as setActiveWorkspace, c as getAutomaticLogsDir, j as slugifyAliasName, k as dedupeAliasName, l as maybeStartProfiling, o as brandHelpArt, t as installCrashHandler, u as profileDir, w as themes, z as listWorkspaces } from "./install-D7MwRv0n.mjs";
5
5
  import { n as printError, r as printTable, t as output } from "./output-C9mb3sUB.mjs";
6
6
  import { t as HttpError } from "./http-error-BF2NZZE3.mjs";
7
7
  import "./billing-blocked-D3l5kJlX.mjs";
8
- import { a as resolveMachineIdentity } from "./client--k9cjfkX.mjs";
8
+ import { a as resolveMachineIdentity } from "./client-CErCpXl9.mjs";
9
9
  import "./tls-cert-Rua2oV7n.mjs";
10
10
  import { i as grantPortalAccess, n as fetchPortalDevices, o as registerPortalDevice, r as findThisDevice, s as revokePortalAccess } from "./api-BFQ4PQDA.mjs";
11
- import { d as daemonPaths, i as queryDaemonStatus, n as ensureDaemonRunning, s as stopDaemon, u as PORTAL_DAEMON_FLAG } from "./daemon-Do1jU2UF.mjs";
11
+ import { d as daemonPaths, i as queryDaemonStatus, n as ensureDaemonRunning, s as stopDaemon, u as PORTAL_DAEMON_FLAG } from "./daemon-Cuy4S6Rw.mjs";
12
12
  import { a as encodeFsRequest, i as decodeFsResponse, n as SANDBOX_STREAM_PATH, r as streamSpecToQuery, t as SandboxStream } from "./client-Btq6bMzX.mjs";
13
13
  import { hideBin } from "yargs/helpers";
14
14
  import yargs from "yargs";
@@ -1584,7 +1584,7 @@ const importCommand = {
1584
1584
  process.exit(1);
1585
1585
  }
1586
1586
  }
1587
- const { runChat } = await import("./boot-DD4T-61U.mjs");
1587
+ const { runChat } = await import("./boot-CleHADz9.mjs");
1588
1588
  await runChat({
1589
1589
  appUrl,
1590
1590
  sessionToken: session.value.sessionToken,
@@ -1614,7 +1614,7 @@ async function runImportPrintMode({ argv, appUrl }) {
1614
1614
  printError(`${session.error.message} For non-interactive use, run \`skydive auth login --web\` first, or set SKYDIVE_SESSION_TOKEN.`);
1615
1615
  process.exit(1);
1616
1616
  }
1617
- const { connectMachineShare } = await import("./print-share-Cz0EO2RK.mjs");
1617
+ const { connectMachineShare } = await import("./print-share-DfTHn1HL.mjs");
1618
1618
  const machineShare = await connectMachineShare({
1619
1619
  appUrl,
1620
1620
  sessionToken: session.value.sessionToken,
@@ -1622,7 +1622,7 @@ async function runImportPrintMode({ argv, appUrl }) {
1622
1622
  });
1623
1623
  const extra = (argv.print ?? "").trim();
1624
1624
  const prompt = buildImportSeedPrompt(process.cwd(), machineOsFromPlatform(process.platform)) + (extra ? `\n\nAdditional instructions: ${extra}` : "");
1625
- const { runPrint } = await import("./print-Bx8qUC9U.mjs");
1625
+ const { runPrint } = await import("./print-VulYMc7x.mjs");
1626
1626
  try {
1627
1627
  const result = await runPrint({
1628
1628
  appUrl,
@@ -2029,7 +2029,7 @@ const chatCommand = {
2029
2029
  sessionToken: auth.value.token,
2030
2030
  agentSelector: argv.agent ?? null
2031
2031
  });
2032
- const { runChat } = await import("./boot-DD4T-61U.mjs");
2032
+ const { runChat } = await import("./boot-CleHADz9.mjs");
2033
2033
  await runChat({
2034
2034
  appUrl: auth.value.appUrl,
2035
2035
  sessionToken: auth.value.token,
@@ -2113,7 +2113,7 @@ async function runPrintMode({ argv, appUrl }) {
2113
2113
  printError(`${auth.error.message} For non-interactive use, run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
2114
2114
  process.exit(1);
2115
2115
  }
2116
- const { runPrint, readStdin } = await import("./print-Bx8qUC9U.mjs");
2116
+ const { runPrint, readStdin } = await import("./print-VulYMc7x.mjs");
2117
2117
  const workspaceId = await resolveWorkspaceFlag({
2118
2118
  appUrl,
2119
2119
  sessionToken: auth.value.token,
@@ -2143,7 +2143,7 @@ async function runPrintMode({ argv, appUrl }) {
2143
2143
  process.exit(1);
2144
2144
  }
2145
2145
  } else {
2146
- const { connectMachineShare } = await import("./print-share-Cz0EO2RK.mjs");
2146
+ const { connectMachineShare } = await import("./print-share-DfTHn1HL.mjs");
2147
2147
  machineShare = await connectMachineShare({
2148
2148
  appUrl: auth.value.appUrl,
2149
2149
  sessionToken: auth.value.token,
@@ -2208,7 +2208,7 @@ const getCommand$1 = {
2208
2208
  printError(`${auth.error.message} Run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
2209
2209
  process.exit(1);
2210
2210
  }
2211
- const { messageGet } = await import("./print-Bx8qUC9U.mjs");
2211
+ const { messageGet } = await import("./print-VulYMc7x.mjs");
2212
2212
  try {
2213
2213
  const result = await messageGet({
2214
2214
  appUrl: auth.value.appUrl,
@@ -2411,7 +2411,7 @@ const switchCommand = {
2411
2411
  printError("The workspace picker needs the Bun runtime and it could not be set up automatically. Pass a workspace slug instead, or install Bun and retry.");
2412
2412
  process.exit(1);
2413
2413
  }
2414
- const { runWorkspacePicker } = await import("./boot-DD4T-61U.mjs");
2414
+ const { runWorkspacePicker } = await import("./boot-CleHADz9.mjs");
2415
2415
  await runWorkspacePicker(session);
2416
2416
  return;
2417
2417
  }
@@ -2490,7 +2490,7 @@ const openCommand = {
2490
2490
  const agent = argv.agent ? resolveAgent$1((await fetchPortalDevices(session)).agents, argv.agent) : null;
2491
2491
  const cwd = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
2492
2492
  const { machineName } = await resolveMachineIdentity(null);
2493
- const { PortalDaemonClient } = await import("./daemon-client-C7nE-lLK.mjs");
2493
+ const { PortalDaemonClient } = await import("./daemon-client-BVX-rWfX.mjs");
2494
2494
  let lastLine = "";
2495
2495
  let signalConnected;
2496
2496
  const connected = new Promise((resolve) => {
@@ -2847,8 +2847,8 @@ const sandboxCommand = {
2847
2847
  }).example("skydive sandbox --agent grace", "Live terminal (Ctrl-] detaches)").example("skydive sandbox --agent grace -- tail -n 50 /tmp/harness.log", "One-shot command (use `--` so its flags reach the sandbox)").example("skydive sandbox --agent grace -- sh -c 'ls /tmp | wc -l'", "Shell features go through an explicit `sh -c`"),
2848
2848
  handler: async (argv) => {
2849
2849
  const session = requireSession(argv);
2850
- const { createRestClient } = await import("./rest-Dc0EEok3.mjs");
2851
- const { resolveAgent } = await import("./print-Bx8qUC9U.mjs");
2850
+ const { createRestClient } = await import("./rest-CRRpVyot.mjs");
2851
+ const { resolveAgent } = await import("./print-VulYMc7x.mjs");
2852
2852
  const client = createRestClient({
2853
2853
  appUrl: session.appUrl,
2854
2854
  sessionToken: session.sessionToken
@@ -3119,8 +3119,8 @@ const fsEditCommand = {
3119
3119
  handler: async (argv) => {
3120
3120
  const session = requireSession(argv);
3121
3121
  const remotePath = argv.path;
3122
- const { createRestClient } = await import("./rest-Dc0EEok3.mjs");
3123
- const { resolveAgent } = await import("./print-Bx8qUC9U.mjs");
3122
+ const { createRestClient } = await import("./rest-CRRpVyot.mjs");
3123
+ const { resolveAgent } = await import("./print-VulYMc7x.mjs");
3124
3124
  const client = createRestClient({
3125
3125
  appUrl: session.appUrl,
3126
3126
  sessionToken: session.sessionToken
@@ -4573,7 +4573,7 @@ if (process.argv.includes(UPDATE_WORKER_FLAG)) {
4573
4573
  process.exit(0);
4574
4574
  }
4575
4575
  if (process.argv.includes(PORTAL_DAEMON_FLAG)) {
4576
- const { runPortalDaemon } = await import("./daemon-CCgNLD0H.mjs");
4576
+ const { runPortalDaemon } = await import("./daemon-BuTGs-Ns.mjs");
4577
4577
  runPortalDaemon(process.argv);
4578
4578
  } else runCli();
4579
4579
  function runCli() {
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { a as errorMessage, i as sendErrorMessage, n as errorDetail, o as isRecord, t as createRestClient } from "./rest-B9__Zsuk.mjs";
3
- import { A as getSavedTheme, F as recordDefaultAgent, V as resolveWebUrl, W as saveTheme, b as DEFAULT_API_URL, c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, g as specKeyFor, h as parseConnectCard, i as resolveAgent, k as getReviewStateDir, l as parseExternalOauthConnectParams, m as computeSettledLabel, p as composeQuestionAnswer, s as MASK_CHAR, u as parseOauthConnectParams, w as getConfigPath, x as DEFAULT_APP_URL } from "./print-D_UEjdSw.mjs";
4
- import { A as installAgentAlias, C as themeVersion, D as machineOsFromPlatform, F as detectShell, L as getActiveWorkspaceId, M as takenAliasNames, O as aliasActivationHint, S as themeModeFromColorFgBg, T as themesForMode, V as setActiveWorkspace, _ as monoTheme, a as WORDMARK, b as themeForMode, d as profilingEnabled, f as record, g as findTheme, h as applyTheme, i as MARK_CELLS, j as slugifyAliasName, k as dedupeAliasName, m as DEFAULT_THEME_ID, n as buildCrashReport, p as writeArtifact, r as writeCrashReport, s as splashFitsWidth, t as installCrashHandler, v as noColorRequested, x as themeMode, y as theme, z as listWorkspaces } from "./install-CtAVvERm.mjs";
2
+ import { a as errorMessage, i as sendErrorMessage, n as errorDetail, o as isRecord, t as createRestClient } from "./rest-ChXt8YGA.mjs";
3
+ import { A as getSavedTheme, F as recordDefaultAgent, V as resolveWebUrl, W as saveTheme, b as DEFAULT_API_URL, c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, g as specKeyFor, h as parseConnectCard, i as resolveAgent, k as getReviewStateDir, l as parseExternalOauthConnectParams, m as computeSettledLabel, p as composeQuestionAnswer, s as MASK_CHAR, u as parseOauthConnectParams, w as getConfigPath, x as DEFAULT_APP_URL } from "./print-B3yhPm0N.mjs";
4
+ import { A as installAgentAlias, C as themeVersion, D as machineOsFromPlatform, F as detectShell, L as getActiveWorkspaceId, M as takenAliasNames, O as aliasActivationHint, S as themeModeFromColorFgBg, T as themesForMode, V as setActiveWorkspace, _ as monoTheme, a as WORDMARK, b as themeForMode, d as profilingEnabled, f as record, g as findTheme, h as applyTheme, i as MARK_CELLS, j as slugifyAliasName, k as dedupeAliasName, m as DEFAULT_THEME_ID, n as buildCrashReport, p as writeArtifact, r as writeCrashReport, s as splashFitsWidth, t as installCrashHandler, v as noColorRequested, x as themeMode, y as theme, z as listWorkspaces } from "./install-D7MwRv0n.mjs";
5
5
  import { t as HttpError } from "./http-error-BF2NZZE3.mjs";
6
6
  import { i as billingBlockedOutcomeFromSendResponse } from "./billing-blocked-D3l5kJlX.mjs";
7
- import { t as PortalClient } from "./client--k9cjfkX.mjs";
7
+ import { t as PortalClient } from "./client-CErCpXl9.mjs";
8
8
  import { t as defaultTlsCertSource } from "./tls-cert-Rua2oV7n.mjs";
9
9
  import "./api-BFQ4PQDA.mjs";
10
- import "./daemon-Do1jU2UF.mjs";
10
+ import "./daemon-Cuy4S6Rw.mjs";
11
11
  import { t as SandboxStream } from "./client-Btq6bMzX.mjs";
12
- import { t as PortalDaemonClient } from "./daemon-client-Dvad009G.mjs";
12
+ import { t as PortalDaemonClient } from "./daemon-client-b10uck-j.mjs";
13
13
  import { t as runRawPtyPassthrough } from "./raw-pty-D5PhKZSl.mjs";
14
14
  import * as os$1 from "node:os";
15
15
  import { homedir, platform, release, tmpdir } from "node:os";
@@ -160,7 +160,7 @@ function errorMessage(err) {
160
160
  * the commit time of the built tree is one monotonic clock they share.
161
161
  */
162
162
  function portalDaemonBuild() {
163
- return "1787785513";
163
+ return "1787865403";
164
164
  }
165
165
  /**
166
166
  * Whether a client carrying `mine` should replace a running daemon carrying
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { t as PortalClient } from "./client-CErCpXl9.mjs";
3
+ import "./api-BFQ4PQDA.mjs";
4
+
5
+ export { PortalClient };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import "./client--k9cjfkX.mjs";
2
+ import "./client-CErCpXl9.mjs";
3
3
  import "./tls-cert-Rua2oV7n.mjs";
4
4
  import "./api-BFQ4PQDA.mjs";
5
- import { a as runPortalDaemon, c as stopDaemonForHandoff, i as queryDaemonStatus, n as ensureDaemonRunning, o as startPortalDaemon, r as isDaemonListening, s as stopDaemon, t as PortalDaemon } from "./daemon-Do1jU2UF.mjs";
5
+ import { a as runPortalDaemon, c as stopDaemonForHandoff, i as queryDaemonStatus, n as ensureDaemonRunning, o as startPortalDaemon, r as isDaemonListening, s as stopDaemon, t as PortalDaemon } from "./daemon-Cuy4S6Rw.mjs";
6
6
 
7
7
  export { runPortalDaemon };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as isRecord, n as isNewerBuild, r as portalDaemonBuild, t as PortalClient } from "./client--k9cjfkX.mjs";
2
+ import { i as isRecord, n as isNewerBuild, r as portalDaemonBuild, t as PortalClient } from "./client-CErCpXl9.mjs";
3
3
  import { t as defaultTlsCertSource } from "./tls-cert-Rua2oV7n.mjs";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
@@ -7,7 +7,7 @@ import { z } from "zod";
7
7
  import { spawn } from "node:child_process";
8
8
  import { createHash } from "node:crypto";
9
9
  import { connect, createServer } from "node:net";
10
- import { access, appendFile, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
10
+ import { access, appendFile, mkdir, readFile, stat, unlink, writeFile } from "node:fs/promises";
11
11
 
12
12
  //#region ../portal-daemon/src/local-protocol.ts
13
13
  /**
@@ -336,6 +336,14 @@ function parseDaemonMessage(line) {
336
336
  /** Grace period after the last client detaches before the daemon exits. */
337
337
  const IDLE_SHUTDOWN_MS = 3e4;
338
338
  /**
339
+ * Cadence of the socket-ownership watch (see checkSocketOwnership). The
340
+ * singleton is enforced ONLY through the socket path, so this bounds how long
341
+ * a daemon can keep dialing after losing the path — the window in which a
342
+ * spawner can create a duplicate dialer. The server-side connect flap guard
343
+ * caps the damage inside that window.
344
+ */
345
+ const SOCKET_WATCH_TICK_MS = 3e4;
346
+ /**
339
347
  * Upper bound an incumbent daemon waits, during a graceful handoff, for the
340
348
  * successor to dial out and claim presence before giving up and releasing the
341
349
  * slot itself. Comfortably above a normal successor boot + WS dial (a few
@@ -358,17 +366,24 @@ var PortalDaemon = class {
358
366
  handingOff = false;
359
367
  sessionToken = null;
360
368
  deviceToken = null;
361
- constructor(appUrl) {
369
+ socketWatch = null;
370
+ socketId = null;
371
+ socketCheckRunning = false;
372
+ socketWatchTickMs;
373
+ exit;
374
+ constructor(appUrl, options) {
362
375
  this.appUrl = canonicalPortalAppUrl(appUrl);
363
376
  this.paths = daemonPaths(this.appUrl);
364
377
  this.fallbackCwd = process.env.HOME ?? process.cwd();
378
+ this.socketWatchTickMs = options?.socketWatchTickMs ?? SOCKET_WATCH_TICK_MS;
379
+ this.exit = options?.exit ?? (() => process.exit(0));
365
380
  }
366
381
  /** Start listening. Rejects if the socket is already held by another daemon. */
367
382
  async listen() {
368
383
  await mkdir(this.paths.dir, { recursive: true });
369
384
  await this.loadState();
370
385
  await this.clearStaleSocket();
371
- return new Promise((resolve, reject) => {
386
+ await new Promise((resolve, reject) => {
372
387
  const server = createServer((socket) => this.onClientConnect(socket));
373
388
  server.on("error", reject);
374
389
  server.listen(this.paths.socketPath, () => {
@@ -377,6 +392,102 @@ var PortalDaemon = class {
377
392
  resolve();
378
393
  });
379
394
  });
395
+ await this.recordSocketIdentity();
396
+ this.startSocketWatch();
397
+ }
398
+ /**
399
+ * Remember which socket FILE we bound (dev+ino), so the watch can tell "our
400
+ * file is still there" from "someone re-created the path" — the path alone
401
+ * can't: a duplicate daemon's fresh socket lives at the identical path.
402
+ */
403
+ async recordSocketIdentity() {
404
+ const s = await stat(this.paths.socketPath);
405
+ this.socketId = {
406
+ dev: s.dev,
407
+ ino: s.ino
408
+ };
409
+ }
410
+ /**
411
+ * The singleton is enforced ONLY through the socket path — status, stop, and
412
+ * newest-build takeover all reach a daemon by connecting to it. If the file
413
+ * vanishes under us (the OS tmp cleaner purges /var/folders temp items that
414
+ * haven't been touched in days; the daemon holds the bound fd and never
415
+ * notices), this daemon keeps its portal connection and its attached clients
416
+ * but becomes unreachable and unmanageable: every new `skydive` spawn finds
417
+ * no socket and starts a DUPLICATE daemon, and the two fight a supersede war
418
+ * over the machine's presence slot (the 2026-08-25 flap loop). So the daemon
419
+ * re-verifies its claim on a timer: re-bind if the path is free, stand down
420
+ * if another daemon beat us to it.
421
+ */
422
+ startSocketWatch() {
423
+ this.socketWatch = setInterval(() => {
424
+ if (this.socketCheckRunning) return;
425
+ this.socketCheckRunning = true;
426
+ this.checkSocketOwnership().catch((error) => this.logError("socket ownership check failed", error)).finally(() => {
427
+ this.socketCheckRunning = false;
428
+ });
429
+ }, this.socketWatchTickMs);
430
+ this.socketWatch.unref();
431
+ }
432
+ stopSocketWatch() {
433
+ if (this.socketWatch) {
434
+ clearInterval(this.socketWatch);
435
+ this.socketWatch = null;
436
+ }
437
+ }
438
+ async checkSocketOwnership() {
439
+ if (!this.server || this.handingOff) return;
440
+ const current = await stat(this.paths.socketPath).catch(() => null);
441
+ if (current && this.socketId && current.dev === this.socketId.dev && current.ino === this.socketId.ino) return;
442
+ if (current) {
443
+ this.logInfo("control socket path is owned by another daemon; standing down");
444
+ this.standDown();
445
+ return;
446
+ }
447
+ try {
448
+ await this.rebindSocket();
449
+ this.logInfo("control socket file vanished (tmp cleaner?); re-bound");
450
+ } catch (error) {
451
+ this.logError("control socket re-bind failed; standing down", error);
452
+ this.standDown();
453
+ }
454
+ }
455
+ /**
456
+ * Re-create the socket file at our path. The old server object keeps serving
457
+ * its ESTABLISHED connections (close() only stops accepting), so attached
458
+ * clients ride through the swap; only the listener moves to the new file.
459
+ */
460
+ async rebindSocket() {
461
+ const old = this.server;
462
+ this.server = null;
463
+ old?.close();
464
+ await mkdir(this.paths.dir, { recursive: true });
465
+ await new Promise((resolve, reject) => {
466
+ const server = createServer((socket) => this.onClientConnect(socket));
467
+ server.on("error", reject);
468
+ server.listen(this.paths.socketPath, () => {
469
+ this.server = server;
470
+ resolve();
471
+ });
472
+ });
473
+ await this.recordSocketIdentity();
474
+ }
475
+ /**
476
+ * This daemon lost the singleton claim: tear everything down and exit.
477
+ * Attached clients see their control socket close and re-attach through the
478
+ * path — reaching whichever daemon owns it now (or spawning a fresh one) —
479
+ * so the machine converges on a single dialer instead of a supersede war.
480
+ */
481
+ standDown() {
482
+ this.stopSocketWatch();
483
+ for (const conn of this.conns) try {
484
+ conn.socket.destroy();
485
+ } catch (_error) {}
486
+ this.conns.clear();
487
+ this.client?.dispose();
488
+ this.server?.close();
489
+ this.server = null;
490
+ this.exit();
380
491
  }
381
492
  async clearStaleSocket() {
382
493
  if (!await pathExists(this.paths.socketPath)) return;
@@ -560,15 +671,17 @@ var PortalDaemon = class {
560
671
  /** Close the listener without exiting the process (tests own the process). */
561
672
  stopForTest() {
562
673
  this.clearIdleTimer();
674
+ this.stopSocketWatch();
563
675
  this.client?.dispose();
564
676
  this.server?.close();
565
677
  this.server = null;
566
678
  }
567
679
  shutdown() {
568
680
  if (this.conns.size > 0) return;
681
+ this.stopSocketWatch();
569
682
  this.client?.dispose();
570
683
  this.server?.close();
571
- process.exit(0);
684
+ this.exit();
572
685
  }
573
686
  /** Snapshot for a `status` control request. */
574
687
  statusResult() {
@@ -597,13 +710,7 @@ var PortalDaemon = class {
597
710
  * their live exec/tunnel relays), drops the portal connection, and exits.
598
711
  */
599
712
  forceShutdown() {
600
- for (const conn of this.conns) try {
601
- conn.socket.destroy();
602
- } catch (_error) {}
603
- this.conns.clear();
604
- this.client?.dispose();
605
- this.server?.close();
606
- process.exit(0);
713
+ this.standDown();
607
714
  }
608
715
  /**
609
716
  * Graceful takeover step-down (a newer daemon is replacing us). Unlike
@@ -624,6 +731,7 @@ var PortalDaemon = class {
624
731
  async gracefulHandoffShutdown() {
625
732
  if (this.handingOff) return;
626
733
  this.handingOff = true;
734
+ this.stopSocketWatch();
627
735
  for (const conn of this.conns) try {
628
736
  conn.socket.destroy();
629
737
  } catch (_error) {}
@@ -634,7 +742,7 @@ var PortalDaemon = class {
634
742
  await this.client.beginHandoff(HANDOFF_MAX_WAIT_MS);
635
743
  this.client.dispose();
636
744
  }
637
- process.exit(0);
745
+ this.exit();
638
746
  }
639
747
  async loadState() {
640
748
  try {
@@ -718,7 +826,7 @@ function runPortalDaemon(argv) {
718
826
  * CLI's re-exec entry above and the standalone `main.ts` entry.
719
827
  */
720
828
  function startPortalDaemon(appUrl) {
721
- return new PortalDaemon(appUrl).listen();
829
+ return new PortalDaemon(appUrl, null).listen();
722
830
  }
723
831
  async function isDaemonListening(socketPath) {
724
832
  if (!await pathExists(socketPath)) return false;
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import "./client-CErCpXl9.mjs";
3
+ import "./tls-cert-Rua2oV7n.mjs";
4
+ import "./api-BFQ4PQDA.mjs";
5
+ import "./daemon-Cuy4S6Rw.mjs";
6
+ import { t as PortalDaemonClient } from "./daemon-client-b10uck-j.mjs";
7
+
8
+ export { PortalDaemonClient };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { d as daemonPaths, f as encodeLine, l as LOCAL_PROTOCOL_VERSION, m as parseDaemonMessage, n as ensureDaemonRunning, p as makeLineParser } from "./daemon-Do1jU2UF.mjs";
2
+ import { d as daemonPaths, f as encodeLine, l as LOCAL_PROTOCOL_VERSION, m as parseDaemonMessage, n as ensureDaemonRunning, p as makeLineParser } from "./daemon-Cuy4S6Rw.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { connect } from "node:net";
5
5
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { c as version$1 } from "./rest-B9__Zsuk.mjs";
3
- import { w as getConfigPath } from "./print-D_UEjdSw.mjs";
2
+ import { c as version$1 } from "./rest-ChXt8YGA.mjs";
3
+ import { w as getConfigPath } from "./print-B3yhPm0N.mjs";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
6
  import { err, ok } from "neverthrow";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as errorMessage, i as sendErrorMessage, o as isRecord, t as createRestClient } from "./rest-B9__Zsuk.mjs";
2
+ import { a as errorMessage, i as sendErrorMessage, o as isRecord, t as createRestClient } from "./rest-ChXt8YGA.mjs";
3
3
  import { t as HttpError } from "./http-error-BF2NZZE3.mjs";
4
4
  import { i as billingBlockedOutcomeFromSendResponse, n as BillingBlockedError } from "./billing-blocked-D3l5kJlX.mjs";
5
5
  import path from "node:path";
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import "./rest-B9__Zsuk.mjs";
3
- import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-D_UEjdSw.mjs";
2
+ import "./rest-ChXt8YGA.mjs";
3
+ import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-B3yhPm0N.mjs";
4
4
  import "./billing-blocked-D3l5kJlX.mjs";
5
5
 
6
6
  export { messageGet, readStdin, resolveAgent, runPrint };
@@ -14,7 +14,7 @@ import { n as printError } from "./output-C9mb3sUB.mjs";
14
14
  * the reply (and to --json).
15
15
  */
16
16
  async function connectMachineShare({ appUrl, sessionToken, timeoutHint }) {
17
- const { PortalClient } = await import("./client-Ct0-JZSS.mjs");
17
+ const { PortalClient } = await import("./client-qY4nTj2Y.mjs");
18
18
  const { defaultTlsCertSource } = await import("./tls-cert-BpCaD5AT.mjs");
19
19
  let signalConnected;
20
20
  const connected = new Promise((resolve) => {
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as sendErrorMessage, n as errorDetail, r as filenameFromContentDisposition, t as createRestClient } from "./rest-B9__Zsuk.mjs";
2
+ import { i as sendErrorMessage, n as errorDetail, r as filenameFromContentDisposition, t as createRestClient } from "./rest-ChXt8YGA.mjs";
3
3
  import { t as HttpError } from "./http-error-BF2NZZE3.mjs";
4
4
  import "./billing-blocked-D3l5kJlX.mjs";
5
5
 
@@ -6,7 +6,7 @@ import { createParser } from "eventsource-parser";
6
6
 
7
7
  //#region package.json
8
8
  var name = "skydive-cli";
9
- var version = "0.5.0";
9
+ var version = "0.6.0-beta.3";
10
10
 
11
11
  //#endregion
12
12
  //#region src/chat/util.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.5.0",
3
+ "version": "0.6.0-beta.3",
4
4
  "description": "Skydive CLI — cloud agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "typecheck": "tsgo --noEmit"
31
31
  },
32
32
  "dependencies": {
33
- "@opentui/react": "0.4.3",
33
+ "@opentui/react": "0.5.8",
34
34
  "conf": "^13.0.1",
35
35
  "diff": "9.0.0",
36
36
  "eventsource-parser": "^3.0.8",
@@ -38,7 +38,7 @@
38
38
  "fuzzysort": "^3.1.0",
39
39
  "neverthrow": "^8.2.0",
40
40
  "open": "^10.1.0",
41
- "react": "^19.0.0",
41
+ "react": "^19.2.0",
42
42
  "react-devtools-core": "^7.0.1",
43
43
  "safe-stable-stringify": "^2.3.1",
44
44
  "semver": "^7.7.4",
@@ -70,9 +70,9 @@
70
70
  },
71
71
  "//optionalDependencies": "The per-platform binary packages (skydive-cli-<os>-<arch>) are INTENTIONALLY not committed here. They are published by release-skydive-cli-binaries.yml and injected — pinned to the exact version being published — into the published launcher's optionalDependencies at release time (scripts/prepare-platform-packages.mjs --pin-version, run in release-skydive-cli.yml). Committing them would make `yarn install` try to resolve versions that only exist post-publish, breaking local dev and CI. npm installs of a published skydive-cli still get them (os/cpu-gated); the launcher (src/launcher.ts) resolves and execs the matching one. Local dev never needs them: it runs the JS bundle or `build:binary` directly.",
72
72
  "optionalDependencies": {
73
- "skydive-cli-darwin-arm64": "0.5.0",
74
- "skydive-cli-darwin-x64": "0.5.0",
75
- "skydive-cli-linux-x64": "0.5.0",
76
- "skydive-cli-linux-arm64": "0.5.0"
73
+ "skydive-cli-darwin-arm64": "0.6.0-beta.3",
74
+ "skydive-cli-darwin-x64": "0.6.0-beta.3",
75
+ "skydive-cli-linux-x64": "0.6.0-beta.3",
76
+ "skydive-cli-linux-arm64": "0.6.0-beta.3"
77
77
  }
78
78
  }
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- import { t as PortalClient } from "./client--k9cjfkX.mjs";
3
- import "./api-BFQ4PQDA.mjs";
4
-
5
- export { PortalClient };
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./client--k9cjfkX.mjs";
3
- import "./tls-cert-Rua2oV7n.mjs";
4
- import "./api-BFQ4PQDA.mjs";
5
- import "./daemon-Do1jU2UF.mjs";
6
- import { t as PortalDaemonClient } from "./daemon-client-Dvad009G.mjs";
7
-
8
- export { PortalDaemonClient };