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