skydive-cli 0.5.0-beta.26 → 0.5.0-beta.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/js/bin.mjs +17 -17
- package/dist/js/{boot-CAktTbtz.mjs → boot-DPB2qX9x.mjs} +150 -16
- package/dist/js/{daemon-r91-81wJ.mjs → daemon-B5SNuIhD.mjs} +1 -1
- package/dist/js/{daemon-Ciwk5Ilc.mjs → daemon-C5e4hKwL.mjs} +1 -1
- package/dist/js/daemon-client-C6d14JU1.mjs +7 -0
- package/dist/js/{daemon-client-DfieDaV3.mjs → daemon-client-CneyOup7.mjs} +1 -1
- package/dist/js/forward-DL6DYqyc.mjs +187 -0
- package/dist/js/{print-Pi0PQRj8.mjs → print-CQkXZ02a.mjs} +2 -2
- package/dist/js/{print-DQtIEVHh.mjs → print-DYiQC5IW.mjs} +105 -6
- package/dist/js/{profiler-PXG4pjU6.mjs → profiler-rC7AZSxL.mjs} +2 -2
- package/dist/js/{rest-DW05dMMc.mjs → rest-DSpGS5XU.mjs} +1 -1
- package/dist/js/{rest-Dx6b-Nq_.mjs → rest-xXfqT4SX.mjs} +2 -0
- package/package.json +1 -1
- package/dist/js/daemon-client-CH2Cy874.mjs +0 -7
- package/dist/js/forward-18QoL5dO.mjs +0 -68
package/dist/js/bin.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as takenAliasNames, B as name, D as dedupeAliasName, E as aliasActivationHint, F as getActiveWorkspaceId, I as getSessionIdentity, L as listWorkspaces, M as defaultInstallEnv, N as detectShell, O as installAgentAlias, P as ensureActiveOrganization, R as resolveWorkspaceId, S as themes, T as machineOsFromPlatform, V as version, a as installCrashHandler, j as SUPPORTED_SHELLS, k as slugifyAliasName, t as maybeStartProfiling, u as brandHelpArt, w as buildImportSeedPrompt, z as setActiveWorkspace } from "./profiler-
|
|
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-rC7AZSxL.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
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-B5SNuIhD.mjs";
|
|
10
10
|
import { i as grantPortalAccess, n as fetchPortalDevices, o as registerPortalDevice, r as findThisDevice, s as revokePortalAccess } from "./api-DG5W6iwx.mjs";
|
|
11
11
|
import { t as SandboxStream } from "./client-c4c5MmgN.mjs";
|
|
12
12
|
import { hideBin } from "yargs/helpers";
|
|
@@ -1584,7 +1584,7 @@ const importCommand = {
|
|
|
1584
1584
|
process.exit(1);
|
|
1585
1585
|
}
|
|
1586
1586
|
}
|
|
1587
|
-
const { runChat } = await import("./boot-
|
|
1587
|
+
const { runChat } = await import("./boot-DPB2qX9x.mjs");
|
|
1588
1588
|
await runChat({
|
|
1589
1589
|
appUrl,
|
|
1590
1590
|
sessionToken: session.value.sessionToken,
|
|
@@ -1622,7 +1622,7 @@ async function runImportPrintMode({ argv, appUrl }) {
|
|
|
1622
1622
|
});
|
|
1623
1623
|
const extra = (argv.print ?? "").trim();
|
|
1624
1624
|
const prompt = buildImportSeedPrompt(process.cwd(), machineOsFromPlatform(process.platform)) + (extra ? `\n\nAdditional instructions: ${extra}` : "");
|
|
1625
|
-
const { runPrint } = await import("./print-
|
|
1625
|
+
const { runPrint } = await import("./print-CQkXZ02a.mjs");
|
|
1626
1626
|
try {
|
|
1627
1627
|
const result = await runPrint({
|
|
1628
1628
|
appUrl,
|
|
@@ -2029,7 +2029,7 @@ const chatCommand = {
|
|
|
2029
2029
|
sessionToken: auth.value.token,
|
|
2030
2030
|
agentSelector: argv.agent ?? null
|
|
2031
2031
|
});
|
|
2032
|
-
const { runChat } = await import("./boot-
|
|
2032
|
+
const { runChat } = await import("./boot-DPB2qX9x.mjs");
|
|
2033
2033
|
await runChat({
|
|
2034
2034
|
appUrl: auth.value.appUrl,
|
|
2035
2035
|
sessionToken: auth.value.token,
|
|
@@ -2113,7 +2113,7 @@ async function runPrintMode({ argv, appUrl }) {
|
|
|
2113
2113
|
printError(`${auth.error.message} For non-interactive use, run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
|
|
2114
2114
|
process.exit(1);
|
|
2115
2115
|
}
|
|
2116
|
-
const { runPrint, readStdin } = await import("./print-
|
|
2116
|
+
const { runPrint, readStdin } = await import("./print-CQkXZ02a.mjs");
|
|
2117
2117
|
const workspaceId = await resolveWorkspaceFlag({
|
|
2118
2118
|
appUrl,
|
|
2119
2119
|
sessionToken: auth.value.token,
|
|
@@ -2208,7 +2208,7 @@ const getCommand$1 = {
|
|
|
2208
2208
|
printError(`${auth.error.message} Run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
|
|
2209
2209
|
process.exit(1);
|
|
2210
2210
|
}
|
|
2211
|
-
const { messageGet } = await import("./print-
|
|
2211
|
+
const { messageGet } = await import("./print-CQkXZ02a.mjs");
|
|
2212
2212
|
try {
|
|
2213
2213
|
const result = await messageGet({
|
|
2214
2214
|
appUrl: auth.value.appUrl,
|
|
@@ -2411,7 +2411,7 @@ const switchCommand = {
|
|
|
2411
2411
|
printError("The workspace picker needs the Bun runtime and it could not be set up automatically. Pass a workspace slug instead, or install Bun and retry.");
|
|
2412
2412
|
process.exit(1);
|
|
2413
2413
|
}
|
|
2414
|
-
const { runWorkspacePicker } = await import("./boot-
|
|
2414
|
+
const { runWorkspacePicker } = await import("./boot-DPB2qX9x.mjs");
|
|
2415
2415
|
await runWorkspacePicker(session);
|
|
2416
2416
|
return;
|
|
2417
2417
|
}
|
|
@@ -2490,7 +2490,7 @@ const openCommand = {
|
|
|
2490
2490
|
const agent = argv.agent ? resolveAgent$1((await fetchPortalDevices(session)).agents, argv.agent) : null;
|
|
2491
2491
|
const cwd = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
|
|
2492
2492
|
const { machineName } = await resolveMachineIdentity(null);
|
|
2493
|
-
const { PortalDaemonClient } = await import("./daemon-client-
|
|
2493
|
+
const { PortalDaemonClient } = await import("./daemon-client-C6d14JU1.mjs");
|
|
2494
2494
|
let lastLine = "";
|
|
2495
2495
|
let signalConnected;
|
|
2496
2496
|
const connected = new Promise((resolve) => {
|
|
@@ -2736,7 +2736,7 @@ const forwardCommand = {
|
|
|
2736
2736
|
const agent = resolveAgent$1((await fetchPortalDevices(session)).agents, argv.agent ?? null);
|
|
2737
2737
|
const targetPort = argv.port;
|
|
2738
2738
|
const localPort = argv["local-port"] ?? targetPort;
|
|
2739
|
-
const { startForward } = await import("./forward-
|
|
2739
|
+
const { startForward } = await import("./forward-DL6DYqyc.mjs");
|
|
2740
2740
|
const listener = await startForward({
|
|
2741
2741
|
auth: session,
|
|
2742
2742
|
agentId: agent.id,
|
|
@@ -2800,8 +2800,8 @@ const sandboxCommand = {
|
|
|
2800
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`"),
|
|
2801
2801
|
handler: async (argv) => {
|
|
2802
2802
|
const session = requireSession(argv);
|
|
2803
|
-
const { createRestClient } = await import("./rest-
|
|
2804
|
-
const { resolveAgent } = await import("./print-
|
|
2803
|
+
const { createRestClient } = await import("./rest-DSpGS5XU.mjs");
|
|
2804
|
+
const { resolveAgent } = await import("./print-CQkXZ02a.mjs");
|
|
2805
2805
|
const client = createRestClient({
|
|
2806
2806
|
appUrl: session.appUrl,
|
|
2807
2807
|
sessionToken: session.sessionToken
|
|
@@ -2958,8 +2958,8 @@ const fsEditCommand = {
|
|
|
2958
2958
|
handler: async (argv) => {
|
|
2959
2959
|
const session = requireSession(argv);
|
|
2960
2960
|
const remotePath = argv.path;
|
|
2961
|
-
const { createRestClient } = await import("./rest-
|
|
2962
|
-
const { resolveAgent } = await import("./print-
|
|
2961
|
+
const { createRestClient } = await import("./rest-DSpGS5XU.mjs");
|
|
2962
|
+
const { resolveAgent } = await import("./print-CQkXZ02a.mjs");
|
|
2963
2963
|
const client = createRestClient({
|
|
2964
2964
|
appUrl: session.appUrl,
|
|
2965
2965
|
sessionToken: session.sessionToken
|
|
@@ -4336,7 +4336,7 @@ if (process.argv.includes(UPDATE_WORKER_FLAG)) {
|
|
|
4336
4336
|
process.exit(0);
|
|
4337
4337
|
}
|
|
4338
4338
|
if (process.argv.includes(PORTAL_DAEMON_FLAG)) {
|
|
4339
|
-
const { runPortalDaemon } = await import("./daemon-
|
|
4339
|
+
const { runPortalDaemon } = await import("./daemon-C5e4hKwL.mjs");
|
|
4340
4340
|
runPortalDaemon(process.argv);
|
|
4341
4341
|
} else runCli();
|
|
4342
4342
|
function runCli() {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as takenAliasNames, C as themesForMode, D as dedupeAliasName, E as aliasActivationHint, F as getActiveWorkspaceId, L as listWorkspaces, N as detectShell, O as installAgentAlias, T as machineOsFromPlatform, _ as theme, a as installCrashHandler, b as themeModeFromColorFgBg, c as MARK_CELLS, d as splashFitsWidth, f as DEFAULT_THEME_ID, g as noColorRequested, h as monoTheme, i as writeArtifact, k as slugifyAliasName, l as WORDMARK, m as findTheme, n as profilingEnabled, o as buildCrashReport, p as applyTheme, r as record, s as writeCrashReport, v as themeForMode, x as themeVersion, y as themeMode, z as setActiveWorkspace } from "./profiler-
|
|
3
|
-
import {
|
|
2
|
+
import { A as takenAliasNames, C as themesForMode, D as dedupeAliasName, E as aliasActivationHint, F as getActiveWorkspaceId, L as listWorkspaces, N as detectShell, O as installAgentAlias, T as machineOsFromPlatform, _ as theme, a as installCrashHandler, b as themeModeFromColorFgBg, c as MARK_CELLS, d as splashFitsWidth, f as DEFAULT_THEME_ID, g as noColorRequested, h as monoTheme, i as writeArtifact, k as slugifyAliasName, l as WORDMARK, m as findTheme, n as profilingEnabled, o as buildCrashReport, p as applyTheme, r as record, s as writeCrashReport, v as themeForMode, x as themeVersion, y as themeMode, z as setActiveWorkspace } from "./profiler-rC7AZSxL.mjs";
|
|
3
|
+
import { B as resolveWebUrl, C as getConfigPath, O as getReviewStateDir, P as recordDefaultAgent, U as saveTheme, b as DEFAULT_APP_URL, c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, h as specKeyFor, i as resolveAgent, k as getSavedTheme, l as parseExternalOauthConnectParams, m as parseConnectCard, p as computeSettledLabel, s as MASK_CHAR, u as parseOauthConnectParams, y as DEFAULT_API_URL } from "./print-DYiQC5IW.mjs";
|
|
4
4
|
import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
|
|
5
|
-
import { a as isRecord, i as errorMessage, n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-
|
|
5
|
+
import { a as isRecord, i as errorMessage, n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-xXfqT4SX.mjs";
|
|
6
6
|
import { i as billingBlockedOutcomeFromSendResponse } from "./billing-blocked-2wju4gC_.mjs";
|
|
7
7
|
import { t as PortalClient } from "./client-DbqRBquD.mjs";
|
|
8
|
-
import "./daemon-
|
|
8
|
+
import "./daemon-B5SNuIhD.mjs";
|
|
9
9
|
import "./api-DG5W6iwx.mjs";
|
|
10
10
|
import { t as SandboxStream } from "./client-c4c5MmgN.mjs";
|
|
11
|
-
import { t as PortalDaemonClient } from "./daemon-client-
|
|
11
|
+
import { t as PortalDaemonClient } from "./daemon-client-CneyOup7.mjs";
|
|
12
12
|
import { t as runRawPtyPassthrough } from "./raw-pty-DY4KelZW.mjs";
|
|
13
13
|
import * as os$1 from "node:os";
|
|
14
14
|
import { homedir, platform, release, tmpdir } from "node:os";
|
|
@@ -6369,6 +6369,22 @@ function applyChunk(items, chunk, agentName = null) {
|
|
|
6369
6369
|
if (!card) return [...items];
|
|
6370
6370
|
const toolCallId = typeof data.toolCallId === "string" ? data.toolCallId : null;
|
|
6371
6371
|
const specKey = specKeyFor(data.spec);
|
|
6372
|
+
if (card.computeRequestId !== null) {
|
|
6373
|
+
const existingIndex = items.findIndex((m) => m.kind === "card" && m.card.computeRequestId === card.computeRequestId);
|
|
6374
|
+
const existing = items[existingIndex];
|
|
6375
|
+
if (existing?.kind === "card") {
|
|
6376
|
+
if (existing.specKey === specKey || card.settled === null) return [...items];
|
|
6377
|
+
const next = [...items];
|
|
6378
|
+
next[existingIndex] = {
|
|
6379
|
+
...existing,
|
|
6380
|
+
specKey,
|
|
6381
|
+
card,
|
|
6382
|
+
status: "done",
|
|
6383
|
+
detail: card.settled
|
|
6384
|
+
};
|
|
6385
|
+
return next;
|
|
6386
|
+
}
|
|
6387
|
+
}
|
|
6372
6388
|
if (items.some((m) => m.kind === "card" && m.toolCallId === toolCallId && m.specKey === specKey)) return [...items];
|
|
6373
6389
|
return [...items, {
|
|
6374
6390
|
kind: "card",
|
|
@@ -6376,8 +6392,8 @@ function applyChunk(items, chunk, agentName = null) {
|
|
|
6376
6392
|
toolCallId,
|
|
6377
6393
|
specKey,
|
|
6378
6394
|
card,
|
|
6379
|
-
status: "idle",
|
|
6380
|
-
detail:
|
|
6395
|
+
status: card.settled === null ? "idle" : "done",
|
|
6396
|
+
detail: card.settled,
|
|
6381
6397
|
agentName
|
|
6382
6398
|
}];
|
|
6383
6399
|
}
|
|
@@ -6639,6 +6655,20 @@ function uiMessagesToItems(messages) {
|
|
|
6639
6655
|
if (card && data) {
|
|
6640
6656
|
const toolCallId = typeof data.toolCallId === "string" ? data.toolCallId : null;
|
|
6641
6657
|
const specKey = specKeyFor(data.spec);
|
|
6658
|
+
if (card.computeRequestId !== null) {
|
|
6659
|
+
const existingIndex = out.findIndex((item) => item.kind === "card" && item.card.computeRequestId === card.computeRequestId);
|
|
6660
|
+
const existing = out[existingIndex];
|
|
6661
|
+
if (existing?.kind === "card") {
|
|
6662
|
+
if (existing.specKey !== specKey && card.settled !== null) out[existingIndex] = {
|
|
6663
|
+
...existing,
|
|
6664
|
+
specKey,
|
|
6665
|
+
card,
|
|
6666
|
+
status: "done",
|
|
6667
|
+
detail: card.settled
|
|
6668
|
+
};
|
|
6669
|
+
continue;
|
|
6670
|
+
}
|
|
6671
|
+
}
|
|
6642
6672
|
if (out.some((item) => item.kind === "card" && item.toolCallId === toolCallId && item.specKey === specKey)) continue;
|
|
6643
6673
|
const ordinal = out.filter((item) => item.kind === "card" && item.toolCallId === toolCallId).length;
|
|
6644
6674
|
out.push({
|
|
@@ -6647,8 +6677,8 @@ function uiMessagesToItems(messages) {
|
|
|
6647
6677
|
toolCallId,
|
|
6648
6678
|
specKey,
|
|
6649
6679
|
card,
|
|
6650
|
-
status: "idle",
|
|
6651
|
-
detail:
|
|
6680
|
+
status: card.settled === null ? "idle" : "done",
|
|
6681
|
+
detail: card.settled,
|
|
6652
6682
|
agentName
|
|
6653
6683
|
});
|
|
6654
6684
|
}
|
|
@@ -11413,9 +11443,11 @@ function ChatScreen({ agent, conversation }) {
|
|
|
11413
11443
|
const forkingRef = useRef(false);
|
|
11414
11444
|
const [credPrompt, setCredPrompt] = useState(null);
|
|
11415
11445
|
const credPromptOpen = credPrompt !== null;
|
|
11446
|
+
const [computePrompt, setComputePrompt] = useState(null);
|
|
11447
|
+
const computePromptOpen = computePrompt !== null;
|
|
11416
11448
|
const [menuDismissed, setMenuDismissed] = useState(false);
|
|
11417
11449
|
const [menuHighlight, setMenuHighlight] = useState(0);
|
|
11418
|
-
const commandQuery = grantPrompt || credPrompt ? null : detectCommandTrigger(input);
|
|
11450
|
+
const commandQuery = grantPrompt || credPrompt || computePrompt ? null : detectCommandTrigger(input);
|
|
11419
11451
|
const menuCommands = commandQuery === null ? [] : filterSlashCommands(commandQuery);
|
|
11420
11452
|
const menuOpen = commandQuery !== null && !menuDismissed;
|
|
11421
11453
|
const menuHighlightClamped = Math.min(menuHighlight, Math.max(0, menuCommands.length - 1));
|
|
@@ -11433,7 +11465,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
11433
11465
|
credPromptRef.current = credPrompt;
|
|
11434
11466
|
const seenRunsRef = useRef(/* @__PURE__ */ new Set());
|
|
11435
11467
|
const shellSessionRef = useRef(null);
|
|
11436
|
-
const hostState = grantPrompt || credPrompt ? "blocked" : run.kind === "idle" ? "idle" : "working";
|
|
11468
|
+
const hostState = grantPrompt || credPrompt || computePrompt ? "blocked" : run.kind === "idle" ? "idle" : "working";
|
|
11437
11469
|
useEffect(() => {
|
|
11438
11470
|
agentHost.transition({ state: hostState });
|
|
11439
11471
|
}, [agentHost, hostState]);
|
|
@@ -11489,11 +11521,12 @@ function ChatScreen({ agent, conversation }) {
|
|
|
11489
11521
|
const reviewBelowBoxRef = useRef(null);
|
|
11490
11522
|
const paneDragRef = useRef(null);
|
|
11491
11523
|
const composerBoxHeight = composerRows + 2;
|
|
11492
|
-
const pendingVisible = !grantPrompt && !credPrompt && !nonWebChannel && attachments.length > 0;
|
|
11524
|
+
const pendingVisible = !grantPrompt && !credPrompt && !computePrompt && !nonWebChannel && attachments.length > 0;
|
|
11493
11525
|
const pendingRows = pendingVisible ? attachments.length + 2 : 0;
|
|
11494
11526
|
const credPromptHeight = 4 + (credPrompt && (credPrompt.error || credPrompt.submitting) ? 1 : 0);
|
|
11495
11527
|
const nonWebNoticeHeight = 5;
|
|
11496
|
-
const
|
|
11528
|
+
const computePromptHeight = 4;
|
|
11529
|
+
const bottomBoxHeight = credPrompt ? credPromptHeight : computePrompt ? computePromptHeight : nonWebChannel ? nonWebNoticeHeight : composerBoxHeight;
|
|
11497
11530
|
const bodyWidth = Math.max(1, width - 2);
|
|
11498
11531
|
const bodyHeight = Math.max(1, height - 3);
|
|
11499
11532
|
const reviewPlacement = filePanePlacement({
|
|
@@ -11547,7 +11580,8 @@ function ChatScreen({ agent, conversation }) {
|
|
|
11547
11580
|
reviewOpen,
|
|
11548
11581
|
reviewFocused,
|
|
11549
11582
|
reviewPlacement,
|
|
11550
|
-
credPromptOpen
|
|
11583
|
+
credPromptOpen,
|
|
11584
|
+
computePromptOpen
|
|
11551
11585
|
]);
|
|
11552
11586
|
useEffect(() => {
|
|
11553
11587
|
if (!rest || !initialConversationId) return;
|
|
@@ -11894,6 +11928,14 @@ function ChatScreen({ agent, conversation }) {
|
|
|
11894
11928
|
});
|
|
11895
11929
|
return;
|
|
11896
11930
|
}
|
|
11931
|
+
if (action.kind === "decide_compute") {
|
|
11932
|
+
setComputePrompt({
|
|
11933
|
+
cardId: item.id,
|
|
11934
|
+
requestId: action.requestId,
|
|
11935
|
+
summary: item.card.subtitle
|
|
11936
|
+
});
|
|
11937
|
+
return;
|
|
11938
|
+
}
|
|
11897
11939
|
updateCard(item.id, {
|
|
11898
11940
|
status: "busy",
|
|
11899
11941
|
detail: null
|
|
@@ -11972,6 +12014,42 @@ function ChatScreen({ agent, conversation }) {
|
|
|
11972
12014
|
portal.status,
|
|
11973
12015
|
setPortalPrompt
|
|
11974
12016
|
]);
|
|
12017
|
+
const decideCompute = useCallback((prompt, decision) => {
|
|
12018
|
+
setComputePrompt(null);
|
|
12019
|
+
if (!rest) return;
|
|
12020
|
+
updateCard(prompt.cardId, {
|
|
12021
|
+
status: "busy",
|
|
12022
|
+
detail: null
|
|
12023
|
+
});
|
|
12024
|
+
const settleIfStillDeciding = (patch) => {
|
|
12025
|
+
setItems((prev) => prev.map((m) => m.kind === "card" && m.id === prompt.cardId && m.status === "busy" && m.card.settled === null ? {
|
|
12026
|
+
...m,
|
|
12027
|
+
...patch
|
|
12028
|
+
} : m));
|
|
12029
|
+
};
|
|
12030
|
+
(async () => {
|
|
12031
|
+
try {
|
|
12032
|
+
const { status } = await rest.decideComputeRequest({
|
|
12033
|
+
requestId: prompt.requestId,
|
|
12034
|
+
decision
|
|
12035
|
+
});
|
|
12036
|
+
settleIfStillDeciding({
|
|
12037
|
+
status: "done",
|
|
12038
|
+
detail: computeSettledLabel(status) ?? status
|
|
12039
|
+
});
|
|
12040
|
+
} catch (err) {
|
|
12041
|
+
settleIfStillDeciding({
|
|
12042
|
+
status: "error",
|
|
12043
|
+
detail: cardActionErrorMessage(err)
|
|
12044
|
+
});
|
|
12045
|
+
}
|
|
12046
|
+
})();
|
|
12047
|
+
}, [rest, updateCard]);
|
|
12048
|
+
useEffect(() => {
|
|
12049
|
+
if (!computePrompt) return;
|
|
12050
|
+
const target = items.find((m) => m.kind === "card" && m.id === computePrompt.cardId);
|
|
12051
|
+
if (!target || target.card.settled !== null || target.status === "done") setComputePrompt(null);
|
|
12052
|
+
}, [computePrompt, items]);
|
|
11975
12053
|
const promptForCards = useCallback(() => {
|
|
11976
12054
|
const targets = itemsRef.current.filter(isActionableCard);
|
|
11977
12055
|
const [only] = targets;
|
|
@@ -12238,7 +12316,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
12238
12316
|
stageAttachment(image);
|
|
12239
12317
|
}, [stageAttachment]);
|
|
12240
12318
|
usePaste((event) => {
|
|
12241
|
-
if (cardPickerOpen || modelPickerOpen || themePickerOpen || helpOpen || credPromptOpen || grantPrompt || nonWebChannel) return;
|
|
12319
|
+
if (cardPickerOpen || modelPickerOpen || themePickerOpen || helpOpen || credPromptOpen || grantPrompt || computePromptOpen || nonWebChannel) return;
|
|
12242
12320
|
const text = event.metadata?.kind === "binary" ? "" : decodePasteBytes(event.bytes);
|
|
12243
12321
|
const route = routePaste({
|
|
12244
12322
|
kind: event.metadata?.kind,
|
|
@@ -12292,6 +12370,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
12292
12370
|
themePickerOpen,
|
|
12293
12371
|
helpOpen,
|
|
12294
12372
|
credPromptOpen,
|
|
12373
|
+
computePromptOpen,
|
|
12295
12374
|
applyComposerText
|
|
12296
12375
|
]);
|
|
12297
12376
|
const cancelRun = useCallback(async () => {
|
|
@@ -12374,7 +12453,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
12374
12453
|
goTo
|
|
12375
12454
|
]);
|
|
12376
12455
|
const archiveConversation = useCallback(() => {
|
|
12377
|
-
if (grantPrompt || credPrompt) return;
|
|
12456
|
+
if (grantPrompt || credPrompt || computePrompt) return;
|
|
12378
12457
|
if (agentRunConversation) {
|
|
12379
12458
|
useStore.getState().showToast({ message: "agent runs can't be archived — ctrl+d in the picker deletes" });
|
|
12380
12459
|
return;
|
|
@@ -12407,6 +12486,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
12407
12486
|
}, [
|
|
12408
12487
|
grantPrompt,
|
|
12409
12488
|
credPrompt,
|
|
12489
|
+
computePrompt,
|
|
12410
12490
|
agentRunConversation,
|
|
12411
12491
|
conversationId,
|
|
12412
12492
|
rest,
|
|
@@ -12846,6 +12926,20 @@ function ChatScreen({ agent, conversation }) {
|
|
|
12846
12926
|
return;
|
|
12847
12927
|
}
|
|
12848
12928
|
}
|
|
12929
|
+
if (computePrompt) {
|
|
12930
|
+
if (key.name === "y") {
|
|
12931
|
+
decideCompute(computePrompt, "approve");
|
|
12932
|
+
return;
|
|
12933
|
+
}
|
|
12934
|
+
if (key.name === "n") {
|
|
12935
|
+
decideCompute(computePrompt, "deny");
|
|
12936
|
+
return;
|
|
12937
|
+
}
|
|
12938
|
+
if (key.name === "escape") {
|
|
12939
|
+
setComputePrompt(null);
|
|
12940
|
+
return;
|
|
12941
|
+
}
|
|
12942
|
+
}
|
|
12849
12943
|
if (credPrompt) {
|
|
12850
12944
|
if (key.name === "escape") {
|
|
12851
12945
|
if (!credPrompt.submitting) setCredPrompt(null);
|
|
@@ -13335,6 +13429,46 @@ function ChatScreen({ agent, conversation }) {
|
|
|
13335
13429
|
children: "n"
|
|
13336
13430
|
})
|
|
13337
13431
|
]
|
|
13432
|
+
}) : computePrompt ? /* @__PURE__ */ jsxs("box", {
|
|
13433
|
+
style: {
|
|
13434
|
+
border: true,
|
|
13435
|
+
borderColor: theme.warning,
|
|
13436
|
+
paddingLeft: 1,
|
|
13437
|
+
paddingRight: 1,
|
|
13438
|
+
height: computePromptHeight,
|
|
13439
|
+
flexShrink: 0,
|
|
13440
|
+
marginTop: 1,
|
|
13441
|
+
flexDirection: "column"
|
|
13442
|
+
},
|
|
13443
|
+
children: [/* @__PURE__ */ jsx("text", {
|
|
13444
|
+
fg: theme.warning,
|
|
13445
|
+
children: computePrompt.summary ?? "more compute requested"
|
|
13446
|
+
}), /* @__PURE__ */ jsxs("text", { children: [
|
|
13447
|
+
/* @__PURE__ */ jsx("span", {
|
|
13448
|
+
fg: theme.success,
|
|
13449
|
+
children: "y"
|
|
13450
|
+
}),
|
|
13451
|
+
/* @__PURE__ */ jsx("span", {
|
|
13452
|
+
fg: theme.muted,
|
|
13453
|
+
children: " upgrade / "
|
|
13454
|
+
}),
|
|
13455
|
+
/* @__PURE__ */ jsx("span", {
|
|
13456
|
+
fg: theme.error,
|
|
13457
|
+
children: "n"
|
|
13458
|
+
}),
|
|
13459
|
+
/* @__PURE__ */ jsx("span", {
|
|
13460
|
+
fg: theme.muted,
|
|
13461
|
+
children: " keep current / "
|
|
13462
|
+
}),
|
|
13463
|
+
/* @__PURE__ */ jsx("span", {
|
|
13464
|
+
fg: theme.dim,
|
|
13465
|
+
children: "esc"
|
|
13466
|
+
}),
|
|
13467
|
+
/* @__PURE__ */ jsx("span", {
|
|
13468
|
+
fg: theme.muted,
|
|
13469
|
+
children: " later"
|
|
13470
|
+
})
|
|
13471
|
+
] })]
|
|
13338
13472
|
}) : nonWebChannel ? /* @__PURE__ */ jsxs("box", {
|
|
13339
13473
|
style: {
|
|
13340
13474
|
border: true,
|
|
@@ -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 "1786741092";
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* Whether a client carrying `mine` should replace a running daemon carrying
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import "./client-DbqRBquD.mjs";
|
|
3
|
-
import { a as runPortalDaemon, i as queryDaemonStatus, n as ensureDaemonRunning, o as startPortalDaemon, r as isDaemonListening, s as stopDaemon, t as PortalDaemon } from "./daemon-
|
|
3
|
+
import { a as runPortalDaemon, i as queryDaemonStatus, n as ensureDaemonRunning, o as startPortalDaemon, r as isDaemonListening, s as stopDaemon, t as PortalDaemon } from "./daemon-B5SNuIhD.mjs";
|
|
4
4
|
import "./api-DG5W6iwx.mjs";
|
|
5
5
|
|
|
6
6
|
export { runPortalDaemon };
|
|
@@ -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-B5SNuIhD.mjs";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { connect } from "node:net";
|
|
5
5
|
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { t as fetchForwardTarget } from "./api-DG5W6iwx.mjs";
|
|
3
|
+
import net from "node:net";
|
|
4
|
+
import { WebSocket, createWebSocketStream } from "ws";
|
|
5
|
+
|
|
6
|
+
//#region src/chat/portal/tunnel-pool.ts
|
|
7
|
+
const MAX_AGE_MS = 4e4;
|
|
8
|
+
const SWEEP_INTERVAL_MS = 1e4;
|
|
9
|
+
const REFILL_BACKOFF_MS = 15e3;
|
|
10
|
+
/**
|
|
11
|
+
* Pre-warmed tunnel pool for the forward portal's accept path.
|
|
12
|
+
*
|
|
13
|
+
* Dialing a tunnel costs a full edge round-trip (~300ms measured). The pool
|
|
14
|
+
* pays that in the background so accept can adopt a warm tunnel and move the
|
|
15
|
+
* first byte immediately. Target starts at `initialSize` and grows by one on
|
|
16
|
+
* each miss, never above `maxSize` — a miss still cold-dials (today's path);
|
|
17
|
+
* growth only helps the next wave. No decay: a runaway client can hold
|
|
18
|
+
* `maxSize` idle edge sockets until `forward` exits.
|
|
19
|
+
*
|
|
20
|
+
* `dial` must resolve with a stream that is already paused, so any bytes a
|
|
21
|
+
* server-first protocol sends before adoption buffer instead of dropping.
|
|
22
|
+
*/
|
|
23
|
+
function createTunnelPool({ initialSize, maxSize, dial, log }) {
|
|
24
|
+
let entries = [];
|
|
25
|
+
let dialsInFlight = 0;
|
|
26
|
+
let backoffUntil = 0;
|
|
27
|
+
let closed = false;
|
|
28
|
+
let targetSize = initialSize;
|
|
29
|
+
function refill() {
|
|
30
|
+
if (closed) return;
|
|
31
|
+
if (Date.now() < backoffUntil) return;
|
|
32
|
+
while (entries.length + dialsInFlight < targetSize) {
|
|
33
|
+
dialsInFlight += 1;
|
|
34
|
+
dial().then((tunnel) => {
|
|
35
|
+
dialsInFlight -= 1;
|
|
36
|
+
if (closed) {
|
|
37
|
+
tunnel.close();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const entry = {
|
|
41
|
+
tunnel,
|
|
42
|
+
dialedAt: Date.now(),
|
|
43
|
+
dead: false
|
|
44
|
+
};
|
|
45
|
+
tunnel.stream.once("close", () => {
|
|
46
|
+
entry.dead = true;
|
|
47
|
+
});
|
|
48
|
+
tunnel.stream.once("error", () => {
|
|
49
|
+
entry.dead = true;
|
|
50
|
+
});
|
|
51
|
+
entries.push(entry);
|
|
52
|
+
}).catch((err) => {
|
|
53
|
+
dialsInFlight -= 1;
|
|
54
|
+
backoffUntil = Date.now() + REFILL_BACKOFF_MS;
|
|
55
|
+
log(`forward: pool dial failed (backing off): ${err instanceof Error ? err.message : String(err)}`);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function sweep() {
|
|
60
|
+
const now = Date.now();
|
|
61
|
+
const keep = [];
|
|
62
|
+
for (const entry of entries) if (entry.dead || now - entry.dialedAt > MAX_AGE_MS) entry.tunnel.close();
|
|
63
|
+
else keep.push(entry);
|
|
64
|
+
entries = keep;
|
|
65
|
+
refill();
|
|
66
|
+
}
|
|
67
|
+
const sweeper = setInterval(sweep, SWEEP_INTERVAL_MS);
|
|
68
|
+
sweeper.unref();
|
|
69
|
+
refill();
|
|
70
|
+
return {
|
|
71
|
+
take: () => {
|
|
72
|
+
while (entries.length > 0) {
|
|
73
|
+
const entry = entries.pop();
|
|
74
|
+
if (!entry) break;
|
|
75
|
+
if (entry.dead || entry.tunnel.stream.destroyed) continue;
|
|
76
|
+
refill();
|
|
77
|
+
return entry.tunnel;
|
|
78
|
+
}
|
|
79
|
+
if (targetSize < maxSize) targetSize += 1;
|
|
80
|
+
refill();
|
|
81
|
+
return null;
|
|
82
|
+
},
|
|
83
|
+
close: () => {
|
|
84
|
+
closed = true;
|
|
85
|
+
clearInterval(sweeper);
|
|
86
|
+
for (const entry of entries) entry.tunnel.close();
|
|
87
|
+
entries = [];
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/chat/portal/forward.ts
|
|
94
|
+
const TARGET_REFRESH_SAFETY_MS = 12e4;
|
|
95
|
+
const INITIAL_POOL_SIZE = 8;
|
|
96
|
+
const MAX_POOL_SIZE = 16;
|
|
97
|
+
/**
|
|
98
|
+
* The reverse portal's client half: listen on the local machine's loopback and
|
|
99
|
+
* pipe each TCP connection to the agent sandbox's daemon (`/portal/tcp`),
|
|
100
|
+
* which pipes to the sandbox's own loopback. The daemon is reached through the
|
|
101
|
+
* agent-webserver edge Worker (sandboxes have public ingress disabled; the
|
|
102
|
+
* Worker owns boot-resolution and injects the sandbox edge-auth token), so a
|
|
103
|
+
* sandbox recycle just costs the next connection a cold-start wait rather
|
|
104
|
+
* than invalidating the forward.
|
|
105
|
+
*/
|
|
106
|
+
async function startForward({ auth, agentId, localPort, targetPort, log }) {
|
|
107
|
+
let target = await fetchForwardTarget(auth, agentId);
|
|
108
|
+
let mintedAt = Date.now();
|
|
109
|
+
async function freshTarget() {
|
|
110
|
+
const ttlMs = target.expiresInSeconds * 1e3;
|
|
111
|
+
if (Date.now() - mintedAt > ttlMs - TARGET_REFRESH_SAFETY_MS) {
|
|
112
|
+
target = await fetchForwardTarget(auth, agentId);
|
|
113
|
+
mintedAt = Date.now();
|
|
114
|
+
}
|
|
115
|
+
return target;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Dial one tunnel through the edge to the daemon's /portal/tcp. Resolves
|
|
119
|
+
* once the upgrade completes, with the duplex PAUSED — the pool holds it
|
|
120
|
+
* warm without dropping any early bytes, and adoption resumes it by piping.
|
|
121
|
+
*/
|
|
122
|
+
async function dialTunnel() {
|
|
123
|
+
const resolved = await freshTarget();
|
|
124
|
+
const ws = new WebSocket(`${resolved.daemonOrigin.replace(/^http/, "ws")}/portal/tcp?port=${targetPort}`, { headers: { authorization: `Bearer ${resolved.token}` } });
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
ws.once("error", reject);
|
|
127
|
+
ws.once("open", () => {
|
|
128
|
+
ws.removeListener("error", reject);
|
|
129
|
+
const stream = createWebSocketStream(ws);
|
|
130
|
+
stream.pause();
|
|
131
|
+
resolve({
|
|
132
|
+
stream,
|
|
133
|
+
close: () => stream.destroy()
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
const pool = createTunnelPool({
|
|
139
|
+
initialSize: INITIAL_POOL_SIZE,
|
|
140
|
+
maxSize: MAX_POOL_SIZE,
|
|
141
|
+
dial: dialTunnel,
|
|
142
|
+
log
|
|
143
|
+
});
|
|
144
|
+
const server = net.createServer((sock) => {
|
|
145
|
+
sock.pause();
|
|
146
|
+
const warm = pool.take();
|
|
147
|
+
if (warm) {
|
|
148
|
+
warm.stream.on("error", () => sock.destroy());
|
|
149
|
+
sock.on("error", () => warm.stream.destroy());
|
|
150
|
+
sock.pipe(warm.stream).pipe(sock);
|
|
151
|
+
sock.resume();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
(async () => {
|
|
155
|
+
let tunnel;
|
|
156
|
+
try {
|
|
157
|
+
tunnel = await dialTunnel();
|
|
158
|
+
} catch (err) {
|
|
159
|
+
log(`forward: tunnel connect failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
160
|
+
sock.destroy();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
tunnel.stream.on("error", () => sock.destroy());
|
|
164
|
+
sock.on("error", () => tunnel.stream.destroy());
|
|
165
|
+
sock.pipe(tunnel.stream).pipe(sock);
|
|
166
|
+
sock.resume();
|
|
167
|
+
})();
|
|
168
|
+
});
|
|
169
|
+
await new Promise((resolve, reject) => {
|
|
170
|
+
server.once("error", reject);
|
|
171
|
+
server.listen(localPort, "127.0.0.1", () => {
|
|
172
|
+
server.removeListener("error", reject);
|
|
173
|
+
resolve();
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
const addr = server.address();
|
|
177
|
+
return {
|
|
178
|
+
port: addr && typeof addr === "object" ? addr.port : localPort,
|
|
179
|
+
close: () => new Promise((resolve) => {
|
|
180
|
+
pool.close();
|
|
181
|
+
server.close(() => resolve());
|
|
182
|
+
})
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//#endregion
|
|
187
|
+
export { startForward };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-
|
|
3
|
-
import "./rest-
|
|
2
|
+
import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-DYiQC5IW.mjs";
|
|
3
|
+
import "./rest-xXfqT4SX.mjs";
|
|
4
4
|
import "./billing-blocked-2wju4gC_.mjs";
|
|
5
5
|
|
|
6
6
|
export { messageGet, readStdin, resolveAgent, runPrint };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
|
|
3
|
-
import { a as isRecord, i as errorMessage, r as sendErrorMessage, t as createRestClient } from "./rest-
|
|
3
|
+
import { a as isRecord, i as errorMessage, r as sendErrorMessage, t as createRestClient } from "./rest-xXfqT4SX.mjs";
|
|
4
4
|
import { i as billingBlockedOutcomeFromSendResponse, n as BillingBlockedError } from "./billing-blocked-2wju4gC_.mjs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import Conf from "conf";
|
|
@@ -365,6 +365,50 @@ function parseButton(element) {
|
|
|
365
365
|
function specKeyFor(spec) {
|
|
366
366
|
return stableStringify(spec) ?? crypto.randomUUID();
|
|
367
367
|
}
|
|
368
|
+
const COMPUTE_TIER_NAMES = {
|
|
369
|
+
small: "Lite",
|
|
370
|
+
large: "Standard",
|
|
371
|
+
xlarge: "Pro",
|
|
372
|
+
xxlarge: "Max",
|
|
373
|
+
xxxlarge: "Ultra"
|
|
374
|
+
};
|
|
375
|
+
function computeTierLabel(tier, memoryGb) {
|
|
376
|
+
const name = typeof tier === "string" ? COMPUTE_TIER_NAMES[tier] ?? null : null;
|
|
377
|
+
const memory = Number(memoryGb);
|
|
378
|
+
const memoryLabel = Number.isFinite(memory) && memory > 0 ? `${Math.round(memory)} GB` : null;
|
|
379
|
+
if (name && memoryLabel) return `${name} (${memoryLabel})`;
|
|
380
|
+
if (name) return name;
|
|
381
|
+
if (memoryLabel) return memoryLabel;
|
|
382
|
+
return typeof tier === "string" && tier ? tier : "unknown tier";
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* The card line for a settled decision, shared by the spec parser (a settled
|
|
386
|
+
* re-emission) and the chat screen (the decision POST's response, which on
|
|
387
|
+
* `alreadyDecided` reports whatever decision actually stuck). Wording follows
|
|
388
|
+
* the web card's chips (Upgraded / Kept current / Withdrawn).
|
|
389
|
+
*/
|
|
390
|
+
/**
|
|
391
|
+
* The full status domain the api emits on a compute request. Anything else —
|
|
392
|
+
* missing, or a value this CLI predates — returns null so the caller can
|
|
393
|
+
* refuse it instead of guessing at unknown semantics.
|
|
394
|
+
*/
|
|
395
|
+
function parseComputeRequestStatus(value) {
|
|
396
|
+
switch (value) {
|
|
397
|
+
case "pending":
|
|
398
|
+
case "approved":
|
|
399
|
+
case "denied":
|
|
400
|
+
case "cancelled": return value;
|
|
401
|
+
default: return null;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function computeSettledLabel(status) {
|
|
405
|
+
switch (status) {
|
|
406
|
+
case "approved": return "upgraded";
|
|
407
|
+
case "denied": return "kept the current tier";
|
|
408
|
+
case "cancelled": return "withdrawn";
|
|
409
|
+
default: return null;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
368
412
|
/**
|
|
369
413
|
* The `platform portal request` consent card. The server emits one
|
|
370
414
|
* DesktopHandoffCard spec for every surface (api routes/portal.ts): the web
|
|
@@ -394,7 +438,47 @@ function parseDesktopHandoffCard(rootEl) {
|
|
|
394
438
|
conversationId: optionalString(props.conversationId)
|
|
395
439
|
}
|
|
396
440
|
},
|
|
397
|
-
state: {}
|
|
441
|
+
state: {},
|
|
442
|
+
computeRequestId: null,
|
|
443
|
+
settled: null
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* The `platform compute request` approval card. The web renders it as the
|
|
448
|
+
* Approve/Deny "More compute requested" card; here it maps onto a
|
|
449
|
+
* decide_compute button (ctrl+r opens the y/n decision prompt). Without this
|
|
450
|
+
* the spec is not a Card, parses to null, and the request is silently
|
|
451
|
+
* dropped — the user never sees it and the agent waits on a decision the
|
|
452
|
+
* terminal can't give (ANY-6653). A settled spec (the decision route re-emits
|
|
453
|
+
* the card with its final status) parses to a button-less resolved card. A
|
|
454
|
+
* status this CLI doesn't know also parses to null — rendering it as pending
|
|
455
|
+
* would offer a decision on a request in a state we can't represent.
|
|
456
|
+
*/
|
|
457
|
+
function parseComputeRequestCard(rootEl) {
|
|
458
|
+
const props = isRecord(rootEl.props) ? rootEl.props : {};
|
|
459
|
+
const requestId = optionalString(props.requestId);
|
|
460
|
+
if (!requestId) return null;
|
|
461
|
+
const status = parseComputeRequestStatus(props.status);
|
|
462
|
+
if (status === null) return null;
|
|
463
|
+
const agentName = optionalString(props.agentName) ?? "This agent";
|
|
464
|
+
const from = computeTierLabel(props.fromTier, props.fromMemoryGb);
|
|
465
|
+
const to = computeTierLabel(props.requestedTier, props.requestedMemoryGb);
|
|
466
|
+
const settled = computeSettledLabel(status);
|
|
467
|
+
return {
|
|
468
|
+
title: "More compute requested",
|
|
469
|
+
subtitle: `${agentName}: ${from} → ${to}`,
|
|
470
|
+
description: optionalString(props.reason),
|
|
471
|
+
fields: [],
|
|
472
|
+
button: settled ? null : {
|
|
473
|
+
label: "Decide",
|
|
474
|
+
action: {
|
|
475
|
+
kind: "decide_compute",
|
|
476
|
+
requestId
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
state: {},
|
|
480
|
+
computeRequestId: requestId,
|
|
481
|
+
settled
|
|
398
482
|
};
|
|
399
483
|
}
|
|
400
484
|
function parseConnectCard(spec) {
|
|
@@ -404,6 +488,7 @@ function parseConnectCard(spec) {
|
|
|
404
488
|
const rootEl = elements[root];
|
|
405
489
|
if (!isRecord(rootEl)) return null;
|
|
406
490
|
if (rootEl.type === "DesktopHandoffCard") return parseDesktopHandoffCard(rootEl);
|
|
491
|
+
if (rootEl.type === "ComputeRequestCard") return parseComputeRequestCard(rootEl);
|
|
407
492
|
if (rootEl.type !== "Card") return null;
|
|
408
493
|
const rootProps = isRecord(rootEl.props) ? rootEl.props : {};
|
|
409
494
|
const title = optionalString(rootProps.title);
|
|
@@ -442,7 +527,9 @@ function parseConnectCard(spec) {
|
|
|
442
527
|
label: preferred.label,
|
|
443
528
|
action: preferred.action
|
|
444
529
|
} : null,
|
|
445
|
-
state: isRecord(spec.state) ? spec.state : {}
|
|
530
|
+
state: isRecord(spec.state) ? spec.state : {},
|
|
531
|
+
computeRequestId: null,
|
|
532
|
+
settled: null
|
|
446
533
|
};
|
|
447
534
|
}
|
|
448
535
|
|
|
@@ -583,6 +670,13 @@ function summarizeConnectCard(card, appUrl) {
|
|
|
583
670
|
agentId: act.agentId
|
|
584
671
|
}
|
|
585
672
|
};
|
|
673
|
+
case "decide_compute": return {
|
|
674
|
+
...base,
|
|
675
|
+
action: {
|
|
676
|
+
kind: "decide_compute",
|
|
677
|
+
requestId: act.requestId
|
|
678
|
+
}
|
|
679
|
+
};
|
|
586
680
|
default: return {
|
|
587
681
|
...base,
|
|
588
682
|
action: { kind: "unsupported" }
|
|
@@ -592,14 +686,16 @@ function summarizeConnectCard(card, appUrl) {
|
|
|
592
686
|
/**
|
|
593
687
|
* Parse a `data-anyone-render-spec` stream chunk into a connect-card summary,
|
|
594
688
|
* or null if the chunk isn't a connect card (other render specs — training,
|
|
595
|
-
* deep-learn
|
|
689
|
+
* deep-learn — parse to null, same as the TUI). A card that arrives already
|
|
690
|
+
* settled (a compute request's decided re-emission) is an FYI, not an action,
|
|
691
|
+
* so it stays out of the "[action needed]" stream too.
|
|
596
692
|
*/
|
|
597
693
|
function connectCardFromChunk(chunk, appUrl) {
|
|
598
694
|
if (chunk["type"] !== "data-anyone-render-spec") return null;
|
|
599
695
|
const data = chunk["data"];
|
|
600
696
|
if (!isRecord(data)) return null;
|
|
601
697
|
const card = parseConnectCard(data["spec"]);
|
|
602
|
-
if (!card) return null;
|
|
698
|
+
if (!card || card.settled !== null) return null;
|
|
603
699
|
return summarizeConnectCard(card, appUrl);
|
|
604
700
|
}
|
|
605
701
|
/** Render a connect-card summary as a human-readable action block. */
|
|
@@ -618,6 +714,9 @@ function formatConnectCard(card) {
|
|
|
618
714
|
case "approve_portal":
|
|
619
715
|
lines.push(`Approve local-machine access for agent ${card.action.agentId} in the TUI or web app.`);
|
|
620
716
|
break;
|
|
717
|
+
case "decide_compute":
|
|
718
|
+
lines.push("Approve or deny this compute increase in the interactive TUI or web app.");
|
|
719
|
+
break;
|
|
621
720
|
case "unsupported":
|
|
622
721
|
lines.push("Open this conversation in the web app to continue.");
|
|
623
722
|
break;
|
|
@@ -837,4 +936,4 @@ async function readStdin() {
|
|
|
837
936
|
}
|
|
838
937
|
|
|
839
938
|
//#endregion
|
|
840
|
-
export {
|
|
939
|
+
export { getShareMachineDefault as A, resolveWebUrl as B, getConfigPath as C, getPromptHistoryPath as D, getPreference as E, resolveAppUrl as F, saveSession as H, resolveChatAuth as I, resolveConfig as L, getStoredApiKeyWorkspaceName as M, getUpdateCheckDisabled as N, getReviewStateDir as O, recordDefaultAgent as P, resolveManagementAuth as R, deleteConfig as S, getLastSeenVersion as T, saveTheme as U, saveConfig as V, setLastSeenVersion as W, API_KEY_FAMILY_PREFIX as _, runPrint as a, DEFAULT_APP_URL as b, cardActionErrorMessage as c, reconcileMaskedInput as d, resolveConnectUrl as f, API_KEYS_URL as g, specKeyFor as h, resolveAgent as i, getStoredApiKeyId as j, getSavedTheme as k, parseExternalOauthConnectParams as l, parseConnectCard as m, messageGet as n, toPrintError as o, computeSettledLabel as p, readStdin as r, MASK_CHAR as s, collectRunText as t, parseOauthConnectParams as u, API_KEY_PREFIX as v, getDefaultAgent as w, PREFERENCES as x, DEFAULT_API_URL as y, resolveSession as z };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { C as getConfigPath } from "./print-DYiQC5IW.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.28";
|
|
12
12
|
|
|
13
13
|
//#endregion
|
|
14
14
|
//#region src/auth/organization.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
|
|
3
|
-
import { n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-
|
|
3
|
+
import { n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-xXfqT4SX.mjs";
|
|
4
4
|
import "./billing-blocked-2wju4gC_.mjs";
|
|
5
5
|
|
|
6
6
|
export { createRestClient };
|
|
@@ -363,6 +363,7 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
363
363
|
const { authorizationUrl } = await post("/api/v1/external-oauth/connect", input, externalOauthConnectResponseSchema);
|
|
364
364
|
return { authorizationUrl: authorizationUrl ?? null };
|
|
365
365
|
},
|
|
366
|
+
decideComputeRequest: async ({ requestId, decision }) => post(`/api/v1/compute-requests/${encodeURIComponent(requestId)}/decision`, { decision }, computeDecisionResponseSchema),
|
|
366
367
|
fulfillCredential: async ({ url, body }) => {
|
|
367
368
|
const target = new URL(url, appUrl).toString();
|
|
368
369
|
const res = await fetch(target, {
|
|
@@ -582,6 +583,7 @@ const finalizeResponseSchema = z.object({
|
|
|
582
583
|
});
|
|
583
584
|
const oauthConnectResponseSchema = z.object({ connectLink: z.string() }).passthrough();
|
|
584
585
|
const externalOauthConnectResponseSchema = z.object({ authorizationUrl: z.string().optional() }).passthrough();
|
|
586
|
+
const computeDecisionResponseSchema = z.object({ status: z.string() }).passthrough();
|
|
585
587
|
const runStreamEventSchema = z.union([z.object({
|
|
586
588
|
kind: z.literal("chunk"),
|
|
587
589
|
chunk: z.record(z.unknown())
|
package/package.json
CHANGED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { t as fetchForwardTarget } from "./api-DG5W6iwx.mjs";
|
|
3
|
-
import net from "node:net";
|
|
4
|
-
import { WebSocket, createWebSocketStream } from "ws";
|
|
5
|
-
|
|
6
|
-
//#region src/chat/portal/forward.ts
|
|
7
|
-
const TARGET_REFRESH_SAFETY_MS = 12e4;
|
|
8
|
-
/**
|
|
9
|
-
* The reverse portal's client half: listen on the local machine's loopback and
|
|
10
|
-
* pipe each TCP connection to the agent sandbox's daemon (`/portal/tcp`),
|
|
11
|
-
* which pipes to the sandbox's own loopback. The daemon is reached through the
|
|
12
|
-
* agent-webserver edge Worker (sandboxes have public ingress disabled; the
|
|
13
|
-
* Worker owns boot-resolution and injects the sandbox edge-auth token), so a
|
|
14
|
-
* sandbox recycle just costs the next connection a cold-start wait rather
|
|
15
|
-
* than invalidating the forward.
|
|
16
|
-
*/
|
|
17
|
-
async function startForward({ auth, agentId, localPort, targetPort, log }) {
|
|
18
|
-
let target = await fetchForwardTarget(auth, agentId);
|
|
19
|
-
let mintedAt = Date.now();
|
|
20
|
-
async function freshTarget() {
|
|
21
|
-
const ttlMs = target.expiresInSeconds * 1e3;
|
|
22
|
-
if (Date.now() - mintedAt > ttlMs - TARGET_REFRESH_SAFETY_MS) {
|
|
23
|
-
target = await fetchForwardTarget(auth, agentId);
|
|
24
|
-
mintedAt = Date.now();
|
|
25
|
-
}
|
|
26
|
-
return target;
|
|
27
|
-
}
|
|
28
|
-
const server = net.createServer((sock) => {
|
|
29
|
-
sock.pause();
|
|
30
|
-
(async () => {
|
|
31
|
-
let resolved;
|
|
32
|
-
try {
|
|
33
|
-
resolved = await freshTarget();
|
|
34
|
-
} catch (err) {
|
|
35
|
-
log(`forward: token refresh failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
36
|
-
sock.destroy();
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
const ws = new WebSocket(`${resolved.daemonOrigin.replace(/^http/, "ws")}/portal/tcp?port=${targetPort}`, { headers: { authorization: `Bearer ${resolved.token}` } });
|
|
40
|
-
ws.on("open", () => {
|
|
41
|
-
const stream = createWebSocketStream(ws);
|
|
42
|
-
stream.on("error", () => sock.destroy());
|
|
43
|
-
sock.on("error", () => stream.destroy());
|
|
44
|
-
sock.pipe(stream).pipe(sock);
|
|
45
|
-
sock.resume();
|
|
46
|
-
});
|
|
47
|
-
ws.on("error", (err) => {
|
|
48
|
-
log(`forward: tunnel connect failed: ${err.message}`);
|
|
49
|
-
sock.destroy();
|
|
50
|
-
});
|
|
51
|
-
})();
|
|
52
|
-
});
|
|
53
|
-
await new Promise((resolve, reject) => {
|
|
54
|
-
server.once("error", reject);
|
|
55
|
-
server.listen(localPort, "127.0.0.1", () => {
|
|
56
|
-
server.removeListener("error", reject);
|
|
57
|
-
resolve();
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
const addr = server.address();
|
|
61
|
-
return {
|
|
62
|
-
port: addr && typeof addr === "object" ? addr.port : localPort,
|
|
63
|
-
close: () => new Promise((resolve) => server.close(() => resolve()))
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
//#endregion
|
|
68
|
-
export { startForward };
|