skydive-cli 0.1.0-beta.341 → 0.1.0-beta.347
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 +21 -1
- package/dist/js/bin.mjs +15 -9
- package/dist/js/{boot-Dr7pEKqB.mjs → boot-BcuOicbb.mjs} +84 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,6 +70,26 @@ skydive portal status
|
|
|
70
70
|
|
|
71
71
|
Global flags: `--json`, `--quiet`, `--api-url <url>`.
|
|
72
72
|
|
|
73
|
+
## Workspaces
|
|
74
|
+
|
|
75
|
+
Every `skydive` command acts inside one **workspace** — the org/team boundary
|
|
76
|
+
that owns your agents, conversations, and secrets. If your account belongs to
|
|
77
|
+
several workspaces (say, your company org and a personal one), the same agent
|
|
78
|
+
name can exist in both, so which workspace is active determines what every
|
|
79
|
+
command sees.
|
|
80
|
+
|
|
81
|
+
- `skydive workspace list` — workspaces on your account, with the active one
|
|
82
|
+
marked.
|
|
83
|
+
- `skydive workspace switch [workspace]` — change the active workspace. All
|
|
84
|
+
commands — `agents`, `secrets`, `keys`, `chat`, `conversations` — follow it.
|
|
85
|
+
- `skydive auth status` — shows who you're logged in as and which workspace is
|
|
86
|
+
active.
|
|
87
|
+
- In the chat TUI, `ctrl+w` switches workspace from the agent picker.
|
|
88
|
+
|
|
89
|
+
The active workspace is stored in the CLI config file and persists across
|
|
90
|
+
invocations. If a command can't find an agent you know exists, check
|
|
91
|
+
`skydive auth status` first — you're probably in the wrong workspace.
|
|
92
|
+
|
|
73
93
|
## `skydive secrets`
|
|
74
94
|
|
|
75
95
|
Manage the environment secrets injected into an agent's sandbox. All
|
|
@@ -250,7 +270,7 @@ environments, and driving agents from other tools:
|
|
|
250
270
|
```sh
|
|
251
271
|
skydive portal open # connect and stay in the foreground (ctrl+c to close)
|
|
252
272
|
skydive portal open --agent grace # …and grant that agent on first connect
|
|
253
|
-
skydive portal open --cwd ~/some/repo # working directory for the agent's commands
|
|
273
|
+
skydive portal open --cwd ~/some/repo # working directory for the agent's commands (a starting point, not a boundary — see above)
|
|
254
274
|
skydive portal grant --agent grace # allow an agent (registers this machine if needed)
|
|
255
275
|
skydive portal revoke --agent grace # remove an agent's access
|
|
256
276
|
skydive portal status # machines, connection state, granted agents
|
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.
|
|
19
|
+
var version$1 = "0.1.0-beta.347";
|
|
20
20
|
|
|
21
21
|
//#endregion
|
|
22
22
|
//#region src/types.ts
|
|
@@ -1171,11 +1171,14 @@ const conversationSummarySchema = z.object({
|
|
|
1171
1171
|
title: z.string().nullable().optional()
|
|
1172
1172
|
})
|
|
1173
1173
|
});
|
|
1174
|
-
const
|
|
1174
|
+
const conversationDetailSchema = z.object({
|
|
1175
1175
|
id: z.string().uuid(),
|
|
1176
|
-
title: z.string().nullable()
|
|
1176
|
+
title: z.string().nullable(),
|
|
1177
|
+
agentId: z.string().uuid(),
|
|
1178
|
+
createdAt: z.string(),
|
|
1179
|
+
updatedAt: z.string()
|
|
1177
1180
|
});
|
|
1178
|
-
const getConversationResponseSchema = z.object({ conversation:
|
|
1181
|
+
const getConversationResponseSchema = z.object({ conversation: conversationDetailSchema });
|
|
1179
1182
|
const listConversationsResponseSchema = z.object({
|
|
1180
1183
|
conversations: z.array(conversationSummarySchema),
|
|
1181
1184
|
nextCursor: z.string().nullable().optional(),
|
|
@@ -2570,8 +2573,9 @@ const chatCommand = {
|
|
|
2570
2573
|
type: "string",
|
|
2571
2574
|
describe: "Target agent, by id, slug, or name. With -p, the agent to send the one-shot prompt to. Without -p, pre-selects the agent and opens its conversation list, skipping the agent picker. Optional when the account has exactly one agent."
|
|
2572
2575
|
}).option("conversation", {
|
|
2576
|
+
alias: "resume",
|
|
2573
2577
|
type: "string",
|
|
2574
|
-
describe: "
|
|
2578
|
+
describe: "Continue an existing conversation by id. With -p, the one-shot lands in that conversation; without -p, the TUI opens directly into it (the agent is resolved from the conversation, no --agent needed)."
|
|
2575
2579
|
}).option("share-machine", {
|
|
2576
2580
|
type: "boolean",
|
|
2577
2581
|
describe: "Share this machine with the agent over the portal so it can run commands here (default-deny; in the TUI you approve per agent, with -p the flag grants the target agent for the run). Defaults to `shareMachineDefault` in the CLI config file; --no-share-machine disables for this invocation."
|
|
@@ -2582,7 +2586,7 @@ const chatCommand = {
|
|
|
2582
2586
|
type: "boolean",
|
|
2583
2587
|
default: true,
|
|
2584
2588
|
describe: "Show a desktop notification when a run finishes or needs your input while this terminal is unfocused (use --no-notify to disable)"
|
|
2585
|
-
}).example("skydive chat -p \"summarize my open PRs\" --agent grace", "One-shot, non-interactive").example("echo \"what changed today?\" | skydive chat -p --agent grace", "Read the prompt from stdin").example("skydive chat --agent grace", "Open Grace's conversation list in the TUI (skips the agent picker)"),
|
|
2589
|
+
}).example("skydive chat -p \"summarize my open PRs\" --agent grace", "One-shot, non-interactive").example("echo \"what changed today?\" | skydive chat -p --agent grace", "Read the prompt from stdin").example("skydive chat --agent grace", "Open Grace's conversation list in the TUI (skips the agent picker)").example("skydive chat --resume 0197e0f3-…", "Reopen a previous conversation in the TUI (the id is printed when you quit a chat)"),
|
|
2586
2590
|
handler: async (argv) => {
|
|
2587
2591
|
const appUrl = resolveAppUrl({ appUrl: argv["api-url"] });
|
|
2588
2592
|
if (argv.print !== void 0) {
|
|
@@ -2618,7 +2622,7 @@ const chatCommand = {
|
|
|
2618
2622
|
printError(`Unknown theme "${themeId}". Valid themes: ${themes.map((t) => t.id).join(", ")}`);
|
|
2619
2623
|
process.exit(1);
|
|
2620
2624
|
}
|
|
2621
|
-
const { runChat } = await import("./boot-
|
|
2625
|
+
const { runChat } = await import("./boot-BcuOicbb.mjs");
|
|
2622
2626
|
await runChat({
|
|
2623
2627
|
appUrl,
|
|
2624
2628
|
sessionToken: session.value.sessionToken,
|
|
@@ -2626,7 +2630,8 @@ const chatCommand = {
|
|
|
2626
2630
|
promptHistoryPath: getPromptHistoryPath(),
|
|
2627
2631
|
theme: themeId,
|
|
2628
2632
|
notifications: argv.notify,
|
|
2629
|
-
agentSelector: argv.agent ?? null
|
|
2633
|
+
agentSelector: argv.agent ?? null,
|
|
2634
|
+
conversationId: argv.conversation ?? null
|
|
2630
2635
|
});
|
|
2631
2636
|
}
|
|
2632
2637
|
};
|
|
@@ -3411,7 +3416,7 @@ const switchCommand = {
|
|
|
3411
3416
|
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.");
|
|
3412
3417
|
process.exit(1);
|
|
3413
3418
|
}
|
|
3414
|
-
const { runWorkspacePicker } = await import("./boot-
|
|
3419
|
+
const { runWorkspacePicker } = await import("./boot-BcuOicbb.mjs");
|
|
3415
3420
|
await runWorkspacePicker(session);
|
|
3416
3421
|
return;
|
|
3417
3422
|
}
|
|
@@ -3795,6 +3800,7 @@ const VALUE_FLAGS = new Set([
|
|
|
3795
3800
|
"-p",
|
|
3796
3801
|
"--agent",
|
|
3797
3802
|
"--conversation",
|
|
3803
|
+
"--resume",
|
|
3798
3804
|
"--theme",
|
|
3799
3805
|
"--api-url"
|
|
3800
3806
|
]);
|
|
@@ -137,18 +137,56 @@ const useStore = create((set, get) => ({
|
|
|
137
137
|
/**
|
|
138
138
|
* Decide the screen the chat TUI boots into.
|
|
139
139
|
*
|
|
140
|
-
* With no
|
|
141
|
-
*
|
|
140
|
+
* With no selector we open the agent picker, exactly as before.
|
|
141
|
+
*
|
|
142
|
+
* With `--agent` (`skydive chat --agent grace`, no `-p`) we resolve it
|
|
142
143
|
* against the org roster and open that agent's conversation list, skipping
|
|
143
144
|
* the picker. Resolution reuses the same `resolveAgent` rules as the `-p`
|
|
144
145
|
* path so a given selector behaves identically in both modes: an id, or a
|
|
145
146
|
* unique case-insensitive slug/name; ambiguous or unknown selectors throw
|
|
146
147
|
* with the candidate list.
|
|
147
148
|
*
|
|
149
|
+
* With `--resume <conversation-id>` we skip both pickers and boot straight
|
|
150
|
+
* into that conversation. The id alone is enough — the owning agent is
|
|
151
|
+
* recovered from the conversation itself — so the "Resume this conversation
|
|
152
|
+
* with: …" hint printed on exit round-trips without the user also passing
|
|
153
|
+
* `--agent`. When both are given, the conversation wins (it pins the agent
|
|
154
|
+
* anyway).
|
|
155
|
+
*
|
|
148
156
|
* Kept out of the React component so it's unit-testable under Node (the
|
|
149
157
|
* component itself can't render without Bun + OpenTUI).
|
|
150
158
|
*/
|
|
151
|
-
async function resolveInitialScreen({ rest, agentSelector }) {
|
|
159
|
+
async function resolveInitialScreen({ rest, agentSelector, conversationId }) {
|
|
160
|
+
if (conversationId) {
|
|
161
|
+
const [conversation, agents] = await Promise.all([rest.getConversation({ conversationId }).catch((err) => {
|
|
162
|
+
if (err instanceof HttpError && err.status === 404) throw new Error(`No conversation with id ${conversationId} — it may have been deleted, or belong to another workspace.`);
|
|
163
|
+
throw err;
|
|
164
|
+
}), rest.listAgents({
|
|
165
|
+
scope: "org",
|
|
166
|
+
onPage: null
|
|
167
|
+
})]);
|
|
168
|
+
const agent = agents.find((a) => a.id === conversation.agentId);
|
|
169
|
+
if (!agent) throw new Error(`Conversation ${conversationId} belongs to an agent you can't access.`);
|
|
170
|
+
return {
|
|
171
|
+
kind: "chat",
|
|
172
|
+
agent,
|
|
173
|
+
conversation: {
|
|
174
|
+
id: conversation.id,
|
|
175
|
+
title: conversation.title,
|
|
176
|
+
createdAt: conversation.createdAt,
|
|
177
|
+
updatedAt: conversation.updatedAt,
|
|
178
|
+
preview: null,
|
|
179
|
+
channel: null,
|
|
180
|
+
channelLabel: null,
|
|
181
|
+
agent: {
|
|
182
|
+
id: agent.id,
|
|
183
|
+
name: agent.name,
|
|
184
|
+
slug: agent.slug ?? null,
|
|
185
|
+
title: agent.title ?? null
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
152
190
|
if (!agentSelector) return { kind: "agent-picker" };
|
|
153
191
|
return {
|
|
154
192
|
kind: "conversation-picker",
|
|
@@ -4337,6 +4375,39 @@ function filterSlashCommands(query) {
|
|
|
4337
4375
|
return [...prefix, ...substring];
|
|
4338
4376
|
}
|
|
4339
4377
|
|
|
4378
|
+
//#endregion
|
|
4379
|
+
//#region src/chat/tui/resume-hint.ts
|
|
4380
|
+
/**
|
|
4381
|
+
* The "Resume this conversation with: …" hint printed when the TUI exits.
|
|
4382
|
+
*
|
|
4383
|
+
* There are two ways out of the app — ChatScreen's quit (double ctrl+c or a
|
|
4384
|
+
* bare `exit`/`quit` line) and the app-root global ctrl+c on non-chat
|
|
4385
|
+
* screens (after `esc` back to the picker) — and both must print the hint:
|
|
4386
|
+
* esc'ing out of a live run then quitting is exactly the case where the id
|
|
4387
|
+
* is the only way back in (the agent keeps working server-side). So the
|
|
4388
|
+
* last-opened conversation is tracked here, module-scoped, and both
|
|
4389
|
+
* teardown sites call `printResumeHint()`.
|
|
4390
|
+
*
|
|
4391
|
+
* The write happens synchronously AFTER `renderer.destroy()` returns, never
|
|
4392
|
+
* on a process 'exit' hook — the native renderer teardown can end the
|
|
4393
|
+
* process without firing JS exit listeners (observed on darwin). By the
|
|
4394
|
+
* time destroy() returns, stdout is unhooked and the alt screen restored,
|
|
4395
|
+
* so the lines land in normal scrollback.
|
|
4396
|
+
*/
|
|
4397
|
+
let lastConversationId = null;
|
|
4398
|
+
/** Record the conversation currently open in the chat screen (null while a
|
|
4399
|
+
* brand-new conversation hasn't been materialized on the server yet). */
|
|
4400
|
+
function setResumeConversation(conversationId) {
|
|
4401
|
+
lastConversationId = conversationId;
|
|
4402
|
+
}
|
|
4403
|
+
/** Print the resume hint for the last opened conversation, if any. Call
|
|
4404
|
+
* after `renderer.destroy()`. Prints at most once per process. */
|
|
4405
|
+
function printResumeHint(write = (text) => process.stdout.write(text)) {
|
|
4406
|
+
if (!lastConversationId) return;
|
|
4407
|
+
write(`\nResume this conversation with:\n skydive chat --resume ${lastConversationId}\n`);
|
|
4408
|
+
lastConversationId = null;
|
|
4409
|
+
}
|
|
4410
|
+
|
|
4340
4411
|
//#endregion
|
|
4341
4412
|
//#region src/chat/tui/screens/model-picker.tsx
|
|
4342
4413
|
/**
|
|
@@ -5534,6 +5605,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
5534
5605
|
}, [agentHost]);
|
|
5535
5606
|
useEffect(() => {
|
|
5536
5607
|
agentHost.setSession(conversationId);
|
|
5608
|
+
setResumeConversation(conversationId);
|
|
5537
5609
|
}, [agentHost, conversationId]);
|
|
5538
5610
|
const scrollRef = useRef(null);
|
|
5539
5611
|
const composerRef = useRef(null);
|
|
@@ -6053,6 +6125,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
6053
6125
|
portalClient?.dispose();
|
|
6054
6126
|
agentHost.dispose();
|
|
6055
6127
|
renderer.destroy();
|
|
6128
|
+
printResumeHint();
|
|
6056
6129
|
}, [
|
|
6057
6130
|
portalClient,
|
|
6058
6131
|
agentHost,
|
|
@@ -6889,7 +6962,7 @@ function ToastView() {
|
|
|
6889
6962
|
|
|
6890
6963
|
//#endregion
|
|
6891
6964
|
//#region src/chat/tui/app.tsx
|
|
6892
|
-
function App({ appUrl, sessionToken, shareMachine, promptHistoryPath, notifications, agentSelector }) {
|
|
6965
|
+
function App({ appUrl, sessionToken, shareMachine, promptHistoryPath, notifications, agentSelector, conversationId }) {
|
|
6893
6966
|
const renderer = useRenderer();
|
|
6894
6967
|
const screen = useStore((s) => s.screen);
|
|
6895
6968
|
useStore((s) => s.themeId);
|
|
@@ -6961,7 +7034,8 @@ function App({ appUrl, sessionToken, shareMachine, promptHistoryPath, notificati
|
|
|
6961
7034
|
try {
|
|
6962
7035
|
const screen = await resolveInitialScreen({
|
|
6963
7036
|
rest,
|
|
6964
|
-
agentSelector
|
|
7037
|
+
agentSelector,
|
|
7038
|
+
conversationId
|
|
6965
7039
|
});
|
|
6966
7040
|
if (cancelled) return;
|
|
6967
7041
|
goTo(screen);
|
|
@@ -6982,6 +7056,7 @@ function App({ appUrl, sessionToken, shareMachine, promptHistoryPath, notificati
|
|
|
6982
7056
|
sessionToken,
|
|
6983
7057
|
promptHistoryPath,
|
|
6984
7058
|
agentSelector,
|
|
7059
|
+
conversationId,
|
|
6985
7060
|
goTo,
|
|
6986
7061
|
setClients,
|
|
6987
7062
|
fail
|
|
@@ -6991,6 +7066,7 @@ function App({ appUrl, sessionToken, shareMachine, promptHistoryPath, notificati
|
|
|
6991
7066
|
useStore.getState().portalClient?.dispose();
|
|
6992
7067
|
agentHost.dispose();
|
|
6993
7068
|
renderer.destroy();
|
|
7069
|
+
printResumeHint();
|
|
6994
7070
|
}
|
|
6995
7071
|
});
|
|
6996
7072
|
return /* @__PURE__ */ jsx(AgentHostContext.Provider, {
|
|
@@ -7284,7 +7360,7 @@ var LinkifiedMarkdownRenderable = class extends MarkdownRenderable {
|
|
|
7284
7360
|
* module — and the heavy OpenTUI dependency graph it pulls in — is
|
|
7285
7361
|
* dynamically imported and never loaded on the Node management path.
|
|
7286
7362
|
*/
|
|
7287
|
-
async function runChat({ appUrl, sessionToken, shareMachine, promptHistoryPath, theme: themeOverride, notifications, agentSelector }) {
|
|
7363
|
+
async function runChat({ appUrl, sessionToken, shareMachine, promptHistoryPath, theme: themeOverride, notifications, agentSelector, conversationId }) {
|
|
7288
7364
|
const renderer = await createCliRenderer({ exitOnCtrlC: false });
|
|
7289
7365
|
extend({ markdown: LinkifiedMarkdownRenderable });
|
|
7290
7366
|
const store = useStore.getState();
|
|
@@ -7308,7 +7384,8 @@ async function runChat({ appUrl, sessionToken, shareMachine, promptHistoryPath,
|
|
|
7308
7384
|
shareMachine,
|
|
7309
7385
|
promptHistoryPath,
|
|
7310
7386
|
notifications,
|
|
7311
|
-
agentSelector: agentSelector ?? null
|
|
7387
|
+
agentSelector: agentSelector ?? null,
|
|
7388
|
+
conversationId: conversationId ?? null
|
|
7312
7389
|
}));
|
|
7313
7390
|
}
|
|
7314
7391
|
/** Open the same workspace picker used by the initial agent screen. */
|