skydive-cli 0.1.0-beta.50 → 0.1.0-beta.65

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
@@ -13,7 +13,7 @@ import fs from "node:fs";
13
13
  import zlib from "node:zlib";
14
14
 
15
15
  //#region package.json
16
- var version$1 = "0.1.0-beta.50";
16
+ var version$1 = "0.1.0-beta.65";
17
17
 
18
18
  //#endregion
19
19
  //#region src/types.ts
@@ -447,7 +447,7 @@ async function getActiveWorkspaceId({ appUrl, sessionToken }) {
447
447
  }
448
448
  async function setActiveWorkspace({ appUrl, sessionToken, organizationId }) {
449
449
  try {
450
- const res = await fetch(`${appUrl}/api/auth/organization/set-active`, {
450
+ const res = await fetch(`${appUrl}/api/v1/workspaces/switch`, {
451
451
  method: "POST",
452
452
  headers: {
453
453
  ...authHeaders(sessionToken),
@@ -455,7 +455,7 @@ async function setActiveWorkspace({ appUrl, sessionToken, organizationId }) {
455
455
  },
456
456
  body: JSON.stringify({ organizationId })
457
457
  });
458
- if (!res.ok) return err({ message: `failed to set active workspace (${res.status})` });
458
+ if (!res.ok) return err({ message: `failed to switch workspace (${res.status})` });
459
459
  return ok(void 0);
460
460
  } catch (e) {
461
461
  return err({ message: e instanceof Error ? e.message : String(e) });
@@ -2046,7 +2046,7 @@ const chatCommand = {
2046
2046
  printError(`Unknown theme "${themeId}". Valid themes: ${themes.map((t) => t.id).join(", ")}`);
2047
2047
  process.exit(1);
2048
2048
  }
2049
- const { runChat } = await import("./boot-SDnaYmi-.mjs");
2049
+ const { runChat } = await import("./boot-BUk9yMvA.mjs");
2050
2050
  await runChat({
2051
2051
  appUrl,
2052
2052
  sessionToken: session.value.sessionToken,
@@ -2137,7 +2137,7 @@ async function runList(argv, { hint }) {
2137
2137
  w.name,
2138
2138
  w.slug
2139
2139
  ]));
2140
- if (hint && !argv.quiet) console.log("\nRun `skydive workspace switch <slug>` to switch which workspace `skydive chat` uses.");
2140
+ if (hint && !argv.quiet) console.log("\nRun `skydive workspace switch` to pick a workspace, or pass its slug directly.");
2141
2141
  }
2142
2142
  const listCommand = {
2143
2143
  command: "list",
@@ -2145,15 +2145,28 @@ const listCommand = {
2145
2145
  handler: (argv) => runList(argv, { hint: false })
2146
2146
  };
2147
2147
  const switchCommand = {
2148
- command: "switch <workspace>",
2148
+ command: "switch [workspace]",
2149
2149
  describe: "Switch which workspace `skydive chat` uses",
2150
2150
  builder: (y) => y.positional("workspace", {
2151
2151
  type: "string",
2152
- demandOption: true,
2152
+ demandOption: false,
2153
2153
  describe: "Workspace slug, name, or ID"
2154
2154
  }),
2155
2155
  handler: async (argv) => {
2156
2156
  const session = requireSession(argv);
2157
+ if (!argv.workspace) {
2158
+ if (argv.json || argv.quiet || isNonInteractive()) {
2159
+ printError("Interactive workspace switching needs a terminal. Pass a workspace slug, name, or ID.");
2160
+ process.exit(1);
2161
+ }
2162
+ if (await ensureBunAndReexec((message) => process.stderr.write(`${message}\n`)) === "unavailable") {
2163
+ 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.");
2164
+ process.exit(1);
2165
+ }
2166
+ const { runWorkspacePicker } = await import("./boot-BUk9yMvA.mjs");
2167
+ await runWorkspacePicker(session);
2168
+ return;
2169
+ }
2157
2170
  const workspaces = await listWorkspaces(session);
2158
2171
  if (workspaces.isErr()) {
2159
2172
  printError(workspaces.error.message);
@@ -2277,4 +2290,4 @@ function resolveArgv(args, tty = {
2277
2290
  createCli(resolveArgv(hideBin(process.argv))).parse();
2278
2291
 
2279
2292
  //#endregion
2280
- export { resolveWebUrl as _, noColorRequested as a, themeMode as c, themesForMode as d, getActiveWorkspaceId as f, getSavedTheme as g, DEFAULT_APP_URL as h, monoTheme as i, themeModeFromColorFgBg as l, DEFAULT_API_URL as m, applyTheme as n, theme as o, listWorkspaces as p, findTheme as r, themeForMode as s, DEFAULT_THEME_ID as t, themeVersion as u, saveTheme as v };
2293
+ export { getSavedTheme as _, noColorRequested as a, themeMode as c, themesForMode as d, getActiveWorkspaceId as f, DEFAULT_APP_URL as g, DEFAULT_API_URL as h, monoTheme as i, themeModeFromColorFgBg as l, setActiveWorkspace as m, applyTheme as n, theme as o, listWorkspaces as p, findTheme as r, themeForMode as s, DEFAULT_THEME_ID as t, themeVersion as u, resolveWebUrl as v, saveTheme as y };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { i as createRestClient, n as resolveAgent, r as HttpError } from "./print-BtMdNPJR.mjs";
3
- import { _ as resolveWebUrl, a as noColorRequested, c as themeMode, d as themesForMode, f as getActiveWorkspaceId, g as getSavedTheme, h as DEFAULT_APP_URL, i as monoTheme, l as themeModeFromColorFgBg, m as DEFAULT_API_URL, n as applyTheme, o as theme, p as listWorkspaces, r as findTheme, s as themeForMode, t as DEFAULT_THEME_ID, u as themeVersion, v as saveTheme } from "./bin.mjs";
3
+ import { _ as getSavedTheme, a as noColorRequested, c as themeMode, d as themesForMode, f as getActiveWorkspaceId, g as DEFAULT_APP_URL, h as DEFAULT_API_URL, i as monoTheme, l as themeModeFromColorFgBg, m as setActiveWorkspace, n as applyTheme, o as theme, p as listWorkspaces, r as findTheme, s as themeForMode, t as DEFAULT_THEME_ID, u as themeVersion, v as resolveWebUrl, y as saveTheme } from "./bin.mjs";
4
4
  import path, { basename, isAbsolute, join, win32 } from "node:path";
5
5
  import { z } from "zod";
6
6
  import open from "open";
@@ -1226,6 +1226,7 @@ function screenLabel(screen, chatTitle) {
1226
1226
  case "error": return "";
1227
1227
  case "agent-picker": return "pick an agent";
1228
1228
  case "agent-create": return "create an agent";
1229
+ case "workspace-picker": return "switch workspace";
1229
1230
  case "conversation-picker": return `${screen.agent.name} · pick a conversation`;
1230
1231
  case "chat": {
1231
1232
  const c = screen.conversation;
@@ -1258,7 +1259,8 @@ function AgentPickerScreen() {
1258
1259
  const [cache, setCache] = useState({});
1259
1260
  const [state, setState] = useState({ kind: "loading" });
1260
1261
  useKeyboard((key) => {
1261
- if (key.name === "n" && key.ctrl) goTo({ kind: "agent-create" });
1262
+ if (key.name === "w" && key.ctrl) goTo({ kind: "workspace-picker" });
1263
+ else if (key.name === "n" && key.ctrl) goTo({ kind: "agent-create" });
1262
1264
  else if (key.name === "tab") setScope((s) => s === "mine" ? "org" : "mine");
1263
1265
  });
1264
1266
  useEffect(() => {
@@ -1324,7 +1326,7 @@ function AgentPickerScreen() {
1324
1326
  });
1325
1327
  if (state.agents.length === 0) return /* @__PURE__ */ jsx("text", {
1326
1328
  fg: theme.muted,
1327
- children: scope === "mine" ? "no agents of yours yet. press tab to see all agents, or ctrl+n to create one." : "no agents in this workspace. press ctrl+n to create one."
1329
+ children: scope === "mine" ? "no agents of yours yet. press tab to see all agents, ctrl+w to switch workspace, or ctrl+n to create one." : "no agents in this workspace. press ctrl+w to switch workspace, or ctrl+n to create one."
1328
1330
  });
1329
1331
  return /* @__PURE__ */ jsx(FilterableAgentList, {
1330
1332
  agents: state.agents,
@@ -1391,7 +1393,7 @@ function FilterableAgentList({ agents, scope, onPick }) {
1391
1393
  " ",
1392
1394
  "· tab ",
1393
1395
  scope === "mine" ? "all" : "mine",
1394
- " · ↑/↓ move · ↵ open · ctrl+n new"
1396
+ " · ↑/↓ move · ↵ open · ctrl+w workspace · ctrl+n new"
1395
1397
  ]
1396
1398
  }),
1397
1399
  /* @__PURE__ */ jsx("box", {
@@ -1568,6 +1570,146 @@ function AgentCreateScreen() {
1568
1570
  });
1569
1571
  }
1570
1572
 
1573
+ //#endregion
1574
+ //#region src/chat/tui/screens/workspace-picker.tsx
1575
+ function WorkspacePicker({ appUrl, sessionToken, onSelect, onCancel }) {
1576
+ const { height } = useTerminalDimensions();
1577
+ const [state, setState] = useState({ kind: "loading" });
1578
+ const [highlight, setHighlight] = useState(0);
1579
+ const [switching, setSwitching] = useState(false);
1580
+ useEffect(() => {
1581
+ let cancelled = false;
1582
+ (async () => {
1583
+ const [workspaces, activeId] = await Promise.all([listWorkspaces({
1584
+ appUrl,
1585
+ sessionToken
1586
+ }), getActiveWorkspaceId({
1587
+ appUrl,
1588
+ sessionToken
1589
+ })]);
1590
+ if (cancelled) return;
1591
+ if (workspaces.isErr()) {
1592
+ setState({
1593
+ kind: "error",
1594
+ message: workspaces.error.message
1595
+ });
1596
+ return;
1597
+ }
1598
+ if (activeId.isErr()) {
1599
+ setState({
1600
+ kind: "error",
1601
+ message: activeId.error.message
1602
+ });
1603
+ return;
1604
+ }
1605
+ setHighlight(Math.max(0, workspaces.value.findIndex((workspace) => workspace.id === activeId.value)));
1606
+ setState({
1607
+ kind: "ready",
1608
+ workspaces: workspaces.value,
1609
+ activeId: activeId.value
1610
+ });
1611
+ })();
1612
+ return () => {
1613
+ cancelled = true;
1614
+ };
1615
+ }, [appUrl, sessionToken]);
1616
+ const rows = state.kind === "ready" ? state.workspaces : [];
1617
+ const clamped = Math.min(highlight, Math.max(0, rows.length - 1));
1618
+ useKeyboard((key) => {
1619
+ if (switching) return;
1620
+ if (key.name === "escape" || key.name === "c" && key.ctrl) onCancel();
1621
+ else if (key.name === "up") setHighlight(Math.max(0, clamped - 1));
1622
+ else if (key.name === "down") setHighlight(Math.min(rows.length - 1, clamped + 1));
1623
+ else if (key.name === "return") {
1624
+ const workspace = rows[clamped];
1625
+ if (!workspace) return;
1626
+ setSwitching(true);
1627
+ (async () => {
1628
+ const result = await setActiveWorkspace({
1629
+ appUrl,
1630
+ sessionToken,
1631
+ organizationId: workspace.id
1632
+ });
1633
+ if (result.isErr()) {
1634
+ setState({
1635
+ kind: "error",
1636
+ message: result.error.message
1637
+ });
1638
+ setSwitching(false);
1639
+ return;
1640
+ }
1641
+ onSelect(workspace);
1642
+ })();
1643
+ }
1644
+ });
1645
+ if (state.kind === "loading") return /* @__PURE__ */ jsx("text", {
1646
+ fg: theme.muted,
1647
+ children: "loading workspaces…"
1648
+ });
1649
+ if (state.kind === "error") return /* @__PURE__ */ jsxs("box", {
1650
+ style: { flexDirection: "column" },
1651
+ children: [/* @__PURE__ */ jsxs("text", {
1652
+ fg: theme.error,
1653
+ children: ["error: ", state.message]
1654
+ }), /* @__PURE__ */ jsx("text", {
1655
+ fg: theme.dim,
1656
+ children: "esc cancel"
1657
+ })]
1658
+ });
1659
+ if (rows.length === 0) return /* @__PURE__ */ jsxs("box", {
1660
+ style: { flexDirection: "column" },
1661
+ children: [/* @__PURE__ */ jsx("text", {
1662
+ fg: theme.muted,
1663
+ children: "no workspaces available"
1664
+ }), /* @__PURE__ */ jsx("text", {
1665
+ fg: theme.dim,
1666
+ children: "esc cancel"
1667
+ })]
1668
+ });
1669
+ const visibleRows = Math.max(3, height - 6);
1670
+ const start = windowStart(clamped, rows.length, visibleRows);
1671
+ const windowed = rows.slice(start, start + visibleRows);
1672
+ return /* @__PURE__ */ jsxs("box", {
1673
+ style: {
1674
+ flexDirection: "column",
1675
+ flexGrow: 1
1676
+ },
1677
+ children: [
1678
+ /* @__PURE__ */ jsx("text", {
1679
+ fg: theme.accent,
1680
+ children: "switch workspace"
1681
+ }),
1682
+ /* @__PURE__ */ jsx("box", {
1683
+ style: {
1684
+ flexDirection: "column",
1685
+ flexGrow: 1,
1686
+ marginTop: 1
1687
+ },
1688
+ children: windowed.map((workspace, index) => {
1689
+ const selected = start + index === clamped;
1690
+ const active = workspace.id === state.activeId;
1691
+ return /* @__PURE__ */ jsxs("text", {
1692
+ fg: selected ? theme.accent : theme.fg,
1693
+ children: [
1694
+ selected ? "› " : " ",
1695
+ active ? "● " : " ",
1696
+ workspace.name,
1697
+ /* @__PURE__ */ jsxs("span", {
1698
+ fg: theme.dim,
1699
+ children: [" ", workspace.slug]
1700
+ })
1701
+ ]
1702
+ }, workspace.id);
1703
+ })
1704
+ }),
1705
+ /* @__PURE__ */ jsx("text", {
1706
+ fg: theme.dim,
1707
+ children: switching ? "switching…" : "↑/↓ move · ↵ select · esc cancel"
1708
+ })
1709
+ ]
1710
+ });
1711
+ }
1712
+
1571
1713
  //#endregion
1572
1714
  //#region src/chat/tui/screens/conversation-picker.tsx
1573
1715
  function ConversationPickerScreen({ agent }) {
@@ -5914,6 +6056,15 @@ function App({ appUrl, sessionToken, shareMachine, promptHistoryPath, notificati
5914
6056
  screen.kind === "error" && /* @__PURE__ */ jsx(ErrorScreen, { message: screen.message }),
5915
6057
  screen.kind === "agent-picker" && rest && /* @__PURE__ */ jsx(AgentPickerScreen, {}),
5916
6058
  screen.kind === "agent-create" && rest && /* @__PURE__ */ jsx(AgentCreateScreen, {}),
6059
+ screen.kind === "workspace-picker" && /* @__PURE__ */ jsx(WorkspacePicker, {
6060
+ appUrl,
6061
+ sessionToken,
6062
+ onCancel: () => goTo({ kind: "agent-picker" }),
6063
+ onSelect: (workspace) => {
6064
+ setWorkspaceName(workspace.name);
6065
+ goTo({ kind: "agent-picker" });
6066
+ }
6067
+ }),
5917
6068
  screen.kind === "conversation-picker" && rest && /* @__PURE__ */ jsx(ConversationPickerScreen, { agent: screen.agent }),
5918
6069
  screen.kind === "chat" && rest && /* @__PURE__ */ jsx(ChatScreen, {
5919
6070
  agent: screen.agent,
@@ -6202,6 +6353,27 @@ async function runChat({ appUrl, sessionToken, shareMachine, promptHistoryPath,
6202
6353
  agentSelector: agentSelector ?? null
6203
6354
  }));
6204
6355
  }
6356
+ /** Open the same workspace picker used by the initial agent screen. */
6357
+ async function runWorkspacePicker({ appUrl, sessionToken }) {
6358
+ const renderer = await createCliRenderer({ exitOnCtrlC: false });
6359
+ const detected = await renderer.waitForThemeMode() ?? themeModeFromColorFgBg() ?? "dark";
6360
+ useStore.getState().setThemeMode(detected);
6361
+ const close = () => renderer.destroy();
6362
+ createRoot(renderer).render(/* @__PURE__ */ jsx("box", {
6363
+ style: {
6364
+ flexDirection: "column",
6365
+ flexGrow: 1,
6366
+ padding: 1,
6367
+ backgroundColor: theme.background
6368
+ },
6369
+ children: /* @__PURE__ */ jsx(WorkspacePicker, {
6370
+ appUrl,
6371
+ sessionToken,
6372
+ onSelect: close,
6373
+ onCancel: close
6374
+ })
6375
+ }));
6376
+ }
6205
6377
 
6206
6378
  //#endregion
6207
- export { runChat };
6379
+ export { runChat, runWorkspacePicker };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.1.0-beta.50",
3
+ "version": "0.1.0-beta.65",
4
4
  "description": "Skydive CLI — manage AI agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",