skydive-cli 0.4.1-beta.6 → 0.5.0-beta.10
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/README.md +15 -4
- package/dist/js/api-DG5W6iwx.mjs +131 -0
- package/dist/js/bin.mjs +79 -33
- package/dist/js/{boot-B48inW5D.mjs → boot-BuxaHgFi.mjs} +241 -74
- package/dist/js/client-BuU34IVE.mjs +5 -0
- package/dist/js/{client-mykp1DVb.mjs → client-DabRpc_T.mjs} +187 -118
- package/dist/js/{daemon-CfbpCjAw.mjs → daemon-CYYE2BTu.mjs} +179 -28
- package/dist/js/{daemon-D4ZX3wNG.mjs → daemon-Dk5LeS1S.mjs} +3 -2
- package/dist/js/{daemon-client-6xtta_n2.mjs → daemon-client-3A9afTFC.mjs} +35 -10
- package/dist/js/daemon-client-Dh7G9RxC.mjs +7 -0
- package/dist/js/forward-18QoL5dO.mjs +68 -0
- package/dist/js/{print-ba_0hiV9.mjs → print-Bo06tvfV.mjs} +2 -2
- package/dist/js/{print-CbayCa87.mjs → print-ClPkgR9z.mjs} +105 -29
- package/dist/js/{print-share-ZV88PqDu.mjs → print-share-CKLPmsg0.mjs} +5 -2
- package/dist/js/{profiler-94qrV53c.mjs → profiler-Zs3BHURc.mjs} +2 -2
- package/dist/js/{raw-pty-B6mAroiI.mjs → raw-pty-DY4KelZW.mjs} +1 -1
- package/dist/js/raw-pty-DmdUf4_w.mjs +5 -0
- package/dist/js/{rest-DADJh0bi.mjs → rest-D29qNkto.mjs} +1 -1
- package/dist/js/{rest-BY2nADw5.mjs → rest-I3imNduB.mjs} +6 -11
- package/package.json +2 -2
- package/dist/js/client-NBj0KZSP.mjs +0 -4
- package/dist/js/daemon-client-BHehCLrO.mjs +0 -6
- package/dist/js/raw-pty-3EkG-jjH.mjs +0 -5
- /package/dist/js/{client-Cn2af31H.mjs → client-c4c5MmgN.mjs} +0 -0
package/README.md
CHANGED
|
@@ -261,6 +261,16 @@ internal API. On launch it walks you through:
|
|
|
261
261
|
formatting, collapsed reasoning, and rich rendering of tool calls
|
|
262
262
|
(`bash`, `edit`/`write` diffs, `read`, `grep`/`glob`).
|
|
263
263
|
|
|
264
|
+
The chat TUI remembers the last agent you talked to: every conversation it
|
|
265
|
+
opens records that agent as your default, and the next bare `skydive chat`
|
|
266
|
+
drops straight into a new conversation with it — no picker. An explicit
|
|
267
|
+
`--agent` or `--resume` still wins for that invocation, and the conversation
|
|
268
|
+
it opens sets the default like any other. You can also seed or override the
|
|
269
|
+
default by hand with `skydive config set defaultAgent <id|slug|name>`. A
|
|
270
|
+
stored value that no longer matches (renamed or archived agent) falls back
|
|
271
|
+
to the agent picker with a notice, and picking an agent there heals it
|
|
272
|
+
automatically.
|
|
273
|
+
|
|
264
274
|
You can change conversation without leaving chat. `/conversation` (aliases
|
|
265
275
|
`/conversations`, `/conv`) reopens the conversation list, and
|
|
266
276
|
`/conversation <id>` — an id, its `/c/<id>` link, or a pasted
|
|
@@ -463,10 +473,11 @@ skydive config path # print the config.json location
|
|
|
463
473
|
|
|
464
474
|
Editable keys:
|
|
465
475
|
|
|
466
|
-
| Key | Type | Effect
|
|
467
|
-
| --------------------- | ------- |
|
|
468
|
-
| `shareMachineDefault` | boolean | When `true`, `skydive chat` (TUI and `-p`) shares this machine over the portal on launch, as if `--share-machine` were passed. An explicit `--share-machine`/`--no-share-machine` overrides it per invocation. Default `false`.
|
|
469
|
-
| `updateCheck` | boolean | When `false`, disables the daily background update check and its "Update available" notice. The persistent equivalent of setting `SKYDIVE_NO_UPDATE_CHECK` (or `NO_UPDATE_NOTIFIER`) in the environment. Default `true`.
|
|
476
|
+
| Key | Type | Effect |
|
|
477
|
+
| --------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
478
|
+
| `shareMachineDefault` | boolean | When `true`, `skydive chat` (TUI and `-p`) shares this machine over the portal on launch, as if `--share-machine` were passed. An explicit `--share-machine`/`--no-share-machine` overrides it per invocation. Default `false`. |
|
|
479
|
+
| `updateCheck` | boolean | When `false`, disables the daily background update check and its "Update available" notice. The persistent equivalent of setting `SKYDIVE_NO_UPDATE_CHECK` (or `NO_UPDATE_NOTIFIER`) in the environment. Default `true`. |
|
|
480
|
+
| `defaultAgent` | string | Agent (id, slug, or name) a bare `skydive chat` opens a new conversation with, skipping the agent picker. Maintained automatically: every conversation the chat TUI opens records its agent here (last used wins). `skydive config set defaultAgent` seeds or overrides it; an explicit `--agent`/`--resume` wins per invocation; a value that no longer matches falls back to the picker. |
|
|
470
481
|
|
|
471
482
|
The remaining keys (`apiKey`, `apiUrl`, `sessionToken`, `appUrl`, `themeDark`,
|
|
472
483
|
`themeLight`, …) are credentials or flow-managed state — leave them to the
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
//#region ../portal-daemon/src/api.ts
|
|
6
|
+
/**
|
|
7
|
+
* The portal's session-authed REST surface, shared by `PortalClient` (the
|
|
8
|
+
* TUI/`portal open` connection) and the `skydive portal` management
|
|
9
|
+
* commands, so the endpoint contracts and response schemas live in exactly
|
|
10
|
+
* one place.
|
|
11
|
+
*/
|
|
12
|
+
const deviceSchema = z.object({
|
|
13
|
+
id: z.string(),
|
|
14
|
+
machineName: z.string(),
|
|
15
|
+
friendlyName: z.string(),
|
|
16
|
+
connected: z.boolean(),
|
|
17
|
+
lastSeen: z.string().nullable(),
|
|
18
|
+
grantedAgentIds: z.array(z.string())
|
|
19
|
+
});
|
|
20
|
+
const devicesResponseSchema = z.object({
|
|
21
|
+
devices: z.array(deviceSchema),
|
|
22
|
+
agents: z.array(z.object({
|
|
23
|
+
id: z.string(),
|
|
24
|
+
name: z.string()
|
|
25
|
+
}))
|
|
26
|
+
});
|
|
27
|
+
const deviceTokenSchema = z.object({ token: z.string().min(1) });
|
|
28
|
+
async function portalFetch(auth, path, init) {
|
|
29
|
+
const res = await fetch(`${auth.appUrl}${path}`, {
|
|
30
|
+
method: init.method,
|
|
31
|
+
headers: {
|
|
32
|
+
authorization: `Bearer ${auth.sessionToken}`,
|
|
33
|
+
accept: "application/json",
|
|
34
|
+
...init.body ? { "content-type": "application/json" } : {}
|
|
35
|
+
},
|
|
36
|
+
...init.body ? { body: init.body } : {}
|
|
37
|
+
});
|
|
38
|
+
if (!res.ok) {
|
|
39
|
+
const body = await res.text().catch(() => "");
|
|
40
|
+
throw new HttpError(res.status, body);
|
|
41
|
+
}
|
|
42
|
+
return res.json();
|
|
43
|
+
}
|
|
44
|
+
async function fetchPortalDevices(auth) {
|
|
45
|
+
const json = await portalFetch(auth, "/api/v1/portal/devices", { method: "GET" });
|
|
46
|
+
return devicesResponseSchema.parse(json);
|
|
47
|
+
}
|
|
48
|
+
const registerResponseSchema = z.object({ device: z.object({ id: z.string() }) });
|
|
49
|
+
/**
|
|
50
|
+
* Register this machine's device row without connecting. Connecting registers
|
|
51
|
+
* as a side effect; this covers granting an agent on a machine that has never
|
|
52
|
+
* shared yet (the grant references the device row).
|
|
53
|
+
*/
|
|
54
|
+
async function registerPortalDevice(auth, { machineName, friendlyName }) {
|
|
55
|
+
const json = await portalFetch(auth, "/api/v1/portal/devices", {
|
|
56
|
+
method: "POST",
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
machineName,
|
|
59
|
+
friendlyName
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
return registerResponseSchema.parse(json).device;
|
|
63
|
+
}
|
|
64
|
+
/** Short-lived token the machine presents when dialing the portal WebSocket. */
|
|
65
|
+
async function mintPortalDeviceToken(auth) {
|
|
66
|
+
const json = await portalFetch(auth, "/api/v1/portal/device-token", { method: "POST" });
|
|
67
|
+
return deviceTokenSchema.parse(json).token;
|
|
68
|
+
}
|
|
69
|
+
const forwardTargetSchema = z.object({
|
|
70
|
+
daemonOrigin: z.string().min(1),
|
|
71
|
+
token: z.string().min(1),
|
|
72
|
+
expiresInSeconds: z.number()
|
|
73
|
+
});
|
|
74
|
+
/**
|
|
75
|
+
* Everything `portal forward` needs to dial an agent's sandbox daemon through
|
|
76
|
+
* the agent-webserver edge Worker: the daemon's stable public origin and a
|
|
77
|
+
* daemon auth token (canUse-gated server-side).
|
|
78
|
+
*/
|
|
79
|
+
async function fetchForwardTarget(auth, agentId) {
|
|
80
|
+
const json = await portalFetch(auth, "/api/v1/portal/forward-target", {
|
|
81
|
+
method: "POST",
|
|
82
|
+
body: JSON.stringify({ agentId })
|
|
83
|
+
});
|
|
84
|
+
return forwardTargetSchema.parse(json);
|
|
85
|
+
}
|
|
86
|
+
async function grantPortalAccess(auth, { deviceId, agentId, conversationId }) {
|
|
87
|
+
await portalFetch(auth, `/api/v1/portal/devices/${encodeURIComponent(deviceId)}/grants`, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
body: JSON.stringify({
|
|
90
|
+
agentId,
|
|
91
|
+
conversationId
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async function revokePortalAccess(auth, { deviceId, agentId }) {
|
|
96
|
+
await portalFetch(auth, `/api/v1/portal/devices/${encodeURIComponent(deviceId)}/grants/${encodeURIComponent(agentId)}`, { method: "DELETE" });
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The device row for a given machine identity. Matching is by `machineName`
|
|
100
|
+
* equality — the stable handle the machine registers under, not the display
|
|
101
|
+
* label.
|
|
102
|
+
*/
|
|
103
|
+
function findThisDevice(devices, machineName) {
|
|
104
|
+
return devices.find((device) => device.machineName === machineName) ?? null;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* One-time grant migration onto the merged device. Earlier CLI builds
|
|
108
|
+
* registered a separate `<machineName>-cli` device, so a user's existing
|
|
109
|
+
* approvals hang off that row; the merged device would start with zero grants
|
|
110
|
+
* and every already-authorized agent would ask again. Copy any grant the
|
|
111
|
+
* merged device is missing (the grant endpoint upserts, so re-runs are
|
|
112
|
+
* no-ops). The legacy row is left in place — an old CLI build may still
|
|
113
|
+
* connect under it. Returns how many grants were copied.
|
|
114
|
+
*/
|
|
115
|
+
async function unifyLegacyCliGrants(auth, machineName) {
|
|
116
|
+
const { devices } = await fetchPortalDevices(auth);
|
|
117
|
+
const merged = findThisDevice(devices, machineName);
|
|
118
|
+
const legacy = findThisDevice(devices, `${machineName}-cli`);
|
|
119
|
+
if (!merged || !legacy) return 0;
|
|
120
|
+
const have = new Set(merged.grantedAgentIds);
|
|
121
|
+
const missing = legacy.grantedAgentIds.filter((id) => !have.has(id));
|
|
122
|
+
for (const agentId of missing) await grantPortalAccess(auth, {
|
|
123
|
+
deviceId: merged.id,
|
|
124
|
+
agentId,
|
|
125
|
+
conversationId: null
|
|
126
|
+
});
|
|
127
|
+
return missing.length;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
//#endregion
|
|
131
|
+
export { mintPortalDeviceToken as a, unifyLegacyCliGrants as c, grantPortalAccess as i, fetchPortalDevices as n, registerPortalDevice as o, findThisDevice as r, revokePortalAccess as s, fetchForwardTarget as t };
|
package/dist/js/bin.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
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-
|
|
3
|
-
import { A as
|
|
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-Zs3BHURc.mjs";
|
|
3
|
+
import { A as getStoredApiKeyId, B as saveConfig, C as getDefaultAgent, E as getPromptHistoryPath, F as resolveChatAuth, I as resolveConfig, L as resolveManagementAuth, M as getUpdateCheckDisabled, P as resolveAppUrl, R as resolveSession, S as getConfigPath, T as getPreference, U as setLastSeenVersion, V as saveSession, _ as API_KEY_PREFIX, b as PREFERENCES, g as API_KEY_FAMILY_PREFIX, h as API_KEYS_URL, i as resolveAgent, j as getStoredApiKeyWorkspaceName, k as getShareMachineDefault, v as DEFAULT_API_URL, w as getLastSeenVersion, x as deleteConfig } from "./print-ClPkgR9z.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
|
-
import { t as createRestClient } from "./rest-
|
|
6
|
+
import { t as createRestClient } from "./rest-I3imNduB.mjs";
|
|
7
7
|
import "./billing-blocked-2wju4gC_.mjs";
|
|
8
|
-
import {
|
|
9
|
-
import { i as queryDaemonStatus, l as PORTAL_DAEMON_FLAG, n as ensureDaemonRunning, s as stopDaemon, u as daemonPaths } from "./daemon-
|
|
10
|
-
import {
|
|
8
|
+
import { r as resolveMachineIdentity } from "./client-DabRpc_T.mjs";
|
|
9
|
+
import { i as queryDaemonStatus, l as PORTAL_DAEMON_FLAG, n as ensureDaemonRunning, s as stopDaemon, u as daemonPaths } from "./daemon-CYYE2BTu.mjs";
|
|
10
|
+
import { i as grantPortalAccess, n as fetchPortalDevices, o as registerPortalDevice, r as findThisDevice, s as revokePortalAccess } from "./api-DG5W6iwx.mjs";
|
|
11
|
+
import { t as SandboxStream } from "./client-c4c5MmgN.mjs";
|
|
11
12
|
import { hideBin } from "yargs/helpers";
|
|
12
13
|
import yargs from "yargs";
|
|
13
14
|
import os, { hostname, tmpdir } from "node:os";
|
|
@@ -1516,7 +1517,7 @@ const importCommand = {
|
|
|
1516
1517
|
process.exit(1);
|
|
1517
1518
|
}
|
|
1518
1519
|
}
|
|
1519
|
-
const { runChat } = await import("./boot-
|
|
1520
|
+
const { runChat } = await import("./boot-BuxaHgFi.mjs");
|
|
1520
1521
|
await runChat({
|
|
1521
1522
|
appUrl,
|
|
1522
1523
|
sessionToken: session.value.sessionToken,
|
|
@@ -1526,6 +1527,7 @@ const importCommand = {
|
|
|
1526
1527
|
agentSelector: argv.agent ?? null,
|
|
1527
1528
|
conversationId: null,
|
|
1528
1529
|
newConversation: false,
|
|
1530
|
+
defaultAgentSelector: getDefaultAgent(),
|
|
1529
1531
|
seedPrompt: buildImportSeedPrompt(process.cwd())
|
|
1530
1532
|
});
|
|
1531
1533
|
}
|
|
@@ -1543,7 +1545,7 @@ async function runImportPrintMode({ argv, appUrl }) {
|
|
|
1543
1545
|
printError(`${session.error.message} For non-interactive use, run \`skydive auth login --web\` first, or set SKYDIVE_SESSION_TOKEN.`);
|
|
1544
1546
|
process.exit(1);
|
|
1545
1547
|
}
|
|
1546
|
-
const { connectMachineShare } = await import("./print-share-
|
|
1548
|
+
const { connectMachineShare } = await import("./print-share-CKLPmsg0.mjs");
|
|
1547
1549
|
const machineShare = await connectMachineShare({
|
|
1548
1550
|
appUrl,
|
|
1549
1551
|
sessionToken: session.value.sessionToken,
|
|
@@ -1551,7 +1553,7 @@ async function runImportPrintMode({ argv, appUrl }) {
|
|
|
1551
1553
|
});
|
|
1552
1554
|
const extra = (argv.print ?? "").trim();
|
|
1553
1555
|
const prompt = buildImportSeedPrompt(process.cwd()) + (extra ? `\n\nAdditional instructions: ${extra}` : "");
|
|
1554
|
-
const { runPrint } = await import("./print-
|
|
1556
|
+
const { runPrint } = await import("./print-Bo06tvfV.mjs");
|
|
1555
1557
|
try {
|
|
1556
1558
|
const result = await runPrint({
|
|
1557
1559
|
appUrl,
|
|
@@ -1928,7 +1930,7 @@ const chatCommand = {
|
|
|
1928
1930
|
sessionToken: auth.value.token,
|
|
1929
1931
|
agentSelector: argv.agent ?? null
|
|
1930
1932
|
});
|
|
1931
|
-
const { runChat } = await import("./boot-
|
|
1933
|
+
const { runChat } = await import("./boot-BuxaHgFi.mjs");
|
|
1932
1934
|
await runChat({
|
|
1933
1935
|
appUrl: auth.value.appUrl,
|
|
1934
1936
|
sessionToken: auth.value.token,
|
|
@@ -1940,6 +1942,7 @@ const chatCommand = {
|
|
|
1940
1942
|
agentSelector: argv.agent ?? null,
|
|
1941
1943
|
conversationId: argv.conversation ?? null,
|
|
1942
1944
|
newConversation: resolveNewConversation(argv),
|
|
1945
|
+
defaultAgentSelector: getDefaultAgent(),
|
|
1943
1946
|
seedPrompt: null
|
|
1944
1947
|
});
|
|
1945
1948
|
}
|
|
@@ -1995,7 +1998,7 @@ async function runPrintMode({ argv, appUrl }) {
|
|
|
1995
1998
|
printError(`${auth.error.message} For non-interactive use, run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
|
|
1996
1999
|
process.exit(1);
|
|
1997
2000
|
}
|
|
1998
|
-
const { runPrint, readStdin } = await import("./print-
|
|
2001
|
+
const { runPrint, readStdin } = await import("./print-Bo06tvfV.mjs");
|
|
1999
2002
|
await ensureAgentWorkspace({
|
|
2000
2003
|
appUrl,
|
|
2001
2004
|
sessionToken: auth.value.token,
|
|
@@ -2020,7 +2023,7 @@ async function runPrintMode({ argv, appUrl }) {
|
|
|
2020
2023
|
process.exit(1);
|
|
2021
2024
|
}
|
|
2022
2025
|
} else {
|
|
2023
|
-
const { connectMachineShare } = await import("./print-share-
|
|
2026
|
+
const { connectMachineShare } = await import("./print-share-CKLPmsg0.mjs");
|
|
2024
2027
|
machineShare = await connectMachineShare({
|
|
2025
2028
|
appUrl: auth.value.appUrl,
|
|
2026
2029
|
sessionToken: auth.value.token,
|
|
@@ -2084,7 +2087,7 @@ const getCommand$1 = {
|
|
|
2084
2087
|
printError(`${auth.error.message} Run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
|
|
2085
2088
|
process.exit(1);
|
|
2086
2089
|
}
|
|
2087
|
-
const { messageGet } = await import("./print-
|
|
2090
|
+
const { messageGet } = await import("./print-Bo06tvfV.mjs");
|
|
2088
2091
|
try {
|
|
2089
2092
|
const result = await messageGet({
|
|
2090
2093
|
appUrl: auth.value.appUrl,
|
|
@@ -2287,7 +2290,7 @@ const switchCommand = {
|
|
|
2287
2290
|
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.");
|
|
2288
2291
|
process.exit(1);
|
|
2289
2292
|
}
|
|
2290
|
-
const { runWorkspacePicker } = await import("./boot-
|
|
2293
|
+
const { runWorkspacePicker } = await import("./boot-BuxaHgFi.mjs");
|
|
2291
2294
|
await runWorkspacePicker(session);
|
|
2292
2295
|
return;
|
|
2293
2296
|
}
|
|
@@ -2366,7 +2369,7 @@ const openCommand = {
|
|
|
2366
2369
|
const agent = argv.agent ? resolveAgent((await fetchPortalDevices(session)).agents, argv.agent) : null;
|
|
2367
2370
|
const cwd = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
|
|
2368
2371
|
const { machineName } = await resolveMachineIdentity();
|
|
2369
|
-
const { PortalDaemonClient } = await import("./daemon-client-
|
|
2372
|
+
const { PortalDaemonClient } = await import("./daemon-client-Dh7G9RxC.mjs");
|
|
2370
2373
|
let lastLine = "";
|
|
2371
2374
|
let signalConnected;
|
|
2372
2375
|
const connected = new Promise((resolve) => {
|
|
@@ -2395,7 +2398,7 @@ const openCommand = {
|
|
|
2395
2398
|
client.enable();
|
|
2396
2399
|
await connected;
|
|
2397
2400
|
if (agent) {
|
|
2398
|
-
await client.grantAgent(agent.id);
|
|
2401
|
+
await client.grantAgent(agent.id, null);
|
|
2399
2402
|
console.log(`portal: granted ${agent.name} access to this machine (persists until revoked)`);
|
|
2400
2403
|
}
|
|
2401
2404
|
console.log(`portal: open. granted agents can run commands on ${machineName} as your user, cwd ${cwd}. ctrl+c to close.`);
|
|
@@ -2415,7 +2418,8 @@ const grantCommand = {
|
|
|
2415
2418
|
})).id;
|
|
2416
2419
|
await grantPortalAccess(session, {
|
|
2417
2420
|
deviceId,
|
|
2418
|
-
agentId: agent.id
|
|
2421
|
+
agentId: agent.id,
|
|
2422
|
+
conversationId: null
|
|
2419
2423
|
});
|
|
2420
2424
|
if (argv.json) {
|
|
2421
2425
|
output(argv, {
|
|
@@ -2424,7 +2428,7 @@ const grantCommand = {
|
|
|
2424
2428
|
});
|
|
2425
2429
|
return;
|
|
2426
2430
|
}
|
|
2427
|
-
console.log(`Granted ${agent.name} access to ${device?.friendlyName ?? friendlyName}. The agent can run commands here whenever its portal is open. Start with \`skydive portal open\`, or pass \`--share-machine\` on a \`chat -p\` run. Revoke with \`skydive portal revoke --agent ${agent.name}\`.`);
|
|
2431
|
+
console.log(`Granted ${agent.name} access to ${device?.friendlyName ?? friendlyName}. The agent can run commands here whenever its portal is open. Start with \`skydive portal open\`, or pass \`--share-machine\` on a \`chat -p\` run. Revoke with \`skydive portal revoke --agent "${agent.name}"\`.`);
|
|
2428
2432
|
}
|
|
2429
2433
|
};
|
|
2430
2434
|
const revokeCommand = {
|
|
@@ -2592,10 +2596,45 @@ const daemonCommand = {
|
|
|
2592
2596
|
builder: (y) => y.command(daemonStatusCommand).command(daemonStopCommand).command(daemonStartCommand).command(daemonRestartCommand).command(daemonLogsCommand).demandCommand(1, "Specify a subcommand: status, stop, start, restart, logs"),
|
|
2593
2597
|
handler: () => {}
|
|
2594
2598
|
};
|
|
2599
|
+
const forwardCommand = {
|
|
2600
|
+
command: "forward <port>",
|
|
2601
|
+
describe: "Forward localhost:<port> to the agent sandbox's port, so a dev server running in the sandbox is reachable at http://localhost:<port> (stays in the foreground; ctrl+c to stop)",
|
|
2602
|
+
builder: (y) => y.positional("port", {
|
|
2603
|
+
type: "number",
|
|
2604
|
+
demandOption: true,
|
|
2605
|
+
describe: "Sandbox port to forward to (e.g. 3000 for a dev server)"
|
|
2606
|
+
}).option("agent", {
|
|
2607
|
+
type: "string",
|
|
2608
|
+
describe: "Agent, by id or name (defaults when the account has one)"
|
|
2609
|
+
}).option("local-port", {
|
|
2610
|
+
type: "number",
|
|
2611
|
+
describe: "Local port to listen on (default: same as <port>)"
|
|
2612
|
+
}),
|
|
2613
|
+
handler: async (argv) => {
|
|
2614
|
+
const session = requireSession(argv);
|
|
2615
|
+
const agent = resolveAgent((await fetchPortalDevices(session)).agents, argv.agent ?? null);
|
|
2616
|
+
const targetPort = argv.port;
|
|
2617
|
+
const localPort = argv["local-port"] ?? targetPort;
|
|
2618
|
+
const { startForward } = await import("./forward-18QoL5dO.mjs");
|
|
2619
|
+
const listener = await startForward({
|
|
2620
|
+
auth: session,
|
|
2621
|
+
agentId: agent.id,
|
|
2622
|
+
localPort,
|
|
2623
|
+
targetPort,
|
|
2624
|
+
log: (msg) => console.error(msg)
|
|
2625
|
+
});
|
|
2626
|
+
console.log(`Forwarding http://localhost:${listener.port} -> ${agent.name}'s sandbox port ${targetPort}. ctrl+c to stop.`);
|
|
2627
|
+
await new Promise((resolve) => {
|
|
2628
|
+
process.once("SIGINT", () => resolve());
|
|
2629
|
+
process.once("SIGTERM", () => resolve());
|
|
2630
|
+
});
|
|
2631
|
+
await listener.close();
|
|
2632
|
+
}
|
|
2633
|
+
};
|
|
2595
2634
|
const portalCommand = {
|
|
2596
2635
|
command: "portal",
|
|
2597
2636
|
describe: "Open this machine's portal to agents and manage their access",
|
|
2598
|
-
builder: (y) => y.command(openCommand).command(grantCommand).command(revokeCommand).command(statusCommand).command(daemonCommand).demandCommand(1, "Specify a subcommand: open, grant, revoke, status, daemon"),
|
|
2637
|
+
builder: (y) => y.command(openCommand).command(grantCommand).command(revokeCommand).command(statusCommand).command(forwardCommand).command(daemonCommand).demandCommand(1, "Specify a subcommand: open, grant, revoke, status, forward, daemon"),
|
|
2599
2638
|
handler: () => {}
|
|
2600
2639
|
};
|
|
2601
2640
|
|
|
@@ -2640,8 +2679,8 @@ const sandboxCommand = {
|
|
|
2640
2679
|
}).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`"),
|
|
2641
2680
|
handler: async (argv) => {
|
|
2642
2681
|
const session = requireSession(argv);
|
|
2643
|
-
const { createRestClient } = await import("./rest-
|
|
2644
|
-
const { resolveAgent } = await import("./print-
|
|
2682
|
+
const { createRestClient } = await import("./rest-D29qNkto.mjs");
|
|
2683
|
+
const { resolveAgent } = await import("./print-Bo06tvfV.mjs");
|
|
2645
2684
|
const client = createRestClient({
|
|
2646
2685
|
appUrl: session.appUrl,
|
|
2647
2686
|
sessionToken: session.sessionToken
|
|
@@ -2710,7 +2749,7 @@ async function runPty({ session, agentId, agentName }) {
|
|
|
2710
2749
|
return 1;
|
|
2711
2750
|
}
|
|
2712
2751
|
console.error(`Connecting to ${agentName}'s sandbox… (Ctrl-] detaches)`);
|
|
2713
|
-
const { runRawPtyPassthrough } = await import("./raw-pty-
|
|
2752
|
+
const { runRawPtyPassthrough } = await import("./raw-pty-DmdUf4_w.mjs");
|
|
2714
2753
|
const result = await runRawPtyPassthrough({
|
|
2715
2754
|
stdin: process.stdin,
|
|
2716
2755
|
stdout: process.stdout,
|
|
@@ -2798,8 +2837,8 @@ const fsEditCommand = {
|
|
|
2798
2837
|
handler: async (argv) => {
|
|
2799
2838
|
const session = requireSession(argv);
|
|
2800
2839
|
const remotePath = argv.path;
|
|
2801
|
-
const { createRestClient } = await import("./rest-
|
|
2802
|
-
const { resolveAgent } = await import("./print-
|
|
2840
|
+
const { createRestClient } = await import("./rest-D29qNkto.mjs");
|
|
2841
|
+
const { resolveAgent } = await import("./print-Bo06tvfV.mjs");
|
|
2803
2842
|
const client = createRestClient({
|
|
2804
2843
|
appUrl: session.appUrl,
|
|
2805
2844
|
sessionToken: session.sessionToken
|
|
@@ -3680,6 +3719,11 @@ function resolvePreference(key) {
|
|
|
3680
3719
|
}
|
|
3681
3720
|
return pref;
|
|
3682
3721
|
}
|
|
3722
|
+
/** Human rendering of a preference value; unset string preferences (null)
|
|
3723
|
+
* print as empty rather than the string "null". */
|
|
3724
|
+
function formatValue(value) {
|
|
3725
|
+
return value === null ? "" : String(value);
|
|
3726
|
+
}
|
|
3683
3727
|
/** One row's worth of state for a preference, used by `list` and `get`. */
|
|
3684
3728
|
function snapshot(pref) {
|
|
3685
3729
|
return {
|
|
@@ -3706,7 +3750,7 @@ const listCommand = {
|
|
|
3706
3750
|
"Description"
|
|
3707
3751
|
], rows.map((r) => [
|
|
3708
3752
|
r.key,
|
|
3709
|
-
|
|
3753
|
+
formatValue(r.value),
|
|
3710
3754
|
r.source,
|
|
3711
3755
|
r.describe
|
|
3712
3756
|
]));
|
|
@@ -3727,7 +3771,7 @@ const getCommand = {
|
|
|
3727
3771
|
output(argv, snapshot(pref));
|
|
3728
3772
|
return;
|
|
3729
3773
|
}
|
|
3730
|
-
console.log(
|
|
3774
|
+
console.log(formatValue(pref.read()));
|
|
3731
3775
|
}
|
|
3732
3776
|
};
|
|
3733
3777
|
const setCommand = {
|
|
@@ -3745,17 +3789,16 @@ const setCommand = {
|
|
|
3745
3789
|
}),
|
|
3746
3790
|
handler: (argv) => {
|
|
3747
3791
|
const pref = resolvePreference(argv.key);
|
|
3748
|
-
const
|
|
3749
|
-
if (
|
|
3750
|
-
printError(`${pref.key}: ${
|
|
3792
|
+
const result = pref.set(argv.value ?? "");
|
|
3793
|
+
if (result.isErr()) {
|
|
3794
|
+
printError(`${pref.key}: ${result.error}`);
|
|
3751
3795
|
process.exit(1);
|
|
3752
3796
|
}
|
|
3753
|
-
pref.write(parsed.value);
|
|
3754
3797
|
if (argv.json) {
|
|
3755
3798
|
output(argv, snapshot(pref));
|
|
3756
3799
|
return;
|
|
3757
3800
|
}
|
|
3758
|
-
if (!argv.quiet) console.log(`Set ${pref.key} = ${
|
|
3801
|
+
if (!argv.quiet) console.log(`Set ${pref.key} = ${formatValue(pref.read())}`);
|
|
3759
3802
|
}
|
|
3760
3803
|
};
|
|
3761
3804
|
const unsetCommand = {
|
|
@@ -3775,7 +3818,10 @@ const unsetCommand = {
|
|
|
3775
3818
|
output(argv, snapshot(pref));
|
|
3776
3819
|
return;
|
|
3777
3820
|
}
|
|
3778
|
-
if (!argv.quiet)
|
|
3821
|
+
if (!argv.quiet) {
|
|
3822
|
+
const fallback = pref.read();
|
|
3823
|
+
console.log(fallback === null ? `Unset ${pref.key}` : `Unset ${pref.key} (now ${formatValue(fallback)} by default)`);
|
|
3824
|
+
}
|
|
3779
3825
|
}
|
|
3780
3826
|
};
|
|
3781
3827
|
const pathCommand = {
|
|
@@ -4169,7 +4215,7 @@ if (process.argv.includes(UPDATE_WORKER_FLAG)) {
|
|
|
4169
4215
|
process.exit(0);
|
|
4170
4216
|
}
|
|
4171
4217
|
if (process.argv.includes(PORTAL_DAEMON_FLAG)) {
|
|
4172
|
-
const { runPortalDaemon } = await import("./daemon-
|
|
4218
|
+
const { runPortalDaemon } = await import("./daemon-Dk5LeS1S.mjs");
|
|
4173
4219
|
runPortalDaemon(process.argv);
|
|
4174
4220
|
} else runCli();
|
|
4175
4221
|
function runCli() {
|