skydive-cli 0.1.0-beta.45
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 +185 -0
- package/dist/js/bin.mjs +2280 -0
- package/dist/js/boot-BQ8xC2R8.mjs +5930 -0
- package/dist/js/print-B76KzChc.mjs +446 -0
- package/package.json +62 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { createParser } from "eventsource-parser";
|
|
4
|
+
|
|
5
|
+
//#region \0rolldown/runtime.js
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __exportAll = (all, no_symbols) => {
|
|
8
|
+
let target = {};
|
|
9
|
+
for (var name in all) {
|
|
10
|
+
__defProp(target, name, {
|
|
11
|
+
get: all[name],
|
|
12
|
+
enumerable: true
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
if (!no_symbols) {
|
|
16
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
17
|
+
}
|
|
18
|
+
return target;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/chat/api/rest.ts
|
|
23
|
+
var HttpError = class extends Error {
|
|
24
|
+
constructor(status, body) {
|
|
25
|
+
super(`HTTP ${status}: ${body.slice(0, 200)}`);
|
|
26
|
+
this.status = status;
|
|
27
|
+
this.body = body;
|
|
28
|
+
this.name = "HttpError";
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const MAX_STREAM_RECONNECTS = 5;
|
|
32
|
+
function createRestClient({ appUrl, sessionToken }) {
|
|
33
|
+
const baseHeaders = {
|
|
34
|
+
authorization: `Bearer ${sessionToken}`,
|
|
35
|
+
accept: "application/json"
|
|
36
|
+
};
|
|
37
|
+
async function get(path, schema) {
|
|
38
|
+
const res = await fetch(`${appUrl}${path}`, { headers: baseHeaders });
|
|
39
|
+
if (!res.ok) throw new HttpError(res.status, await res.text().catch(() => ""));
|
|
40
|
+
return schema.parse(await res.json());
|
|
41
|
+
}
|
|
42
|
+
async function post(path, body, schema, method = "POST") {
|
|
43
|
+
const res = await fetch(`${appUrl}${path}`, {
|
|
44
|
+
method,
|
|
45
|
+
headers: {
|
|
46
|
+
...baseHeaders,
|
|
47
|
+
"content-type": "application/json"
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify(body)
|
|
50
|
+
});
|
|
51
|
+
if (!res.ok) throw new HttpError(res.status, await res.text().catch(() => ""));
|
|
52
|
+
return schema.parse(await res.json());
|
|
53
|
+
}
|
|
54
|
+
async function del(path) {
|
|
55
|
+
const res = await fetch(`${appUrl}${path}`, {
|
|
56
|
+
method: "DELETE",
|
|
57
|
+
headers: baseHeaders
|
|
58
|
+
});
|
|
59
|
+
if (!res.ok) throw new HttpError(res.status, await res.text().catch(() => ""));
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
listAgents: async ({ scope, onPage }) => {
|
|
63
|
+
const all = [];
|
|
64
|
+
let cursor;
|
|
65
|
+
const maxAgents = 2e3;
|
|
66
|
+
do {
|
|
67
|
+
const params = new URLSearchParams({
|
|
68
|
+
limit: "100",
|
|
69
|
+
scope,
|
|
70
|
+
sort: "mine_first_usage",
|
|
71
|
+
includeStats: "false"
|
|
72
|
+
});
|
|
73
|
+
if (cursor) params.set("cursor", cursor);
|
|
74
|
+
const page = await get(`/api/v1/agents?${params.toString()}`, listAgentsResponseSchema);
|
|
75
|
+
all.push(...page.agents);
|
|
76
|
+
cursor = page.nextCursor ?? void 0;
|
|
77
|
+
onPage?.([...all]);
|
|
78
|
+
} while (cursor && all.length < maxAgents);
|
|
79
|
+
return all;
|
|
80
|
+
},
|
|
81
|
+
createAgent: async ({ name }) => {
|
|
82
|
+
const { agent } = await post("/api/v1/agents", { name }, createAgentResponseSchema);
|
|
83
|
+
return agent;
|
|
84
|
+
},
|
|
85
|
+
getConversation: async ({ conversationId }) => {
|
|
86
|
+
const { conversation } = await get(`/api/v1/conversations/${encodeURIComponent(conversationId)}`, getConversationResponseSchema);
|
|
87
|
+
return conversation;
|
|
88
|
+
},
|
|
89
|
+
listModels: async () => {
|
|
90
|
+
const { models } = await get("/api/v1/models", listModelsResponseSchema);
|
|
91
|
+
return models;
|
|
92
|
+
},
|
|
93
|
+
updateAgentModel: async ({ agentId, model }) => {
|
|
94
|
+
const { agent } = await post(`/api/v1/agents/${encodeURIComponent(agentId)}`, { model }, updateAgentResponseSchema, "PATCH");
|
|
95
|
+
return { model: agent.model ?? null };
|
|
96
|
+
},
|
|
97
|
+
listConversations: async ({ agentId, limit }) => {
|
|
98
|
+
const params = new URLSearchParams({
|
|
99
|
+
agentId,
|
|
100
|
+
includeTotal: "false"
|
|
101
|
+
});
|
|
102
|
+
if (limit) params.set("limit", String(limit));
|
|
103
|
+
const { conversations } = await get(`/api/v1/conversations?${params.toString()}`, listConversationsResponseSchema);
|
|
104
|
+
return conversations;
|
|
105
|
+
},
|
|
106
|
+
listMessages: async ({ conversationId }) => {
|
|
107
|
+
const { messages } = await get(`/api/v1/conversations/${encodeURIComponent(conversationId)}/messages`, listMessagesResponseSchema);
|
|
108
|
+
return messages;
|
|
109
|
+
},
|
|
110
|
+
uploadAttachment: async ({ agentId, fileName, mediaType, data }) => {
|
|
111
|
+
const size = data.byteLength;
|
|
112
|
+
const presign = await post("/api/v1/attachments/presign", {
|
|
113
|
+
agentId,
|
|
114
|
+
fileName,
|
|
115
|
+
mediaType,
|
|
116
|
+
size
|
|
117
|
+
}, presignResponseSchema);
|
|
118
|
+
const putRes = await fetch(presign.uploadUrl, {
|
|
119
|
+
method: "PUT",
|
|
120
|
+
headers: { "content-type": mediaType },
|
|
121
|
+
body: new Uint8Array(data)
|
|
122
|
+
});
|
|
123
|
+
if (!putRes.ok) throw new HttpError(putRes.status, await putRes.text().catch(() => ""));
|
|
124
|
+
const finalized = await post(`/api/v1/attachments/${encodeURIComponent(presign.id)}/finalize`, {
|
|
125
|
+
agentId,
|
|
126
|
+
fileName: presign.fileName,
|
|
127
|
+
mediaType: presign.mediaType,
|
|
128
|
+
size
|
|
129
|
+
}, finalizeResponseSchema);
|
|
130
|
+
return {
|
|
131
|
+
id: presign.id,
|
|
132
|
+
fileName: finalized.fileName,
|
|
133
|
+
mediaType: finalized.mediaType,
|
|
134
|
+
sizeBytes: finalized.sizeBytes ?? size
|
|
135
|
+
};
|
|
136
|
+
},
|
|
137
|
+
deleteConversation: async ({ conversationId }) => {
|
|
138
|
+
await del(`/api/v1/conversations/${encodeURIComponent(conversationId)}`);
|
|
139
|
+
},
|
|
140
|
+
sendMessage: async ({ clientSurface, ...input }) => post("/api/v1/chat/send", {
|
|
141
|
+
...input,
|
|
142
|
+
clientSurface
|
|
143
|
+
}, sendResultSchema),
|
|
144
|
+
activeRun: async ({ conversationId }) => {
|
|
145
|
+
const { run } = await get(`/api/v1/chat/active-run?${new URLSearchParams({ conversationId }).toString()}`, activeRunResponseSchema);
|
|
146
|
+
return run;
|
|
147
|
+
},
|
|
148
|
+
cancelRun: async ({ runId }) => {
|
|
149
|
+
await post(`/api/v1/chat/runs/${encodeURIComponent(runId)}/cancel`, {}, z.object({ ok: z.boolean() }));
|
|
150
|
+
},
|
|
151
|
+
cancelSteer: async ({ directiveId }) => {
|
|
152
|
+
await post(`/api/v1/chat/steer/${encodeURIComponent(directiveId)}/cancel`, {}, z.object({ ok: z.boolean() }));
|
|
153
|
+
},
|
|
154
|
+
oauthConnect: async (input) => {
|
|
155
|
+
const { connectLink } = await post("/api/v1/oauth/connect", input, oauthConnectResponseSchema);
|
|
156
|
+
return { connectLink };
|
|
157
|
+
},
|
|
158
|
+
externalOauthConnect: async (input) => {
|
|
159
|
+
const { authorizationUrl } = await post("/api/v1/external-oauth/connect", input, externalOauthConnectResponseSchema);
|
|
160
|
+
return { authorizationUrl: authorizationUrl ?? null };
|
|
161
|
+
},
|
|
162
|
+
fulfillCredential: async ({ url, body }) => {
|
|
163
|
+
const target = new URL(url, appUrl).toString();
|
|
164
|
+
const res = await fetch(target, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: {
|
|
167
|
+
...baseHeaders,
|
|
168
|
+
"content-type": "application/json"
|
|
169
|
+
},
|
|
170
|
+
body: JSON.stringify(body)
|
|
171
|
+
});
|
|
172
|
+
if (!res.ok) throw new HttpError(res.status, await res.text().catch(() => ""));
|
|
173
|
+
},
|
|
174
|
+
streamRun: async ({ runId, signal, onEvent }) => {
|
|
175
|
+
let lastEventId = null;
|
|
176
|
+
let finished = false;
|
|
177
|
+
let reconnects = 0;
|
|
178
|
+
for (;;) {
|
|
179
|
+
if (signal.aborted) return;
|
|
180
|
+
try {
|
|
181
|
+
const headers = {
|
|
182
|
+
authorization: `Bearer ${sessionToken}`,
|
|
183
|
+
accept: "text/event-stream"
|
|
184
|
+
};
|
|
185
|
+
if (lastEventId) headers["last-event-id"] = lastEventId;
|
|
186
|
+
const res = await fetch(`${appUrl}/api/v1/chat/runs/${encodeURIComponent(runId)}/stream`, {
|
|
187
|
+
headers,
|
|
188
|
+
signal
|
|
189
|
+
});
|
|
190
|
+
if (!res.ok || !res.body) throw new HttpError(res.status, await res.text().catch(() => ""));
|
|
191
|
+
reconnects = 0;
|
|
192
|
+
const parser = createParser({ onEvent: (message) => {
|
|
193
|
+
if (message.id) lastEventId = message.id;
|
|
194
|
+
if (message.event === "error") {
|
|
195
|
+
const { error } = streamErrorSchema.parse(JSON.parse(message.data));
|
|
196
|
+
throw new Error(error);
|
|
197
|
+
}
|
|
198
|
+
const event = runStreamEventSchema.parse(JSON.parse(message.data));
|
|
199
|
+
if (event.kind === "finished") finished = true;
|
|
200
|
+
onEvent(event.kind === "finished" ? {
|
|
201
|
+
...event,
|
|
202
|
+
error: event.error ?? null
|
|
203
|
+
} : event);
|
|
204
|
+
} });
|
|
205
|
+
const decoder = new TextDecoder();
|
|
206
|
+
const reader = res.body.getReader();
|
|
207
|
+
try {
|
|
208
|
+
for (;;) {
|
|
209
|
+
const { done, value } = await reader.read();
|
|
210
|
+
if (done) break;
|
|
211
|
+
parser.feed(decoder.decode(value, { stream: true }));
|
|
212
|
+
if (finished) return;
|
|
213
|
+
}
|
|
214
|
+
} finally {
|
|
215
|
+
try {
|
|
216
|
+
await reader.cancel();
|
|
217
|
+
} catch (_error) {}
|
|
218
|
+
}
|
|
219
|
+
} catch (err) {
|
|
220
|
+
if (signal.aborted) return;
|
|
221
|
+
if (err instanceof HttpError && err.status >= 400 && err.status < 500) throw err;
|
|
222
|
+
reconnects += 1;
|
|
223
|
+
if (reconnects > MAX_STREAM_RECONNECTS) throw err;
|
|
224
|
+
await sleep(Math.min(500 * 2 ** reconnects, 5e3));
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (finished) return;
|
|
228
|
+
reconnects += 1;
|
|
229
|
+
if (reconnects > MAX_STREAM_RECONNECTS) throw new Error("run stream ended unexpectedly");
|
|
230
|
+
await sleep(Math.min(500 * 2 ** reconnects, 5e3));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function sleep(ms) {
|
|
236
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
237
|
+
}
|
|
238
|
+
const agentSummarySchema = z.object({
|
|
239
|
+
id: z.string().uuid(),
|
|
240
|
+
name: z.string(),
|
|
241
|
+
slug: z.string().nullable().optional(),
|
|
242
|
+
title: z.string().nullable().optional(),
|
|
243
|
+
description: z.string().nullable().optional(),
|
|
244
|
+
createdAt: z.string(),
|
|
245
|
+
creatorName: z.string().nullable().optional(),
|
|
246
|
+
model: z.string().nullable().optional(),
|
|
247
|
+
modelLocked: z.boolean().optional()
|
|
248
|
+
});
|
|
249
|
+
const platformModelSchema = z.object({
|
|
250
|
+
id: z.string(),
|
|
251
|
+
displayName: z.string(),
|
|
252
|
+
providerDisplay: z.string().optional(),
|
|
253
|
+
reasoning: z.boolean().optional(),
|
|
254
|
+
compliant: z.boolean().optional()
|
|
255
|
+
}).passthrough();
|
|
256
|
+
const listModelsResponseSchema = z.object({ models: z.array(platformModelSchema) });
|
|
257
|
+
const updateAgentResponseSchema = z.object({ agent: z.object({ model: z.string().nullable().optional() }).passthrough() });
|
|
258
|
+
const listAgentsResponseSchema = z.object({
|
|
259
|
+
agents: z.array(agentSummarySchema),
|
|
260
|
+
nextCursor: z.string().nullable().optional(),
|
|
261
|
+
totalCount: z.number().nullable().optional()
|
|
262
|
+
});
|
|
263
|
+
const createAgentResponseSchema = z.object({ agent: agentSummarySchema });
|
|
264
|
+
const conversationSummarySchema = z.object({
|
|
265
|
+
id: z.string().uuid(),
|
|
266
|
+
title: z.string().nullable(),
|
|
267
|
+
createdAt: z.string(),
|
|
268
|
+
updatedAt: z.string(),
|
|
269
|
+
preview: z.string().nullable(),
|
|
270
|
+
channel: z.string().nullable(),
|
|
271
|
+
channelLabel: z.string().nullable(),
|
|
272
|
+
agent: z.object({
|
|
273
|
+
id: z.string().uuid(),
|
|
274
|
+
name: z.string(),
|
|
275
|
+
slug: z.string().nullable().optional(),
|
|
276
|
+
title: z.string().nullable().optional()
|
|
277
|
+
})
|
|
278
|
+
});
|
|
279
|
+
const conversationTitleSchema = z.object({
|
|
280
|
+
id: z.string().uuid(),
|
|
281
|
+
title: z.string().nullable()
|
|
282
|
+
});
|
|
283
|
+
const getConversationResponseSchema = z.object({ conversation: conversationTitleSchema });
|
|
284
|
+
const listConversationsResponseSchema = z.object({
|
|
285
|
+
conversations: z.array(conversationSummarySchema),
|
|
286
|
+
nextCursor: z.string().nullable().optional(),
|
|
287
|
+
totalCount: z.number().optional()
|
|
288
|
+
});
|
|
289
|
+
const uiMessagePartSchema = z.union([
|
|
290
|
+
z.object({
|
|
291
|
+
type: z.literal("text"),
|
|
292
|
+
text: z.string()
|
|
293
|
+
}),
|
|
294
|
+
z.object({
|
|
295
|
+
type: z.literal("reasoning"),
|
|
296
|
+
text: z.string().optional()
|
|
297
|
+
}),
|
|
298
|
+
z.object({
|
|
299
|
+
type: z.literal("dynamic-tool"),
|
|
300
|
+
toolCallId: z.string(),
|
|
301
|
+
toolName: z.string(),
|
|
302
|
+
input: z.unknown().optional(),
|
|
303
|
+
output: z.unknown().optional(),
|
|
304
|
+
state: z.string().optional(),
|
|
305
|
+
errorText: z.string().optional()
|
|
306
|
+
}),
|
|
307
|
+
z.object({ type: z.string() }).passthrough()
|
|
308
|
+
]);
|
|
309
|
+
const uiMessageSchema = z.object({
|
|
310
|
+
id: z.string(),
|
|
311
|
+
role: z.string(),
|
|
312
|
+
parts: z.array(uiMessagePartSchema)
|
|
313
|
+
});
|
|
314
|
+
const listMessagesResponseSchema = z.object({ messages: z.array(uiMessageSchema) });
|
|
315
|
+
const sendResultSchema = z.object({
|
|
316
|
+
runId: z.string(),
|
|
317
|
+
conversationId: z.string().uuid(),
|
|
318
|
+
isNewConversation: z.boolean(),
|
|
319
|
+
steered: z.boolean().optional(),
|
|
320
|
+
directive: z.object({ id: z.string() }).passthrough().optional()
|
|
321
|
+
});
|
|
322
|
+
const presignResponseSchema = z.object({
|
|
323
|
+
id: z.string(),
|
|
324
|
+
uploadUrl: z.string(),
|
|
325
|
+
fileName: z.string(),
|
|
326
|
+
mediaType: z.string()
|
|
327
|
+
});
|
|
328
|
+
const finalizeResponseSchema = z.object({
|
|
329
|
+
fileName: z.string(),
|
|
330
|
+
mediaType: z.string(),
|
|
331
|
+
sizeBytes: z.number().nullable().optional()
|
|
332
|
+
});
|
|
333
|
+
const activeRunResponseSchema = z.object({ run: z.object({ runId: z.string() }).nullable() });
|
|
334
|
+
const oauthConnectResponseSchema = z.object({ connectLink: z.string() }).passthrough();
|
|
335
|
+
const externalOauthConnectResponseSchema = z.object({ authorizationUrl: z.string().optional() }).passthrough();
|
|
336
|
+
const runStreamEventSchema = z.union([z.object({
|
|
337
|
+
kind: z.literal("chunk"),
|
|
338
|
+
chunk: z.record(z.unknown())
|
|
339
|
+
}), z.object({
|
|
340
|
+
kind: z.literal("finished"),
|
|
341
|
+
status: z.string(),
|
|
342
|
+
error: z.string().nullish()
|
|
343
|
+
})]);
|
|
344
|
+
const streamErrorSchema = z.object({ error: z.string() });
|
|
345
|
+
|
|
346
|
+
//#endregion
|
|
347
|
+
//#region src/chat/print.ts
|
|
348
|
+
var print_exports = /* @__PURE__ */ __exportAll({
|
|
349
|
+
readStdin: () => readStdin,
|
|
350
|
+
resolveAgent: () => resolveAgent,
|
|
351
|
+
runPrint: () => runPrint
|
|
352
|
+
});
|
|
353
|
+
/**
|
|
354
|
+
* Non-interactive chat, à la `claude -p`. Sends a single prompt to an
|
|
355
|
+
* agent, streams the run, and prints the assistant's reply to stdout
|
|
356
|
+
* before exiting. No OpenTUI, no Bun requirement — this rides the same
|
|
357
|
+
* Node-friendly REST client the TUI uses, so it runs anywhere the
|
|
358
|
+
* management commands do (CI, pipes, scripts).
|
|
359
|
+
*
|
|
360
|
+
* Resolution rules kept deliberately strict because there's no human to
|
|
361
|
+
* disambiguate: an `--agent` selector must match exactly one agent, and
|
|
362
|
+
* when it's omitted we only auto-pick if the account has exactly one.
|
|
363
|
+
*/
|
|
364
|
+
async function runPrint({ appUrl, sessionToken, prompt, agentSelector, conversationId, json }) {
|
|
365
|
+
const client = createRestClient({
|
|
366
|
+
appUrl,
|
|
367
|
+
sessionToken
|
|
368
|
+
});
|
|
369
|
+
const agent = resolveAgent(await client.listAgents({
|
|
370
|
+
scope: "org",
|
|
371
|
+
onPage: null
|
|
372
|
+
}), agentSelector);
|
|
373
|
+
const send = await client.sendMessage({
|
|
374
|
+
agentId: agent.id,
|
|
375
|
+
conversationId,
|
|
376
|
+
content: prompt,
|
|
377
|
+
attachmentIds: [],
|
|
378
|
+
clientSurface: "cli"
|
|
379
|
+
});
|
|
380
|
+
let text = "";
|
|
381
|
+
const controller = new AbortController();
|
|
382
|
+
let streamError = null;
|
|
383
|
+
await client.streamRun({
|
|
384
|
+
runId: send.runId,
|
|
385
|
+
signal: controller.signal,
|
|
386
|
+
onEvent: (event) => {
|
|
387
|
+
if (event.kind === "finished") {
|
|
388
|
+
if (event.error) streamError = event.error;
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const chunk = event.chunk;
|
|
392
|
+
if (chunk["type"] === "text-delta") {
|
|
393
|
+
const delta = typeof chunk["delta"] === "string" ? chunk["delta"] : typeof chunk["text"] === "string" ? chunk["text"] : "";
|
|
394
|
+
if (delta) {
|
|
395
|
+
text += delta;
|
|
396
|
+
if (!json) process.stdout.write(delta);
|
|
397
|
+
}
|
|
398
|
+
} else if (chunk["type"] === "error") streamError = typeof chunk["errorText"] === "string" ? chunk["errorText"] : "unknown error";
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
if (streamError) throw new Error(streamError);
|
|
402
|
+
if (!json && text && !text.endsWith("\n")) process.stdout.write("\n");
|
|
403
|
+
return {
|
|
404
|
+
agentId: agent.id,
|
|
405
|
+
agentName: agent.name,
|
|
406
|
+
conversationId: send.conversationId,
|
|
407
|
+
isNewConversation: send.isNewConversation,
|
|
408
|
+
runId: send.runId,
|
|
409
|
+
text
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Pick the target agent. With no selector, auto-pick only when the
|
|
414
|
+
* account has exactly one agent; otherwise the user must name one (there's
|
|
415
|
+
* no picker in non-interactive mode). A selector matches by id first, then
|
|
416
|
+
* a unique case-insensitive slug/name; ambiguous or missing matches throw
|
|
417
|
+
* with the candidate list so the caller knows what to pass.
|
|
418
|
+
*/
|
|
419
|
+
function resolveAgent(agents, selector) {
|
|
420
|
+
if (!selector) {
|
|
421
|
+
const [only, ...rest] = agents;
|
|
422
|
+
if (!only) throw new Error("No agents on this account.");
|
|
423
|
+
if (rest.length === 0) return only;
|
|
424
|
+
throw new Error(`Multiple agents on this account — pass --agent <id|slug|name>. Candidates:\n${formatCandidates(agents)}`);
|
|
425
|
+
}
|
|
426
|
+
const byId = agents.find((a) => a.id === selector);
|
|
427
|
+
if (byId) return byId;
|
|
428
|
+
const needle = selector.toLowerCase();
|
|
429
|
+
const matches = agents.filter((a) => a.slug && a.slug.toLowerCase() === needle || a.name.toLowerCase() === needle);
|
|
430
|
+
const [firstMatch, ...restMatches] = matches;
|
|
431
|
+
if (firstMatch && restMatches.length === 0) return firstMatch;
|
|
432
|
+
if (restMatches.length > 0) throw new Error(`Multiple agents match "${selector}" — pass the id instead. Candidates:\n${formatCandidates(matches)}`);
|
|
433
|
+
throw new Error(`No agent matches "${selector}". Candidates:\n${formatCandidates(agents)}`);
|
|
434
|
+
}
|
|
435
|
+
function formatCandidates(agents) {
|
|
436
|
+
return agents.slice(0, 25).map((a) => ` ${a.id} ${a.slug ?? a.name}`).join("\n");
|
|
437
|
+
}
|
|
438
|
+
/** Read all of stdin as UTF-8. Used when `-p` is passed with no value. */
|
|
439
|
+
async function readStdin() {
|
|
440
|
+
const chunks = [];
|
|
441
|
+
for await (const chunk of process.stdin) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
442
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
//#endregion
|
|
446
|
+
export { createRestClient as i, resolveAgent as n, HttpError as r, print_exports as t };
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "skydive-cli",
|
|
3
|
+
"version": "0.1.0-beta.45",
|
|
4
|
+
"description": "Skydive CLI — manage AI agents from the command line",
|
|
5
|
+
"homepage": "https://skydive.com",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"bin": {
|
|
8
|
+
"skydive": "./dist/js/bin.mjs"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"type": "module",
|
|
14
|
+
"exports": {
|
|
15
|
+
"./bin": "./dist/js/bin.mjs"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public",
|
|
19
|
+
"registry": "https://registry.npmjs.org"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsdown --no-dts --out-dir dist/js",
|
|
23
|
+
"test:unit": "vitest run --passWithNoTests && yarn test:tui",
|
|
24
|
+
"test:ci": "vitest run --coverage --coverage.reporter=lcovonly --reporter=default --reporter=github-actions --minWorkers=1 --maxWorkers=2 --passWithNoTests && yarn test:tui",
|
|
25
|
+
"test:tui": "bun test .tui.test",
|
|
26
|
+
"render:frames": "bun scripts/render-frames.tsx",
|
|
27
|
+
"typecheck": "tsgo --noEmit"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@opentui/react": "0.4.3",
|
|
31
|
+
"conf": "^13.0.1",
|
|
32
|
+
"diff": "9.0.0",
|
|
33
|
+
"eventsource-parser": "^3.0.8",
|
|
34
|
+
"file-type": "^21.3.4",
|
|
35
|
+
"neverthrow": "^8.2.0",
|
|
36
|
+
"open": "^10.1.0",
|
|
37
|
+
"react": "^19.0.0",
|
|
38
|
+
"react-devtools-core": "^7.0.1",
|
|
39
|
+
"web-tree-sitter": "0.25.10",
|
|
40
|
+
"ws": "^8.18.0",
|
|
41
|
+
"yargs": "^17.7.2",
|
|
42
|
+
"zod": "^3.24.1",
|
|
43
|
+
"zustand": "^5.0.2"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@createinc/anyone-portal-protocol": "0.0.0",
|
|
47
|
+
"@createinc/tsconfig": "0.0.0",
|
|
48
|
+
"@types/bun": "^1.3.14",
|
|
49
|
+
"@types/node": "^24.0.0",
|
|
50
|
+
"@types/react": "^19.0.0",
|
|
51
|
+
"@types/yargs": "^17.0.33",
|
|
52
|
+
"@typescript/native-preview": "^7.0.0-dev.20260113.1",
|
|
53
|
+
"@vitest/coverage-v8": "^2.1.8",
|
|
54
|
+
"tsdown": "^0.20.3",
|
|
55
|
+
"typescript": "^5.9.3",
|
|
56
|
+
"vitest": "^2.1.8"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"bun": ">=1.3.14",
|
|
60
|
+
"node": ">=20.0.0"
|
|
61
|
+
}
|
|
62
|
+
}
|