skydive-cli 0.5.0-beta.27 → 0.5.0-beta.28

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,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { A as takenAliasNames, B as name, D as dedupeAliasName, E as aliasActivationHint, F as getActiveWorkspaceId, I as getSessionIdentity, L as listWorkspaces, M as defaultInstallEnv, N as detectShell, O as installAgentAlias, P as ensureActiveOrganization, R as resolveWorkspaceId, S as themes, T as machineOsFromPlatform, V as version, a as installCrashHandler, j as SUPPORTED_SHELLS, k as slugifyAliasName, t as maybeStartProfiling, u as brandHelpArt, w as buildImportSeedPrompt, z as setActiveWorkspace } from "./profiler-DLIjDaz_.mjs";
2
+ import { A as takenAliasNames, B as name, D as dedupeAliasName, E as aliasActivationHint, F as getActiveWorkspaceId, I as getSessionIdentity, L as listWorkspaces, M as defaultInstallEnv, N as detectShell, O as installAgentAlias, P as ensureActiveOrganization, R as resolveWorkspaceId, S as themes, T as machineOsFromPlatform, V as version, a as installCrashHandler, j as SUPPORTED_SHELLS, k as slugifyAliasName, t as maybeStartProfiling, u as brandHelpArt, w as buildImportSeedPrompt, z as setActiveWorkspace } from "./profiler-rC7AZSxL.mjs";
3
3
  import { A as getShareMachineDefault, C as getConfigPath, D as getPromptHistoryPath, E as getPreference, F as resolveAppUrl, H as saveSession, I as resolveChatAuth, L as resolveConfig, M as getStoredApiKeyWorkspaceName, N as getUpdateCheckDisabled, R as resolveManagementAuth, S as deleteConfig, T as getLastSeenVersion, V as saveConfig, W as setLastSeenVersion, _ as API_KEY_FAMILY_PREFIX, g as API_KEYS_URL, i as resolveAgent$1, j as getStoredApiKeyId, v as API_KEY_PREFIX, w as getDefaultAgent, x as PREFERENCES, y as DEFAULT_API_URL, z as resolveSession } from "./print-DYiQC5IW.mjs";
4
4
  import { n as printError, r as printTable, t as output } from "./output-DYzzdXYV.mjs";
5
5
  import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
6
6
  import { t as createRestClient } from "./rest-xXfqT4SX.mjs";
7
7
  import "./billing-blocked-2wju4gC_.mjs";
8
8
  import { r as resolveMachineIdentity } from "./client-DbqRBquD.mjs";
9
- import { i as queryDaemonStatus, l as PORTAL_DAEMON_FLAG, n as ensureDaemonRunning, s as stopDaemon, u as daemonPaths } from "./daemon-DGsTcIVx.mjs";
9
+ import { i as queryDaemonStatus, l as PORTAL_DAEMON_FLAG, n as ensureDaemonRunning, s as stopDaemon, u as daemonPaths } from "./daemon-B5SNuIhD.mjs";
10
10
  import { i as grantPortalAccess, n as fetchPortalDevices, o as registerPortalDevice, r as findThisDevice, s as revokePortalAccess } from "./api-DG5W6iwx.mjs";
11
11
  import { t as SandboxStream } from "./client-c4c5MmgN.mjs";
12
12
  import { hideBin } from "yargs/helpers";
@@ -1584,7 +1584,7 @@ const importCommand = {
1584
1584
  process.exit(1);
1585
1585
  }
1586
1586
  }
1587
- const { runChat } = await import("./boot-DDmudCUI.mjs");
1587
+ const { runChat } = await import("./boot-DPB2qX9x.mjs");
1588
1588
  await runChat({
1589
1589
  appUrl,
1590
1590
  sessionToken: session.value.sessionToken,
@@ -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-DDmudCUI.mjs");
2032
+ const { runChat } = await import("./boot-DPB2qX9x.mjs");
2033
2033
  await runChat({
2034
2034
  appUrl: auth.value.appUrl,
2035
2035
  sessionToken: auth.value.token,
@@ -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-DDmudCUI.mjs");
2414
+ const { runWorkspacePicker } = await import("./boot-DPB2qX9x.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-JFzZrNRz.mjs");
2493
+ const { PortalDaemonClient } = await import("./daemon-client-C6d14JU1.mjs");
2494
2494
  let lastLine = "";
2495
2495
  let signalConnected;
2496
2496
  const connected = new Promise((resolve) => {
@@ -2736,7 +2736,7 @@ const forwardCommand = {
2736
2736
  const agent = resolveAgent$1((await fetchPortalDevices(session)).agents, argv.agent ?? null);
2737
2737
  const targetPort = argv.port;
2738
2738
  const localPort = argv["local-port"] ?? targetPort;
2739
- const { startForward } = await import("./forward-18QoL5dO.mjs");
2739
+ const { startForward } = await import("./forward-DL6DYqyc.mjs");
2740
2740
  const listener = await startForward({
2741
2741
  auth: session,
2742
2742
  agentId: agent.id,
@@ -4336,7 +4336,7 @@ if (process.argv.includes(UPDATE_WORKER_FLAG)) {
4336
4336
  process.exit(0);
4337
4337
  }
4338
4338
  if (process.argv.includes(PORTAL_DAEMON_FLAG)) {
4339
- const { runPortalDaemon } = await import("./daemon-DnADJ1gG.mjs");
4339
+ const { runPortalDaemon } = await import("./daemon-C5e4hKwL.mjs");
4340
4340
  runPortalDaemon(process.argv);
4341
4341
  } else runCli();
4342
4342
  function runCli() {
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import { A as takenAliasNames, C as themesForMode, D as dedupeAliasName, E as aliasActivationHint, F as getActiveWorkspaceId, L as listWorkspaces, N as detectShell, O as installAgentAlias, T as machineOsFromPlatform, _ as theme, a as installCrashHandler, b as themeModeFromColorFgBg, c as MARK_CELLS, d as splashFitsWidth, f as DEFAULT_THEME_ID, g as noColorRequested, h as monoTheme, i as writeArtifact, k as slugifyAliasName, l as WORDMARK, m as findTheme, n as profilingEnabled, o as buildCrashReport, p as applyTheme, r as record, s as writeCrashReport, v as themeForMode, x as themeVersion, y as themeMode, z as setActiveWorkspace } from "./profiler-DLIjDaz_.mjs";
2
+ import { A as takenAliasNames, C as themesForMode, D as dedupeAliasName, E as aliasActivationHint, F as getActiveWorkspaceId, L as listWorkspaces, N as detectShell, O as installAgentAlias, T as machineOsFromPlatform, _ as theme, a as installCrashHandler, b as themeModeFromColorFgBg, c as MARK_CELLS, d as splashFitsWidth, f as DEFAULT_THEME_ID, g as noColorRequested, h as monoTheme, i as writeArtifact, k as slugifyAliasName, l as WORDMARK, m as findTheme, n as profilingEnabled, o as buildCrashReport, p as applyTheme, r as record, s as writeCrashReport, v as themeForMode, x as themeVersion, y as themeMode, z as setActiveWorkspace } from "./profiler-rC7AZSxL.mjs";
3
3
  import { B as resolveWebUrl, C as getConfigPath, O as getReviewStateDir, P as recordDefaultAgent, U as saveTheme, b as DEFAULT_APP_URL, c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, h as specKeyFor, i as resolveAgent, k as getSavedTheme, l as parseExternalOauthConnectParams, m as parseConnectCard, p as computeSettledLabel, s as MASK_CHAR, u as parseOauthConnectParams, y as DEFAULT_API_URL } from "./print-DYiQC5IW.mjs";
4
4
  import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
5
5
  import { a as isRecord, i as errorMessage, n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-xXfqT4SX.mjs";
6
6
  import { i as billingBlockedOutcomeFromSendResponse } from "./billing-blocked-2wju4gC_.mjs";
7
7
  import { t as PortalClient } from "./client-DbqRBquD.mjs";
8
- import "./daemon-DGsTcIVx.mjs";
8
+ import "./daemon-B5SNuIhD.mjs";
9
9
  import "./api-DG5W6iwx.mjs";
10
10
  import { t as SandboxStream } from "./client-c4c5MmgN.mjs";
11
- import { t as PortalDaemonClient } from "./daemon-client-CwkdmYOc.mjs";
11
+ import { t as PortalDaemonClient } from "./daemon-client-CneyOup7.mjs";
12
12
  import { t as runRawPtyPassthrough } from "./raw-pty-DY4KelZW.mjs";
13
13
  import * as os$1 from "node:os";
14
14
  import { homedir, platform, release, tmpdir } from "node:os";
@@ -20,7 +20,7 @@ import { access, appendFile, mkdir, readFile, unlink, writeFile } from "node:fs/
20
20
  * the commit time of the built tree is one monotonic clock they share.
21
21
  */
22
22
  function portalDaemonBuild() {
23
- return "1786738566";
23
+ return "1786741092";
24
24
  }
25
25
  /**
26
26
  * Whether a client carrying `mine` should replace a running daemon carrying
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import "./client-DbqRBquD.mjs";
3
- import { a as runPortalDaemon, i as queryDaemonStatus, n as ensureDaemonRunning, o as startPortalDaemon, r as isDaemonListening, s as stopDaemon, t as PortalDaemon } from "./daemon-DGsTcIVx.mjs";
3
+ import { a as runPortalDaemon, i as queryDaemonStatus, n as ensureDaemonRunning, o as startPortalDaemon, r as isDaemonListening, s as stopDaemon, t as PortalDaemon } from "./daemon-B5SNuIhD.mjs";
4
4
  import "./api-DG5W6iwx.mjs";
5
5
 
6
6
  export { runPortalDaemon };
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import "./client-DbqRBquD.mjs";
3
+ import "./daemon-B5SNuIhD.mjs";
4
+ import "./api-DG5W6iwx.mjs";
5
+ import { t as PortalDaemonClient } from "./daemon-client-CneyOup7.mjs";
6
+
7
+ export { PortalDaemonClient };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { c as LOCAL_PROTOCOL_VERSION, d as encodeLine, f as makeLineParser, n as ensureDaemonRunning, p as parseDaemonMessage, u as daemonPaths } from "./daemon-DGsTcIVx.mjs";
2
+ import { c as LOCAL_PROTOCOL_VERSION, d as encodeLine, f as makeLineParser, n as ensureDaemonRunning, p as parseDaemonMessage, u as daemonPaths } from "./daemon-B5SNuIhD.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { connect } from "node:net";
5
5
 
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env node
2
+ import { t as fetchForwardTarget } from "./api-DG5W6iwx.mjs";
3
+ import net from "node:net";
4
+ import { WebSocket, createWebSocketStream } from "ws";
5
+
6
+ //#region src/chat/portal/tunnel-pool.ts
7
+ const MAX_AGE_MS = 4e4;
8
+ const SWEEP_INTERVAL_MS = 1e4;
9
+ const REFILL_BACKOFF_MS = 15e3;
10
+ /**
11
+ * Pre-warmed tunnel pool for the forward portal's accept path.
12
+ *
13
+ * Dialing a tunnel costs a full edge round-trip (~300ms measured). The pool
14
+ * pays that in the background so accept can adopt a warm tunnel and move the
15
+ * first byte immediately. Target starts at `initialSize` and grows by one on
16
+ * each miss, never above `maxSize` — a miss still cold-dials (today's path);
17
+ * growth only helps the next wave. No decay: a runaway client can hold
18
+ * `maxSize` idle edge sockets until `forward` exits.
19
+ *
20
+ * `dial` must resolve with a stream that is already paused, so any bytes a
21
+ * server-first protocol sends before adoption buffer instead of dropping.
22
+ */
23
+ function createTunnelPool({ initialSize, maxSize, dial, log }) {
24
+ let entries = [];
25
+ let dialsInFlight = 0;
26
+ let backoffUntil = 0;
27
+ let closed = false;
28
+ let targetSize = initialSize;
29
+ function refill() {
30
+ if (closed) return;
31
+ if (Date.now() < backoffUntil) return;
32
+ while (entries.length + dialsInFlight < targetSize) {
33
+ dialsInFlight += 1;
34
+ dial().then((tunnel) => {
35
+ dialsInFlight -= 1;
36
+ if (closed) {
37
+ tunnel.close();
38
+ return;
39
+ }
40
+ const entry = {
41
+ tunnel,
42
+ dialedAt: Date.now(),
43
+ dead: false
44
+ };
45
+ tunnel.stream.once("close", () => {
46
+ entry.dead = true;
47
+ });
48
+ tunnel.stream.once("error", () => {
49
+ entry.dead = true;
50
+ });
51
+ entries.push(entry);
52
+ }).catch((err) => {
53
+ dialsInFlight -= 1;
54
+ backoffUntil = Date.now() + REFILL_BACKOFF_MS;
55
+ log(`forward: pool dial failed (backing off): ${err instanceof Error ? err.message : String(err)}`);
56
+ });
57
+ }
58
+ }
59
+ function sweep() {
60
+ const now = Date.now();
61
+ const keep = [];
62
+ for (const entry of entries) if (entry.dead || now - entry.dialedAt > MAX_AGE_MS) entry.tunnel.close();
63
+ else keep.push(entry);
64
+ entries = keep;
65
+ refill();
66
+ }
67
+ const sweeper = setInterval(sweep, SWEEP_INTERVAL_MS);
68
+ sweeper.unref();
69
+ refill();
70
+ return {
71
+ take: () => {
72
+ while (entries.length > 0) {
73
+ const entry = entries.pop();
74
+ if (!entry) break;
75
+ if (entry.dead || entry.tunnel.stream.destroyed) continue;
76
+ refill();
77
+ return entry.tunnel;
78
+ }
79
+ if (targetSize < maxSize) targetSize += 1;
80
+ refill();
81
+ return null;
82
+ },
83
+ close: () => {
84
+ closed = true;
85
+ clearInterval(sweeper);
86
+ for (const entry of entries) entry.tunnel.close();
87
+ entries = [];
88
+ }
89
+ };
90
+ }
91
+
92
+ //#endregion
93
+ //#region src/chat/portal/forward.ts
94
+ const TARGET_REFRESH_SAFETY_MS = 12e4;
95
+ const INITIAL_POOL_SIZE = 8;
96
+ const MAX_POOL_SIZE = 16;
97
+ /**
98
+ * The reverse portal's client half: listen on the local machine's loopback and
99
+ * pipe each TCP connection to the agent sandbox's daemon (`/portal/tcp`),
100
+ * which pipes to the sandbox's own loopback. The daemon is reached through the
101
+ * agent-webserver edge Worker (sandboxes have public ingress disabled; the
102
+ * Worker owns boot-resolution and injects the sandbox edge-auth token), so a
103
+ * sandbox recycle just costs the next connection a cold-start wait rather
104
+ * than invalidating the forward.
105
+ */
106
+ async function startForward({ auth, agentId, localPort, targetPort, log }) {
107
+ let target = await fetchForwardTarget(auth, agentId);
108
+ let mintedAt = Date.now();
109
+ async function freshTarget() {
110
+ const ttlMs = target.expiresInSeconds * 1e3;
111
+ if (Date.now() - mintedAt > ttlMs - TARGET_REFRESH_SAFETY_MS) {
112
+ target = await fetchForwardTarget(auth, agentId);
113
+ mintedAt = Date.now();
114
+ }
115
+ return target;
116
+ }
117
+ /**
118
+ * Dial one tunnel through the edge to the daemon's /portal/tcp. Resolves
119
+ * once the upgrade completes, with the duplex PAUSED — the pool holds it
120
+ * warm without dropping any early bytes, and adoption resumes it by piping.
121
+ */
122
+ async function dialTunnel() {
123
+ const resolved = await freshTarget();
124
+ const ws = new WebSocket(`${resolved.daemonOrigin.replace(/^http/, "ws")}/portal/tcp?port=${targetPort}`, { headers: { authorization: `Bearer ${resolved.token}` } });
125
+ return new Promise((resolve, reject) => {
126
+ ws.once("error", reject);
127
+ ws.once("open", () => {
128
+ ws.removeListener("error", reject);
129
+ const stream = createWebSocketStream(ws);
130
+ stream.pause();
131
+ resolve({
132
+ stream,
133
+ close: () => stream.destroy()
134
+ });
135
+ });
136
+ });
137
+ }
138
+ const pool = createTunnelPool({
139
+ initialSize: INITIAL_POOL_SIZE,
140
+ maxSize: MAX_POOL_SIZE,
141
+ dial: dialTunnel,
142
+ log
143
+ });
144
+ const server = net.createServer((sock) => {
145
+ sock.pause();
146
+ const warm = pool.take();
147
+ if (warm) {
148
+ warm.stream.on("error", () => sock.destroy());
149
+ sock.on("error", () => warm.stream.destroy());
150
+ sock.pipe(warm.stream).pipe(sock);
151
+ sock.resume();
152
+ return;
153
+ }
154
+ (async () => {
155
+ let tunnel;
156
+ try {
157
+ tunnel = await dialTunnel();
158
+ } catch (err) {
159
+ log(`forward: tunnel connect failed: ${err instanceof Error ? err.message : String(err)}`);
160
+ sock.destroy();
161
+ return;
162
+ }
163
+ tunnel.stream.on("error", () => sock.destroy());
164
+ sock.on("error", () => tunnel.stream.destroy());
165
+ sock.pipe(tunnel.stream).pipe(sock);
166
+ sock.resume();
167
+ })();
168
+ });
169
+ await new Promise((resolve, reject) => {
170
+ server.once("error", reject);
171
+ server.listen(localPort, "127.0.0.1", () => {
172
+ server.removeListener("error", reject);
173
+ resolve();
174
+ });
175
+ });
176
+ const addr = server.address();
177
+ return {
178
+ port: addr && typeof addr === "object" ? addr.port : localPort,
179
+ close: () => new Promise((resolve) => {
180
+ pool.close();
181
+ server.close(() => resolve());
182
+ })
183
+ };
184
+ }
185
+
186
+ //#endregion
187
+ export { startForward };
@@ -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.5.0-beta.27";
11
+ var version$1 = "0.5.0-beta.28";
12
12
 
13
13
  //#endregion
14
14
  //#region src/auth/organization.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.5.0-beta.27",
3
+ "version": "0.5.0-beta.28",
4
4
  "description": "Skydive CLI — cloud agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./client-DbqRBquD.mjs";
3
- import "./daemon-DGsTcIVx.mjs";
4
- import "./api-DG5W6iwx.mjs";
5
- import { t as PortalDaemonClient } from "./daemon-client-CwkdmYOc.mjs";
6
-
7
- export { PortalDaemonClient };
@@ -1,68 +0,0 @@
1
- #!/usr/bin/env node
2
- import { t as fetchForwardTarget } from "./api-DG5W6iwx.mjs";
3
- import net from "node:net";
4
- import { WebSocket, createWebSocketStream } from "ws";
5
-
6
- //#region src/chat/portal/forward.ts
7
- const TARGET_REFRESH_SAFETY_MS = 12e4;
8
- /**
9
- * The reverse portal's client half: listen on the local machine's loopback and
10
- * pipe each TCP connection to the agent sandbox's daemon (`/portal/tcp`),
11
- * which pipes to the sandbox's own loopback. The daemon is reached through the
12
- * agent-webserver edge Worker (sandboxes have public ingress disabled; the
13
- * Worker owns boot-resolution and injects the sandbox edge-auth token), so a
14
- * sandbox recycle just costs the next connection a cold-start wait rather
15
- * than invalidating the forward.
16
- */
17
- async function startForward({ auth, agentId, localPort, targetPort, log }) {
18
- let target = await fetchForwardTarget(auth, agentId);
19
- let mintedAt = Date.now();
20
- async function freshTarget() {
21
- const ttlMs = target.expiresInSeconds * 1e3;
22
- if (Date.now() - mintedAt > ttlMs - TARGET_REFRESH_SAFETY_MS) {
23
- target = await fetchForwardTarget(auth, agentId);
24
- mintedAt = Date.now();
25
- }
26
- return target;
27
- }
28
- const server = net.createServer((sock) => {
29
- sock.pause();
30
- (async () => {
31
- let resolved;
32
- try {
33
- resolved = await freshTarget();
34
- } catch (err) {
35
- log(`forward: token refresh failed: ${err instanceof Error ? err.message : String(err)}`);
36
- sock.destroy();
37
- return;
38
- }
39
- const ws = new WebSocket(`${resolved.daemonOrigin.replace(/^http/, "ws")}/portal/tcp?port=${targetPort}`, { headers: { authorization: `Bearer ${resolved.token}` } });
40
- ws.on("open", () => {
41
- const stream = createWebSocketStream(ws);
42
- stream.on("error", () => sock.destroy());
43
- sock.on("error", () => stream.destroy());
44
- sock.pipe(stream).pipe(sock);
45
- sock.resume();
46
- });
47
- ws.on("error", (err) => {
48
- log(`forward: tunnel connect failed: ${err.message}`);
49
- sock.destroy();
50
- });
51
- })();
52
- });
53
- await new Promise((resolve, reject) => {
54
- server.once("error", reject);
55
- server.listen(localPort, "127.0.0.1", () => {
56
- server.removeListener("error", reject);
57
- resolve();
58
- });
59
- });
60
- const addr = server.address();
61
- return {
62
- port: addr && typeof addr === "object" ? addr.port : localPort,
63
- close: () => new Promise((resolve) => server.close(() => resolve()))
64
- };
65
- }
66
-
67
- //#endregion
68
- export { startForward };