skydive-cli 0.5.0-beta.21 → 0.5.0-beta.23
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 +160 -46
- package/dist/js/{boot-DcQhJadu.mjs → boot-D4Ov5CWq.mjs} +119 -14
- package/dist/js/{daemon-Dj9tGT12.mjs → daemon-Cggp7WiN.mjs} +1 -1
- package/dist/js/{daemon-C6tZH1dO.mjs → daemon-OY-PnbML.mjs} +1 -1
- package/dist/js/{daemon-client-BNK77jX_.mjs → daemon-client-BKBisL3m.mjs} +1 -1
- package/dist/js/daemon-client-QBkMkgyS.mjs +7 -0
- package/dist/js/{print-OvYyj6Uk.mjs → print-DQtIEVHh.mjs} +3 -2
- package/dist/js/{print-C8DHKrJu.mjs → print-Pi0PQRj8.mjs} +1 -1
- package/dist/js/{profiler-Bl25cyUp.mjs → profiler-C4n2dBYm.mjs} +222 -3
- package/package.json +1 -1
- package/dist/js/daemon-client-CgpMgM8Q.mjs +0 -7
package/dist/js/bin.mjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as
|
|
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-
|
|
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-C4n2dBYm.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$1, j as getStoredApiKeyWorkspaceName, k as getShareMachineDefault, v as DEFAULT_API_URL, w as getLastSeenVersion, x as deleteConfig } from "./print-DQtIEVHh.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-Dx6b-Nq_.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-
|
|
9
|
+
import { i as queryDaemonStatus, l as PORTAL_DAEMON_FLAG, n as ensureDaemonRunning, s as stopDaemon, u as daemonPaths } from "./daemon-Cggp7WiN.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";
|
|
13
13
|
import yargs from "yargs";
|
|
14
|
-
import
|
|
14
|
+
import { hostname, tmpdir } from "node:os";
|
|
15
15
|
import path, { join } from "node:path";
|
|
16
16
|
import { err, ok } from "neverthrow";
|
|
17
17
|
import { z } from "zod";
|
|
@@ -70,10 +70,12 @@ var SkydiveApiClient = class {
|
|
|
70
70
|
baseUrl;
|
|
71
71
|
token;
|
|
72
72
|
authKind;
|
|
73
|
+
workspaceId;
|
|
73
74
|
constructor(config) {
|
|
74
75
|
this.baseUrl = `${config.apiUrl.replace(/\/$/, "")}/v1`;
|
|
75
76
|
this.token = config.token;
|
|
76
77
|
this.authKind = config.kind;
|
|
78
|
+
this.workspaceId = config.workspaceId;
|
|
77
79
|
}
|
|
78
80
|
/**
|
|
79
81
|
* List one page of agents in the caller's workspace. The server caps a
|
|
@@ -159,6 +161,7 @@ var SkydiveApiClient = class {
|
|
|
159
161
|
authorization: `Bearer ${this.token}`,
|
|
160
162
|
"user-agent": USER_AGENT
|
|
161
163
|
};
|
|
164
|
+
if (this.workspaceId) headers["x-workspace-id"] = this.workspaceId;
|
|
162
165
|
if (params.body) headers["content-type"] = "application/json";
|
|
163
166
|
let response;
|
|
164
167
|
try {
|
|
@@ -718,11 +721,6 @@ const authCommand = {
|
|
|
718
721
|
* matches on it to keep notices off the user's prompt (see isCompletionProbe).
|
|
719
722
|
*/
|
|
720
723
|
const COMPLETION_PROBE_FLAG = "--get-yargs-completions";
|
|
721
|
-
const SUPPORTED_SHELLS = [
|
|
722
|
-
"bash",
|
|
723
|
-
"zsh",
|
|
724
|
-
"fish"
|
|
725
|
-
];
|
|
726
724
|
/**
|
|
727
725
|
* True when this invocation is a shell asking for completion candidates
|
|
728
726
|
* rather than a user running a command. Such a run must print candidates and
|
|
@@ -894,24 +892,6 @@ complete -c ${name} -f -k -a '(${fn})'
|
|
|
894
892
|
${blockEnd(name)}
|
|
895
893
|
`;
|
|
896
894
|
}
|
|
897
|
-
const defaultInstallEnv = () => ({
|
|
898
|
-
home: os.homedir(),
|
|
899
|
-
platform: process.platform,
|
|
900
|
-
env: process.env,
|
|
901
|
-
exists: fs.existsSync
|
|
902
|
-
});
|
|
903
|
-
/**
|
|
904
|
-
* The shell to install for when the user didn't name one. `$SHELL` is the
|
|
905
|
-
* only signal available: a completion install runs as a child process, so the
|
|
906
|
-
* invoking shell's own variables (`$ZSH_VERSION`, `$FISH_VERSION`) aren't
|
|
907
|
-
* visible here.
|
|
908
|
-
*/
|
|
909
|
-
function detectShell(env = process.env) {
|
|
910
|
-
const shell = env["SHELL"];
|
|
911
|
-
if (!shell) return null;
|
|
912
|
-
const name = path.basename(shell);
|
|
913
|
-
return SUPPORTED_SHELLS.find((candidate) => name === candidate) ?? null;
|
|
914
|
-
}
|
|
915
895
|
function completionTarget(shell, deps = defaultInstallEnv()) {
|
|
916
896
|
const { home, platform, env, exists } = deps;
|
|
917
897
|
if (shell === "fish") {
|
|
@@ -1032,7 +1012,7 @@ function requireSession(argv) {
|
|
|
1032
1012
|
* {@link resolveManagementAuth}) or exit with a friendly hint, then build the
|
|
1033
1013
|
* `/v1` client. Shared by `agents`, `keys` and `secrets`.
|
|
1034
1014
|
*/
|
|
1035
|
-
function requireManagementClient(argv) {
|
|
1015
|
+
function requireManagementClient(argv, opts = {}) {
|
|
1036
1016
|
const auth = resolveManagementAuth({ apiUrl: argv["api-url"] });
|
|
1037
1017
|
if (auth.isErr()) {
|
|
1038
1018
|
printError(auth.error.message);
|
|
@@ -1040,7 +1020,10 @@ function requireManagementClient(argv) {
|
|
|
1040
1020
|
}
|
|
1041
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.
|
|
1042
1022
|
`);
|
|
1043
|
-
return new SkydiveApiClient(
|
|
1023
|
+
return new SkydiveApiClient({
|
|
1024
|
+
...auth.value,
|
|
1025
|
+
...opts.workspaceId ? { workspaceId: opts.workspaceId } : {}
|
|
1026
|
+
});
|
|
1044
1027
|
}
|
|
1045
1028
|
/** Resolve the session (see {@link requireSession}) and build a REST client. */
|
|
1046
1029
|
function requireRestClient(argv) {
|
|
@@ -1057,6 +1040,31 @@ function requireRestClient(argv) {
|
|
|
1057
1040
|
|
|
1058
1041
|
//#endregion
|
|
1059
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
|
+
}
|
|
1060
1068
|
const listCommand$5 = {
|
|
1061
1069
|
command: "list",
|
|
1062
1070
|
describe: "List agents in the active workspace",
|
|
@@ -1206,10 +1214,82 @@ const createCommand$1 = {
|
|
|
1206
1214
|
if (agent.url) console.log(` API: ${agent.url}`);
|
|
1207
1215
|
}
|
|
1208
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
|
+
};
|
|
1209
1289
|
const agentsCommand = {
|
|
1210
1290
|
command: "agents",
|
|
1211
1291
|
describe: "Manage agents",
|
|
1212
|
-
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"),
|
|
1213
1293
|
handler: () => {}
|
|
1214
1294
|
};
|
|
1215
1295
|
|
|
@@ -1504,10 +1584,11 @@ const importCommand = {
|
|
|
1504
1584
|
process.exit(1);
|
|
1505
1585
|
}
|
|
1506
1586
|
}
|
|
1507
|
-
const { runChat } = await import("./boot-
|
|
1587
|
+
const { runChat } = await import("./boot-D4Ov5CWq.mjs");
|
|
1508
1588
|
await runChat({
|
|
1509
1589
|
appUrl,
|
|
1510
1590
|
sessionToken: session.value.sessionToken,
|
|
1591
|
+
workspaceId: void 0,
|
|
1511
1592
|
shareMachine: true,
|
|
1512
1593
|
promptHistoryPath: getPromptHistoryPath(),
|
|
1513
1594
|
notifications: true,
|
|
@@ -1541,7 +1622,7 @@ async function runImportPrintMode({ argv, appUrl }) {
|
|
|
1541
1622
|
});
|
|
1542
1623
|
const extra = (argv.print ?? "").trim();
|
|
1543
1624
|
const prompt = buildImportSeedPrompt(process.cwd(), machineOsFromPlatform(process.platform)) + (extra ? `\n\nAdditional instructions: ${extra}` : "");
|
|
1544
|
-
const { runPrint } = await import("./print-
|
|
1625
|
+
const { runPrint } = await import("./print-Pi0PQRj8.mjs");
|
|
1545
1626
|
try {
|
|
1546
1627
|
const result = await runPrint({
|
|
1547
1628
|
appUrl,
|
|
@@ -1849,6 +1930,24 @@ async function resolveAgentWorkspace({ appUrl, sessionToken, agentSelector }) {
|
|
|
1849
1930
|
|
|
1850
1931
|
//#endregion
|
|
1851
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
|
+
}
|
|
1852
1951
|
const chatCommand = {
|
|
1853
1952
|
command: "chat",
|
|
1854
1953
|
describe: "Open the interactive chat TUI, or -p for a one-shot",
|
|
@@ -1859,6 +1958,9 @@ const chatCommand = {
|
|
|
1859
1958
|
}).option("agent", {
|
|
1860
1959
|
type: "string",
|
|
1861
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."
|
|
1862
1964
|
}).option("conversation", {
|
|
1863
1965
|
alias: "resume",
|
|
1864
1966
|
type: "string",
|
|
@@ -1912,21 +2014,27 @@ const chatCommand = {
|
|
|
1912
2014
|
process.exit(1);
|
|
1913
2015
|
}
|
|
1914
2016
|
}
|
|
2017
|
+
const workspaceId = await resolveWorkspaceFlag({
|
|
2018
|
+
appUrl,
|
|
2019
|
+
sessionToken: auth.value.token,
|
|
2020
|
+
selector: argv.workspace
|
|
2021
|
+
});
|
|
1915
2022
|
const themeId = argv.theme ?? process.env["SKYDIVE_THEME"] ?? void 0;
|
|
1916
2023
|
if (themeId !== void 0 && !themes.some((t) => t.id === themeId)) {
|
|
1917
2024
|
printError(`Unknown theme "${themeId}". Valid themes: ${themes.map((t) => t.id).join(", ")}`);
|
|
1918
2025
|
process.exit(1);
|
|
1919
2026
|
}
|
|
1920
|
-
await ensureAgentWorkspace({
|
|
2027
|
+
if (!workspaceId) await ensureAgentWorkspace({
|
|
1921
2028
|
appUrl,
|
|
1922
2029
|
sessionToken: auth.value.token,
|
|
1923
2030
|
agentSelector: argv.agent ?? null
|
|
1924
2031
|
});
|
|
1925
|
-
const { runChat } = await import("./boot-
|
|
2032
|
+
const { runChat } = await import("./boot-D4Ov5CWq.mjs");
|
|
1926
2033
|
await runChat({
|
|
1927
2034
|
appUrl: auth.value.appUrl,
|
|
1928
2035
|
sessionToken: auth.value.token,
|
|
1929
2036
|
authKind: auth.value.kind,
|
|
2037
|
+
workspaceId,
|
|
1930
2038
|
shareMachine: resolveShareMachine(argv),
|
|
1931
2039
|
promptHistoryPath: getPromptHistoryPath(),
|
|
1932
2040
|
theme: themeId,
|
|
@@ -2005,8 +2113,13 @@ async function runPrintMode({ argv, appUrl }) {
|
|
|
2005
2113
|
printError(`${auth.error.message} For non-interactive use, run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
|
|
2006
2114
|
process.exit(1);
|
|
2007
2115
|
}
|
|
2008
|
-
const { runPrint, readStdin } = await import("./print-
|
|
2009
|
-
await
|
|
2116
|
+
const { runPrint, readStdin } = await import("./print-Pi0PQRj8.mjs");
|
|
2117
|
+
const workspaceId = await resolveWorkspaceFlag({
|
|
2118
|
+
appUrl,
|
|
2119
|
+
sessionToken: auth.value.token,
|
|
2120
|
+
selector: argv.workspace
|
|
2121
|
+
});
|
|
2122
|
+
if (!workspaceId) await ensureAgentWorkspace({
|
|
2010
2123
|
appUrl,
|
|
2011
2124
|
sessionToken: auth.value.token,
|
|
2012
2125
|
agentSelector: argv.agent ?? null
|
|
@@ -2041,6 +2154,7 @@ async function runPrintMode({ argv, appUrl }) {
|
|
|
2041
2154
|
const result = await runPrint({
|
|
2042
2155
|
appUrl: auth.value.appUrl,
|
|
2043
2156
|
sessionToken: auth.value.token,
|
|
2157
|
+
workspaceId,
|
|
2044
2158
|
prompt,
|
|
2045
2159
|
agentSelector: argv.agent ?? null,
|
|
2046
2160
|
conversationId: argv.conversation ?? null,
|
|
@@ -2094,7 +2208,7 @@ const getCommand$1 = {
|
|
|
2094
2208
|
printError(`${auth.error.message} Run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
|
|
2095
2209
|
process.exit(1);
|
|
2096
2210
|
}
|
|
2097
|
-
const { messageGet } = await import("./print-
|
|
2211
|
+
const { messageGet } = await import("./print-Pi0PQRj8.mjs");
|
|
2098
2212
|
try {
|
|
2099
2213
|
const result = await messageGet({
|
|
2100
2214
|
appUrl: auth.value.appUrl,
|
|
@@ -2153,7 +2267,7 @@ const listCommand$2 = {
|
|
|
2153
2267
|
}),
|
|
2154
2268
|
handler: async (argv) => {
|
|
2155
2269
|
const client = requireRestClient(argv);
|
|
2156
|
-
const agent = resolveAgent(await client.listAgents({
|
|
2270
|
+
const agent = resolveAgent$1(await client.listAgents({
|
|
2157
2271
|
scope: "org",
|
|
2158
2272
|
onPage: null
|
|
2159
2273
|
}), argv.agent ?? null);
|
|
@@ -2297,7 +2411,7 @@ const switchCommand = {
|
|
|
2297
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.");
|
|
2298
2412
|
process.exit(1);
|
|
2299
2413
|
}
|
|
2300
|
-
const { runWorkspacePicker } = await import("./boot-
|
|
2414
|
+
const { runWorkspacePicker } = await import("./boot-D4Ov5CWq.mjs");
|
|
2301
2415
|
await runWorkspacePicker(session);
|
|
2302
2416
|
return;
|
|
2303
2417
|
}
|
|
@@ -2349,7 +2463,7 @@ async function resolveGrantTarget(argv) {
|
|
|
2349
2463
|
const [{ devices, agents }, identity] = await Promise.all([fetchPortalDevices(session), resolveMachineIdentity(null)]);
|
|
2350
2464
|
return {
|
|
2351
2465
|
session,
|
|
2352
|
-
agent: resolveAgent(agents, argv.agent),
|
|
2466
|
+
agent: resolveAgent$1(agents, argv.agent),
|
|
2353
2467
|
device: findThisDevice(devices, identity.machineName),
|
|
2354
2468
|
identity
|
|
2355
2469
|
};
|
|
@@ -2373,10 +2487,10 @@ const openCommand = {
|
|
|
2373
2487
|
}),
|
|
2374
2488
|
handler: async (argv) => {
|
|
2375
2489
|
const session = requireSession(argv);
|
|
2376
|
-
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;
|
|
2377
2491
|
const cwd = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
|
|
2378
2492
|
const { machineName } = await resolveMachineIdentity(null);
|
|
2379
|
-
const { PortalDaemonClient } = await import("./daemon-client-
|
|
2493
|
+
const { PortalDaemonClient } = await import("./daemon-client-QBkMkgyS.mjs");
|
|
2380
2494
|
let lastLine = "";
|
|
2381
2495
|
let signalConnected;
|
|
2382
2496
|
const connected = new Promise((resolve) => {
|
|
@@ -2619,7 +2733,7 @@ const forwardCommand = {
|
|
|
2619
2733
|
}),
|
|
2620
2734
|
handler: async (argv) => {
|
|
2621
2735
|
const session = requireSession(argv);
|
|
2622
|
-
const agent = resolveAgent((await fetchPortalDevices(session)).agents, argv.agent ?? null);
|
|
2736
|
+
const agent = resolveAgent$1((await fetchPortalDevices(session)).agents, argv.agent ?? null);
|
|
2623
2737
|
const targetPort = argv.port;
|
|
2624
2738
|
const localPort = argv["local-port"] ?? targetPort;
|
|
2625
2739
|
const { startForward } = await import("./forward-18QoL5dO.mjs");
|
|
@@ -2687,7 +2801,7 @@ const sandboxCommand = {
|
|
|
2687
2801
|
handler: async (argv) => {
|
|
2688
2802
|
const session = requireSession(argv);
|
|
2689
2803
|
const { createRestClient } = await import("./rest-DW05dMMc.mjs");
|
|
2690
|
-
const { resolveAgent } = await import("./print-
|
|
2804
|
+
const { resolveAgent } = await import("./print-Pi0PQRj8.mjs");
|
|
2691
2805
|
const client = createRestClient({
|
|
2692
2806
|
appUrl: session.appUrl,
|
|
2693
2807
|
sessionToken: session.sessionToken
|
|
@@ -2845,7 +2959,7 @@ const fsEditCommand = {
|
|
|
2845
2959
|
const session = requireSession(argv);
|
|
2846
2960
|
const remotePath = argv.path;
|
|
2847
2961
|
const { createRestClient } = await import("./rest-DW05dMMc.mjs");
|
|
2848
|
-
const { resolveAgent } = await import("./print-
|
|
2962
|
+
const { resolveAgent } = await import("./print-Pi0PQRj8.mjs");
|
|
2849
2963
|
const client = createRestClient({
|
|
2850
2964
|
appUrl: session.appUrl,
|
|
2851
2965
|
sessionToken: session.sessionToken
|
|
@@ -4222,7 +4336,7 @@ if (process.argv.includes(UPDATE_WORKER_FLAG)) {
|
|
|
4222
4336
|
process.exit(0);
|
|
4223
4337
|
}
|
|
4224
4338
|
if (process.argv.includes(PORTAL_DAEMON_FLAG)) {
|
|
4225
|
-
const { runPortalDaemon } = await import("./daemon-
|
|
4339
|
+
const { runPortalDaemon } = await import("./daemon-OY-PnbML.mjs");
|
|
4226
4340
|
runPortalDaemon(process.argv);
|
|
4227
4341
|
} else runCli();
|
|
4228
4342
|
function runCli() {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as
|
|
3
|
-
import { D as getReviewStateDir, H as saveTheme, N as recordDefaultAgent, O as getSavedTheme, S as getConfigPath, c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, i as resolveAgent, l as parseExternalOauthConnectParams, m as specKeyFor, p as parseConnectCard, s as MASK_CHAR, u as parseOauthConnectParams, v as DEFAULT_API_URL, y as DEFAULT_APP_URL, z as resolveWebUrl } from "./print-
|
|
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-C4n2dBYm.mjs";
|
|
3
|
+
import { D as getReviewStateDir, H as saveTheme, N as recordDefaultAgent, O as getSavedTheme, S as getConfigPath, c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, i as resolveAgent, l as parseExternalOauthConnectParams, m as specKeyFor, p as parseConnectCard, s as MASK_CHAR, u as parseOauthConnectParams, v as DEFAULT_API_URL, y as DEFAULT_APP_URL, z as resolveWebUrl } from "./print-DQtIEVHh.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-Dx6b-Nq_.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-
|
|
8
|
+
import "./daemon-Cggp7WiN.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-
|
|
11
|
+
import { t as PortalDaemonClient } from "./daemon-client-BKBisL3m.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";
|
|
@@ -2141,7 +2141,54 @@ function AgentCreateScreen() {
|
|
|
2141
2141
|
cancelled = true;
|
|
2142
2142
|
};
|
|
2143
2143
|
}, [rest]);
|
|
2144
|
+
const openChat = (agent) => {
|
|
2145
|
+
goTo({
|
|
2146
|
+
kind: "chat",
|
|
2147
|
+
agent,
|
|
2148
|
+
conversation: {
|
|
2149
|
+
kind: "new",
|
|
2150
|
+
agentId: agent.id,
|
|
2151
|
+
nonce: crypto.randomUUID()
|
|
2152
|
+
}
|
|
2153
|
+
});
|
|
2154
|
+
};
|
|
2155
|
+
const acceptAlias = (agent, alias) => {
|
|
2156
|
+
try {
|
|
2157
|
+
const shell = detectShell();
|
|
2158
|
+
if (!shell) {
|
|
2159
|
+
openChat(agent);
|
|
2160
|
+
return;
|
|
2161
|
+
}
|
|
2162
|
+
const result = installAgentAlias({
|
|
2163
|
+
alias,
|
|
2164
|
+
agentId: agent.id,
|
|
2165
|
+
shell
|
|
2166
|
+
});
|
|
2167
|
+
setState({
|
|
2168
|
+
kind: "aliased",
|
|
2169
|
+
agent,
|
|
2170
|
+
message: `Added \`${result.alias}\`. ${aliasActivationHint(result)}`
|
|
2171
|
+
});
|
|
2172
|
+
} catch {
|
|
2173
|
+
openChat(agent);
|
|
2174
|
+
}
|
|
2175
|
+
};
|
|
2144
2176
|
useKeyboard((key) => {
|
|
2177
|
+
if (state.kind === "offer-alias") {
|
|
2178
|
+
if (key.name === "y") {
|
|
2179
|
+
acceptAlias(state.agent, state.alias);
|
|
2180
|
+
return;
|
|
2181
|
+
}
|
|
2182
|
+
if (key.name === "n" || key.name === "escape" || key.name === "return") {
|
|
2183
|
+
openChat(state.agent);
|
|
2184
|
+
return;
|
|
2185
|
+
}
|
|
2186
|
+
return;
|
|
2187
|
+
}
|
|
2188
|
+
if (state.kind === "aliased") {
|
|
2189
|
+
openChat(state.agent);
|
|
2190
|
+
return;
|
|
2191
|
+
}
|
|
2145
2192
|
if (key.name === "escape" && state.kind !== "creating") goTo({ kind: "agent-picker" });
|
|
2146
2193
|
});
|
|
2147
2194
|
const createAgent = async () => {
|
|
@@ -2157,15 +2204,13 @@ function AgentCreateScreen() {
|
|
|
2157
2204
|
setState({ kind: "creating" });
|
|
2158
2205
|
try {
|
|
2159
2206
|
const agent = await rest.createAgent({ name: result.name });
|
|
2160
|
-
|
|
2161
|
-
|
|
2207
|
+
const shell = detectShell();
|
|
2208
|
+
if (shell) setState({
|
|
2209
|
+
kind: "offer-alias",
|
|
2162
2210
|
agent,
|
|
2163
|
-
|
|
2164
|
-
kind: "new",
|
|
2165
|
-
agentId: agent.id,
|
|
2166
|
-
nonce: crypto.randomUUID()
|
|
2167
|
-
}
|
|
2211
|
+
alias: dedupeAliasName(slugifyAliasName(agent.name), takenAliasNames(shell))
|
|
2168
2212
|
});
|
|
2213
|
+
else openChat(agent);
|
|
2169
2214
|
} catch (err) {
|
|
2170
2215
|
if (err instanceof HttpError && err.status === 401) {
|
|
2171
2216
|
fail("Session expired. Run `skydive auth login --web`, then retry.");
|
|
@@ -2205,6 +2250,11 @@ function AgentCreateScreen() {
|
|
|
2205
2250
|
});
|
|
2206
2251
|
}
|
|
2207
2252
|
};
|
|
2253
|
+
if (state.kind === "offer-alias") return /* @__PURE__ */ jsx(AliasOffer, {
|
|
2254
|
+
agentName: state.agent.name,
|
|
2255
|
+
alias: state.alias
|
|
2256
|
+
});
|
|
2257
|
+
if (state.kind === "aliased") return /* @__PURE__ */ jsx(AliasResult, { message: state.message });
|
|
2208
2258
|
return /* @__PURE__ */ jsxs("box", {
|
|
2209
2259
|
style: {
|
|
2210
2260
|
flexDirection: "column",
|
|
@@ -2257,6 +2307,58 @@ function AgentCreateScreen() {
|
|
|
2257
2307
|
]
|
|
2258
2308
|
});
|
|
2259
2309
|
}
|
|
2310
|
+
/**
|
|
2311
|
+
* The post-create prompt offering to alias the agent's name to a shell
|
|
2312
|
+
* command. Presentational so the frame gallery can render it directly.
|
|
2313
|
+
*/
|
|
2314
|
+
function AliasOffer({ agentName, alias }) {
|
|
2315
|
+
return /* @__PURE__ */ jsxs("box", {
|
|
2316
|
+
style: {
|
|
2317
|
+
flexDirection: "column",
|
|
2318
|
+
flexGrow: 1,
|
|
2319
|
+
gap: 1
|
|
2320
|
+
},
|
|
2321
|
+
children: [
|
|
2322
|
+
/* @__PURE__ */ jsxs("text", {
|
|
2323
|
+
fg: theme.fg,
|
|
2324
|
+
children: [
|
|
2325
|
+
"Created ",
|
|
2326
|
+
agentName,
|
|
2327
|
+
"."
|
|
2328
|
+
]
|
|
2329
|
+
}),
|
|
2330
|
+
/* @__PURE__ */ jsxs("text", {
|
|
2331
|
+
fg: theme.muted,
|
|
2332
|
+
children: [
|
|
2333
|
+
"Add a shell alias so you can run `",
|
|
2334
|
+
alias,
|
|
2335
|
+
"` to open this agent?"
|
|
2336
|
+
]
|
|
2337
|
+
}),
|
|
2338
|
+
/* @__PURE__ */ jsx("text", {
|
|
2339
|
+
fg: theme.dim,
|
|
2340
|
+
children: "y add alias · n skip"
|
|
2341
|
+
})
|
|
2342
|
+
]
|
|
2343
|
+
});
|
|
2344
|
+
}
|
|
2345
|
+
/** The confirmation shown after an alias is written. */
|
|
2346
|
+
function AliasResult({ message }) {
|
|
2347
|
+
return /* @__PURE__ */ jsxs("box", {
|
|
2348
|
+
style: {
|
|
2349
|
+
flexDirection: "column",
|
|
2350
|
+
flexGrow: 1,
|
|
2351
|
+
gap: 1
|
|
2352
|
+
},
|
|
2353
|
+
children: [/* @__PURE__ */ jsx("text", {
|
|
2354
|
+
fg: theme.fg,
|
|
2355
|
+
children: message
|
|
2356
|
+
}), /* @__PURE__ */ jsx("text", {
|
|
2357
|
+
fg: theme.dim,
|
|
2358
|
+
children: "press any key to continue"
|
|
2359
|
+
})]
|
|
2360
|
+
});
|
|
2361
|
+
}
|
|
2260
2362
|
|
|
2261
2363
|
//#endregion
|
|
2262
2364
|
//#region src/chat/tui/screens/first-run.tsx
|
|
@@ -13337,7 +13439,7 @@ function ToastView() {
|
|
|
13337
13439
|
|
|
13338
13440
|
//#endregion
|
|
13339
13441
|
//#region src/chat/tui/app.tsx
|
|
13340
|
-
function App({ appUrl, sessionToken, authKind, shareMachine, promptHistoryPath, notifications, agentSelector, conversationId, newConversation, defaultAgentSelector, forceOnboarding }) {
|
|
13442
|
+
function App({ appUrl, sessionToken, authKind, workspaceId, shareMachine, promptHistoryPath, notifications, agentSelector, conversationId, newConversation, defaultAgentSelector, forceOnboarding }) {
|
|
13341
13443
|
const renderer = useRenderer();
|
|
13342
13444
|
const screen = useStore((s) => s.screen);
|
|
13343
13445
|
useEffect(() => {
|
|
@@ -13410,7 +13512,8 @@ function App({ appUrl, sessionToken, authKind, shareMachine, promptHistoryPath,
|
|
|
13410
13512
|
useEffect(() => {
|
|
13411
13513
|
const rest = createRestClient({
|
|
13412
13514
|
appUrl,
|
|
13413
|
-
sessionToken
|
|
13515
|
+
sessionToken,
|
|
13516
|
+
workspaceId
|
|
13414
13517
|
});
|
|
13415
13518
|
setClients({
|
|
13416
13519
|
appUrl,
|
|
@@ -13446,6 +13549,7 @@ function App({ appUrl, sessionToken, authKind, shareMachine, promptHistoryPath,
|
|
|
13446
13549
|
}, [
|
|
13447
13550
|
appUrl,
|
|
13448
13551
|
sessionToken,
|
|
13552
|
+
workspaceId,
|
|
13449
13553
|
promptHistoryPath,
|
|
13450
13554
|
agentSelector,
|
|
13451
13555
|
conversationId,
|
|
@@ -14031,7 +14135,7 @@ function onRenderCommit(id, phase, actualDuration, baseDuration, startTime, comm
|
|
|
14031
14135
|
* module — and the heavy OpenTUI dependency graph it pulls in — is
|
|
14032
14136
|
* dynamically imported and never loaded on the Node management path.
|
|
14033
14137
|
*/
|
|
14034
|
-
async function runChat({ appUrl, sessionToken, authKind = "session", shareMachine, promptHistoryPath, theme: themeOverride, notifications, agentSelector, conversationId, newConversation, defaultAgentSelector, forceOnboarding, seedPrompt }) {
|
|
14138
|
+
async function runChat({ appUrl, sessionToken, authKind = "session", workspaceId, shareMachine, promptHistoryPath, theme: themeOverride, notifications, agentSelector, conversationId, newConversation, defaultAgentSelector, forceOnboarding, seedPrompt }) {
|
|
14035
14139
|
await registerGrammars();
|
|
14036
14140
|
const watchdog = startMemoryWatchdog();
|
|
14037
14141
|
installCrashHandler({ memoryProvider: () => watchdog.snapshot() });
|
|
@@ -14068,6 +14172,7 @@ async function runChat({ appUrl, sessionToken, authKind = "session", shareMachin
|
|
|
14068
14172
|
appUrl,
|
|
14069
14173
|
sessionToken,
|
|
14070
14174
|
authKind,
|
|
14175
|
+
workspaceId,
|
|
14071
14176
|
shareMachine,
|
|
14072
14177
|
promptHistoryPath,
|
|
14073
14178
|
notifications,
|
|
@@ -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 "
|
|
23
|
+
return "1786696942";
|
|
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-
|
|
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-Cggp7WiN.mjs";
|
|
4
4
|
import "./api-DG5W6iwx.mjs";
|
|
5
5
|
|
|
6
6
|
export { runPortalDaemon };
|
|
@@ -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-
|
|
2
|
+
import { c as LOCAL_PROTOCOL_VERSION, d as encodeLine, f as makeLineParser, n as ensureDaemonRunning, p as parseDaemonMessage, u as daemonPaths } from "./daemon-Cggp7WiN.mjs";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { connect } from "node:net";
|
|
5
5
|
|
|
@@ -638,10 +638,11 @@ function formatConnectCard(card) {
|
|
|
638
638
|
* disambiguate: an `--agent` selector must match exactly one agent, and
|
|
639
639
|
* when it's omitted we only auto-pick if the account has exactly one.
|
|
640
640
|
*/
|
|
641
|
-
async function runPrint({ appUrl, sessionToken, prompt, agentSelector, conversationId, json, machineShare, grantTargetAgent }) {
|
|
641
|
+
async function runPrint({ appUrl, sessionToken, workspaceId, prompt, agentSelector, conversationId, json, machineShare, grantTargetAgent }) {
|
|
642
642
|
const client = createRestClient({
|
|
643
643
|
appUrl,
|
|
644
|
-
sessionToken
|
|
644
|
+
sessionToken,
|
|
645
|
+
workspaceId
|
|
645
646
|
});
|
|
646
647
|
const agent = resolveAgent(await client.listAgents({
|
|
647
648
|
scope: "org",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-
|
|
2
|
+
import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-DQtIEVHh.mjs";
|
|
3
3
|
import "./rest-Dx6b-Nq_.mjs";
|
|
4
4
|
import "./billing-blocked-2wju4gC_.mjs";
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { S as getConfigPath } from "./print-
|
|
2
|
+
import { S as getConfigPath } from "./print-DQtIEVHh.mjs";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { err, ok } from "neverthrow";
|
|
@@ -8,7 +8,7 @@ import fs from "node:fs";
|
|
|
8
8
|
|
|
9
9
|
//#region package.json
|
|
10
10
|
var name = "skydive-cli";
|
|
11
|
-
var version$1 = "0.5.0-beta.
|
|
11
|
+
var version$1 = "0.5.0-beta.23";
|
|
12
12
|
|
|
13
13
|
//#endregion
|
|
14
14
|
//#region src/auth/organization.ts
|
|
@@ -32,6 +32,32 @@ async function listWorkspaces({ appUrl, sessionToken }) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
|
+
* Match a workspace selector against a roster the way `workspace switch` does:
|
|
36
|
+
* exact id first, then case-insensitive slug, then case-insensitive name.
|
|
37
|
+
* Returns null when nothing matches. Pure — the caller owns fetching and error
|
|
38
|
+
* messaging.
|
|
39
|
+
*/
|
|
40
|
+
function matchWorkspace(workspaces, selector) {
|
|
41
|
+
const needle = selector.trim().toLowerCase();
|
|
42
|
+
return workspaces.find((w) => w.id === selector) ?? workspaces.find((w) => w.slug.toLowerCase() === needle) ?? workspaces.find((w) => w.name.toLowerCase() === needle) ?? null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Resolve a workspace selector (slug, name, or id) to its id by listing the
|
|
46
|
+
* account's workspaces and matching. Used by any command that takes a
|
|
47
|
+
* `--workspace` flag to scope requests without persisting an active-workspace
|
|
48
|
+
* switch.
|
|
49
|
+
*/
|
|
50
|
+
async function resolveWorkspaceId({ appUrl, sessionToken, selector }) {
|
|
51
|
+
const workspaces = await listWorkspaces({
|
|
52
|
+
appUrl,
|
|
53
|
+
sessionToken
|
|
54
|
+
});
|
|
55
|
+
if (workspaces.isErr()) return err(workspaces.error);
|
|
56
|
+
const match = matchWorkspace(workspaces.value, selector);
|
|
57
|
+
if (!match) return err({ message: `No workspace matches "${selector}". Run \`skydive workspace list\` to see available workspaces.` });
|
|
58
|
+
return ok(match);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
35
61
|
* Resolve who the current chat session belongs to: the signed-in user's
|
|
36
62
|
* email/name and the active workspace. Used by `auth status` so a user running
|
|
37
63
|
* multiple accounts can answer "am I logged into the right one?" without
|
|
@@ -125,6 +151,199 @@ async function ensureActiveOrganization({ appUrl, sessionToken }) {
|
|
|
125
151
|
});
|
|
126
152
|
}
|
|
127
153
|
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region src/shell/shell-env.ts
|
|
156
|
+
/**
|
|
157
|
+
* Shared, dependency-light shell primitives used by anything that edits the
|
|
158
|
+
* user's shell startup files — `completion install` and `agents alias`. Kept
|
|
159
|
+
* free of yargs and the CLI's output layer so the TUI can import it without
|
|
160
|
+
* dragging command machinery into its bundle or creating a TUI -> commands
|
|
161
|
+
* import cycle.
|
|
162
|
+
*/
|
|
163
|
+
const SUPPORTED_SHELLS = [
|
|
164
|
+
"bash",
|
|
165
|
+
"zsh",
|
|
166
|
+
"fish"
|
|
167
|
+
];
|
|
168
|
+
const defaultInstallEnv = () => ({
|
|
169
|
+
home: os.homedir(),
|
|
170
|
+
platform: process.platform,
|
|
171
|
+
env: process.env,
|
|
172
|
+
exists: fs.existsSync
|
|
173
|
+
});
|
|
174
|
+
/**
|
|
175
|
+
* The shell to act on when the user didn't name one. `$SHELL` is the only
|
|
176
|
+
* signal available: an install runs as a child process, so the invoking
|
|
177
|
+
* shell's own variables (`$ZSH_VERSION`, `$FISH_VERSION`) aren't visible here.
|
|
178
|
+
*/
|
|
179
|
+
function detectShell(env = process.env) {
|
|
180
|
+
const shell = env["SHELL"];
|
|
181
|
+
if (!shell) return null;
|
|
182
|
+
const name = path.basename(shell);
|
|
183
|
+
return SUPPORTED_SHELLS.find((candidate) => name === candidate) ?? null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/commands/agent-alias.ts
|
|
188
|
+
/**
|
|
189
|
+
* Shell aliases that point a friendly command name at a specific agent's chat.
|
|
190
|
+
*
|
|
191
|
+
* When someone creates an agent named "Ripple", they almost always want to
|
|
192
|
+
* reach it again by typing `ripple`, not `skydive chat --agent <uuid>`. This
|
|
193
|
+
* module writes a managed alias block into the user's shell startup file so
|
|
194
|
+
* `ripple` opens that agent. It reuses the same shell detection and startup-file
|
|
195
|
+
* machinery as `completion install` (imported from ./completion), so the two
|
|
196
|
+
* features agree on which file to touch and how to detect the shell.
|
|
197
|
+
*
|
|
198
|
+
* The alias always targets the agent **id**, never its name: names are not
|
|
199
|
+
* unique and can be renamed, so an id keeps `ripple` pointed at the same agent
|
|
200
|
+
* even if a second "Ripple" is created later or this one is renamed.
|
|
201
|
+
*/
|
|
202
|
+
/**
|
|
203
|
+
* Turn a display name into a shell-safe alias token: lowercase, non-alnum runs
|
|
204
|
+
* collapsed to a single hyphen, leading/trailing hyphens trimmed. "Ripple" ->
|
|
205
|
+
* "ripple", "My Agent" -> "my-agent", "Ripple 2.0" -> "ripple-2-0".
|
|
206
|
+
*
|
|
207
|
+
* A leading digit is prefixed with `a-` because bash rejects an alias name that
|
|
208
|
+
* starts with a digit (`alias 2fast=...` is a syntax error). An empty result
|
|
209
|
+
* (a name that was all punctuation) falls back to "agent" so callers always get
|
|
210
|
+
* a usable token to offer or dedupe from.
|
|
211
|
+
*/
|
|
212
|
+
function slugifyAliasName(name) {
|
|
213
|
+
const slug = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
214
|
+
if (!slug) return "agent";
|
|
215
|
+
return /^[0-9]/.test(slug) ? `a-${slug}` : slug;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* The alias token to actually install, given a preferred token and the set of
|
|
219
|
+
* names already taken (existing aliases, and — the caller's choice — commands
|
|
220
|
+
* on PATH). Prefers the clean token; on a collision appends the lowest integer
|
|
221
|
+
* that frees it: `ripple` -> `ripple2` -> `ripple3`. Matching is
|
|
222
|
+
* case-insensitive because shells treat `Ripple` and `ripple` as distinct
|
|
223
|
+
* aliases but a user typing one means the other.
|
|
224
|
+
*/
|
|
225
|
+
function dedupeAliasName(preferred, taken) {
|
|
226
|
+
const takenLower = new Set(Array.from(taken, (t) => t.trim().toLowerCase()).filter(Boolean));
|
|
227
|
+
if (!takenLower.has(preferred.toLowerCase())) return preferred;
|
|
228
|
+
for (let n = 2;; n += 1) {
|
|
229
|
+
const candidate = `${preferred}${n}`;
|
|
230
|
+
if (!takenLower.has(candidate.toLowerCase())) return candidate;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/** The startup file an alias block belongs in, per shell. */
|
|
234
|
+
function aliasTarget(shell, deps = defaultInstallEnv()) {
|
|
235
|
+
const { home, platform, env, exists } = deps;
|
|
236
|
+
if (shell === "fish") {
|
|
237
|
+
const configHome = env["XDG_CONFIG_HOME"] || path.join(home, ".config");
|
|
238
|
+
return path.join(configHome, "fish", "config.fish");
|
|
239
|
+
}
|
|
240
|
+
if (shell === "zsh") {
|
|
241
|
+
const zdotdir = env["ZDOTDIR"] || home;
|
|
242
|
+
return path.join(zdotdir, ".zshrc");
|
|
243
|
+
}
|
|
244
|
+
const rc = path.join(home, ".bashrc");
|
|
245
|
+
const profile = path.join(home, ".bash_profile");
|
|
246
|
+
const preferred = platform === "darwin" ? profile : rc;
|
|
247
|
+
const alternate = platform === "darwin" ? rc : profile;
|
|
248
|
+
if (!exists(preferred) && exists(alternate)) return alternate;
|
|
249
|
+
return preferred;
|
|
250
|
+
}
|
|
251
|
+
const BLOCK_START = "###-begin-skydive-agent-aliases-###";
|
|
252
|
+
const BLOCK_END = "###-end-skydive-agent-aliases-###";
|
|
253
|
+
const aliasLine = (entry) => {
|
|
254
|
+
const ws = entry.workspaceId ? ` --workspace ${entry.workspaceId}` : "";
|
|
255
|
+
return `alias ${entry.alias}='skydive chat --agent ${entry.agentId}${ws} --new' # skydive-agent:${entry.agentId}`;
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* Parse the alias entries already inside the managed block. Used to merge a new
|
|
259
|
+
* alias with existing ones and to answer "is this token taken?". Tolerant of
|
|
260
|
+
* hand-edits: only well-formed lines carrying our trailing marker are read
|
|
261
|
+
* back; anything else in the block is preserved verbatim on rewrite.
|
|
262
|
+
*/
|
|
263
|
+
function parseAliasBlock(existing) {
|
|
264
|
+
const start = existing.indexOf(BLOCK_START);
|
|
265
|
+
const end = existing.indexOf(BLOCK_END);
|
|
266
|
+
if (start === -1 || end <= start) return [];
|
|
267
|
+
const body = existing.slice(start + 35, end);
|
|
268
|
+
const entries = [];
|
|
269
|
+
for (const line of body.split("\n")) {
|
|
270
|
+
const match = line.match(/^alias\s+([^=]+)=.*# skydive-agent:([0-9a-f-]+)\s*$/i);
|
|
271
|
+
if (match?.[1] && match[2]) {
|
|
272
|
+
const wsMatch = line.match(/--workspace\s+(\S+)/);
|
|
273
|
+
entries.push({
|
|
274
|
+
alias: match[1].trim(),
|
|
275
|
+
agentId: match[2],
|
|
276
|
+
workspaceId: wsMatch?.[1]
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return entries;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Merge `entry` into the managed alias block of `existing`, returning the new
|
|
284
|
+
* file contents. Idempotent: re-installing the same alias for the same agent is
|
|
285
|
+
* a no-op edit; a new agent id adds a line; an existing agent id has its alias
|
|
286
|
+
* token refreshed. The block is created if absent.
|
|
287
|
+
*/
|
|
288
|
+
function spliceAliasBlock(existing, entry) {
|
|
289
|
+
const current = parseAliasBlock(existing).filter((e) => e.agentId !== entry.agentId);
|
|
290
|
+
current.push(entry);
|
|
291
|
+
current.sort((a, b) => a.alias.localeCompare(b.alias));
|
|
292
|
+
const block = [
|
|
293
|
+
BLOCK_START,
|
|
294
|
+
"# skydive agent aliases, managed by `skydive agents alias`. Edits between",
|
|
295
|
+
"# these markers are overwritten when an alias is added or refreshed.",
|
|
296
|
+
...current.map(aliasLine),
|
|
297
|
+
BLOCK_END
|
|
298
|
+
].join("\n");
|
|
299
|
+
const start = existing.indexOf(BLOCK_START);
|
|
300
|
+
const endMarker = BLOCK_END;
|
|
301
|
+
const end = existing.indexOf(endMarker);
|
|
302
|
+
if (start !== -1 && end > start) return `${existing.slice(0, start)}${block}\n${existing.slice(end + 33).replace(/^\n/, "")}`;
|
|
303
|
+
return `${existing}${existing === "" || existing.endsWith("\n\n") ? "" : "\n"}\n${block}\n`;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Write (or refresh) an agent alias in the user's shell startup file. Returns
|
|
307
|
+
* the resolved shell, file, and the alias token actually used. Does not dedupe
|
|
308
|
+
* on its own — pass a token already reconciled with `takenAliasNames` so the
|
|
309
|
+
* caller controls the suggestion UX.
|
|
310
|
+
*/
|
|
311
|
+
function installAgentAlias(input, deps = defaultInstallEnv()) {
|
|
312
|
+
const target = aliasTarget(input.shell, deps);
|
|
313
|
+
const existing = deps.exists(target) ? fs.readFileSync(target, "utf8") : "";
|
|
314
|
+
const contents = spliceAliasBlock(existing, {
|
|
315
|
+
alias: input.alias,
|
|
316
|
+
agentId: input.agentId,
|
|
317
|
+
workspaceId: input.workspaceId
|
|
318
|
+
});
|
|
319
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
320
|
+
fs.writeFileSync(target, contents, "utf8");
|
|
321
|
+
return {
|
|
322
|
+
shell: input.shell,
|
|
323
|
+
path: target,
|
|
324
|
+
alias: input.alias,
|
|
325
|
+
agentId: input.agentId,
|
|
326
|
+
updated: existing.includes(BLOCK_START)
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Alias tokens already defined in our managed block of the given shell's
|
|
331
|
+
* startup file. The dedupe source: a fresh install shouldn't collide with an
|
|
332
|
+
* alias we wrote for another agent. (We don't try to read the user's own
|
|
333
|
+
* hand-written aliases or PATH — a shell child process can't see the parent's
|
|
334
|
+
* live alias table, and probing PATH is noisy; the dedupe covers our own
|
|
335
|
+
* blocks, which is where real collisions come from in practice.)
|
|
336
|
+
*/
|
|
337
|
+
function takenAliasNames(shell, deps = defaultInstallEnv()) {
|
|
338
|
+
const target = aliasTarget(shell, deps);
|
|
339
|
+
if (!deps.exists(target)) return [];
|
|
340
|
+
return parseAliasBlock(fs.readFileSync(target, "utf8")).map((e) => e.alias);
|
|
341
|
+
}
|
|
342
|
+
/** What the user must do before the alias works in the shell they're in. */
|
|
343
|
+
function aliasActivationHint(result) {
|
|
344
|
+
return result.shell === "fish" ? "Open a new fish shell to use it." : `Open a new shell, or run: source ${result.path}`;
|
|
345
|
+
}
|
|
346
|
+
|
|
128
347
|
//#endregion
|
|
129
348
|
//#region src/chat/import-seed.ts
|
|
130
349
|
/** Map `process.platform` to the OS family used by the seed prompts. Every
|
|
@@ -1299,4 +1518,4 @@ function maybeStartProfiling(argv, cliVersion) {
|
|
|
1299
1518
|
}
|
|
1300
1519
|
|
|
1301
1520
|
//#endregion
|
|
1302
|
-
export {
|
|
1521
|
+
export { takenAliasNames as A, name as B, themesForMode as C, dedupeAliasName as D, aliasActivationHint as E, getActiveWorkspaceId as F, getSessionIdentity as I, listWorkspaces as L, defaultInstallEnv as M, detectShell as N, installAgentAlias as O, ensureActiveOrganization as P, resolveWorkspaceId as R, themes as S, machineOsFromPlatform as T, version$1 as V, theme as _, installCrashHandler as a, themeModeFromColorFgBg as b, MARK_CELLS as c, splashFitsWidth as d, DEFAULT_THEME_ID as f, noColorRequested as g, monoTheme as h, writeArtifact as i, SUPPORTED_SHELLS as j, slugifyAliasName as k, WORDMARK as l, findTheme as m, profilingEnabled as n, buildCrashReport as o, applyTheme as p, record as r, writeCrashReport as s, maybeStartProfiling as t, brandHelpArt as u, themeForMode as v, buildImportSeedPrompt as w, themeVersion as x, themeMode as y, setActiveWorkspace as z };
|
package/package.json
CHANGED