skydive-cli 0.4.1-beta.6 → 0.5.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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-BY2nADw5.mjs";
3
+ import { a as isRecord, i as errorMessage, r as sendErrorMessage, t as createRestClient } from "./rest-I3imNduB.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";
@@ -9,7 +9,7 @@ import stableStringify from "safe-stable-stringify";
9
9
 
10
10
  //#region src/config.ts
11
11
  /** Default host for the public management API (`/v1`, API-key auth). */
12
- const DEFAULT_API_URL = "https://api.skydive.com";
12
+ const DEFAULT_API_URL = typeof SKYDIVE_BUILD_API_URL === "string" ? SKYDIVE_BUILD_API_URL : "https://api.skydive.com";
13
13
  /**
14
14
  * Default origin for the interactive chat client (`skydive chat`).
15
15
  *
@@ -20,9 +20,9 @@ const DEFAULT_API_URL = "https://api.skydive.com";
20
20
  * `DEFAULT_API_URL`; override with `--api-url` / `SKYDIVE_APP_URL` for local
21
21
  * dev or while the DNS record is still being provisioned.
22
22
  */
23
- const DEFAULT_APP_URL = "https://api.skydive.com";
23
+ const DEFAULT_APP_URL = DEFAULT_API_URL;
24
24
  /** Web front door, for pages opened in the user's browser. */
25
- const DEFAULT_WEB_URL = "https://skydive.com";
25
+ const DEFAULT_WEB_URL = typeof SKYDIVE_BUILD_WEB_URL === "string" ? SKYDIVE_BUILD_WEB_URL : "https://skydive.com";
26
26
  function resolveWebUrl(appUrl) {
27
27
  if (appUrl == null) return appUrl;
28
28
  return appUrl === DEFAULT_APP_URL ? DEFAULT_WEB_URL : appUrl;
@@ -213,6 +213,32 @@ function saveUpdateCheck(value) {
213
213
  }
214
214
  store.set("updateCheck", value);
215
215
  }
216
+ /**
217
+ * Agent a bare `skydive chat` (no --agent/--resume) opens a new conversation
218
+ * with. Any selector `--agent` accepts works: an id, or a unique
219
+ * case-insensitive slug/name, resolved against the active workspace's roster
220
+ * at launch. Null when unset (the launch falls back to the agent picker).
221
+ * A hand-edited blank value counts as unset rather than as a selector no
222
+ * agent could ever match.
223
+ */
224
+ function getDefaultAgent() {
225
+ const value = store.get("defaultAgent")?.trim();
226
+ return value ? value : null;
227
+ }
228
+ function saveDefaultAgent(value) {
229
+ store.set("defaultAgent", value);
230
+ }
231
+ /**
232
+ * Remember the agent the chat TUI just opened a conversation with, so the
233
+ * next bare `skydive chat` returns to it (last used wins). Records the id —
234
+ * stable across renames, unlike a slug/name selector. Skips the write when
235
+ * the value already matches: this runs on every chat-screen entry, and an
236
+ * unchanged default shouldn't rewrite config.json.
237
+ */
238
+ function recordDefaultAgent(agentId) {
239
+ if (getDefaultAgent() === agentId) return;
240
+ store.set("defaultAgent", agentId);
241
+ }
216
242
  function parseBoolean(raw) {
217
243
  const v = raw.trim().toLowerCase();
218
244
  if ([
@@ -229,25 +255,40 @@ function parseBoolean(raw) {
229
255
  ].includes(v)) return ok(false);
230
256
  return err(`Expected a boolean (true/false), got "${raw}".`);
231
257
  }
232
- const PREFERENCES = [{
233
- key: "shareMachineDefault",
234
- type: "boolean",
235
- describe: "Share this machine over the portal on `skydive chat` launch, as if --share-machine were passed. Default false.",
236
- read: () => getShareMachineDefault(),
237
- isSet: () => store.has("shareMachineDefault"),
238
- parse: parseBoolean,
239
- write: (value) => saveShareMachineDefault(value),
240
- clear: () => store.delete("shareMachineDefault")
241
- }, {
242
- key: "updateCheck",
243
- type: "boolean",
244
- describe: "Run the daily background update check and its \"Update available\" notice. Set false to disable. Default true.",
245
- read: () => !getUpdateCheckDisabled(),
246
- isSet: () => store.has("updateCheck"),
247
- parse: parseBoolean,
248
- write: (value) => saveUpdateCheck(value),
249
- clear: () => store.delete("updateCheck")
250
- }];
258
+ const PREFERENCES = [
259
+ {
260
+ key: "shareMachineDefault",
261
+ type: "boolean",
262
+ describe: "Share this machine over the portal on `skydive chat` launch, as if --share-machine were passed. Default false.",
263
+ read: () => getShareMachineDefault(),
264
+ isSet: () => store.has("shareMachineDefault"),
265
+ set: (raw) => parseBoolean(raw).map(saveShareMachineDefault),
266
+ clear: () => store.delete("shareMachineDefault")
267
+ },
268
+ {
269
+ key: "updateCheck",
270
+ type: "boolean",
271
+ describe: "Run the daily background update check and its \"Update available\" notice. Set false to disable. Default true.",
272
+ read: () => !getUpdateCheckDisabled(),
273
+ isSet: () => store.has("updateCheck"),
274
+ set: (raw) => parseBoolean(raw).map(saveUpdateCheck),
275
+ clear: () => store.delete("updateCheck")
276
+ },
277
+ {
278
+ key: "defaultAgent",
279
+ type: "string",
280
+ describe: "Agent (id, slug, or name) a bare `skydive chat` opens a new conversation with, skipping the agent picker. Unset by default.",
281
+ read: () => getDefaultAgent(),
282
+ isSet: () => getDefaultAgent() !== null,
283
+ set: (raw) => {
284
+ const value = raw.trim();
285
+ if (!value) return err("Expected an agent id, slug, or name (use `config unset defaultAgent` to clear it).");
286
+ saveDefaultAgent(value);
287
+ return ok(void 0);
288
+ },
289
+ clear: () => store.delete("defaultAgent")
290
+ }
291
+ ];
251
292
  function getPreference(key) {
252
293
  return PREFERENCES.find((p) => p.key === key);
253
294
  }
@@ -282,14 +323,15 @@ function parseButton(element) {
282
323
  const params = isRecord(press.params) ? press.params : {};
283
324
  const primary = props.variant === "primary";
284
325
  if (press.action === "approve_portal_access") {
285
- const agentId = params.agentId;
286
- if (typeof agentId !== "string" || !agentId) return null;
326
+ const agentId = optionalString(params.agentId);
327
+ if (!agentId) return null;
287
328
  return {
288
329
  label,
289
330
  action: {
290
331
  kind: "grant_portal",
291
332
  agentId,
292
- deviceId: typeof params.deviceId === "string" && params.deviceId ? params.deviceId : null
333
+ deviceId: optionalString(params.deviceId),
334
+ conversationId: optionalString(params.conversationId)
293
335
  },
294
336
  primary
295
337
  };
@@ -323,12 +365,46 @@ function parseButton(element) {
323
365
  function specKeyFor(spec) {
324
366
  return stableStringify(spec) ?? crypto.randomUUID();
325
367
  }
368
+ /**
369
+ * The `platform portal request` consent card. The server emits one
370
+ * DesktopHandoffCard spec for every surface (api routes/portal.ts): the web
371
+ * renders it as the "continue in the Skydive desktop app" handoff, but this
372
+ * terminal's own portal daemon can provide the machine, so here it maps onto
373
+ * the existing grant_portal approval instead of a pointer to the app. Without
374
+ * this the spec is not a Card, parses to null, and the request is silently
375
+ * dropped — `platform portal request` looks desktop-only from the CLI
376
+ * (ANY-6521).
377
+ */
378
+ function parseDesktopHandoffCard(rootEl) {
379
+ const props = isRecord(rootEl.props) ? rootEl.props : {};
380
+ const agentId = optionalString(props.agentId);
381
+ if (!agentId) return null;
382
+ const agentName = optionalString(props.agentName) ?? "This agent";
383
+ return {
384
+ title: `Let ${agentName} use your computer?`,
385
+ subtitle: null,
386
+ description: `Approving shares this machine with ${agentName} while you're signed in. Revoke anytime: skydive portal revoke --agent "${agentName}"`,
387
+ fields: [],
388
+ button: {
389
+ label: "Approve",
390
+ action: {
391
+ kind: "grant_portal",
392
+ agentId,
393
+ deviceId: optionalString(props.deviceId),
394
+ conversationId: optionalString(props.conversationId)
395
+ }
396
+ },
397
+ state: {}
398
+ };
399
+ }
326
400
  function parseConnectCard(spec) {
327
401
  if (!isRecord(spec)) return null;
328
402
  const { root, elements } = spec;
329
403
  if (typeof root !== "string" || !isRecord(elements)) return null;
330
404
  const rootEl = elements[root];
331
- if (!isRecord(rootEl) || rootEl.type !== "Card") return null;
405
+ if (!isRecord(rootEl)) return null;
406
+ if (rootEl.type === "DesktopHandoffCard") return parseDesktopHandoffCard(rootEl);
407
+ if (rootEl.type !== "Card") return null;
332
408
  const rootProps = isRecord(rootEl.props) ? rootEl.props : {};
333
409
  const title = optionalString(rootProps.title);
334
410
  if (!title) return null;
@@ -572,7 +648,7 @@ async function runPrint({ appUrl, sessionToken, prompt, agentSelector, conversat
572
648
  onPage: null
573
649
  }), agentSelector);
574
650
  if (machineShare) {
575
- if (!machineShare.isGranted(agent.id) && grantTargetAgent) await machineShare.grantAgent(agent.id);
651
+ if (!machineShare.isGranted(agent.id) && grantTargetAgent) await machineShare.grantAgent(agent.id, null);
576
652
  if (machineShare.isGranted(agent.id)) console.error(`portal: shared this machine with ${agent.name} for this run (grant persists until revoked)`);
577
653
  else console.error(`portal: this machine is shared (shareMachineDefault), but ${agent.name} has no grant. Approve its request, or run \`skydive portal grant --agent ${agent.name}\`.`);
578
654
  }
@@ -760,4 +836,4 @@ async function readStdin() {
760
836
  }
761
837
 
762
838
  //#endregion
763
- export { getStoredApiKeyWorkspaceName as A, saveTheme as B, getLastSeenVersion as C, getSavedTheme as D, getReviewStateDir as E, resolveManagementAuth as F, resolveSession as I, resolveWebUrl as L, resolveAppUrl as M, resolveChatAuth as N, getShareMachineDefault as O, resolveConfig as P, saveConfig as R, getConfigPath as S, getPromptHistoryPath as T, setLastSeenVersion 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, getUpdateCheckDisabled as j, getStoredApiKeyId 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, getPreference as w, deleteConfig as x, DEFAULT_APP_URL as y, saveSession as z };
839
+ 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 };
@@ -14,14 +14,17 @@ import { n as printError } from "./output-DYzzdXYV.mjs";
14
14
  * the reply (and to --json).
15
15
  */
16
16
  async function connectMachineShare({ appUrl, sessionToken, timeoutHint }) {
17
- const { PortalClient } = await import("./client-NBj0KZSP.mjs");
17
+ const { PortalClient } = await import("./client-BuU34IVE.mjs");
18
18
  let signalConnected;
19
19
  const connected = new Promise((resolve) => {
20
20
  signalConnected = resolve;
21
21
  });
22
22
  const machineShare = new PortalClient({
23
23
  appUrl,
24
- sessionToken,
24
+ credentials: () => ({
25
+ sessionToken,
26
+ deviceToken: null
27
+ }),
25
28
  resolveCwd: () => process.cwd(),
26
29
  onState: (state) => {
27
30
  if (state.status === "connected") signalConnected();
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { S as getConfigPath } from "./print-CbayCa87.mjs";
2
+ import { S as getConfigPath } from "./print-ClPkgR9z.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.4.1-beta.6";
11
+ var version$1 = "0.5.0-beta.10";
12
12
 
13
13
  //#endregion
14
14
  //#region src/auth/organization.ts
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as SandboxStream } from "./client-Cn2af31H.mjs";
2
+ import { t as SandboxStream } from "./client-c4c5MmgN.mjs";
3
3
 
4
4
  //#region src/chat/sandbox/raw-pty.ts
5
5
  /**
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import "./client-c4c5MmgN.mjs";
3
+ import { t as runRawPtyPassthrough } from "./raw-pty-DY4KelZW.mjs";
4
+
5
+ export { runRawPtyPassthrough };
@@ -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-BY2nADw5.mjs";
3
+ import { n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-I3imNduB.mjs";
4
4
  import "./billing-blocked-2wju4gC_.mjs";
5
5
 
6
6
  export { createRestClient };
@@ -193,13 +193,14 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
193
193
  }
194
194
  };
195
195
  return {
196
- listAgents: async ({ scope, onPage }) => {
196
+ listAgents: async ({ scope, onPage, limit }) => {
197
197
  const all = [];
198
198
  let cursor;
199
- const maxAgents = 2e3;
199
+ const maxAgents = limit ?? 2e3;
200
+ const pageSize = Math.min(100, maxAgents);
200
201
  do {
201
202
  const params = new URLSearchParams({
202
- limit: "100",
203
+ limit: String(pageSize),
203
204
  scope,
204
205
  sort: "mine_first_usage",
205
206
  includeStats: "false"
@@ -218,10 +219,7 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
218
219
  },
219
220
  getAgent: async ({ agentId }) => {
220
221
  const { agent } = await get(`/api/v1/agents/${encodeURIComponent(agentId)}`, getAgentResponseSchema);
221
- return {
222
- id: agent.id,
223
- name: agent.name
224
- };
222
+ return agent;
225
223
  },
226
224
  suggestAgentIdentity: async () => {
227
225
  const { suggestion } = await get("/api/v1/agents/suggest", suggestAgentResponseSchema);
@@ -453,10 +451,7 @@ const listAgentsResponseSchema = z.object({
453
451
  totalCount: z.number().nullable().optional()
454
452
  });
455
453
  const createAgentResponseSchema = z.object({ agent: agentSummarySchema });
456
- const getAgentResponseSchema = z.object({ agent: z.object({
457
- id: z.string(),
458
- name: z.string()
459
- }).passthrough() });
454
+ const getAgentResponseSchema = z.object({ agent: agentSummarySchema });
460
455
  const agentSuggestionSchema = z.object({ name: z.string() });
461
456
  const suggestAgentResponseSchema = z.object({ suggestion: agentSuggestionSchema.nullable() });
462
457
  const conversationAgentSchema = z.object({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.4.1-beta.6",
3
+ "version": "0.5.0-beta.10",
4
4
  "description": "Skydive CLI — cloud agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",
@@ -23,7 +23,7 @@
23
23
  "build:binary": "node ./scripts/embed-changelog.mjs && bun scripts/build-binary.ts",
24
24
  "test:unit": "vitest run --passWithNoTests && yarn test:tui",
25
25
  "test:ci": "vitest run --coverage --coverage.reporter=lcovonly --reporter=default --reporter=github-actions --minWorkers=1 --maxWorkers=2 --passWithNoTests && yarn test:tui",
26
- "test:tui": "bun test .tui.test",
26
+ "test:tui": "node scripts/run-tui-tests.mjs",
27
27
  "render:frames": "bun scripts/render-frames.tsx",
28
28
  "render:send-errors": "bun scripts/render-send-errors.tsx",
29
29
  "typecheck": "tsgo --noEmit"
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env node
2
- import { t as PortalClient } from "./client-mykp1DVb.mjs";
3
-
4
- export { PortalClient };
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./client-mykp1DVb.mjs";
3
- import "./daemon-CfbpCjAw.mjs";
4
- import { t as PortalDaemonClient } from "./daemon-client-6xtta_n2.mjs";
5
-
6
- export { PortalDaemonClient };
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./client-Cn2af31H.mjs";
3
- import { t as runRawPtyPassthrough } from "./raw-pty-B6mAroiI.mjs";
4
-
5
- export { runRawPtyPassthrough };