skydive-cli 0.4.0-beta.1324 → 0.4.0-beta.1340

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.
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { a as billingBlockedOutcomeSchema, i as billingBlockedOutcomeFromSendResponse, n as BillingBlockedError, r as billingBlockedJsonResult, t as BILLING_BLOCKED_EXIT_CODE } from "./billing-blocked-CwfG1BTR.mjs";
2
+ import { a as billingBlockedOutcomeSchema, i as billingBlockedOutcomeFromSendResponse, n as BillingBlockedError, r as billingBlockedJsonResult, t as BILLING_BLOCKED_EXIT_CODE } from "./billing-blocked-2wju4gC_.mjs";
3
3
 
4
4
  export { BILLING_BLOCKED_EXIT_CODE, BillingBlockedError, billingBlockedJsonResult };
package/dist/js/bin.mjs CHANGED
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { C as ensureActiveOrganization, D as setActiveWorkspace, E as listWorkspaces, O as name, T as getSessionIdentity, a as installCrashHandler, k as version, t as maybeStartProfiling, u as brandHelpArt, w as getActiveWorkspaceId, x as themes } from "./profiler-pZXBjTHx.mjs";
3
- import { A as getStoredApiKeyWorkspaceName, C as getLastSeenVersion, F as resolveManagementAuth, I as resolveSession, M as resolveAppUrl, N as resolveChatAuth, O as getShareMachineDefault, P as resolveConfig, R as saveConfig, S as getConfigPath, T as getPromptHistoryPath, V as setLastSeenVersion, _ as API_KEY_PREFIX, b as PREFERENCES, g as API_KEY_FAMILY_PREFIX, h as API_KEYS_URL, i as resolveAgent, j as getUpdateCheckDisabled, k as getStoredApiKeyId, v as DEFAULT_API_URL, w as getPreference, x as deleteConfig, z as saveSession } from "./print-BaXFFR3b.mjs";
2
+ import { C as ensureActiveOrganization, D as setActiveWorkspace, E as listWorkspaces, O as name, T as getSessionIdentity, a as installCrashHandler, k as version, t as maybeStartProfiling, u as brandHelpArt, w as getActiveWorkspaceId, x as themes } from "./profiler-BoPzxsbP.mjs";
3
+ import { A as getStoredApiKeyWorkspaceName, C as getLastSeenVersion, F as resolveManagementAuth, I as resolveSession, M as resolveAppUrl, N as resolveChatAuth, O as getShareMachineDefault, P as resolveConfig, R as saveConfig, S as getConfigPath, T as getPromptHistoryPath, V as setLastSeenVersion, _ as API_KEY_PREFIX, b as PREFERENCES, g as API_KEY_FAMILY_PREFIX, h as API_KEYS_URL, i as resolveAgent, j as getUpdateCheckDisabled, k as getStoredApiKeyId, v as DEFAULT_API_URL, w as getPreference, x as deleteConfig, z as saveSession } from "./print-CbayCa87.mjs";
4
4
  import { n as printError, r as printTable, t as output } from "./output-DYzzdXYV.mjs";
5
- import { n as createRestClient, t as HttpError } from "./rest-D89MGnWD.mjs";
6
- import "./billing-blocked-CwfG1BTR.mjs";
7
- import { a as registerPortalDevice, i as grantPortalAccess, n as fetchPortalDevices, o as revokePortalAccess, r as findThisDevice, s as machineIdentity } from "./client-yn4bC-OV.mjs";
8
- import { c as PORTAL_DAEMON_FLAG, i as queryDaemonStatus, l as daemonPaths, n as ensureDaemonRunning, o as stopDaemon } from "./daemon-C0Qr2WSJ.mjs";
9
- import { t as SandboxStream } from "./client-D8s9vY4p.mjs";
5
+ import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
6
+ import { t as createRestClient } from "./rest-BY2nADw5.mjs";
7
+ import "./billing-blocked-2wju4gC_.mjs";
8
+ import { a as registerPortalDevice, c as machineIdentity, i as grantPortalAccess, n as fetchPortalDevices, o as revokePortalAccess, r as findThisDevice } from "./client-Cf7cvftg.mjs";
9
+ import { c as PORTAL_DAEMON_FLAG, i as queryDaemonStatus, l as daemonPaths, n as ensureDaemonRunning, o as stopDaemon } from "./daemon-DaUaCMwC.mjs";
10
+ import { t as SandboxStream } from "./client-Cn2af31H.mjs";
10
11
  import { hideBin } from "yargs/helpers";
11
12
  import yargs from "yargs";
12
13
  import os, { hostname } from "node:os";
@@ -1514,7 +1515,7 @@ const importCommand = {
1514
1515
  process.exit(1);
1515
1516
  }
1516
1517
  }
1517
- const { runChat } = await import("./boot-BmzC25Rc.mjs");
1518
+ const { runChat } = await import("./boot-Cz7I47B-.mjs");
1518
1519
  await runChat({
1519
1520
  appUrl,
1520
1521
  sessionToken: session.value.sessionToken,
@@ -1541,7 +1542,7 @@ async function runImportPrintMode({ argv, appUrl }) {
1541
1542
  printError(`${session.error.message} For non-interactive use, run \`skydive auth login --web\` first, or set SKYDIVE_SESSION_TOKEN.`);
1542
1543
  process.exit(1);
1543
1544
  }
1544
- const { connectMachineShare } = await import("./print-share-mBbyQQDb.mjs");
1545
+ const { connectMachineShare } = await import("./print-share-CT4ltX7u.mjs");
1545
1546
  const machineShare = await connectMachineShare({
1546
1547
  appUrl,
1547
1548
  sessionToken: session.value.sessionToken,
@@ -1549,7 +1550,7 @@ async function runImportPrintMode({ argv, appUrl }) {
1549
1550
  });
1550
1551
  const extra = (argv.print ?? "").trim();
1551
1552
  const prompt = buildImportSeedPrompt(process.cwd()) + (extra ? `\n\nAdditional instructions: ${extra}` : "");
1552
- const { runPrint } = await import("./print-CvB9w4jf.mjs");
1553
+ const { runPrint } = await import("./print-ba_0hiV9.mjs");
1553
1554
  try {
1554
1555
  const result = await runPrint({
1555
1556
  appUrl,
@@ -1926,7 +1927,7 @@ const chatCommand = {
1926
1927
  sessionToken: auth.value.token,
1927
1928
  agentSelector: argv.agent ?? null
1928
1929
  });
1929
- const { runChat } = await import("./boot-BmzC25Rc.mjs");
1930
+ const { runChat } = await import("./boot-Cz7I47B-.mjs");
1930
1931
  await runChat({
1931
1932
  appUrl: auth.value.appUrl,
1932
1933
  sessionToken: auth.value.token,
@@ -1993,7 +1994,7 @@ async function runPrintMode({ argv, appUrl }) {
1993
1994
  printError(`${auth.error.message} For non-interactive use, run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
1994
1995
  process.exit(1);
1995
1996
  }
1996
- const { runPrint, readStdin } = await import("./print-CvB9w4jf.mjs");
1997
+ const { runPrint, readStdin } = await import("./print-ba_0hiV9.mjs");
1997
1998
  await ensureAgentWorkspace({
1998
1999
  appUrl,
1999
2000
  sessionToken: auth.value.token,
@@ -2018,7 +2019,7 @@ async function runPrintMode({ argv, appUrl }) {
2018
2019
  process.exit(1);
2019
2020
  }
2020
2021
  } else {
2021
- const { connectMachineShare } = await import("./print-share-mBbyQQDb.mjs");
2022
+ const { connectMachineShare } = await import("./print-share-CT4ltX7u.mjs");
2022
2023
  machineShare = await connectMachineShare({
2023
2024
  appUrl: auth.value.appUrl,
2024
2025
  sessionToken: auth.value.token,
@@ -2038,7 +2039,7 @@ async function runPrintMode({ argv, appUrl }) {
2038
2039
  });
2039
2040
  if (argv.json) output(argv, result);
2040
2041
  } catch (error) {
2041
- const { BillingBlockedError, BILLING_BLOCKED_EXIT_CODE, billingBlockedJsonResult } = await import("./billing-blocked-euid6MN9.mjs");
2042
+ const { BillingBlockedError, BILLING_BLOCKED_EXIT_CODE, billingBlockedJsonResult } = await import("./billing-blocked-Dgu5-oDy.mjs");
2042
2043
  if (error instanceof BillingBlockedError) {
2043
2044
  if (argv.json) output(argv, billingBlockedJsonResult(error, null));
2044
2045
  printError(error.message);
@@ -2082,7 +2083,7 @@ const getCommand$1 = {
2082
2083
  printError(`${auth.error.message} Run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
2083
2084
  process.exit(1);
2084
2085
  }
2085
- const { messageGet } = await import("./print-CvB9w4jf.mjs");
2086
+ const { messageGet } = await import("./print-ba_0hiV9.mjs");
2086
2087
  try {
2087
2088
  const result = await messageGet({
2088
2089
  appUrl: auth.value.appUrl,
@@ -2092,7 +2093,7 @@ const getCommand$1 = {
2092
2093
  });
2093
2094
  if (argv.json) output(argv, result);
2094
2095
  } catch (error) {
2095
- const { BillingBlockedError, BILLING_BLOCKED_EXIT_CODE, billingBlockedJsonResult } = await import("./billing-blocked-euid6MN9.mjs");
2096
+ const { BillingBlockedError, BILLING_BLOCKED_EXIT_CODE, billingBlockedJsonResult } = await import("./billing-blocked-Dgu5-oDy.mjs");
2096
2097
  if (error instanceof BillingBlockedError) {
2097
2098
  if (argv.json) output(argv, billingBlockedJsonResult(error, argv["message-id"]));
2098
2099
  printError(error.message);
@@ -2285,7 +2286,7 @@ const switchCommand = {
2285
2286
  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.");
2286
2287
  process.exit(1);
2287
2288
  }
2288
- const { runWorkspacePicker } = await import("./boot-BmzC25Rc.mjs");
2289
+ const { runWorkspacePicker } = await import("./boot-Cz7I47B-.mjs");
2289
2290
  await runWorkspacePicker(session);
2290
2291
  return;
2291
2292
  }
@@ -2363,16 +2364,15 @@ const openCommand = {
2363
2364
  const agent = argv.agent ? resolveAgent((await fetchPortalDevices(session)).agents, argv.agent) : null;
2364
2365
  const cwd = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
2365
2366
  const { machineName } = machineIdentity();
2366
- const { PortalClient } = await import("./client-GNFwd8ou.mjs");
2367
+ const { PortalDaemonClient } = await import("./daemon-client-DlblQIR9.mjs");
2367
2368
  let lastLine = "";
2368
2369
  let signalConnected;
2369
2370
  const connected = new Promise((resolve) => {
2370
2371
  signalConnected = resolve;
2371
2372
  });
2372
- const client = new PortalClient({
2373
+ const client = new PortalDaemonClient({
2373
2374
  appUrl: session.appUrl,
2374
2375
  sessionToken: session.sessionToken,
2375
- resolveCwd: () => cwd,
2376
2376
  onState: (state) => {
2377
2377
  if (state.status === "connected") signalConnected();
2378
2378
  const line = state.status === "error" ? `portal: connection error: ${state.error ?? "unknown"}; retrying` : `portal: ${state.status}`;
@@ -2388,6 +2388,8 @@ const openCommand = {
2388
2388
  };
2389
2389
  process.on("SIGINT", stop);
2390
2390
  process.on("SIGTERM", stop);
2391
+ await client.start();
2392
+ client.setFallbackCwd(cwd);
2391
2393
  client.enable();
2392
2394
  await connected;
2393
2395
  if (agent) {
@@ -2636,8 +2638,8 @@ const sandboxCommand = {
2636
2638
  }).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`"),
2637
2639
  handler: async (argv) => {
2638
2640
  const session = requireSession(argv);
2639
- const { createRestClient } = await import("./rest-D4gqf1ck.mjs");
2640
- const { resolveAgent } = await import("./print-CvB9w4jf.mjs");
2641
+ const { createRestClient } = await import("./rest-DADJh0bi.mjs");
2642
+ const { resolveAgent } = await import("./print-ba_0hiV9.mjs");
2641
2643
  const client = createRestClient({
2642
2644
  appUrl: session.appUrl,
2643
2645
  sessionToken: session.sessionToken
@@ -2706,7 +2708,7 @@ async function runPty({ session, agentId, agentName }) {
2706
2708
  return 1;
2707
2709
  }
2708
2710
  console.error(`Connecting to ${agentName}'s sandbox… (Ctrl-] detaches)`);
2709
- const { runRawPtyPassthrough } = await import("./raw-pty-t5DC-e1T.mjs");
2711
+ const { runRawPtyPassthrough } = await import("./raw-pty-3EkG-jjH.mjs");
2710
2712
  const result = await runRawPtyPassthrough({
2711
2713
  stdin: process.stdin,
2712
2714
  stdout: process.stdout,
@@ -3896,7 +3898,7 @@ if (process.argv.includes(UPDATE_WORKER_FLAG)) {
3896
3898
  process.exit(0);
3897
3899
  }
3898
3900
  if (process.argv.includes(PORTAL_DAEMON_FLAG)) {
3899
- const { runPortalDaemon } = await import("./daemon-C_h_KgJC.mjs");
3901
+ const { runPortalDaemon } = await import("./daemon-imLBqTGg.mjs");
3900
3902
  runPortalDaemon(process.argv);
3901
3903
  } else runCli();
3902
3904
  function runCli() {
@@ -1,12 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import { D as setActiveWorkspace, E as listWorkspaces, S as themesForMode, _ as themeForMode, a as installCrashHandler, b as themeVersion, c as MARK_CELLS, d as DEFAULT_THEME_ID, f as applyTheme, g as theme, h as noColorRequested, i as writeArtifact, l as WORDMARK, m as monoTheme, n as profilingEnabled, o as buildCrashReport, p as findTheme, r as record, s as writeCrashReport, v as themeMode, w as getActiveWorkspaceId, y as themeModeFromColorFgBg } from "./profiler-pZXBjTHx.mjs";
3
- import { B as saveTheme, D as getSavedTheme, E as getReviewStateDir, L as resolveWebUrl, S as getConfigPath, c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, i as resolveAgent, l as parseExternalOauthConnectParams, m as specKeyFor, p as parseConnectCard, s as MASK_CHAR, u as parseOauthConnectParams, v as DEFAULT_API_URL, y as DEFAULT_APP_URL } from "./print-BaXFFR3b.mjs";
4
- import { a as errorMessage, i as sendErrorMessage, n as createRestClient, o as isRecord, r as errorDetail, t as HttpError } from "./rest-D89MGnWD.mjs";
5
- import { i as billingBlockedOutcomeFromSendResponse } from "./billing-blocked-CwfG1BTR.mjs";
6
- import { t as PortalClient } from "./client-yn4bC-OV.mjs";
7
- import { d as makeLineParser, f as parseDaemonMessage, l as daemonPaths, n as ensureDaemonRunning, s as LOCAL_PROTOCOL_VERSION, u as encodeLine } from "./daemon-C0Qr2WSJ.mjs";
8
- import { t as SandboxStream } from "./client-D8s9vY4p.mjs";
9
- import { t as runRawPtyPassthrough } from "./raw-pty-DAmb8uqt.mjs";
2
+ import { D as setActiveWorkspace, E as listWorkspaces, S as themesForMode, _ as themeForMode, a as installCrashHandler, b as themeVersion, c as MARK_CELLS, d as DEFAULT_THEME_ID, f as applyTheme, g as theme, h as noColorRequested, i as writeArtifact, l as WORDMARK, m as monoTheme, n as profilingEnabled, o as buildCrashReport, p as findTheme, r as record, s as writeCrashReport, v as themeMode, w as getActiveWorkspaceId, y as themeModeFromColorFgBg } from "./profiler-BoPzxsbP.mjs";
3
+ import { B as saveTheme, D as getSavedTheme, E as getReviewStateDir, L as resolveWebUrl, S as getConfigPath, c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, i as resolveAgent, l as parseExternalOauthConnectParams, m as specKeyFor, p as parseConnectCard, s as MASK_CHAR, u as parseOauthConnectParams, v as DEFAULT_API_URL, y as DEFAULT_APP_URL } from "./print-CbayCa87.mjs";
4
+ import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
5
+ import { a as isRecord, i as errorMessage, n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-BY2nADw5.mjs";
6
+ import { i as billingBlockedOutcomeFromSendResponse } from "./billing-blocked-2wju4gC_.mjs";
7
+ import { t as PortalClient } from "./client-Cf7cvftg.mjs";
8
+ import "./daemon-DaUaCMwC.mjs";
9
+ import { t as SandboxStream } from "./client-Cn2af31H.mjs";
10
+ import { t as PortalDaemonClient } from "./daemon-client-CJxbCLOb.mjs";
11
+ import { t as runRawPtyPassthrough } from "./raw-pty-B6mAroiI.mjs";
10
12
  import * as os$1 from "node:os";
11
13
  import { homedir, platform, release, tmpdir } from "node:os";
12
14
  import path, { basename, extname, isAbsolute, join, win32 } from "node:path";
@@ -14,7 +16,7 @@ import { z } from "zod";
14
16
  import open from "open";
15
17
  import { constants } from "node:fs";
16
18
  import { execFile, spawn } from "node:child_process";
17
- import { createHash, randomUUID } from "node:crypto";
19
+ import { createHash } from "node:crypto";
18
20
  import { connect, createConnection } from "node:net";
19
21
  import { access, appendFile, mkdir, mkdtemp, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
20
22
  import { MarkdownRenderable, RenderableEvents, StyledText, SyntaxStyle, addDefaultParsers, createCliRenderer, decodePasteBytes, detectLinks, fg, link } from "@opentui/core";
@@ -293,152 +295,7 @@ async function resolveInitialScreen({ rest, agentSelector, conversationId, newCo
293
295
  }
294
296
 
295
297
  //#endregion
296
- //#region src/chat/portal/daemon-client.ts
297
- var PortalDaemonClient = class {
298
- sessionId = randomUUID();
299
- socket = null;
300
- disposed = false;
301
- reconnectTimer = null;
302
- lastBind = null;
303
- wantEnabled = false;
304
- pendingGrants = /* @__PURE__ */ new Set();
305
- grantedAgentIds = /* @__PURE__ */ new Set();
306
- constructor(opts) {
307
- this.opts = opts;
308
- }
309
- /** Spawn the daemon if needed and attach. Idempotent. */
310
- async start() {
311
- if (this.disposed) return;
312
- await ensureDaemonRunning(this.opts.appUrl);
313
- this.connect();
314
- }
315
- connect() {
316
- if (this.disposed) return;
317
- const { socketPath } = daemonPaths(this.opts.appUrl);
318
- const socket = connect(socketPath);
319
- socket.setEncoding("utf8");
320
- this.socket = socket;
321
- const parse = makeLineParser();
322
- socket.on("connect", () => {
323
- this.send({
324
- t: "hello",
325
- v: LOCAL_PROTOCOL_VERSION,
326
- sessionId: this.sessionId,
327
- token: this.opts.sessionToken
328
- });
329
- if (this.wantEnabled) this.send({ t: "enable" });
330
- if (this.lastBind) this.send({
331
- t: "bind",
332
- sessionId: this.sessionId,
333
- conversationId: this.lastBind.conversationId,
334
- cwd: this.lastBind.cwd
335
- });
336
- for (const agentId of this.pendingGrants) this.send({
337
- t: "grant",
338
- agentId
339
- });
340
- });
341
- socket.on("data", (chunk) => {
342
- for (const line of parse(chunk)) {
343
- const msg = parseDaemonMessage(line);
344
- if (msg) this.onMessage(msg);
345
- }
346
- });
347
- const onClose = () => {
348
- if (this.socket === socket) this.socket = null;
349
- this.scheduleReconnect();
350
- };
351
- socket.on("close", onClose);
352
- socket.on("error", onClose);
353
- }
354
- scheduleReconnect() {
355
- if (this.disposed || this.reconnectTimer) return;
356
- this.reconnectTimer = setTimeout(() => {
357
- this.reconnectTimer = null;
358
- this.start();
359
- }, 500);
360
- this.reconnectTimer.unref();
361
- }
362
- onMessage(msg) {
363
- if (msg.t === "state") {
364
- this.grantedAgentIds = new Set(msg.grantedAgentIds);
365
- this.opts.onState({
366
- status: msg.status,
367
- machineName: msg.machineName,
368
- friendlyName: msg.friendlyName,
369
- error: msg.error,
370
- grantedAgentIds: msg.grantedAgentIds
371
- });
372
- }
373
- }
374
- /** Whether this agent is currently authorized on the machine (last daemon state). */
375
- isGranted(agentId) {
376
- return this.grantedAgentIds.has(agentId);
377
- }
378
- /** Whether the user has turned machine sharing on from this CLI. */
379
- isEnabled() {
380
- return this.wantEnabled;
381
- }
382
- send(msg) {
383
- if (this.socket?.writable) this.socket.write(encodeLine(msg));
384
- }
385
- /** Turn machine sharing on (the daemon connects the portal). */
386
- enable() {
387
- this.wantEnabled = true;
388
- this.send({ t: "enable" });
389
- }
390
- /** Turn machine sharing off. */
391
- disable() {
392
- this.wantEnabled = false;
393
- this.send({ t: "disable" });
394
- }
395
- /** Register / update this conversation's working directory for exec routing. */
396
- bind(conversationId, cwd) {
397
- this.lastBind = {
398
- conversationId,
399
- cwd
400
- };
401
- this.send({
402
- t: "bind",
403
- sessionId: this.sessionId,
404
- conversationId,
405
- cwd
406
- });
407
- }
408
- /**
409
- * Authorize one agent to run commands on this machine. Resolves once the
410
- * request is sent to the daemon (the daemon performs the grant and pushes the
411
- * updated state); kept async so it's a drop-in for the old in-process client's
412
- * awaited `grantAgent`.
413
- */
414
- grantAgent(agentId) {
415
- this.pendingGrants.add(agentId);
416
- this.send({
417
- t: "grant",
418
- agentId
419
- });
420
- return Promise.resolve();
421
- }
422
- /** Detach this CLI. The daemon keeps running for other clients. */
423
- dispose() {
424
- this.disposed = true;
425
- if (this.reconnectTimer) {
426
- clearTimeout(this.reconnectTimer);
427
- this.reconnectTimer = null;
428
- }
429
- this.send({
430
- t: "bye",
431
- sessionId: this.sessionId
432
- });
433
- try {
434
- this.socket?.destroy();
435
- } catch (_error) {}
436
- this.socket = null;
437
- }
438
- };
439
-
440
- //#endregion
441
- //#region src/chat/portal/create-portal-client.ts
298
+ //#region ../portal-daemon/src/create-portal-client.ts
442
299
  /**
443
300
  * Escape hatch: setting `SKYDIVE_NO_DAEMON` (to any non-empty value)
444
301
  * makes the CLI bypass the portal daemon and open the portal connection
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { a as errorMessage, t as HttpError } from "./rest-D89MGnWD.mjs";
2
+ import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
3
3
  import os from "node:os";
4
4
  import { z } from "zod";
5
5
  import { spawn } from "node:child_process";
6
6
  import { WebSocket } from "ws";
7
7
 
8
- //#region src/chat/portal/machine.ts
8
+ //#region ../portal-daemon/src/machine.ts
9
9
  /**
10
10
  * Identity this machine registers under when the CLI shares it via the portal.
11
11
  *
@@ -60,6 +60,19 @@ function portalWsUrl(appUrl, machine, label) {
60
60
  return url.toString();
61
61
  }
62
62
 
63
+ //#endregion
64
+ //#region ../portal-daemon/src/util.ts
65
+ /** Narrowing helper for the unknown JSON payloads the daemon reads (persisted
66
+ * state, wire frames). A type predicate (not an `as` cast), so call sites can
67
+ * read properties without asserting. */
68
+ function isRecord(value) {
69
+ return typeof value === "object" && value !== null && !Array.isArray(value);
70
+ }
71
+ /** Best-effort message from an unknown thrown value. */
72
+ function errorMessage(err) {
73
+ return err instanceof Error ? err.message : String(err);
74
+ }
75
+
63
76
  //#endregion
64
77
  //#region ../portal-protocol/src/index.ts
65
78
  const MAX_WS_FRAME_BYTES = 16 * 1024 * 1024;
@@ -126,7 +139,7 @@ function decodeFrame(frame) {
126
139
  }
127
140
 
128
141
  //#endregion
129
- //#region src/chat/portal/exec.ts
142
+ //#region ../portal-daemon/src/exec.ts
130
143
  /**
131
144
  * Runs portal `exec` directives locally. Each `open` spawns a child process
132
145
  * whose stdout/stderr stream back as data frames and whose stdin is fed by
@@ -252,7 +265,7 @@ var JobManager = class {
252
265
  };
253
266
 
254
267
  //#endregion
255
- //#region src/chat/portal/api.ts
268
+ //#region ../portal-daemon/src/api.ts
256
269
  /**
257
270
  * The portal's session-authed REST surface, shared by `PortalClient` (the
258
271
  * TUI/`portal open` connection) and the `skydive portal` management
@@ -335,7 +348,7 @@ function findThisDevice(devices, machineName) {
335
348
  }
336
349
 
337
350
  //#endregion
338
- //#region src/chat/portal/client.ts
351
+ //#region ../portal-daemon/src/client.ts
339
352
  const INITIAL_BACKOFF_MS = 500;
340
353
  const MAX_BACKOFF_MS = 1e4;
341
354
  /**
@@ -503,4 +516,4 @@ function sleep(ms) {
503
516
  }
504
517
 
505
518
  //#endregion
506
- export { registerPortalDevice as a, grantPortalAccess as i, fetchPortalDevices as n, revokePortalAccess as o, findThisDevice as r, machineIdentity as s, PortalClient as t };
519
+ export { registerPortalDevice as a, machineIdentity as c, grantPortalAccess as i, fetchPortalDevices as n, revokePortalAccess as o, findThisDevice as r, isRecord as s, PortalClient as t };
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { t as PortalClient } from "./client-Cf7cvftg.mjs";
3
+
4
+ export { PortalClient };
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { o as isRecord } from "./rest-D89MGnWD.mjs";
3
- import { t as PortalClient } from "./client-yn4bC-OV.mjs";
2
+ import { s as isRecord, t as PortalClient } from "./client-Cf7cvftg.mjs";
4
3
  import os from "node:os";
5
4
  import path from "node:path";
6
5
  import { z } from "zod";
@@ -9,7 +8,7 @@ import { createHash } from "node:crypto";
9
8
  import { connect, createServer } from "node:net";
10
9
  import { access, appendFile, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
11
10
 
12
- //#region src/chat/portal/local-protocol.ts
11
+ //#region ../portal-daemon/src/local-protocol.ts
13
12
  /**
14
13
  * Local IPC between the portal DAEMON and the `skydive` CLI processes attached
15
14
  * to it, on one host.
@@ -96,6 +95,15 @@ const clientBindSchema = z.object({
96
95
  /** `enable`/`disable` — turn machine sharing on/off (user-initiated). */
97
96
  const clientEnableSchema = z.object({ t: z.literal("enable") });
98
97
  const clientDisableSchema = z.object({ t: z.literal("disable") });
98
+ /**
99
+ * `cwd` — set the directory an exec runs in when its conversation is unknown.
100
+ * `skydive portal open` has no conversation of its own: it shares the machine
101
+ * for one directory, so it sets the fallback directly instead of binding.
102
+ */
103
+ const clientCwdSchema = z.object({
104
+ t: z.literal("cwd"),
105
+ cwd: z.string().min(1)
106
+ });
99
107
  /** `grant` — authorize one agent to reach this machine (user-initiated). */
100
108
  const clientGrantSchema = z.object({
101
109
  t: z.literal("grant"),
@@ -123,6 +131,7 @@ const clientMessageSchema = z.discriminatedUnion("t", [
123
131
  clientBindSchema,
124
132
  clientEnableSchema,
125
133
  clientDisableSchema,
134
+ clientCwdSchema,
126
135
  clientGrantSchema,
127
136
  clientByeSchema,
128
137
  clientStatusSchema,
@@ -220,7 +229,7 @@ function parseDaemonMessage(line) {
220
229
  }
221
230
 
222
231
  //#endregion
223
- //#region src/chat/portal/daemon.ts
232
+ //#region ../portal-daemon/src/daemon.ts
224
233
  /**
225
234
  * The portal DAEMON: one long-lived process per (user, appUrl) that owns the
226
235
  * SINGLE portal WebSocket to the server and is the only writer of the machine's
@@ -290,7 +299,8 @@ var PortalDaemon = class {
290
299
  const conn = {
291
300
  socket,
292
301
  sessionId: null,
293
- conversations: /* @__PURE__ */ new Set()
302
+ conversations: /* @__PURE__ */ new Set(),
303
+ wantsShare: false
294
304
  };
295
305
  this.conns.add(conn);
296
306
  this.clearIdleTimer();
@@ -324,12 +334,17 @@ var PortalDaemon = class {
324
334
  this.persistState();
325
335
  return;
326
336
  case "enable":
337
+ conn.wantsShare = true;
327
338
  this.ensureClient();
328
339
  this.client?.enable();
329
340
  return;
330
341
  case "disable":
342
+ for (const other of this.conns) other.wantsShare = false;
331
343
  this.client?.disable();
332
344
  return;
345
+ case "cwd":
346
+ this.fallbackCwd = msg.cwd;
347
+ return;
333
348
  case "grant":
334
349
  this.ensureClient();
335
350
  this.client?.grantAgent(msg.agentId).catch((error) => {
@@ -354,6 +369,8 @@ var PortalDaemon = class {
354
369
  try {
355
370
  conn.socket.destroy();
356
371
  } catch (_error) {}
372
+ conn.wantsShare = false;
373
+ if (![...this.conns].some((c) => c.wantsShare)) this.client?.disable();
357
374
  if (this.conns.size === 0) this.armIdleTimer();
358
375
  }
359
376
  /** Create the single PortalClient the first time a client needs the portal. */
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env node
2
+ import { d as makeLineParser, f as parseDaemonMessage, l as daemonPaths, n as ensureDaemonRunning, s as LOCAL_PROTOCOL_VERSION, u as encodeLine } from "./daemon-DaUaCMwC.mjs";
3
+ import { randomUUID } from "node:crypto";
4
+ import { connect } from "node:net";
5
+
6
+ //#region ../portal-daemon/src/daemon-client.ts
7
+ var PortalDaemonClient = class {
8
+ sessionId = randomUUID();
9
+ socket = null;
10
+ disposed = false;
11
+ reconnectTimer = null;
12
+ lastBind = null;
13
+ fallbackCwd = null;
14
+ wantEnabled = false;
15
+ pendingGrants = /* @__PURE__ */ new Set();
16
+ grantedAgentIds = /* @__PURE__ */ new Set();
17
+ constructor(opts) {
18
+ this.opts = opts;
19
+ }
20
+ /** Spawn the daemon if needed and attach. Idempotent. */
21
+ async start() {
22
+ if (this.disposed) return;
23
+ await ensureDaemonRunning(this.opts.appUrl);
24
+ this.connect();
25
+ }
26
+ connect() {
27
+ if (this.disposed) return;
28
+ const { socketPath } = daemonPaths(this.opts.appUrl);
29
+ const socket = connect(socketPath);
30
+ socket.setEncoding("utf8");
31
+ this.socket = socket;
32
+ const parse = makeLineParser();
33
+ socket.on("connect", () => {
34
+ this.send({
35
+ t: "hello",
36
+ v: LOCAL_PROTOCOL_VERSION,
37
+ sessionId: this.sessionId,
38
+ token: this.opts.sessionToken
39
+ });
40
+ if (this.wantEnabled) this.send({ t: "enable" });
41
+ if (this.fallbackCwd) this.send({
42
+ t: "cwd",
43
+ cwd: this.fallbackCwd
44
+ });
45
+ if (this.lastBind) this.send({
46
+ t: "bind",
47
+ sessionId: this.sessionId,
48
+ conversationId: this.lastBind.conversationId,
49
+ cwd: this.lastBind.cwd
50
+ });
51
+ for (const agentId of this.pendingGrants) this.send({
52
+ t: "grant",
53
+ agentId
54
+ });
55
+ });
56
+ socket.on("data", (chunk) => {
57
+ for (const line of parse(chunk)) {
58
+ const msg = parseDaemonMessage(line);
59
+ if (msg) this.onMessage(msg);
60
+ }
61
+ });
62
+ const onClose = () => {
63
+ if (this.socket === socket) this.socket = null;
64
+ this.scheduleReconnect();
65
+ };
66
+ socket.on("close", onClose);
67
+ socket.on("error", onClose);
68
+ }
69
+ scheduleReconnect() {
70
+ if (this.disposed || this.reconnectTimer) return;
71
+ this.reconnectTimer = setTimeout(() => {
72
+ this.reconnectTimer = null;
73
+ this.start();
74
+ }, 500);
75
+ this.reconnectTimer.unref();
76
+ }
77
+ onMessage(msg) {
78
+ if (msg.t === "state") {
79
+ this.grantedAgentIds = new Set(msg.grantedAgentIds);
80
+ this.opts.onState({
81
+ status: msg.status,
82
+ machineName: msg.machineName,
83
+ friendlyName: msg.friendlyName,
84
+ error: msg.error,
85
+ grantedAgentIds: msg.grantedAgentIds
86
+ });
87
+ }
88
+ }
89
+ /** Whether this agent is currently authorized on the machine (last daemon state). */
90
+ isGranted(agentId) {
91
+ return this.grantedAgentIds.has(agentId);
92
+ }
93
+ /** Whether the user has turned machine sharing on from this CLI. */
94
+ isEnabled() {
95
+ return this.wantEnabled;
96
+ }
97
+ send(msg) {
98
+ if (this.socket?.writable) this.socket.write(encodeLine(msg));
99
+ }
100
+ /** Turn machine sharing on (the daemon connects the portal). */
101
+ enable() {
102
+ this.wantEnabled = true;
103
+ this.send({ t: "enable" });
104
+ }
105
+ /** Turn machine sharing off. */
106
+ disable() {
107
+ this.wantEnabled = false;
108
+ this.send({ t: "disable" });
109
+ }
110
+ /**
111
+ * Set the directory an exec runs in when the daemon can't place its
112
+ * conversation. `skydive portal open` shares one directory and has no
113
+ * conversation of its own, so this is how it routes.
114
+ */
115
+ setFallbackCwd(cwd) {
116
+ this.fallbackCwd = cwd;
117
+ this.send({
118
+ t: "cwd",
119
+ cwd
120
+ });
121
+ }
122
+ /** Register / update this conversation's working directory for exec routing. */
123
+ bind(conversationId, cwd) {
124
+ this.lastBind = {
125
+ conversationId,
126
+ cwd
127
+ };
128
+ this.send({
129
+ t: "bind",
130
+ sessionId: this.sessionId,
131
+ conversationId,
132
+ cwd
133
+ });
134
+ }
135
+ /**
136
+ * Authorize one agent to run commands on this machine. Resolves once the
137
+ * request is sent to the daemon (the daemon performs the grant and pushes the
138
+ * updated state); kept async so it's a drop-in for the old in-process client's
139
+ * awaited `grantAgent`.
140
+ */
141
+ grantAgent(agentId) {
142
+ this.pendingGrants.add(agentId);
143
+ this.send({
144
+ t: "grant",
145
+ agentId
146
+ });
147
+ return Promise.resolve();
148
+ }
149
+ /** Detach this CLI. The daemon keeps running for other clients. */
150
+ dispose() {
151
+ this.disposed = true;
152
+ if (this.reconnectTimer) {
153
+ clearTimeout(this.reconnectTimer);
154
+ this.reconnectTimer = null;
155
+ }
156
+ this.send({
157
+ t: "bye",
158
+ sessionId: this.sessionId
159
+ });
160
+ try {
161
+ this.socket?.destroy();
162
+ } catch (_error) {}
163
+ this.socket = null;
164
+ }
165
+ };
166
+
167
+ //#endregion
168
+ export { PortalDaemonClient as t };
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import "./client-Cf7cvftg.mjs";
3
+ import "./daemon-DaUaCMwC.mjs";
4
+ import { t as PortalDaemonClient } from "./daemon-client-CJxbCLOb.mjs";
5
+
6
+ export { PortalDaemonClient };
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import "./client-Cf7cvftg.mjs";
3
+ import { a as runPortalDaemon, i as queryDaemonStatus, n as ensureDaemonRunning, o as stopDaemon, r as isDaemonListening, t as PortalDaemon } from "./daemon-DaUaCMwC.mjs";
4
+
5
+ export { runPortalDaemon };
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ //#region ../portal-daemon/src/http-error.ts
3
+ /**
4
+ * HTTP failure that keeps the FULL response body. The message self-clips to
5
+ * 200 chars because it flows into logs and one-line UIs; consumers that can
6
+ * afford the whole body (an expandable transcript block) read `body` directly.
7
+ * The CLI re-exports this class from its REST layer so `instanceof` checks
8
+ * hold across both surfaces.
9
+ */
10
+ var HttpError = class extends Error {
11
+ constructor(status, body) {
12
+ super(`HTTP ${status}: ${body.slice(0, 200)}`);
13
+ this.status = status;
14
+ this.body = body;
15
+ this.name = "HttpError";
16
+ }
17
+ };
18
+
19
+ //#endregion
20
+ export { HttpError as t };
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { a as errorMessage, i as sendErrorMessage, n as createRestClient, o as isRecord, t as HttpError } from "./rest-D89MGnWD.mjs";
3
- import { i as billingBlockedOutcomeFromSendResponse, n as BillingBlockedError } from "./billing-blocked-CwfG1BTR.mjs";
2
+ import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
3
+ import { a as isRecord, i as errorMessage, r as sendErrorMessage, t as createRestClient } from "./rest-BY2nADw5.mjs";
4
+ import { i as billingBlockedOutcomeFromSendResponse, n as BillingBlockedError } from "./billing-blocked-2wju4gC_.mjs";
4
5
  import path from "node:path";
5
6
  import Conf from "conf";
6
7
  import { err, ok } from "neverthrow";
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-BaXFFR3b.mjs";
3
- import "./rest-D89MGnWD.mjs";
4
- import "./billing-blocked-CwfG1BTR.mjs";
2
+ import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-CbayCa87.mjs";
3
+ import "./rest-BY2nADw5.mjs";
4
+ import "./billing-blocked-2wju4gC_.mjs";
5
5
 
6
6
  export { messageGet, readStdin, resolveAgent, runPrint };
@@ -14,7 +14,7 @@ import { n as printError } from "./output-DYzzdXYV.mjs";
14
14
  * the reply (and to --json).
15
15
  */
16
16
  async function connectMachineShare({ appUrl, sessionToken, timeoutHint }) {
17
- const { PortalClient } = await import("./client-GNFwd8ou.mjs");
17
+ const { PortalClient } = await import("./client-CneLVPei.mjs");
18
18
  let signalConnected;
19
19
  const connected = new Promise((resolve) => {
20
20
  signalConnected = resolve;
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { S as getConfigPath } from "./print-BaXFFR3b.mjs";
2
+ import { S as getConfigPath } from "./print-CbayCa87.mjs";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { err, ok } from "neverthrow";
@@ -8,7 +8,7 @@ import fs from "node:fs";
8
8
 
9
9
  //#region package.json
10
10
  var name = "skydive-cli";
11
- var version$1 = "0.4.0-beta.1324";
11
+ var version$1 = "0.4.0-beta.1340";
12
12
 
13
13
  //#endregion
14
14
  //#region src/auth/organization.ts
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import "./client-Cn2af31H.mjs";
3
+ import { t as runRawPtyPassthrough } from "./raw-pty-B6mAroiI.mjs";
4
+
5
+ export { runRawPtyPassthrough };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as SandboxStream } from "./client-D8s9vY4p.mjs";
2
+ import { t as SandboxStream } from "./client-Cn2af31H.mjs";
3
3
 
4
4
  //#region src/chat/sandbox/raw-pty.ts
5
5
  /**
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as billingBlockedOutcomeSchema } from "./billing-blocked-CwfG1BTR.mjs";
2
+ import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
3
+ import { a as billingBlockedOutcomeSchema } from "./billing-blocked-2wju4gC_.mjs";
3
4
  import { z } from "zod";
4
5
  import { createParser } from "eventsource-parser";
5
6
 
@@ -17,14 +18,6 @@ function errorMessage(err) {
17
18
 
18
19
  //#endregion
19
20
  //#region src/chat/api/rest.ts
20
- var HttpError = class extends Error {
21
- constructor(status, body) {
22
- super(`HTTP ${status}: ${body.slice(0, 200)}`);
23
- this.status = status;
24
- this.body = body;
25
- this.name = "HttpError";
26
- }
27
- };
28
21
  const ERROR_DETAIL_MAX_BODY = 2e3;
29
22
  /**
30
23
  * Fullest renderable text for a thrown value. `HttpError.message` clips the
@@ -614,4 +607,4 @@ const conversationStreamEventSchema = z.discriminatedUnion("kind", [
614
607
  ]);
615
608
 
616
609
  //#endregion
617
- export { errorMessage as a, sendErrorMessage as i, createRestClient as n, isRecord as o, errorDetail as r, HttpError as t };
610
+ export { isRecord as a, errorMessage as i, errorDetail as n, sendErrorMessage as r, createRestClient as t };
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
3
+ import { n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-BY2nADw5.mjs";
4
+ import "./billing-blocked-2wju4gC_.mjs";
5
+
6
+ export { createRestClient };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.4.0-beta.1324",
3
+ "version": "0.4.0-beta.1340",
4
4
  "description": "Skydive CLI — cloud agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",
@@ -48,7 +48,7 @@
48
48
  "zustand": "^5.0.2"
49
49
  },
50
50
  "devDependencies": {
51
- "@createinc/anyone-portal-protocol": "0.0.0",
51
+ "@createinc/anyone-portal-daemon": "0.0.0",
52
52
  "@createinc/anyone-sandbox-stream-protocol": "0.0.0",
53
53
  "@createinc/tsconfig": "0.0.0",
54
54
  "@types/bun": "^1.3.14",
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./rest-D89MGnWD.mjs";
3
- import "./billing-blocked-CwfG1BTR.mjs";
4
- import { t as PortalClient } from "./client-yn4bC-OV.mjs";
5
-
6
- export { PortalClient };
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./rest-D89MGnWD.mjs";
3
- import "./billing-blocked-CwfG1BTR.mjs";
4
- import "./client-yn4bC-OV.mjs";
5
- import { a as runPortalDaemon, i as queryDaemonStatus, n as ensureDaemonRunning, o as stopDaemon, r as isDaemonListening, t as PortalDaemon } from "./daemon-C0Qr2WSJ.mjs";
6
-
7
- export { runPortalDaemon };
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./client-D8s9vY4p.mjs";
3
- import { t as runRawPtyPassthrough } from "./raw-pty-DAmb8uqt.mjs";
4
-
5
- export { runRawPtyPassthrough };
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- import { i as sendErrorMessage, n as createRestClient, r as errorDetail, t as HttpError } from "./rest-D89MGnWD.mjs";
3
- import "./billing-blocked-CwfG1BTR.mjs";
4
-
5
- export { createRestClient };