skydive-cli 0.1.0-beta.316 → 0.1.0-beta.339

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/README.md CHANGED
@@ -249,9 +249,9 @@ environments, and driving agents from other tools:
249
249
 
250
250
  ```sh
251
251
  skydive portal open # connect and stay in the foreground (ctrl+c to close)
252
- skydive portal open --agent grace # \u2026and grant that agent on first connect
252
+ skydive portal open --agent grace # …and grant that agent on first connect
253
253
  skydive portal open --cwd ~/some/repo # working directory for the agent's commands
254
- skydive portal grant --agent grace # allow an agent (machine must have opened once)
254
+ skydive portal grant --agent grace # allow an agent (registers this machine if needed)
255
255
  skydive portal revoke --agent grace # remove an agent's access
256
256
  skydive portal status # machines, connection state, granted agents
257
257
  ```
package/dist/js/bin.mjs CHANGED
@@ -16,7 +16,7 @@ import zlib from "node:zlib";
16
16
  import os from "node:os";
17
17
 
18
18
  //#region package.json
19
- var version$1 = "0.1.0-beta.316";
19
+ var version$1 = "0.1.0-beta.339";
20
20
 
21
21
  //#endregion
22
22
  //#region src/types.ts
@@ -971,7 +971,7 @@ function createRestClient({ appUrl, sessionToken }) {
971
971
  const { agent } = await post(`/api/v1/agents/${encodeURIComponent(agentId)}`, { model }, updateAgentResponseSchema, "PATCH");
972
972
  return { model: agent.model ?? null };
973
973
  },
974
- listConversations: async ({ agentId, limit, onPage }) => {
974
+ listConversations: async ({ agentId, limit, channels, onPage }) => {
975
975
  const all = [];
976
976
  const maxConversations = limit ?? 5e3;
977
977
  let cursor;
@@ -983,6 +983,7 @@ function createRestClient({ appUrl, sessionToken }) {
983
983
  });
984
984
  params.set("limit", String(Math.min(remaining, 100)));
985
985
  if (cursor) params.set("cursor", cursor);
986
+ for (const channel of channels ?? []) params.append("channels", channel);
986
987
  const page = await get(`/api/v1/conversations?${params.toString()}`, listConversationsResponseSchema);
987
988
  all.push(...page.conversations);
988
989
  cursor = page.nextCursor ?? void 0;
@@ -2617,7 +2618,7 @@ const chatCommand = {
2617
2618
  printError(`Unknown theme "${themeId}". Valid themes: ${themes.map((t) => t.id).join(", ")}`);
2618
2619
  process.exit(1);
2619
2620
  }
2620
- const { runChat } = await import("./boot-DmPfuIod.mjs");
2621
+ const { runChat } = await import("./boot-CzNAkqIm.mjs");
2621
2622
  await runChat({
2622
2623
  appUrl,
2623
2624
  sessionToken: session.value.sessionToken,
@@ -3410,7 +3411,7 @@ const switchCommand = {
3410
3411
  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.");
3411
3412
  process.exit(1);
3412
3413
  }
3413
- const { runWorkspacePicker } = await import("./boot-DmPfuIod.mjs");
3414
+ const { runWorkspacePicker } = await import("./boot-CzNAkqIm.mjs");
3414
3415
  await runWorkspacePicker(session);
3415
3416
  return;
3416
3417
  }
@@ -3547,6 +3548,22 @@ async function fetchPortalDevices(auth) {
3547
3548
  const json = await portalFetch(auth, "/api/v1/portal/devices", { method: "GET" });
3548
3549
  return devicesResponseSchema.parse(json);
3549
3550
  }
3551
+ const registerResponseSchema = z.object({ device: z.object({ id: z.string() }) });
3552
+ /**
3553
+ * Register this machine's device row without connecting. Connecting registers
3554
+ * as a side effect; this covers granting an agent on a machine that has never
3555
+ * shared yet (the grant references the device row).
3556
+ */
3557
+ async function registerPortalDevice(auth, { machineName, friendlyName }) {
3558
+ const json = await portalFetch(auth, "/api/v1/portal/devices", {
3559
+ method: "POST",
3560
+ body: JSON.stringify({
3561
+ machineName,
3562
+ friendlyName
3563
+ })
3564
+ });
3565
+ return registerResponseSchema.parse(json).device;
3566
+ }
3550
3567
  /** Short-lived token the machine presents when dialing the portal WebSocket. */
3551
3568
  async function mintPortalDeviceToken(auth) {
3552
3569
  const json = await portalFetch(auth, "/api/v1/portal/device-token", { method: "POST" });
@@ -3576,19 +3593,17 @@ function findThisDevice(devices, machineName) {
3576
3593
  * Resolve everything a grant/revoke needs in one round trip: the devices
3577
3594
  * response carries both the user's machines and the org's agent roster, so
3578
3595
  * the `--agent` selector (id or name) resolves without a separate paginated
3579
- * agent listing.
3596
+ * agent listing. `device` is null when this machine has no device row yet
3597
+ * (it has never shared or been registered) — grant registers on the fly,
3598
+ * revoke has nothing to remove.
3580
3599
  */
3581
3600
  async function resolveGrantTarget(argv) {
3582
3601
  const session = requireSession(argv);
3583
3602
  const { devices, agents } = await fetchPortalDevices(session);
3584
- const agent = resolveAgent(agents, argv.agent);
3585
- const { machineName } = machineIdentity();
3586
- const device = findThisDevice(devices, machineName);
3587
- if (!device) throw new Error(`this machine (${machineName}) isn't registered with the portal yet — run \`skydive portal open\` once to register it.`);
3588
3603
  return {
3589
3604
  session,
3590
- agent,
3591
- device
3605
+ agent: resolveAgent(agents, argv.agent),
3606
+ device: findThisDevice(devices, machineIdentity().machineName)
3592
3607
  };
3593
3608
  }
3594
3609
  function agentOption(y) {
@@ -3654,18 +3669,23 @@ const grantCommand = {
3654
3669
  builder: agentOption,
3655
3670
  handler: async (argv) => {
3656
3671
  const { session, agent, device } = await resolveGrantTarget(argv);
3672
+ const { machineName, friendlyName } = machineIdentity();
3673
+ const deviceId = device ? device.id : (await registerPortalDevice(session, {
3674
+ machineName,
3675
+ friendlyName
3676
+ })).id;
3657
3677
  await grantPortalAccess(session, {
3658
- deviceId: device.id,
3678
+ deviceId,
3659
3679
  agentId: agent.id
3660
3680
  });
3661
3681
  if (argv.json) {
3662
3682
  output(argv, {
3663
- deviceId: device.id,
3683
+ deviceId,
3664
3684
  agentId: agent.id
3665
3685
  });
3666
3686
  return;
3667
3687
  }
3668
- console.log(`Granted ${agent.name} access to ${device.friendlyName}. The agent can run commands here whenever its portal is open (\`skydive portal open\` or the chat TUI). Revoke with \`skydive portal revoke --agent ${agent.name}\`.`);
3688
+ console.log(`Granted ${agent.name} access to ${device?.friendlyName ?? friendlyName}. The agent can run commands here whenever its portal is open — start with \`skydive portal open\`, or pass \`--share-machine\` on a \`chat -p\` run. Revoke with \`skydive portal revoke --agent ${agent.name}\`.`);
3669
3689
  }
3670
3690
  };
3671
3691
  const revokeCommand = {
@@ -3674,6 +3694,7 @@ const revokeCommand = {
3674
3694
  builder: agentOption,
3675
3695
  handler: async (argv) => {
3676
3696
  const { session, agent, device } = await resolveGrantTarget(argv);
3697
+ if (!device) throw new Error(`this machine (${machineIdentity().machineName}) isn't registered with the portal — there's nothing to revoke.`);
3677
3698
  await revokePortalAccess(session, {
3678
3699
  deviceId: device.id,
3679
3700
  agentId: agent.id
@@ -1331,17 +1331,37 @@ function WorkspacePicker({ appUrl, sessionToken, onSelect, onCancel }) {
1331
1331
 
1332
1332
  //#endregion
1333
1333
  //#region src/chat/tui/screens/conversation-picker.tsx
1334
+ const humanChannels = [
1335
+ "web",
1336
+ "slack",
1337
+ "email",
1338
+ "imessage"
1339
+ ];
1334
1340
  function ConversationPickerScreen({ agent }) {
1335
1341
  const rest = useStore((s) => s.rest);
1336
1342
  const goTo = useStore((s) => s.goTo);
1343
+ const [showAutomated, setShowAutomated] = useState(false);
1344
+ const [cache, setCache] = useState({});
1337
1345
  const [state, setState] = useState({ kind: "loading" });
1346
+ const cacheKey = showAutomated ? "all" : "human";
1338
1347
  useEffect(() => {
1339
1348
  if (!rest) return;
1349
+ const cached = cache[cacheKey];
1350
+ if (cached) {
1351
+ setState({
1352
+ kind: "ready",
1353
+ conversations: cached,
1354
+ complete: true
1355
+ });
1356
+ return;
1357
+ }
1340
1358
  let cancelled = false;
1359
+ setState({ kind: "loading" });
1341
1360
  (async () => {
1342
1361
  try {
1343
1362
  const conversations = await rest.listConversations({
1344
1363
  agentId: agent.id,
1364
+ channels: showAutomated ? void 0 : humanChannels,
1345
1365
  onPage: (partial) => {
1346
1366
  if (!cancelled) setState({
1347
1367
  kind: "ready",
@@ -1351,6 +1371,10 @@ function ConversationPickerScreen({ agent }) {
1351
1371
  }
1352
1372
  });
1353
1373
  if (cancelled) return;
1374
+ setCache((prev) => ({
1375
+ ...prev,
1376
+ [cacheKey]: conversations
1377
+ }));
1354
1378
  setState({
1355
1379
  kind: "ready",
1356
1380
  conversations,
@@ -1367,7 +1391,13 @@ function ConversationPickerScreen({ agent }) {
1367
1391
  return () => {
1368
1392
  cancelled = true;
1369
1393
  };
1370
- }, [rest, agent.id]);
1394
+ }, [
1395
+ rest,
1396
+ agent.id,
1397
+ showAutomated,
1398
+ cacheKey,
1399
+ cache
1400
+ ]);
1371
1401
  if (state.kind === "loading") return /* @__PURE__ */ jsx("text", {
1372
1402
  fg: theme.muted,
1373
1403
  children: "loading conversations…"
@@ -1380,10 +1410,12 @@ function ConversationPickerScreen({ agent }) {
1380
1410
  agent,
1381
1411
  conversations: state.conversations,
1382
1412
  complete: state.complete,
1413
+ showAutomated,
1414
+ onToggleAutomated: () => setShowAutomated((shown) => !shown),
1383
1415
  onPick: goTo
1384
1416
  });
1385
1417
  }
1386
- function ConversationList({ agent, conversations, complete, onPick }) {
1418
+ function ConversationList({ agent, conversations, complete, showAutomated, onToggleAutomated, onPick }) {
1387
1419
  const { width, height } = useTerminalDimensions();
1388
1420
  const rest = useStore((s) => s.rest);
1389
1421
  const [query, setQuery] = useState("");
@@ -1392,6 +1424,9 @@ function ConversationList({ agent, conversations, complete, onPick }) {
1392
1424
  const [confirmId, setConfirmId] = useState(null);
1393
1425
  const [error, setError] = useState(null);
1394
1426
  const list = conversations.filter((c) => !removedIds.has(c.id));
1427
+ useEffect(() => {
1428
+ setHighlight(0);
1429
+ }, [showAutomated]);
1395
1430
  const hits = fuzzyFilter(query, list, conversationKeys);
1396
1431
  const rows = [{ kind: "new" }, ...hits.map((hit) => ({
1397
1432
  kind: "conv",
@@ -1446,7 +1481,8 @@ function ConversationList({ agent, conversations, complete, onPick }) {
1446
1481
  setError(null);
1447
1482
  setConfirmId(row.hit.item.id);
1448
1483
  }
1449
- } else if (key.name === "return") {
1484
+ } else if (key.name === "tab") onToggleAutomated();
1485
+ else if (key.name === "return") {
1450
1486
  const row = rows[clamped];
1451
1487
  if (row) open(row);
1452
1488
  }
@@ -1493,7 +1529,13 @@ function ConversationList({ agent, conversations, complete, onPick }) {
1493
1529
  "/",
1494
1530
  list.length,
1495
1531
  complete ? "" : " (loading…)",
1496
- " · ↑/↓ move · ↵ open · ctrl+d delete · esc back"
1532
+ " ·",
1533
+ " ",
1534
+ showAutomated ? "incl. agent runs" : "yours",
1535
+ " · tab",
1536
+ " ",
1537
+ showAutomated ? "hide" : "show",
1538
+ " agent runs · ↑/↓ move · ↵ open · ctrl+d delete · esc back"
1497
1539
  ]
1498
1540
  }),
1499
1541
  /* @__PURE__ */ jsx("box", {
@@ -3865,6 +3907,10 @@ const keybindGroups = [
3865
3907
  keys: "ctrl+d",
3866
3908
  action: "delete conversation (y/↵ confirm, n/esc keep)"
3867
3909
  },
3910
+ {
3911
+ keys: "tab",
3912
+ action: "show / hide the agent's own runs (schedules, webhooks, subagents)"
3913
+ },
3868
3914
  {
3869
3915
  keys: "esc",
3870
3916
  action: "back to agent picker"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.1.0-beta.316",
3
+ "version": "0.1.0-beta.339",
4
4
  "description": "Skydive CLI — manage AI agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",