skydive-cli 0.5.0-beta.3 → 0.5.0-beta.30
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/CHANGELOG.md +3 -0
- package/README.md +15 -4
- package/dist/js/api-DG5W6iwx.mjs +131 -0
- package/dist/js/bin.mjs +251 -84
- package/dist/js/{boot-BVVbrf2k.mjs → boot-0-dpPM1_.mjs} +2051 -591
- package/dist/js/{client-Dd5sMXPv.mjs → client-DbqRBquD.mjs} +206 -133
- package/dist/js/client-hH2PJL8y.mjs +5 -0
- package/dist/js/{daemon-DDBIumf1.mjs → daemon-Ce8WLdGI.mjs} +3 -2
- package/dist/js/{daemon-Bq93vOIk.mjs → daemon-DuxaUuek.mjs} +93 -7
- package/dist/js/{daemon-client-Bewt98dE.mjs → daemon-client-C4heMWTq.mjs} +12 -8
- package/dist/js/daemon-client-LJdnM_nE.mjs +7 -0
- package/dist/js/forward-DL6DYqyc.mjs +187 -0
- package/dist/js/{print-ba_0hiV9.mjs → print-CQkXZ02a.mjs} +2 -2
- package/dist/js/{print-CbayCa87.mjs → print-DYiQC5IW.mjs} +211 -35
- package/dist/js/{print-share-C1LPsgTP.mjs → print-share-BH9gvCls.mjs} +3 -1
- package/dist/js/{profiler-DtuXkIkR.mjs → profiler-BoO7ldO0.mjs} +258 -4
- 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-DSpGS5XU.mjs} +1 -1
- package/dist/js/{rest-BY2nADw5.mjs → rest-xXfqT4SX.mjs} +27 -12
- package/package.json +1 -1
- package/dist/js/client-CKBQ8pft.mjs +0 -4
- package/dist/js/daemon-client-srAit8Fz.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/dist/js/bin.mjs
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { A as
|
|
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-BoO7ldO0.mjs";
|
|
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
|
-
import { t as createRestClient } from "./rest-
|
|
6
|
+
import { t as createRestClient } from "./rest-xXfqT4SX.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-DbqRBquD.mjs";
|
|
9
|
+
import { i as queryDaemonStatus, l as PORTAL_DAEMON_FLAG, n as ensureDaemonRunning, s as stopDaemon, u as daemonPaths } from "./daemon-DuxaUuek.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
|
-
import
|
|
14
|
+
import { hostname, tmpdir } from "node:os";
|
|
14
15
|
import path, { join } from "node:path";
|
|
15
16
|
import { err, ok } from "neverthrow";
|
|
16
17
|
import { z } from "zod";
|
|
@@ -69,10 +70,12 @@ var SkydiveApiClient = class {
|
|
|
69
70
|
baseUrl;
|
|
70
71
|
token;
|
|
71
72
|
authKind;
|
|
73
|
+
workspaceId;
|
|
72
74
|
constructor(config) {
|
|
73
75
|
this.baseUrl = `${config.apiUrl.replace(/\/$/, "")}/v1`;
|
|
74
76
|
this.token = config.token;
|
|
75
77
|
this.authKind = config.kind;
|
|
78
|
+
this.workspaceId = config.workspaceId;
|
|
76
79
|
}
|
|
77
80
|
/**
|
|
78
81
|
* List one page of agents in the caller's workspace. The server caps a
|
|
@@ -158,6 +161,7 @@ var SkydiveApiClient = class {
|
|
|
158
161
|
authorization: `Bearer ${this.token}`,
|
|
159
162
|
"user-agent": USER_AGENT
|
|
160
163
|
};
|
|
164
|
+
if (this.workspaceId) headers["x-workspace-id"] = this.workspaceId;
|
|
161
165
|
if (params.body) headers["content-type"] = "application/json";
|
|
162
166
|
let response;
|
|
163
167
|
try {
|
|
@@ -717,11 +721,6 @@ const authCommand = {
|
|
|
717
721
|
* matches on it to keep notices off the user's prompt (see isCompletionProbe).
|
|
718
722
|
*/
|
|
719
723
|
const COMPLETION_PROBE_FLAG = "--get-yargs-completions";
|
|
720
|
-
const SUPPORTED_SHELLS = [
|
|
721
|
-
"bash",
|
|
722
|
-
"zsh",
|
|
723
|
-
"fish"
|
|
724
|
-
];
|
|
725
724
|
/**
|
|
726
725
|
* True when this invocation is a shell asking for completion candidates
|
|
727
726
|
* rather than a user running a command. Such a run must print candidates and
|
|
@@ -893,24 +892,6 @@ complete -c ${name} -f -k -a '(${fn})'
|
|
|
893
892
|
${blockEnd(name)}
|
|
894
893
|
`;
|
|
895
894
|
}
|
|
896
|
-
const defaultInstallEnv = () => ({
|
|
897
|
-
home: os.homedir(),
|
|
898
|
-
platform: process.platform,
|
|
899
|
-
env: process.env,
|
|
900
|
-
exists: fs.existsSync
|
|
901
|
-
});
|
|
902
|
-
/**
|
|
903
|
-
* The shell to install for when the user didn't name one. `$SHELL` is the
|
|
904
|
-
* only signal available: a completion install runs as a child process, so the
|
|
905
|
-
* invoking shell's own variables (`$ZSH_VERSION`, `$FISH_VERSION`) aren't
|
|
906
|
-
* visible here.
|
|
907
|
-
*/
|
|
908
|
-
function detectShell(env = process.env) {
|
|
909
|
-
const shell = env["SHELL"];
|
|
910
|
-
if (!shell) return null;
|
|
911
|
-
const name = path.basename(shell);
|
|
912
|
-
return SUPPORTED_SHELLS.find((candidate) => name === candidate) ?? null;
|
|
913
|
-
}
|
|
914
895
|
function completionTarget(shell, deps = defaultInstallEnv()) {
|
|
915
896
|
const { home, platform, env, exists } = deps;
|
|
916
897
|
if (shell === "fish") {
|
|
@@ -1031,7 +1012,7 @@ function requireSession(argv) {
|
|
|
1031
1012
|
* {@link resolveManagementAuth}) or exit with a friendly hint, then build the
|
|
1032
1013
|
* `/v1` client. Shared by `agents`, `keys` and `secrets`.
|
|
1033
1014
|
*/
|
|
1034
|
-
function requireManagementClient(argv) {
|
|
1015
|
+
function requireManagementClient(argv, opts = {}) {
|
|
1035
1016
|
const auth = resolveManagementAuth({ apiUrl: argv["api-url"] });
|
|
1036
1017
|
if (auth.isErr()) {
|
|
1037
1018
|
printError(auth.error.message);
|
|
@@ -1039,7 +1020,10 @@ function requireManagementClient(argv) {
|
|
|
1039
1020
|
}
|
|
1040
1021
|
if (auth.value.kind === "api-key" && auth.value.pinnedWorkspaceName) process.stderr.write(`Using this machine's API key, which always acts in the "${auth.value.pinnedWorkspaceName}" workspace (even after \`workspace switch\`). Run \`skydive auth login\` to change that.
|
|
1041
1022
|
`);
|
|
1042
|
-
return new SkydiveApiClient(
|
|
1023
|
+
return new SkydiveApiClient({
|
|
1024
|
+
...auth.value,
|
|
1025
|
+
...opts.workspaceId ? { workspaceId: opts.workspaceId } : {}
|
|
1026
|
+
});
|
|
1043
1027
|
}
|
|
1044
1028
|
/** Resolve the session (see {@link requireSession}) and build a REST client. */
|
|
1045
1029
|
function requireRestClient(argv) {
|
|
@@ -1056,6 +1040,31 @@ function requireRestClient(argv) {
|
|
|
1056
1040
|
|
|
1057
1041
|
//#endregion
|
|
1058
1042
|
//#region src/commands/agents.ts
|
|
1043
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1044
|
+
/**
|
|
1045
|
+
* Resolve an agent from an id or a name. A uuid selector hits `GET
|
|
1046
|
+
* /agents/:id` directly; anything else is matched case-insensitively against
|
|
1047
|
+
* the roster's names. Ambiguity is an error rather than a silent pick, so an
|
|
1048
|
+
* alias never lands on the wrong agent.
|
|
1049
|
+
*/
|
|
1050
|
+
async function resolveAgent(client, selector) {
|
|
1051
|
+
if (UUID_RE.test(selector)) {
|
|
1052
|
+
const result = await client.getAgent(selector);
|
|
1053
|
+
if (result.isErr()) throw new Error(result.error.message);
|
|
1054
|
+
return result.value;
|
|
1055
|
+
}
|
|
1056
|
+
const list = await client.listAgents({
|
|
1057
|
+
scope: "org",
|
|
1058
|
+
limit: 100
|
|
1059
|
+
});
|
|
1060
|
+
if (list.isErr()) throw new Error(list.error.message);
|
|
1061
|
+
const needle = selector.trim().toLowerCase();
|
|
1062
|
+
const matches = list.value.agents.filter((a) => a.name.toLowerCase() === needle);
|
|
1063
|
+
const [first] = matches;
|
|
1064
|
+
if (matches.length === 1 && first) return first;
|
|
1065
|
+
if (matches.length === 0) throw new Error(`No agent named "${selector}". Pass an id, or run \`skydive agents list\`.`);
|
|
1066
|
+
throw new Error(`Multiple agents named "${selector}". Pass the id instead:\n${matches.map((a) => ` ${a.name} ${a.id}`).join("\n")}`);
|
|
1067
|
+
}
|
|
1059
1068
|
const listCommand$5 = {
|
|
1060
1069
|
command: "list",
|
|
1061
1070
|
describe: "List agents in the active workspace",
|
|
@@ -1205,10 +1214,82 @@ const createCommand$1 = {
|
|
|
1205
1214
|
if (agent.url) console.log(` API: ${agent.url}`);
|
|
1206
1215
|
}
|
|
1207
1216
|
};
|
|
1217
|
+
/**
|
|
1218
|
+
* Resolve the `--workspace` selector (slug/name/id) to a workspace, using the
|
|
1219
|
+
* chat/session credential (workspace listing is a session endpoint). Exits
|
|
1220
|
+
* with a clear message on a bad selector or when listing isn't available
|
|
1221
|
+
* (e.g. a pinned API key). Returns null when the flag is absent.
|
|
1222
|
+
*/
|
|
1223
|
+
async function resolveAliasWorkspace(argv) {
|
|
1224
|
+
if (!argv.workspace) return null;
|
|
1225
|
+
const auth = resolveChatAuth({ appUrl: resolveAppUrl({ appUrl: argv["api-url"] }) });
|
|
1226
|
+
if (auth.isErr()) {
|
|
1227
|
+
printError(`${auth.error.message} Run \`skydive auth login\` to resolve a workspace by name.`);
|
|
1228
|
+
process.exit(1);
|
|
1229
|
+
}
|
|
1230
|
+
const resolved = await resolveWorkspaceId({
|
|
1231
|
+
appUrl: auth.value.appUrl,
|
|
1232
|
+
sessionToken: auth.value.token,
|
|
1233
|
+
selector: argv.workspace
|
|
1234
|
+
});
|
|
1235
|
+
if (resolved.isErr()) {
|
|
1236
|
+
printError(resolved.error.message);
|
|
1237
|
+
process.exit(1);
|
|
1238
|
+
}
|
|
1239
|
+
return resolved.value;
|
|
1240
|
+
}
|
|
1241
|
+
const aliasCommand = {
|
|
1242
|
+
command: "alias <agent> [name]",
|
|
1243
|
+
describe: "Add a shell alias that opens an agent's chat",
|
|
1244
|
+
builder: (y) => y.positional("agent", {
|
|
1245
|
+
type: "string",
|
|
1246
|
+
demandOption: true,
|
|
1247
|
+
describe: "Agent id or name to alias"
|
|
1248
|
+
}).positional("name", {
|
|
1249
|
+
type: "string",
|
|
1250
|
+
describe: "Alias to use (default: derived from the agent name)"
|
|
1251
|
+
}).option("shell", {
|
|
1252
|
+
type: "string",
|
|
1253
|
+
choices: SUPPORTED_SHELLS,
|
|
1254
|
+
describe: "Shell to install for (default: $SHELL)"
|
|
1255
|
+
}).option("workspace", {
|
|
1256
|
+
type: "string",
|
|
1257
|
+
describe: "Workspace the agent lives in (slug, name, or id). Scopes the lookup to it and pins it in the alias, so the alias opens the right agent even when it isn't your active workspace."
|
|
1258
|
+
}).example("skydive agents alias Ripple", "alias `ripple` -> that agent").example("skydive agents alias Ripple rip", "use a custom alias token").example("skydive agents alias Ripple --workspace acme", "pin the workspace in the alias"),
|
|
1259
|
+
handler: async (argv) => {
|
|
1260
|
+
const shell = argv.shell ? SUPPORTED_SHELLS.find((s) => s === argv.shell) : detectShell();
|
|
1261
|
+
if (!shell) throw new Error(argv.shell ? `Unsupported shell "${argv.shell}". Supported: ${SUPPORTED_SHELLS.join(", ")}.` : `Could not tell which shell to use from $SHELL. Name one with --shell (${SUPPORTED_SHELLS.join(", ")}).`);
|
|
1262
|
+
const workspace = await resolveAliasWorkspace(argv);
|
|
1263
|
+
const agent = await resolveAgent(requireManagementClient(argv, { workspaceId: workspace?.id }), argv.agent);
|
|
1264
|
+
const preferred = argv.name ? slugifyAliasName(argv.name) : slugifyAliasName(agent.name);
|
|
1265
|
+
const alias = dedupeAliasName(preferred, takenAliasNames(shell));
|
|
1266
|
+
const result = installAgentAlias({
|
|
1267
|
+
alias,
|
|
1268
|
+
agentId: agent.id,
|
|
1269
|
+
shell,
|
|
1270
|
+
...workspace ? { workspaceId: workspace.id } : {}
|
|
1271
|
+
}, void 0);
|
|
1272
|
+
if (argv.json) {
|
|
1273
|
+
output(argv, {
|
|
1274
|
+
...result,
|
|
1275
|
+
workspace: workspace ?? null
|
|
1276
|
+
});
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
if (argv.quiet) {
|
|
1280
|
+
console.log(result.alias);
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1283
|
+
const where = workspace ? ` (workspace ${workspace.name})` : "";
|
|
1284
|
+
console.log(`${result.updated ? "Updated" : "Added"} alias \`${result.alias}\` -> ${agent.name}${where} in ${result.path}`);
|
|
1285
|
+
if (alias !== preferred) console.log(`(\`${preferred}\` was taken, so used \`${alias}\`.)`);
|
|
1286
|
+
console.log(aliasActivationHint(result));
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1208
1289
|
const agentsCommand = {
|
|
1209
1290
|
command: "agents",
|
|
1210
1291
|
describe: "Manage agents",
|
|
1211
|
-
builder: (y) => y.command(listCommand$5).command(getCommand$2).command(createCommand$1).demandCommand(1, "Specify a subcommand: list, get, create"),
|
|
1292
|
+
builder: (y) => y.command(listCommand$5).command(getCommand$2).command(createCommand$1).command(aliasCommand).demandCommand(1, "Specify a subcommand: list, get, create, alias"),
|
|
1212
1293
|
handler: () => {}
|
|
1213
1294
|
};
|
|
1214
1295
|
|
|
@@ -1462,19 +1543,6 @@ function ensureBunAndReexec(onProgress) {
|
|
|
1462
1543
|
|
|
1463
1544
|
//#endregion
|
|
1464
1545
|
//#region src/commands/import.ts
|
|
1465
|
-
/**
|
|
1466
|
-
* The first message of the import conversation, sent as the user. It hands
|
|
1467
|
-
* the real work to the agent's `import-config` skill; this text only needs
|
|
1468
|
-
* to establish intent, anchor the starting directory, and set the two
|
|
1469
|
-
* expectations the user cares about most (plan first, no credentials).
|
|
1470
|
-
*/
|
|
1471
|
-
function buildImportSeedPrompt(projectDir) {
|
|
1472
|
-
return [
|
|
1473
|
-
"I'm migrating from another coding agent. Import my setup from this machine.",
|
|
1474
|
-
"",
|
|
1475
|
-
`Use your import-config skill. I ran this from \`${projectDir}\`, so start there and in my home directory. Don't assume one tool — do the discovery sweep so you catch whatever I actually use (Claude Code, Cursor, Codex, Gemini CLI, Copilot, Windsurf, Cline, OpenCode, Aider, and any nested AGENTS.md). Show me the plan first: everything you found, what you'll bring over, where it lands in you, and anything you're leaving out (credentials especially). Then wait for my OK before committing anything.`
|
|
1476
|
-
].join("\n");
|
|
1477
|
-
}
|
|
1478
1546
|
const importCommand = {
|
|
1479
1547
|
command: "import",
|
|
1480
1548
|
describe: "Import your existing coding-agent setup (Claude Code, Cursor, Codex, Gemini, Copilot, and more) into an agent (opens a machine-shared chat)",
|
|
@@ -1516,17 +1584,20 @@ const importCommand = {
|
|
|
1516
1584
|
process.exit(1);
|
|
1517
1585
|
}
|
|
1518
1586
|
}
|
|
1519
|
-
const { runChat } = await import("./boot-
|
|
1587
|
+
const { runChat } = await import("./boot-0-dpPM1_.mjs");
|
|
1520
1588
|
await runChat({
|
|
1521
1589
|
appUrl,
|
|
1522
1590
|
sessionToken: session.value.sessionToken,
|
|
1591
|
+
workspaceId: void 0,
|
|
1523
1592
|
shareMachine: true,
|
|
1524
1593
|
promptHistoryPath: getPromptHistoryPath(),
|
|
1525
1594
|
notifications: true,
|
|
1526
1595
|
agentSelector: argv.agent ?? null,
|
|
1527
1596
|
conversationId: null,
|
|
1528
1597
|
newConversation: false,
|
|
1529
|
-
|
|
1598
|
+
defaultAgentSelector: getDefaultAgent(),
|
|
1599
|
+
forceOnboarding: false,
|
|
1600
|
+
seedPrompt: buildImportSeedPrompt(process.cwd(), machineOsFromPlatform(process.platform))
|
|
1530
1601
|
});
|
|
1531
1602
|
}
|
|
1532
1603
|
};
|
|
@@ -1543,15 +1614,15 @@ async function runImportPrintMode({ argv, appUrl }) {
|
|
|
1543
1614
|
printError(`${session.error.message} For non-interactive use, run \`skydive auth login --web\` first, or set SKYDIVE_SESSION_TOKEN.`);
|
|
1544
1615
|
process.exit(1);
|
|
1545
1616
|
}
|
|
1546
|
-
const { connectMachineShare } = await import("./print-share-
|
|
1617
|
+
const { connectMachineShare } = await import("./print-share-BH9gvCls.mjs");
|
|
1547
1618
|
const machineShare = await connectMachineShare({
|
|
1548
1619
|
appUrl,
|
|
1549
1620
|
sessionToken: session.value.sessionToken,
|
|
1550
1621
|
timeoutHint: "Check `skydive portal status`, then re-run."
|
|
1551
1622
|
});
|
|
1552
1623
|
const extra = (argv.print ?? "").trim();
|
|
1553
|
-
const prompt = buildImportSeedPrompt(process.cwd()) + (extra ? `\n\nAdditional instructions: ${extra}` : "");
|
|
1554
|
-
const { runPrint } = await import("./print-
|
|
1624
|
+
const prompt = buildImportSeedPrompt(process.cwd(), machineOsFromPlatform(process.platform)) + (extra ? `\n\nAdditional instructions: ${extra}` : "");
|
|
1625
|
+
const { runPrint } = await import("./print-CQkXZ02a.mjs");
|
|
1555
1626
|
try {
|
|
1556
1627
|
const result = await runPrint({
|
|
1557
1628
|
appUrl,
|
|
@@ -1859,6 +1930,24 @@ async function resolveAgentWorkspace({ appUrl, sessionToken, agentSelector }) {
|
|
|
1859
1930
|
|
|
1860
1931
|
//#endregion
|
|
1861
1932
|
//#region src/commands/chat.ts
|
|
1933
|
+
/**
|
|
1934
|
+
* Resolve the `--workspace` selector to an id, or undefined when the flag is
|
|
1935
|
+
* absent. A bad selector is fatal — a chat scoped to the wrong (or a
|
|
1936
|
+
* nonexistent) workspace is worse than failing fast with how to list them.
|
|
1937
|
+
*/
|
|
1938
|
+
async function resolveWorkspaceFlag({ appUrl, sessionToken, selector }) {
|
|
1939
|
+
if (!selector) return void 0;
|
|
1940
|
+
const resolved = await resolveWorkspaceId({
|
|
1941
|
+
appUrl,
|
|
1942
|
+
sessionToken,
|
|
1943
|
+
selector
|
|
1944
|
+
});
|
|
1945
|
+
if (resolved.isErr()) {
|
|
1946
|
+
printError(resolved.error.message);
|
|
1947
|
+
process.exit(1);
|
|
1948
|
+
}
|
|
1949
|
+
return resolved.value.id;
|
|
1950
|
+
}
|
|
1862
1951
|
const chatCommand = {
|
|
1863
1952
|
command: "chat",
|
|
1864
1953
|
describe: "Open the interactive chat TUI, or -p for a one-shot",
|
|
@@ -1869,6 +1958,9 @@ const chatCommand = {
|
|
|
1869
1958
|
}).option("agent", {
|
|
1870
1959
|
type: "string",
|
|
1871
1960
|
describe: "Target agent, by id, slug, or name. With -p, the agent to send the one-shot prompt to. Without -p, pre-selects the agent and opens its conversation list, skipping the agent picker. Optional when the account has exactly one agent."
|
|
1961
|
+
}).option("workspace", {
|
|
1962
|
+
type: "string",
|
|
1963
|
+
describe: "Scope this chat to a workspace, by slug, name, or id. Every request for this invocation is scoped to it (no persistent switch of your active workspace), so a slug/name --agent resolves inside it and the roster is that workspace. Useful in an alias for an agent that lives in a workspace other than your active one."
|
|
1872
1964
|
}).option("conversation", {
|
|
1873
1965
|
alias: "resume",
|
|
1874
1966
|
type: "string",
|
|
@@ -1877,6 +1969,10 @@ const chatCommand = {
|
|
|
1877
1969
|
type: "boolean",
|
|
1878
1970
|
default: false,
|
|
1879
1971
|
describe: "Start a fresh conversation instead of opening the conversation list. With --agent, opens a new conversation with that agent directly. Without --agent, opens a new conversation once you pick an agent (auto-picked if the account has only one). Ignored with --resume, which always continues the named conversation."
|
|
1972
|
+
}).option("onboarding", {
|
|
1973
|
+
type: "boolean",
|
|
1974
|
+
default: false,
|
|
1975
|
+
describe: "Force the first-run onboarding flow (auto-create a first agent, then offer the machine import) even when the workspace already has agents. Also settable via SKYDIVE_FORCE_ONBOARDING=1. Handy for replaying the new-user experience; ignored with -p, --resume, or --agent."
|
|
1880
1976
|
}).option("share-machine", {
|
|
1881
1977
|
type: "boolean",
|
|
1882
1978
|
describe: "Share this machine with the agent over the portal so it can run commands here (default-deny; in the TUI you approve per agent, with -p the flag grants the target agent for the run). Defaults to `shareMachineDefault` in the CLI config file; --no-share-machine disables for this invocation."
|
|
@@ -1918,21 +2014,27 @@ const chatCommand = {
|
|
|
1918
2014
|
process.exit(1);
|
|
1919
2015
|
}
|
|
1920
2016
|
}
|
|
2017
|
+
const workspaceId = await resolveWorkspaceFlag({
|
|
2018
|
+
appUrl,
|
|
2019
|
+
sessionToken: auth.value.token,
|
|
2020
|
+
selector: argv.workspace
|
|
2021
|
+
});
|
|
1921
2022
|
const themeId = argv.theme ?? process.env["SKYDIVE_THEME"] ?? void 0;
|
|
1922
2023
|
if (themeId !== void 0 && !themes.some((t) => t.id === themeId)) {
|
|
1923
2024
|
printError(`Unknown theme "${themeId}". Valid themes: ${themes.map((t) => t.id).join(", ")}`);
|
|
1924
2025
|
process.exit(1);
|
|
1925
2026
|
}
|
|
1926
|
-
await ensureAgentWorkspace({
|
|
2027
|
+
if (!workspaceId) await ensureAgentWorkspace({
|
|
1927
2028
|
appUrl,
|
|
1928
2029
|
sessionToken: auth.value.token,
|
|
1929
2030
|
agentSelector: argv.agent ?? null
|
|
1930
2031
|
});
|
|
1931
|
-
const { runChat } = await import("./boot-
|
|
2032
|
+
const { runChat } = await import("./boot-0-dpPM1_.mjs");
|
|
1932
2033
|
await runChat({
|
|
1933
2034
|
appUrl: auth.value.appUrl,
|
|
1934
2035
|
sessionToken: auth.value.token,
|
|
1935
2036
|
authKind: auth.value.kind,
|
|
2037
|
+
workspaceId,
|
|
1936
2038
|
shareMachine: resolveShareMachine(argv),
|
|
1937
2039
|
promptHistoryPath: getPromptHistoryPath(),
|
|
1938
2040
|
theme: themeId,
|
|
@@ -1940,6 +2042,8 @@ const chatCommand = {
|
|
|
1940
2042
|
agentSelector: argv.agent ?? null,
|
|
1941
2043
|
conversationId: argv.conversation ?? null,
|
|
1942
2044
|
newConversation: resolveNewConversation(argv),
|
|
2045
|
+
forceOnboarding: resolveForceOnboarding(argv),
|
|
2046
|
+
defaultAgentSelector: getDefaultAgent(),
|
|
1943
2047
|
seedPrompt: null
|
|
1944
2048
|
});
|
|
1945
2049
|
}
|
|
@@ -1961,6 +2065,20 @@ function resolveNewConversation(argv) {
|
|
|
1961
2065
|
return argv.new === true && !argv.conversation;
|
|
1962
2066
|
}
|
|
1963
2067
|
/**
|
|
2068
|
+
* Whether to force the first-run onboarding flow: the `--onboarding` flag or
|
|
2069
|
+
* `SKYDIVE_FORCE_ONBOARDING` (any non-empty, non-"0"/"false" value). Lets the
|
|
2070
|
+
* new-user experience be replayed in a workspace that already has agents. The
|
|
2071
|
+
* TUI applies it only to a bare interactive launch — an explicit
|
|
2072
|
+
* `--agent`/`--resume`/`--new` still takes precedence.
|
|
2073
|
+
*/
|
|
2074
|
+
function resolveForceOnboarding(argv, env = process.env) {
|
|
2075
|
+
if (argv.onboarding === true) return true;
|
|
2076
|
+
const raw = env["SKYDIVE_FORCE_ONBOARDING"];
|
|
2077
|
+
if (raw === void 0) return false;
|
|
2078
|
+
const value = raw.trim().toLowerCase();
|
|
2079
|
+
return value !== "" && value !== "0" && value !== "false";
|
|
2080
|
+
}
|
|
2081
|
+
/**
|
|
1964
2082
|
* Cross-workspace `--agent <uuid>` handling shared by the TUI and -p paths:
|
|
1965
2083
|
* on a hit in another workspace, switch to it and say so on stderr (stdout
|
|
1966
2084
|
* may carry the -p reply/JSON envelope); when the id matches no workspace on
|
|
@@ -1995,8 +2113,13 @@ async function runPrintMode({ argv, appUrl }) {
|
|
|
1995
2113
|
printError(`${auth.error.message} For non-interactive use, run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
|
|
1996
2114
|
process.exit(1);
|
|
1997
2115
|
}
|
|
1998
|
-
const { runPrint, readStdin } = await import("./print-
|
|
1999
|
-
await
|
|
2116
|
+
const { runPrint, readStdin } = await import("./print-CQkXZ02a.mjs");
|
|
2117
|
+
const workspaceId = await resolveWorkspaceFlag({
|
|
2118
|
+
appUrl,
|
|
2119
|
+
sessionToken: auth.value.token,
|
|
2120
|
+
selector: argv.workspace
|
|
2121
|
+
});
|
|
2122
|
+
if (!workspaceId) await ensureAgentWorkspace({
|
|
2000
2123
|
appUrl,
|
|
2001
2124
|
sessionToken: auth.value.token,
|
|
2002
2125
|
agentSelector: argv.agent ?? null
|
|
@@ -2020,7 +2143,7 @@ async function runPrintMode({ argv, appUrl }) {
|
|
|
2020
2143
|
process.exit(1);
|
|
2021
2144
|
}
|
|
2022
2145
|
} else {
|
|
2023
|
-
const { connectMachineShare } = await import("./print-share-
|
|
2146
|
+
const { connectMachineShare } = await import("./print-share-BH9gvCls.mjs");
|
|
2024
2147
|
machineShare = await connectMachineShare({
|
|
2025
2148
|
appUrl: auth.value.appUrl,
|
|
2026
2149
|
sessionToken: auth.value.token,
|
|
@@ -2031,6 +2154,7 @@ async function runPrintMode({ argv, appUrl }) {
|
|
|
2031
2154
|
const result = await runPrint({
|
|
2032
2155
|
appUrl: auth.value.appUrl,
|
|
2033
2156
|
sessionToken: auth.value.token,
|
|
2157
|
+
workspaceId,
|
|
2034
2158
|
prompt,
|
|
2035
2159
|
agentSelector: argv.agent ?? null,
|
|
2036
2160
|
conversationId: argv.conversation ?? null,
|
|
@@ -2084,7 +2208,7 @@ const getCommand$1 = {
|
|
|
2084
2208
|
printError(`${auth.error.message} Run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
|
|
2085
2209
|
process.exit(1);
|
|
2086
2210
|
}
|
|
2087
|
-
const { messageGet } = await import("./print-
|
|
2211
|
+
const { messageGet } = await import("./print-CQkXZ02a.mjs");
|
|
2088
2212
|
try {
|
|
2089
2213
|
const result = await messageGet({
|
|
2090
2214
|
appUrl: auth.value.appUrl,
|
|
@@ -2143,7 +2267,7 @@ const listCommand$2 = {
|
|
|
2143
2267
|
}),
|
|
2144
2268
|
handler: async (argv) => {
|
|
2145
2269
|
const client = requireRestClient(argv);
|
|
2146
|
-
const agent = resolveAgent(await client.listAgents({
|
|
2270
|
+
const agent = resolveAgent$1(await client.listAgents({
|
|
2147
2271
|
scope: "org",
|
|
2148
2272
|
onPage: null
|
|
2149
2273
|
}), argv.agent ?? null);
|
|
@@ -2287,7 +2411,7 @@ const switchCommand = {
|
|
|
2287
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.");
|
|
2288
2412
|
process.exit(1);
|
|
2289
2413
|
}
|
|
2290
|
-
const { runWorkspacePicker } = await import("./boot-
|
|
2414
|
+
const { runWorkspacePicker } = await import("./boot-0-dpPM1_.mjs");
|
|
2291
2415
|
await runWorkspacePicker(session);
|
|
2292
2416
|
return;
|
|
2293
2417
|
}
|
|
@@ -2336,10 +2460,10 @@ const workspaceCommand = {
|
|
|
2336
2460
|
*/
|
|
2337
2461
|
async function resolveGrantTarget(argv) {
|
|
2338
2462
|
const session = requireSession(argv);
|
|
2339
|
-
const [{ devices, agents }, identity] = await Promise.all([fetchPortalDevices(session), resolveMachineIdentity()]);
|
|
2463
|
+
const [{ devices, agents }, identity] = await Promise.all([fetchPortalDevices(session), resolveMachineIdentity(null)]);
|
|
2340
2464
|
return {
|
|
2341
2465
|
session,
|
|
2342
|
-
agent: resolveAgent(agents, argv.agent),
|
|
2466
|
+
agent: resolveAgent$1(agents, argv.agent),
|
|
2343
2467
|
device: findThisDevice(devices, identity.machineName),
|
|
2344
2468
|
identity
|
|
2345
2469
|
};
|
|
@@ -2363,10 +2487,10 @@ const openCommand = {
|
|
|
2363
2487
|
}),
|
|
2364
2488
|
handler: async (argv) => {
|
|
2365
2489
|
const session = requireSession(argv);
|
|
2366
|
-
const agent = argv.agent ? resolveAgent((await fetchPortalDevices(session)).agents, argv.agent) : null;
|
|
2490
|
+
const agent = argv.agent ? resolveAgent$1((await fetchPortalDevices(session)).agents, argv.agent) : null;
|
|
2367
2491
|
const cwd = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
|
|
2368
|
-
const { machineName } = await resolveMachineIdentity();
|
|
2369
|
-
const { PortalDaemonClient } = await import("./daemon-client-
|
|
2492
|
+
const { machineName } = await resolveMachineIdentity(null);
|
|
2493
|
+
const { PortalDaemonClient } = await import("./daemon-client-LJdnM_nE.mjs");
|
|
2370
2494
|
let lastLine = "";
|
|
2371
2495
|
let signalConnected;
|
|
2372
2496
|
const connected = new Promise((resolve) => {
|
|
@@ -2395,7 +2519,7 @@ const openCommand = {
|
|
|
2395
2519
|
client.enable();
|
|
2396
2520
|
await connected;
|
|
2397
2521
|
if (agent) {
|
|
2398
|
-
await client.grantAgent(agent.id);
|
|
2522
|
+
await client.grantAgent(agent.id, null);
|
|
2399
2523
|
console.log(`portal: granted ${agent.name} access to this machine (persists until revoked)`);
|
|
2400
2524
|
}
|
|
2401
2525
|
console.log(`portal: open. granted agents can run commands on ${machineName} as your user, cwd ${cwd}. ctrl+c to close.`);
|
|
@@ -2415,7 +2539,8 @@ const grantCommand = {
|
|
|
2415
2539
|
})).id;
|
|
2416
2540
|
await grantPortalAccess(session, {
|
|
2417
2541
|
deviceId,
|
|
2418
|
-
agentId: agent.id
|
|
2542
|
+
agentId: agent.id,
|
|
2543
|
+
conversationId: null
|
|
2419
2544
|
});
|
|
2420
2545
|
if (argv.json) {
|
|
2421
2546
|
output(argv, {
|
|
@@ -2424,7 +2549,7 @@ const grantCommand = {
|
|
|
2424
2549
|
});
|
|
2425
2550
|
return;
|
|
2426
2551
|
}
|
|
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}\`.`);
|
|
2552
|
+
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
2553
|
}
|
|
2429
2554
|
};
|
|
2430
2555
|
const revokeCommand = {
|
|
@@ -2464,7 +2589,7 @@ const statusCommand = {
|
|
|
2464
2589
|
console.log("No machines registered. Run `skydive portal open` to register this one.");
|
|
2465
2590
|
return;
|
|
2466
2591
|
}
|
|
2467
|
-
const { headers, rows } = buildDeviceTable(devices, agents, (await resolveMachineIdentity()).machineName);
|
|
2592
|
+
const { headers, rows } = buildDeviceTable(devices, agents, (await resolveMachineIdentity(null)).machineName);
|
|
2468
2593
|
printTable(headers, rows);
|
|
2469
2594
|
}
|
|
2470
2595
|
};
|
|
@@ -2592,10 +2717,45 @@ const daemonCommand = {
|
|
|
2592
2717
|
builder: (y) => y.command(daemonStatusCommand).command(daemonStopCommand).command(daemonStartCommand).command(daemonRestartCommand).command(daemonLogsCommand).demandCommand(1, "Specify a subcommand: status, stop, start, restart, logs"),
|
|
2593
2718
|
handler: () => {}
|
|
2594
2719
|
};
|
|
2720
|
+
const forwardCommand = {
|
|
2721
|
+
command: "forward <port>",
|
|
2722
|
+
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)",
|
|
2723
|
+
builder: (y) => y.positional("port", {
|
|
2724
|
+
type: "number",
|
|
2725
|
+
demandOption: true,
|
|
2726
|
+
describe: "Sandbox port to forward to (e.g. 3000 for a dev server)"
|
|
2727
|
+
}).option("agent", {
|
|
2728
|
+
type: "string",
|
|
2729
|
+
describe: "Agent, by id or name (defaults when the account has one)"
|
|
2730
|
+
}).option("local-port", {
|
|
2731
|
+
type: "number",
|
|
2732
|
+
describe: "Local port to listen on (default: same as <port>)"
|
|
2733
|
+
}),
|
|
2734
|
+
handler: async (argv) => {
|
|
2735
|
+
const session = requireSession(argv);
|
|
2736
|
+
const agent = resolveAgent$1((await fetchPortalDevices(session)).agents, argv.agent ?? null);
|
|
2737
|
+
const targetPort = argv.port;
|
|
2738
|
+
const localPort = argv["local-port"] ?? targetPort;
|
|
2739
|
+
const { startForward } = await import("./forward-DL6DYqyc.mjs");
|
|
2740
|
+
const listener = await startForward({
|
|
2741
|
+
auth: session,
|
|
2742
|
+
agentId: agent.id,
|
|
2743
|
+
localPort,
|
|
2744
|
+
targetPort,
|
|
2745
|
+
log: (msg) => console.error(msg)
|
|
2746
|
+
});
|
|
2747
|
+
console.log(`Forwarding http://localhost:${listener.port} -> ${agent.name}'s sandbox port ${targetPort}. ctrl+c to stop.`);
|
|
2748
|
+
await new Promise((resolve) => {
|
|
2749
|
+
process.once("SIGINT", () => resolve());
|
|
2750
|
+
process.once("SIGTERM", () => resolve());
|
|
2751
|
+
});
|
|
2752
|
+
await listener.close();
|
|
2753
|
+
}
|
|
2754
|
+
};
|
|
2595
2755
|
const portalCommand = {
|
|
2596
2756
|
command: "portal",
|
|
2597
2757
|
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"),
|
|
2758
|
+
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
2759
|
handler: () => {}
|
|
2600
2760
|
};
|
|
2601
2761
|
|
|
@@ -2640,8 +2800,8 @@ const sandboxCommand = {
|
|
|
2640
2800
|
}).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
2801
|
handler: async (argv) => {
|
|
2642
2802
|
const session = requireSession(argv);
|
|
2643
|
-
const { createRestClient } = await import("./rest-
|
|
2644
|
-
const { resolveAgent } = await import("./print-
|
|
2803
|
+
const { createRestClient } = await import("./rest-DSpGS5XU.mjs");
|
|
2804
|
+
const { resolveAgent } = await import("./print-CQkXZ02a.mjs");
|
|
2645
2805
|
const client = createRestClient({
|
|
2646
2806
|
appUrl: session.appUrl,
|
|
2647
2807
|
sessionToken: session.sessionToken
|
|
@@ -2710,7 +2870,7 @@ async function runPty({ session, agentId, agentName }) {
|
|
|
2710
2870
|
return 1;
|
|
2711
2871
|
}
|
|
2712
2872
|
console.error(`Connecting to ${agentName}'s sandbox… (Ctrl-] detaches)`);
|
|
2713
|
-
const { runRawPtyPassthrough } = await import("./raw-pty-
|
|
2873
|
+
const { runRawPtyPassthrough } = await import("./raw-pty-DmdUf4_w.mjs");
|
|
2714
2874
|
const result = await runRawPtyPassthrough({
|
|
2715
2875
|
stdin: process.stdin,
|
|
2716
2876
|
stdout: process.stdout,
|
|
@@ -2798,8 +2958,8 @@ const fsEditCommand = {
|
|
|
2798
2958
|
handler: async (argv) => {
|
|
2799
2959
|
const session = requireSession(argv);
|
|
2800
2960
|
const remotePath = argv.path;
|
|
2801
|
-
const { createRestClient } = await import("./rest-
|
|
2802
|
-
const { resolveAgent } = await import("./print-
|
|
2961
|
+
const { createRestClient } = await import("./rest-DSpGS5XU.mjs");
|
|
2962
|
+
const { resolveAgent } = await import("./print-CQkXZ02a.mjs");
|
|
2803
2963
|
const client = createRestClient({
|
|
2804
2964
|
appUrl: session.appUrl,
|
|
2805
2965
|
sessionToken: session.sessionToken
|
|
@@ -3680,6 +3840,11 @@ function resolvePreference(key) {
|
|
|
3680
3840
|
}
|
|
3681
3841
|
return pref;
|
|
3682
3842
|
}
|
|
3843
|
+
/** Human rendering of a preference value; unset string preferences (null)
|
|
3844
|
+
* print as empty rather than the string "null". */
|
|
3845
|
+
function formatValue(value) {
|
|
3846
|
+
return value === null ? "" : String(value);
|
|
3847
|
+
}
|
|
3683
3848
|
/** One row's worth of state for a preference, used by `list` and `get`. */
|
|
3684
3849
|
function snapshot(pref) {
|
|
3685
3850
|
return {
|
|
@@ -3706,7 +3871,7 @@ const listCommand = {
|
|
|
3706
3871
|
"Description"
|
|
3707
3872
|
], rows.map((r) => [
|
|
3708
3873
|
r.key,
|
|
3709
|
-
|
|
3874
|
+
formatValue(r.value),
|
|
3710
3875
|
r.source,
|
|
3711
3876
|
r.describe
|
|
3712
3877
|
]));
|
|
@@ -3727,7 +3892,7 @@ const getCommand = {
|
|
|
3727
3892
|
output(argv, snapshot(pref));
|
|
3728
3893
|
return;
|
|
3729
3894
|
}
|
|
3730
|
-
console.log(
|
|
3895
|
+
console.log(formatValue(pref.read()));
|
|
3731
3896
|
}
|
|
3732
3897
|
};
|
|
3733
3898
|
const setCommand = {
|
|
@@ -3745,17 +3910,16 @@ const setCommand = {
|
|
|
3745
3910
|
}),
|
|
3746
3911
|
handler: (argv) => {
|
|
3747
3912
|
const pref = resolvePreference(argv.key);
|
|
3748
|
-
const
|
|
3749
|
-
if (
|
|
3750
|
-
printError(`${pref.key}: ${
|
|
3913
|
+
const result = pref.set(argv.value ?? "");
|
|
3914
|
+
if (result.isErr()) {
|
|
3915
|
+
printError(`${pref.key}: ${result.error}`);
|
|
3751
3916
|
process.exit(1);
|
|
3752
3917
|
}
|
|
3753
|
-
pref.write(parsed.value);
|
|
3754
3918
|
if (argv.json) {
|
|
3755
3919
|
output(argv, snapshot(pref));
|
|
3756
3920
|
return;
|
|
3757
3921
|
}
|
|
3758
|
-
if (!argv.quiet) console.log(`Set ${pref.key} = ${
|
|
3922
|
+
if (!argv.quiet) console.log(`Set ${pref.key} = ${formatValue(pref.read())}`);
|
|
3759
3923
|
}
|
|
3760
3924
|
};
|
|
3761
3925
|
const unsetCommand = {
|
|
@@ -3775,7 +3939,10 @@ const unsetCommand = {
|
|
|
3775
3939
|
output(argv, snapshot(pref));
|
|
3776
3940
|
return;
|
|
3777
3941
|
}
|
|
3778
|
-
if (!argv.quiet)
|
|
3942
|
+
if (!argv.quiet) {
|
|
3943
|
+
const fallback = pref.read();
|
|
3944
|
+
console.log(fallback === null ? `Unset ${pref.key}` : `Unset ${pref.key} (now ${formatValue(fallback)} by default)`);
|
|
3945
|
+
}
|
|
3779
3946
|
}
|
|
3780
3947
|
};
|
|
3781
3948
|
const pathCommand = {
|
|
@@ -4026,7 +4193,7 @@ function setupUpdateCheck(argv) {
|
|
|
4026
4193
|
|
|
4027
4194
|
//#endregion
|
|
4028
4195
|
//#region src/changelog.generated.ts
|
|
4029
|
-
const CHANGELOG_MD = "# Changelog\n\nAll notable changes to the Skydive CLI are documented here.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n### Added\n\n- `skydive fs` — a namespace for working with the files on an agent's computer (its sandbox) from your own machine. Its first subcommand, `skydive fs edit <path>`, reads a remote file down over the same relay `skydive sandbox` uses, opens it in your local editor (`$VISUAL`/`$EDITOR`, override with `--editor`, defaults to `vi`), and writes your changes back atomically when you save and close — so you can edit a remote file as if it were local without opening the chat TUI. A path that doesn't exist yet is created on save; no changes means nothing is written. Access is gated on EDIT permission for the agent, exactly like `skydive sandbox`. (A first-class `fs` relay channel and `skydive fs mount` land in follow-ups.)\n\n## [0.4.0] - 2026-08-07\n\n### Added\n\n- `skydive chat` works with API key auth: an API key alone now opens the chat TUI (and headless `chat -p`) without a browser login, using the key's pinned workspace.\n- `skydive config` command to view and set the safe, hand-editable CLI preferences (`shareMachineDefault`, `updateCheck`) without opening `config.json`. `config`/`config list` shows each key, its effective value, and whether it comes from the file or the built-in default; `config get`/`set`/`unset` read and change one key; `config path` prints the config file location. It only ever touches those preference keys, never credentials or flow-managed state.\n- Billing-blocked sends and runs now surface the server-authored recovery\n guidance from the typed billing outcome. The chat TUI renders it as a\n warning-toned system-notice row — live when a send is refused or a run is\n stopped at the billing boundary, and again when reopening a conversation\n that holds a persisted billing notice — instead of a generic error.\n Headless `chat -p` and `messages get` print the guidance (state, recovery\n step, and manage-billing URL) on stderr, keep any partial output already\n streamed on stdout, and exit with code `5` (distinct from the generic `1`)\n so scripted callers can tell a billing pause from a failure.\n- Syntax highlighting for 14 more languages in the chat transcript and file/review panes (python, bash, go, rust, c, cpp, java, ruby, css, html, json, yaml, and more) — previously only JavaScript/TypeScript rendered colored.\n\n### Changed\n\n- `skydive agent list` lists the full agent roster by default instead of a truncated page.\n\n### Fixed\n\n- Long multi-line bash tool-call commands (e.g. heredocs) are clamped with a `+N more · click to expand` tail instead of dumping their whole body into the scrollback.\n- Tool error output is clipped the same way as ok output, so a failing command no longer floods the transcript.\n- The TUI toast overlay has an opaque background instead of letting the transcript bleed through.\n\n## [0.3.0] - 2026-08-04\n\n### Added\n\n**Chat TUI**\n\n- `/fork` slash command to fork a conversation at any point and continue in a new thread.\n- `/conversation` switches threads from inside a live session.\n- `/compact` triggers manual conversation compaction.\n- `/portal` and `/copy` slash commands.\n- Archive the current conversation from the chat TUI.\n- `--new` flag on `skydive chat` to start a fresh conversation.\n- Debounced server-side conversation search in the picker, with title matches ranked first.\n- File review pane orientation toggle, and review comments in the file pane.\n- Pinned plan card controls and a jump-to-latest hint.\n- Terminal title (OSC) follows the live conversation title on every screen; inside cmux the workspace is renamed too, and linked PRs are pushed to the cmux sidebar.\n- Every participating agent shows in the conversation list; connect cards each have their own identity, and ctrl+r asks which connect card to act on.\n- Colored Skydive splash art in `skydive --help` and the TUI pickers.\n- New-agent name prefilled from the server's suggestion.\n\n**CLI & platform**\n\n- `skydive update` self-updater, with an update notice when a new version is available.\n- Shell completions for bash, zsh, and fish via `completion install`.\n- Portal daemon: one connection per host with per-conversation working directories.\n- Agent-led coding-agent import (`skydive import`).\n- Per-workspace scoping on the REST client, and cross-org `--agent <uuid>` auto-switches workspace.\n- `skydive-beta` wrapper for the canary channel.\n\n### Fixed\n\n- `skydive update` no longer claims an update is available on every npm install.\n- Chat memory is bounded (payload clamps + windowed rows) with a memory watchdog and OOM diagnostic report; transcript rows are memoized so streaming stops re-rendering the whole chat.\n- Chat-send failures map to real error messages.\n- Notification and sidebar previews use the last text block of the reply and strip inline markdown.\n- Picker rows no longer overlap when a conversation title contains a newline, tab, or control character.\n- Sending to non-web channel conversations is blocked in the TUI.\n- GitHub bot connect opens on the web front door, not api.skydive.com.\n- TUI activity counter times from the run, not the screen.\n\n## [0.2.0] - 2026-07-29\n\n### Added\n\n**Sandbox & machine access**\n\n- Standalone `skydive sandbox` command for direct access to an agent's sandbox.\n- `/sandbox` in the chat TUI — live PTY session and one-shot command execution.\n- Headless machine sharing via `skydive portal`, with self-registering portal grants.\n- `shareMachineDefault` config key for always-on portal sharing.\n\n**Chat TUI**\n\n- File pane (`ctrl+g`) with Changes and Files tabs: a shared file tree plus source viewer and a workspace browser, mouse-resizable and responsive to terminal size.\n- Agent todo list rendered in the chat TUI, above the composer, mirroring the web chat's todo card.\n- Fuzzy finder in the conversation and agent pickers.\n- Slash-command autocomplete menu.\n- Paste or drag-and-drop any file into the composer; paste clipboard images with Cmd+V.\n- Run local shell commands with `!` in the composer.\n- Conversation recaps, streamed live title updates, and per-agent attribution on assistant turns.\n- Working timer rolls up into minutes and hours.\n- Conversation picker paginates past 50 conversations and shows only your own conversations.\n- Esc leaves a live run; typing `exit` quits the chat.\n- Cursor Dark theme.\n- Picker rows no longer overlap when a conversation title contains a newline, tab, or control character.\n\n**Headless & scripting**\n\n- Resume a conversation by id.\n- `conversations list` and `conversations show` for transcript reads.\n- `messages get`, with run recovery keyed on message id.\n- Connect cards surface in headless `-p` mode so a driving agent never gets stuck.\n\n**Authentication**\n\n- `skydive auth login` via the browser now auto-mints an API key, so one login yields both a chat session and a usable management credential. Management commands announce the key's pinned workspace when it drives them, so a workspace mismatch is visible at use time.\n- Workspace picker on the device authorization page.\n- Account and workspace identity shown in `auth status`.\n\n**Platform**\n\n- Standalone binary builds compiling the CLI into a per-target executable.\n- Interactive workspace switcher; management commands follow the active workspace.\n- Terminal host integrations and agent notifications.\n\n### Fixed\n\n- Transcript errors collapse to one line, click to expand (REST and portal errors keep their full body).\n- Dragged/pasted image file paths attach the file instead of inserting path text, including macOS paths with literal parentheses.\n- Bare URLs in chat markdown are hyperlinked so they survive text wrap.\n- Composer draft is preserved across TUI overlays.\n- Relative connect links resolve before opening the browser.\n- Numbered markdown headings render colored in the TUI.\n- Chat transcript pages by 75% of a screen; picker rows stay on one line.\n- Run starts push to the TUI over the conversation stream.\n- Security: remediated high-severity dependency findings and cleared tar/shell-quote CVEs.\n\n## [0.1.0] - 2026-07-21\n\nInitial public release: `skydive chat` TUI, agent and conversation management,\ndevice authorization, and headless `-p` mode.\n";
|
|
4196
|
+
const CHANGELOG_MD = "# Changelog\n\nAll notable changes to the Skydive CLI are documented here.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n### Added\n\n- `/search` (alias `/find`) in the chat TUI — opens the conversation picker with its search field focused, so \"find a conversation\" has a discoverable command instead of being a feature you had to know the bare picker already had. Pass a query inline (`/search billing deep dive`) and the picker opens already narrowed to the matches; bare `/search` opens it ready for you to type. The search runs server-side across conversation titles, message bodies, and participant names, the same search the picker exposes.\n- First run in a workspace with no agents now sets you up automatically: instead of dropping you on an empty agent picker, `skydive` creates your first agent for you (with a suggested name and avatar, mirroring the web onboarding flow) and then offers to have it learn how you already work — reading the other coding agents set up on your machine (Claude Code, Cursor, Codex, and more), the same import that `skydive import` runs, with a plan shown first and credentials never copied. Decline and you go straight into the new agent's chat. You can replay this flow anytime with `skydive --onboarding` (or `SKYDIVE_FORCE_ONBOARDING=1`), even in a workspace that already has agents.\n- `/computer` (alias `/stats`) in the chat TUI — a snapshot of the agent's sandbox drawn as colored bar graphs: CPU load (scaled to core count), memory, swap (when present), and disk usage, plus uptime and process count. Bars turn amber past 75%% and red past 90%% so a glance tells you whether the box is under pressure. The card keeps itself current quietly (a slow background refresh, no flicker or \"updating\" chrome); only the most recent card refreshes, and it silently stops while you're scrolled up (no wasted polls on a card you're not looking at) and resumes at the bottom. Stats are read over the same relay `skydive sandbox` uses, so it's gated on EDIT access to the agent like the other sandbox commands.\n- `skydive fs` — a namespace for working with the files on an agent's computer (its sandbox) from your own machine. Its first subcommand, `skydive fs edit <path>`, reads a remote file down over the same relay `skydive sandbox` uses, opens it in your local editor (`$VISUAL`/`$EDITOR`, override with `--editor`, defaults to `vi`), and writes your changes back atomically when you save and close — so you can edit a remote file as if it were local without opening the chat TUI. A path that doesn't exist yet is created on save; no changes means nothing is written. Access is gated on EDIT permission for the agent, exactly like `skydive sandbox`. (A first-class `fs` relay channel and `skydive fs mount` land in follow-ups.)\n\n## [0.4.0] - 2026-08-07\n\n### Added\n\n- `skydive chat` works with API key auth: an API key alone now opens the chat TUI (and headless `chat -p`) without a browser login, using the key's pinned workspace.\n- `skydive config` command to view and set the safe, hand-editable CLI preferences (`shareMachineDefault`, `updateCheck`) without opening `config.json`. `config`/`config list` shows each key, its effective value, and whether it comes from the file or the built-in default; `config get`/`set`/`unset` read and change one key; `config path` prints the config file location. It only ever touches those preference keys, never credentials or flow-managed state.\n- Billing-blocked sends and runs now surface the server-authored recovery\n guidance from the typed billing outcome. The chat TUI renders it as a\n warning-toned system-notice row — live when a send is refused or a run is\n stopped at the billing boundary, and again when reopening a conversation\n that holds a persisted billing notice — instead of a generic error.\n Headless `chat -p` and `messages get` print the guidance (state, recovery\n step, and manage-billing URL) on stderr, keep any partial output already\n streamed on stdout, and exit with code `5` (distinct from the generic `1`)\n so scripted callers can tell a billing pause from a failure.\n- Syntax highlighting for 14 more languages in the chat transcript and file/review panes (python, bash, go, rust, c, cpp, java, ruby, css, html, json, yaml, and more) — previously only JavaScript/TypeScript rendered colored.\n\n### Changed\n\n- `skydive agent list` lists the full agent roster by default instead of a truncated page.\n\n### Fixed\n\n- Long multi-line bash tool-call commands (e.g. heredocs) are clamped with a `+N more · click to expand` tail instead of dumping their whole body into the scrollback.\n- Tool error output is clipped the same way as ok output, so a failing command no longer floods the transcript.\n- The TUI toast overlay has an opaque background instead of letting the transcript bleed through.\n\n## [0.3.0] - 2026-08-04\n\n### Added\n\n**Chat TUI**\n\n- `/fork` slash command to fork a conversation at any point and continue in a new thread.\n- `/conversation` switches threads from inside a live session.\n- `/compact` triggers manual conversation compaction.\n- `/portal` and `/copy` slash commands.\n- Archive the current conversation from the chat TUI.\n- `--new` flag on `skydive chat` to start a fresh conversation.\n- Debounced server-side conversation search in the picker, with title matches ranked first.\n- File review pane orientation toggle, and review comments in the file pane.\n- Pinned plan card controls and a jump-to-latest hint.\n- Terminal title (OSC) follows the live conversation title on every screen; inside cmux the workspace is renamed too, and linked PRs are pushed to the cmux sidebar.\n- Every participating agent shows in the conversation list; connect cards each have their own identity, and ctrl+r asks which connect card to act on.\n- Colored Skydive splash art in `skydive --help` and the TUI pickers.\n- New-agent name prefilled from the server's suggestion.\n\n**CLI & platform**\n\n- `skydive update` self-updater, with an update notice when a new version is available.\n- Shell completions for bash, zsh, and fish via `completion install`.\n- Portal daemon: one connection per host with per-conversation working directories.\n- Agent-led coding-agent import (`skydive import`).\n- Per-workspace scoping on the REST client, and cross-org `--agent <uuid>` auto-switches workspace.\n- `skydive-beta` wrapper for the canary channel.\n\n### Fixed\n\n- `skydive update` no longer claims an update is available on every npm install.\n- Chat memory is bounded (payload clamps + windowed rows) with a memory watchdog and OOM diagnostic report; transcript rows are memoized so streaming stops re-rendering the whole chat.\n- Chat-send failures map to real error messages.\n- Notification and sidebar previews use the last text block of the reply and strip inline markdown.\n- Picker rows no longer overlap when a conversation title contains a newline, tab, or control character.\n- Sending to non-web channel conversations is blocked in the TUI.\n- GitHub bot connect opens on the web front door, not api.skydive.com.\n- TUI activity counter times from the run, not the screen.\n\n## [0.2.0] - 2026-07-29\n\n### Added\n\n**Sandbox & machine access**\n\n- Standalone `skydive sandbox` command for direct access to an agent's sandbox.\n- `/sandbox` in the chat TUI — live PTY session and one-shot command execution.\n- Headless machine sharing via `skydive portal`, with self-registering portal grants.\n- `shareMachineDefault` config key for always-on portal sharing.\n\n**Chat TUI**\n\n- File pane (`ctrl+g`) with Changes and Files tabs: a shared file tree plus source viewer and a workspace browser, mouse-resizable and responsive to terminal size.\n- Agent todo list rendered in the chat TUI, above the composer, mirroring the web chat's todo card.\n- Fuzzy finder in the conversation and agent pickers.\n- Slash-command autocomplete menu.\n- Paste or drag-and-drop any file into the composer; paste clipboard images with Cmd+V.\n- Run local shell commands with `!` in the composer.\n- Conversation recaps, streamed live title updates, and per-agent attribution on assistant turns.\n- Working timer rolls up into minutes and hours.\n- Conversation picker paginates past 50 conversations and shows only your own conversations.\n- Esc leaves a live run; typing `exit` quits the chat.\n- Cursor Dark theme.\n- Picker rows no longer overlap when a conversation title contains a newline, tab, or control character.\n\n**Headless & scripting**\n\n- Resume a conversation by id.\n- `conversations list` and `conversations show` for transcript reads.\n- `messages get`, with run recovery keyed on message id.\n- Connect cards surface in headless `-p` mode so a driving agent never gets stuck.\n\n**Authentication**\n\n- `skydive auth login` via the browser now auto-mints an API key, so one login yields both a chat session and a usable management credential. Management commands announce the key's pinned workspace when it drives them, so a workspace mismatch is visible at use time.\n- Workspace picker on the device authorization page.\n- Account and workspace identity shown in `auth status`.\n\n**Platform**\n\n- Standalone binary builds compiling the CLI into a per-target executable.\n- Interactive workspace switcher; management commands follow the active workspace.\n- Terminal host integrations and agent notifications.\n\n### Fixed\n\n- Transcript errors collapse to one line, click to expand (REST and portal errors keep their full body).\n- Dragged/pasted image file paths attach the file instead of inserting path text, including macOS paths with literal parentheses.\n- Bare URLs in chat markdown are hyperlinked so they survive text wrap.\n- Composer draft is preserved across TUI overlays.\n- Relative connect links resolve before opening the browser.\n- Numbered markdown headings render colored in the TUI.\n- Chat transcript pages by 75% of a screen; picker rows stay on one line.\n- Run starts push to the TUI over the conversation stream.\n- Security: remediated high-severity dependency findings and cleared tar/shell-quote CVEs.\n\n## [0.1.0] - 2026-07-21\n\nInitial public release: `skydive chat` TUI, agent and conversation management,\ndevice authorization, and headless `-p` mode.\n";
|
|
4030
4197
|
|
|
4031
4198
|
//#endregion
|
|
4032
4199
|
//#region src/whats-new.ts
|
|
@@ -4169,7 +4336,7 @@ if (process.argv.includes(UPDATE_WORKER_FLAG)) {
|
|
|
4169
4336
|
process.exit(0);
|
|
4170
4337
|
}
|
|
4171
4338
|
if (process.argv.includes(PORTAL_DAEMON_FLAG)) {
|
|
4172
|
-
const { runPortalDaemon } = await import("./daemon-
|
|
4339
|
+
const { runPortalDaemon } = await import("./daemon-Ce8WLdGI.mjs");
|
|
4173
4340
|
runPortalDaemon(process.argv);
|
|
4174
4341
|
} else runCli();
|
|
4175
4342
|
function runCli() {
|