skydive-cli 0.5.0-beta.9 → 0.6.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.
- package/CHANGELOG.md +13 -0
- package/README.md +61 -13
- package/dist/js/api-BFQ4PQDA.mjs +315 -0
- package/dist/js/{billing-blocked-Dgu5-oDy.mjs → billing-blocked-SE6tySLd.mjs} +1 -1
- package/dist/js/bin.mjs +674 -307
- package/dist/js/{boot-Q-Kh3nn5.mjs → boot-BEvwMeO6.mjs} +4708 -1005
- package/dist/js/chunk-BbwQpWto.mjs +33 -0
- package/dist/js/{client-DabRpc_T.mjs → client-B-5eaVyt.mjs} +437 -39
- package/dist/js/{client-c4c5MmgN.mjs → client-Btq6bMzX.mjs} +108 -2
- package/dist/js/client-DebOAhwd.mjs +5 -0
- package/dist/js/{daemon-D21wQ7DI.mjs → daemon-CLc3zjcv.mjs} +238 -50
- package/dist/js/daemon-ClXSvjcv.mjs +7 -0
- package/dist/js/{daemon-client-DPUNjhBB.mjs → daemon-client-DSIb7j28.mjs} +1 -1
- package/dist/js/daemon-client-pYWyRTyi.mjs +8 -0
- package/dist/js/dist-CRtjM7ba.mjs +1750 -0
- package/dist/js/forward-C-f04uyE.mjs +208 -0
- package/dist/js/{profiler-BkCV__ao.mjs → install-Bg_9I2-2.mjs} +545 -215
- package/dist/js/launcher.mjs +49 -0
- package/dist/js/localhost-cert-Bn-UBUmj.mjs +67 -0
- package/dist/js/{print-BpuyEfWX.mjs → print-CPBx8690.mjs} +257 -35
- package/dist/js/{print-Wakr3GJd.mjs → print-CT1G1LeA.mjs} +3 -3
- package/dist/js/{print-share-CKLPmsg0.mjs → print-share-DW5pHVDz.mjs} +9 -3
- package/dist/js/raw-pty-Ci2qFR9F.mjs +5 -0
- package/dist/js/{raw-pty-DY4KelZW.mjs → raw-pty-D5PhKZSl.mjs} +1 -1
- package/dist/js/{rest-I3imNduB.mjs → rest-B2bynGwY.mjs} +153 -19
- package/dist/js/rest-DejyWmRu.mjs +6 -0
- package/dist/js/tls-cert-BpCaD5AT.mjs +4 -0
- package/dist/js/tls-cert-Rua2oV7n.mjs +67 -0
- package/package.json +15 -6
- package/dist/js/api-DG5W6iwx.mjs +0 -131
- package/dist/js/client-BuU34IVE.mjs +0 -5
- package/dist/js/daemon-LSDSvMaC.mjs +0 -6
- package/dist/js/daemon-client-CUSq-Wuh.mjs +0 -7
- package/dist/js/forward-18QoL5dO.mjs +0 -68
- package/dist/js/raw-pty-DmdUf4_w.mjs +0 -5
- package/dist/js/rest-D29qNkto.mjs +0 -6
- /package/dist/js/{billing-blocked-2wju4gC_.mjs → billing-blocked-D3l5kJlX.mjs} +0 -0
- /package/dist/js/{http-error-DzyrsLAZ.mjs → http-error-BF2NZZE3.mjs} +0 -0
- /package/dist/js/{output-DYzzdXYV.mjs → output-C9mb3sUB.mjs} +0 -0
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as HttpError } from "./http-error-
|
|
3
|
-
import { a as billingBlockedOutcomeSchema } from "./billing-blocked-
|
|
2
|
+
import { t as HttpError } from "./http-error-BF2NZZE3.mjs";
|
|
3
|
+
import { a as billingBlockedOutcomeSchema } from "./billing-blocked-D3l5kJlX.mjs";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { createParser } from "eventsource-parser";
|
|
6
6
|
|
|
7
|
+
//#region package.json
|
|
8
|
+
var name = "skydive-cli";
|
|
9
|
+
var version = "0.6.0-beta.10";
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
7
12
|
//#region src/chat/util.ts
|
|
8
13
|
/** Narrowing helper for the many `unknown` payloads the chat stream and
|
|
9
14
|
* tool inputs/outputs carry. A type predicate (not an `as` cast), so call
|
|
@@ -18,8 +23,24 @@ function errorMessage(err) {
|
|
|
18
23
|
|
|
19
24
|
//#endregion
|
|
20
25
|
//#region src/chat/api/rest.ts
|
|
26
|
+
const CLI_VERSION_HEADER = "x-skydive-cli-version";
|
|
21
27
|
const ERROR_DETAIL_MAX_BODY = 2e3;
|
|
22
28
|
/**
|
|
29
|
+
* Pull the filename out of a `Content-Disposition` header. Handles the
|
|
30
|
+
* RFC 5987 `filename*=UTF-8''...` form (percent-decoded) and the plain
|
|
31
|
+
* quoted/bare `filename=` form, preferring the extended form when both are
|
|
32
|
+
* present (the API emits both for non-ASCII names). Returns '' when the header
|
|
33
|
+
* is absent or carries no filename, so callers fall back to a cached name.
|
|
34
|
+
*/
|
|
35
|
+
function filenameFromContentDisposition(header) {
|
|
36
|
+
if (!header) return "";
|
|
37
|
+
const extended = /filename\*=(?:UTF-8'')?([^;]+)/i.exec(header);
|
|
38
|
+
if (extended?.[1]) try {
|
|
39
|
+
return decodeURIComponent(extended[1].trim().replace(/^"|"$/g, ""));
|
|
40
|
+
} catch (_error) {}
|
|
41
|
+
return /filename="?([^";]+)"?/i.exec(header)?.[1]?.trim() ?? "";
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
23
44
|
* Fullest renderable text for a thrown value. `HttpError.message` clips the
|
|
24
45
|
* response body to 200 chars (it flows into logs and one-line UIs); the
|
|
25
46
|
* transcript renders errors collapsed to a single line, so it can afford the
|
|
@@ -104,7 +125,8 @@ const MAX_STREAM_RECONNECTS = 5;
|
|
|
104
125
|
function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
105
126
|
const baseHeaders = {
|
|
106
127
|
authorization: `Bearer ${sessionToken}`,
|
|
107
|
-
accept: "application/json"
|
|
128
|
+
accept: "application/json",
|
|
129
|
+
[CLI_VERSION_HEADER]: version
|
|
108
130
|
};
|
|
109
131
|
if (workspaceId) baseHeaders["x-workspace-id"] = workspaceId;
|
|
110
132
|
async function get(path, schema) {
|
|
@@ -124,6 +146,13 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
124
146
|
if (!res.ok) throw new HttpError(res.status, await res.text().catch(() => ""));
|
|
125
147
|
return schema.parse(await res.json());
|
|
126
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* The agent PATCH is a merge: only the keys present are written, so each
|
|
151
|
+
* caller sends exactly the settings it means to change.
|
|
152
|
+
*/
|
|
153
|
+
function patchAgent(agentId, body) {
|
|
154
|
+
return post(`/api/v1/agents/${encodeURIComponent(agentId)}`, body, updateAgentResponseSchema, "PATCH");
|
|
155
|
+
}
|
|
127
156
|
async function del(path) {
|
|
128
157
|
const res = await fetch(`${appUrl}${path}`, {
|
|
129
158
|
method: "DELETE",
|
|
@@ -140,7 +169,8 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
140
169
|
try {
|
|
141
170
|
const headers = {
|
|
142
171
|
authorization: `Bearer ${sessionToken}`,
|
|
143
|
-
accept: "text/event-stream"
|
|
172
|
+
accept: "text/event-stream",
|
|
173
|
+
[CLI_VERSION_HEADER]: version
|
|
144
174
|
};
|
|
145
175
|
if (workspaceId) headers["x-workspace-id"] = workspaceId;
|
|
146
176
|
if (lastEventId) headers["last-event-id"] = lastEventId;
|
|
@@ -214,9 +244,19 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
214
244
|
return all;
|
|
215
245
|
},
|
|
216
246
|
createAgent: async ({ name }) => {
|
|
217
|
-
const { agent } = await post("/api/v1/agents", { name }, createAgentResponseSchema);
|
|
247
|
+
const { agent } = await post("/api/v1/agents", name == null ? {} : { name }, createAgentResponseSchema);
|
|
218
248
|
return agent;
|
|
219
249
|
},
|
|
250
|
+
createOnboardingConversation: async ({ agentId, projectDir, os }) => {
|
|
251
|
+
const result = await post(`/api/v1/agents/${encodeURIComponent(agentId)}/onboarding-conversation`, {
|
|
252
|
+
projectDir,
|
|
253
|
+
os
|
|
254
|
+
}, onboardingConversationResponseSchema);
|
|
255
|
+
return {
|
|
256
|
+
conversationId: result.conversationId,
|
|
257
|
+
runId: result.runId
|
|
258
|
+
};
|
|
259
|
+
},
|
|
220
260
|
getAgent: async ({ agentId }) => {
|
|
221
261
|
const { agent } = await get(`/api/v1/agents/${encodeURIComponent(agentId)}`, getAgentResponseSchema);
|
|
222
262
|
return agent;
|
|
@@ -233,9 +273,36 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
233
273
|
const { models } = await get("/api/v1/models", listModelsResponseSchema);
|
|
234
274
|
return models;
|
|
235
275
|
},
|
|
236
|
-
|
|
237
|
-
const {
|
|
238
|
-
|
|
276
|
+
listModelEfforts: async () => {
|
|
277
|
+
const { modelConfigOverrides } = await get("/api/v1/models/preferred", modelEffortPreferencesResponseSchema);
|
|
278
|
+
const efforts = {};
|
|
279
|
+
for (const [modelId, config] of Object.entries(modelConfigOverrides ?? {})) if (config.thinkingLevel) efforts[modelId] = config.thinkingLevel;
|
|
280
|
+
return efforts;
|
|
281
|
+
},
|
|
282
|
+
setModelEffort: async ({ modelId, thinkingLevel }) => {
|
|
283
|
+
await post("/api/v1/models/config", {
|
|
284
|
+
modelId,
|
|
285
|
+
config: { thinkingLevel }
|
|
286
|
+
}, z.unknown(), "PATCH");
|
|
287
|
+
},
|
|
288
|
+
updateAgentModel: async ({ agentId, model, thinkingLevel }) => {
|
|
289
|
+
const { agent } = await patchAgent(agentId, {
|
|
290
|
+
model,
|
|
291
|
+
thinkingLevel
|
|
292
|
+
});
|
|
293
|
+
return {
|
|
294
|
+
model: agent.model ?? null,
|
|
295
|
+
thinkingLevel: agent.thinkingLevel ?? null
|
|
296
|
+
};
|
|
297
|
+
},
|
|
298
|
+
setConversationModel: async ({ conversationId, agentId, model, thinkingLevel }) => post(`/api/v1/conversations/${conversationId}/model`, {
|
|
299
|
+
agentId,
|
|
300
|
+
model,
|
|
301
|
+
thinkingLevel
|
|
302
|
+
}, conversationModelResponseSchema),
|
|
303
|
+
updateAgentEffort: async ({ agentId, thinkingLevel }) => {
|
|
304
|
+
const { agent } = await patchAgent(agentId, { thinkingLevel });
|
|
305
|
+
return { thinkingLevel: agent.thinkingLevel ?? null };
|
|
239
306
|
},
|
|
240
307
|
listConversations: async ({ agentId, limit, channels, archived, query, onPage }) => {
|
|
241
308
|
const trimmedQuery = query?.trim();
|
|
@@ -260,9 +327,17 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
260
327
|
} while (cursor && all.length < maxConversations);
|
|
261
328
|
return limit ? all.slice(0, limit) : all;
|
|
262
329
|
},
|
|
263
|
-
listMessages: async ({ conversationId }) => {
|
|
264
|
-
const
|
|
265
|
-
|
|
330
|
+
listMessages: async ({ conversationId, limit, before }) => {
|
|
331
|
+
const params = new URLSearchParams();
|
|
332
|
+
if (limit !== void 0) params.set("limit", String(limit));
|
|
333
|
+
if (before !== void 0) params.set("before", before);
|
|
334
|
+
const query = params.toString();
|
|
335
|
+
const { messages, hasMore, nextBefore } = await get(`/api/v1/conversations/${encodeURIComponent(conversationId)}/messages${query ? `?${query}` : ""}`, listMessagesResponseSchema);
|
|
336
|
+
return {
|
|
337
|
+
messages,
|
|
338
|
+
hasMore: hasMore ?? false,
|
|
339
|
+
nextBefore: nextBefore ?? null
|
|
340
|
+
};
|
|
266
341
|
},
|
|
267
342
|
listWorkspaceFiles: async ({ agentId }) => {
|
|
268
343
|
const { files } = await get(`/api/v1/workspace-files?${new URLSearchParams({ agentId }).toString()}`, listWorkspaceFilesResponseSchema);
|
|
@@ -289,6 +364,13 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
289
364
|
const { result } = await get(`/api/v1/trpc/v2.subagentTasks.list?input=${encodeURIComponent(JSON.stringify({ conversationId }))}`, subagentTasksResponseSchema);
|
|
290
365
|
return new Map(result.data.subagentTasks.map((task) => [task.id, task]));
|
|
291
366
|
},
|
|
367
|
+
computeLiveStats: async (agentId) => {
|
|
368
|
+
const { stats } = await get(`/api/v1/compute/live-stats?agentId=${encodeURIComponent(agentId)}`, liveStatsResponseSchema);
|
|
369
|
+
return stats;
|
|
370
|
+
},
|
|
371
|
+
restartAgent: async (agentId) => {
|
|
372
|
+
await post(`/api/v1/agents/${encodeURIComponent(agentId)}/restart`, {}, z.object({ ok: z.boolean() }));
|
|
373
|
+
},
|
|
292
374
|
uploadAttachment: async ({ agentId, fileName, mediaType, data }) => {
|
|
293
375
|
const size = data.byteLength;
|
|
294
376
|
const presign = await post("/api/v1/attachments/presign", {
|
|
@@ -316,6 +398,17 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
316
398
|
sizeBytes: finalized.sizeBytes ?? size
|
|
317
399
|
};
|
|
318
400
|
},
|
|
401
|
+
downloadAttachment: async ({ attachmentId }) => {
|
|
402
|
+
const res = await fetch(`${appUrl}/api/v1/attachments/${encodeURIComponent(attachmentId)}/download?disposition=inline`, { headers: baseHeaders });
|
|
403
|
+
if (!res.ok) throw new HttpError(res.status, await res.text().catch(() => ""));
|
|
404
|
+
const data = new Uint8Array(await res.arrayBuffer());
|
|
405
|
+
const mediaType = res.headers.get("content-type") ?? "application/octet-stream";
|
|
406
|
+
return {
|
|
407
|
+
data,
|
|
408
|
+
fileName: filenameFromContentDisposition(res.headers.get("content-disposition")),
|
|
409
|
+
mediaType
|
|
410
|
+
};
|
|
411
|
+
},
|
|
319
412
|
forkConversation: async ({ conversationId }) => {
|
|
320
413
|
const { conversation } = await post(`/api/v1/conversations/${encodeURIComponent(conversationId)}/fork`, {}, forkConversationResponseSchema);
|
|
321
414
|
return conversation;
|
|
@@ -329,6 +422,9 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
329
422
|
setConversationArchived: async ({ conversationId, archived }) => {
|
|
330
423
|
await post(`/api/v1/conversations/${encodeURIComponent(conversationId)}/archive`, { archived }, z.object({ archived: z.boolean() }));
|
|
331
424
|
},
|
|
425
|
+
markConversationRead: async ({ conversationId }) => {
|
|
426
|
+
await post(`/api/v1/conversations/${encodeURIComponent(conversationId)}/read`, {}, z.object({ read: z.boolean() }));
|
|
427
|
+
},
|
|
332
428
|
renameConversation: async ({ conversationId, title }) => {
|
|
333
429
|
await post(`/api/v1/conversations/${encodeURIComponent(conversationId)}`, { title }, z.object({ conversation: z.object({ id: z.string() }) }), "PATCH");
|
|
334
430
|
},
|
|
@@ -350,6 +446,7 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
350
446
|
const { authorizationUrl } = await post("/api/v1/external-oauth/connect", input, externalOauthConnectResponseSchema);
|
|
351
447
|
return { authorizationUrl: authorizationUrl ?? null };
|
|
352
448
|
},
|
|
449
|
+
decideComputeRequest: async ({ requestId, decision }) => post(`/api/v1/compute-requests/${encodeURIComponent(requestId)}/decision`, { decision }, computeDecisionResponseSchema),
|
|
353
450
|
fulfillCredential: async ({ url, body }) => {
|
|
354
451
|
const target = new URL(url, appUrl).toString();
|
|
355
452
|
const res = await fetch(target, {
|
|
@@ -383,6 +480,7 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
383
480
|
headers: {
|
|
384
481
|
authorization: `Bearer ${sessionToken}`,
|
|
385
482
|
accept: "text/event-stream",
|
|
483
|
+
[CLI_VERSION_HEADER]: version,
|
|
386
484
|
...workspaceId ? { "x-workspace-id": workspaceId } : {}
|
|
387
485
|
},
|
|
388
486
|
signal
|
|
@@ -434,23 +532,38 @@ const agentSummarySchema = z.object({
|
|
|
434
532
|
createdAt: z.string(),
|
|
435
533
|
creatorName: z.string().nullable().optional(),
|
|
436
534
|
model: z.string().nullable().optional(),
|
|
437
|
-
modelLocked: z.boolean().optional()
|
|
535
|
+
modelLocked: z.boolean().optional(),
|
|
536
|
+
thinkingLevel: z.string().nullable().optional()
|
|
438
537
|
});
|
|
439
538
|
const platformModelSchema = z.object({
|
|
440
539
|
id: z.string(),
|
|
441
540
|
displayName: z.string(),
|
|
442
541
|
providerDisplay: z.string().optional(),
|
|
443
542
|
reasoning: z.boolean().optional(),
|
|
444
|
-
compliant: z.boolean().optional()
|
|
543
|
+
compliant: z.boolean().optional(),
|
|
544
|
+
thinkingLevels: z.array(z.string()).optional(),
|
|
545
|
+
thinkingLevelLabels: z.record(z.string(), z.string()).optional()
|
|
445
546
|
}).passthrough();
|
|
446
547
|
const listModelsResponseSchema = z.object({ models: z.array(platformModelSchema) });
|
|
447
|
-
const
|
|
548
|
+
const modelEffortPreferencesResponseSchema = z.object({ modelConfigOverrides: z.record(z.string(), z.object({ thinkingLevel: z.string().optional() })).optional() });
|
|
549
|
+
const conversationModelResponseSchema = z.object({
|
|
550
|
+
model: z.string().nullable(),
|
|
551
|
+
thinkingLevel: z.string().nullable()
|
|
552
|
+
});
|
|
553
|
+
const updateAgentResponseSchema = z.object({ agent: z.object({
|
|
554
|
+
model: z.string().nullable().optional(),
|
|
555
|
+
thinkingLevel: z.string().nullable().optional()
|
|
556
|
+
}).passthrough() });
|
|
448
557
|
const listAgentsResponseSchema = z.object({
|
|
449
558
|
agents: z.array(agentSummarySchema),
|
|
450
559
|
nextCursor: z.string().nullable().optional(),
|
|
451
560
|
totalCount: z.number().nullable().optional()
|
|
452
561
|
});
|
|
453
562
|
const createAgentResponseSchema = z.object({ agent: agentSummarySchema });
|
|
563
|
+
const onboardingConversationResponseSchema = z.object({
|
|
564
|
+
conversationId: z.string(),
|
|
565
|
+
runId: z.string().nullable()
|
|
566
|
+
});
|
|
454
567
|
const getAgentResponseSchema = z.object({ agent: agentSummarySchema });
|
|
455
568
|
const agentSuggestionSchema = z.object({ name: z.string() });
|
|
456
569
|
const suggestAgentResponseSchema = z.object({ suggestion: agentSuggestionSchema.nullable() });
|
|
@@ -469,6 +582,7 @@ const conversationSummarySchema = z.object({
|
|
|
469
582
|
channel: z.string().nullable(),
|
|
470
583
|
channelLabel: z.string().nullable(),
|
|
471
584
|
viewerArchivedAt: z.string().nullable().optional(),
|
|
585
|
+
unread: z.boolean().optional(),
|
|
472
586
|
agent: conversationAgentSchema,
|
|
473
587
|
agents: z.array(conversationAgentSchema).optional()
|
|
474
588
|
});
|
|
@@ -479,7 +593,8 @@ const conversationDetailSchema = z.object({
|
|
|
479
593
|
createdAt: z.string(),
|
|
480
594
|
updatedAt: z.string(),
|
|
481
595
|
channel: z.string().nullable(),
|
|
482
|
-
channelLabel: z.string().nullable()
|
|
596
|
+
channelLabel: z.string().nullable(),
|
|
597
|
+
parentConversationId: z.string().uuid().nullable().optional()
|
|
483
598
|
});
|
|
484
599
|
const conversationTitleSchema = z.object({
|
|
485
600
|
id: z.string().uuid(),
|
|
@@ -527,10 +642,27 @@ const subagentTaskSnapshotSchema = z.object({
|
|
|
527
642
|
status: z.string(),
|
|
528
643
|
title: z.string().nullable(),
|
|
529
644
|
createdAt: z.string(),
|
|
530
|
-
completedAt: z.string().nullable()
|
|
645
|
+
completedAt: z.string().nullable(),
|
|
646
|
+
childConversationId: z.string().nullish(),
|
|
647
|
+
childRunStartedAt: z.string().nullish(),
|
|
648
|
+
contextTokens: z.number().nullish(),
|
|
649
|
+
latestActivity: z.string().nullish()
|
|
531
650
|
});
|
|
532
651
|
const subagentTasksResponseSchema = z.object({ result: z.object({ data: z.object({ subagentTasks: z.array(subagentTaskSnapshotSchema) }) }) });
|
|
533
|
-
const
|
|
652
|
+
const liveStatsResponseSchema = z.object({ stats: z.object({
|
|
653
|
+
sampledAt: z.number(),
|
|
654
|
+
cpuUsedPct: z.number(),
|
|
655
|
+
cpuCount: z.number(),
|
|
656
|
+
memUsedBytes: z.number(),
|
|
657
|
+
memTotalBytes: z.number(),
|
|
658
|
+
diskUsedBytes: z.number(),
|
|
659
|
+
diskTotalBytes: z.number()
|
|
660
|
+
}).nullable() });
|
|
661
|
+
const listMessagesResponseSchema = z.object({
|
|
662
|
+
messages: z.array(uiMessageSchema),
|
|
663
|
+
hasMore: z.boolean().optional(),
|
|
664
|
+
nextBefore: z.string().nullish()
|
|
665
|
+
});
|
|
534
666
|
const workspaceFileSchema = z.object({
|
|
535
667
|
id: z.string(),
|
|
536
668
|
path: z.string(),
|
|
@@ -564,6 +696,7 @@ const finalizeResponseSchema = z.object({
|
|
|
564
696
|
});
|
|
565
697
|
const oauthConnectResponseSchema = z.object({ connectLink: z.string() }).passthrough();
|
|
566
698
|
const externalOauthConnectResponseSchema = z.object({ authorizationUrl: z.string().optional() }).passthrough();
|
|
699
|
+
const computeDecisionResponseSchema = z.object({ status: z.string() }).passthrough();
|
|
567
700
|
const runStreamEventSchema = z.union([z.object({
|
|
568
701
|
kind: z.literal("chunk"),
|
|
569
702
|
chunk: z.record(z.unknown())
|
|
@@ -584,7 +717,8 @@ const conversationStreamEventSchema = z.discriminatedUnion("kind", [
|
|
|
584
717
|
kind: z.literal("agent"),
|
|
585
718
|
id: z.string(),
|
|
586
719
|
model: z.string().nullable(),
|
|
587
|
-
modelLocked: z.boolean()
|
|
720
|
+
modelLocked: z.boolean(),
|
|
721
|
+
thinkingLevel: z.string().nullish()
|
|
588
722
|
}),
|
|
589
723
|
z.object({
|
|
590
724
|
kind: z.literal("run"),
|
|
@@ -602,4 +736,4 @@ const conversationStreamEventSchema = z.discriminatedUnion("kind", [
|
|
|
602
736
|
]);
|
|
603
737
|
|
|
604
738
|
//#endregion
|
|
605
|
-
export {
|
|
739
|
+
export { errorMessage as a, version as c, sendErrorMessage as i, errorDetail as n, isRecord as o, filenameFromContentDisposition as r, name as s, createRestClient as t };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { i as sendErrorMessage, n as errorDetail, r as filenameFromContentDisposition, t as createRestClient } from "./rest-B2bynGwY.mjs";
|
|
3
|
+
import { t as HttpError } from "./http-error-BF2NZZE3.mjs";
|
|
4
|
+
import "./billing-blocked-D3l5kJlX.mjs";
|
|
5
|
+
|
|
6
|
+
export { createRestClient };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import dns from "node:dns";
|
|
4
|
+
|
|
5
|
+
//#region ../portal-daemon/src/tls-cert.ts
|
|
6
|
+
/**
|
|
7
|
+
* File-backed cert source, configured by environment:
|
|
8
|
+
* SKYDIVE_PORTAL_TLS_KEY / SKYDIVE_PORTAL_TLS_CERT (PEM file paths) and
|
|
9
|
+
* SKYDIVE_PORTAL_TLS_HOST (the name the cert covers). All three or nothing —
|
|
10
|
+
* a partial configuration is reported once and treated as absent, so a typo
|
|
11
|
+
* degrades to today's behavior instead of a crash loop.
|
|
12
|
+
*/
|
|
13
|
+
function envTlsCertSource(env, log) {
|
|
14
|
+
return async () => {
|
|
15
|
+
const keyPath = env.SKYDIVE_PORTAL_TLS_KEY;
|
|
16
|
+
const certPath = env.SKYDIVE_PORTAL_TLS_CERT;
|
|
17
|
+
const hostname = env.SKYDIVE_PORTAL_TLS_HOST;
|
|
18
|
+
if (!keyPath && !certPath && !hostname) return null;
|
|
19
|
+
if (!keyPath || !certPath || !hostname) {
|
|
20
|
+
log("forward: SKYDIVE_PORTAL_TLS_KEY, SKYDIVE_PORTAL_TLS_CERT and SKYDIVE_PORTAL_TLS_HOST must all be set to enable the TLS origin; continuing with http only");
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const [key, cert] = await Promise.all([readFile(keyPath, "utf8"), readFile(certPath, "utf8")]);
|
|
25
|
+
return {
|
|
26
|
+
key,
|
|
27
|
+
cert,
|
|
28
|
+
hostname
|
|
29
|
+
};
|
|
30
|
+
} catch (err) {
|
|
31
|
+
log(`forward: could not read TLS key/cert (${err instanceof Error ? err.message : String(err)}); continuing with http only`);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The default cert source for both tunnel surfaces: an explicit env/file
|
|
38
|
+
* configuration wins (BYO cert — also the escape hatch for machines where the
|
|
39
|
+
* trust prompt is unwanted, e.g. CI), otherwise the fully automatic
|
|
40
|
+
* devcert-backed localhost source.
|
|
41
|
+
*/
|
|
42
|
+
function defaultTlsCertSource(env, log) {
|
|
43
|
+
const fromEnv = envTlsCertSource(env, log);
|
|
44
|
+
return async () => {
|
|
45
|
+
if (env.SKYDIVE_PORTAL_TLS_KEY || env.SKYDIVE_PORTAL_TLS_CERT || env.SKYDIVE_PORTAL_TLS_HOST) return fromEnv();
|
|
46
|
+
const { localhostCertSource } = await import("./localhost-cert-Bn-UBUmj.mjs");
|
|
47
|
+
return localhostCertSource(log)();
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Detect resolver-level DNS rebind protection: some routers/resolvers refuse
|
|
52
|
+
* public names that answer with loopback addresses (Plex documents this exact
|
|
53
|
+
* failure for *.plex.direct). The https origin simply won't resolve for such
|
|
54
|
+
* users — warn with the cause, and leave the http origin (unaffected) as the
|
|
55
|
+
* fallback. Never fatal: the browser may use a different resolver than the
|
|
56
|
+
* OS, so we start the listener regardless.
|
|
57
|
+
*/
|
|
58
|
+
async function warnOnRebindProtection(hostname, log) {
|
|
59
|
+
try {
|
|
60
|
+
if (!(await dns.promises.lookup(hostname, { all: true })).some((a) => a.address === "127.0.0.1" || a.address === "::1")) log(`forward: ${hostname} did not resolve to 127.0.0.1 — a resolver on this network may block public names pointing at loopback (DNS rebind protection). The https origin may not work here; http://localhost is unaffected.`);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
log(`forward: ${hostname} did not resolve (${error instanceof Error ? error.message : String(error)}) — a resolver on this network may block public names pointing at loopback (DNS rebind protection). The https origin may not work here; http://localhost is unaffected.`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
export { envTlsCertSource as n, warnOnRebindProtection as r, defaultTlsCertSource as t };
|
package/package.json
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skydive-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0-beta.10",
|
|
4
4
|
"description": "Skydive CLI — cloud agents from the command line",
|
|
5
5
|
"homepage": "https://skydive.com",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
8
|
-
"skydive": "./dist/js/
|
|
8
|
+
"skydive": "./dist/js/launcher.mjs"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist"
|
|
12
12
|
],
|
|
13
13
|
"type": "module",
|
|
14
14
|
"exports": {
|
|
15
|
-
"./bin": "./dist/js/bin.mjs"
|
|
15
|
+
"./bin": "./dist/js/bin.mjs",
|
|
16
|
+
"./launcher": "./dist/js/launcher.mjs"
|
|
16
17
|
},
|
|
17
18
|
"publishConfig": {
|
|
18
19
|
"access": "public",
|
|
@@ -24,12 +25,13 @@
|
|
|
24
25
|
"test:unit": "vitest run --passWithNoTests && yarn test:tui",
|
|
25
26
|
"test:ci": "vitest run --coverage --coverage.reporter=lcovonly --reporter=default --reporter=github-actions --minWorkers=1 --maxWorkers=2 --passWithNoTests && yarn test:tui",
|
|
26
27
|
"test:tui": "node scripts/run-tui-tests.mjs",
|
|
28
|
+
"test:blackbox": "bun test test/blackbox --timeout 120000",
|
|
27
29
|
"render:frames": "bun scripts/render-frames.tsx",
|
|
28
30
|
"render:send-errors": "bun scripts/render-send-errors.tsx",
|
|
29
31
|
"typecheck": "tsgo --noEmit"
|
|
30
32
|
},
|
|
31
33
|
"dependencies": {
|
|
32
|
-
"@opentui/react": "0.
|
|
34
|
+
"@opentui/react": "0.5.8",
|
|
33
35
|
"conf": "^13.0.1",
|
|
34
36
|
"diff": "9.0.0",
|
|
35
37
|
"eventsource-parser": "^3.0.8",
|
|
@@ -37,11 +39,11 @@
|
|
|
37
39
|
"fuzzysort": "^3.1.0",
|
|
38
40
|
"neverthrow": "^8.2.0",
|
|
39
41
|
"open": "^10.1.0",
|
|
40
|
-
"react": "^19.
|
|
42
|
+
"react": "^19.2.0",
|
|
41
43
|
"react-devtools-core": "^7.0.1",
|
|
42
44
|
"safe-stable-stringify": "^2.3.1",
|
|
43
45
|
"semver": "^7.7.4",
|
|
44
|
-
"
|
|
46
|
+
"tar": "7.5.21",
|
|
45
47
|
"web-tree-sitter": "0.25.10",
|
|
46
48
|
"ws": "^8.21.0",
|
|
47
49
|
"yargs": "^17.7.2",
|
|
@@ -66,5 +68,12 @@
|
|
|
66
68
|
"engines": {
|
|
67
69
|
"bun": ">=1.3.14",
|
|
68
70
|
"node": ">=20.0.0"
|
|
71
|
+
},
|
|
72
|
+
"//optionalDependencies": "The per-platform binary packages (skydive-cli-<os>-<arch>) are INTENTIONALLY not committed here. They are published by release-skydive-cli-binaries.yml and injected — pinned to the exact version being published — into the published launcher's optionalDependencies at release time (scripts/prepare-platform-packages.mjs --pin-version, run in release-skydive-cli.yml). Committing them would make `yarn install` try to resolve versions that only exist post-publish, breaking local dev and CI. npm installs of a published skydive-cli still get them (os/cpu-gated); the launcher (src/launcher.ts) resolves and execs the matching one. Local dev never needs them: it runs the JS bundle or `build:binary` directly.",
|
|
73
|
+
"optionalDependencies": {
|
|
74
|
+
"skydive-cli-darwin-arm64": "0.6.0-beta.10",
|
|
75
|
+
"skydive-cli-darwin-x64": "0.6.0-beta.10",
|
|
76
|
+
"skydive-cli-linux-x64": "0.6.0-beta.10",
|
|
77
|
+
"skydive-cli-linux-arm64": "0.6.0-beta.10"
|
|
69
78
|
}
|
|
70
79
|
}
|
package/dist/js/api-DG5W6iwx.mjs
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
|
|
5
|
-
//#region ../portal-daemon/src/api.ts
|
|
6
|
-
/**
|
|
7
|
-
* The portal's session-authed REST surface, shared by `PortalClient` (the
|
|
8
|
-
* TUI/`portal open` connection) and the `skydive portal` management
|
|
9
|
-
* commands, so the endpoint contracts and response schemas live in exactly
|
|
10
|
-
* one place.
|
|
11
|
-
*/
|
|
12
|
-
const deviceSchema = z.object({
|
|
13
|
-
id: z.string(),
|
|
14
|
-
machineName: z.string(),
|
|
15
|
-
friendlyName: z.string(),
|
|
16
|
-
connected: z.boolean(),
|
|
17
|
-
lastSeen: z.string().nullable(),
|
|
18
|
-
grantedAgentIds: z.array(z.string())
|
|
19
|
-
});
|
|
20
|
-
const devicesResponseSchema = z.object({
|
|
21
|
-
devices: z.array(deviceSchema),
|
|
22
|
-
agents: z.array(z.object({
|
|
23
|
-
id: z.string(),
|
|
24
|
-
name: z.string()
|
|
25
|
-
}))
|
|
26
|
-
});
|
|
27
|
-
const deviceTokenSchema = z.object({ token: z.string().min(1) });
|
|
28
|
-
async function portalFetch(auth, path, init) {
|
|
29
|
-
const res = await fetch(`${auth.appUrl}${path}`, {
|
|
30
|
-
method: init.method,
|
|
31
|
-
headers: {
|
|
32
|
-
authorization: `Bearer ${auth.sessionToken}`,
|
|
33
|
-
accept: "application/json",
|
|
34
|
-
...init.body ? { "content-type": "application/json" } : {}
|
|
35
|
-
},
|
|
36
|
-
...init.body ? { body: init.body } : {}
|
|
37
|
-
});
|
|
38
|
-
if (!res.ok) {
|
|
39
|
-
const body = await res.text().catch(() => "");
|
|
40
|
-
throw new HttpError(res.status, body);
|
|
41
|
-
}
|
|
42
|
-
return res.json();
|
|
43
|
-
}
|
|
44
|
-
async function fetchPortalDevices(auth) {
|
|
45
|
-
const json = await portalFetch(auth, "/api/v1/portal/devices", { method: "GET" });
|
|
46
|
-
return devicesResponseSchema.parse(json);
|
|
47
|
-
}
|
|
48
|
-
const registerResponseSchema = z.object({ device: z.object({ id: z.string() }) });
|
|
49
|
-
/**
|
|
50
|
-
* Register this machine's device row without connecting. Connecting registers
|
|
51
|
-
* as a side effect; this covers granting an agent on a machine that has never
|
|
52
|
-
* shared yet (the grant references the device row).
|
|
53
|
-
*/
|
|
54
|
-
async function registerPortalDevice(auth, { machineName, friendlyName }) {
|
|
55
|
-
const json = await portalFetch(auth, "/api/v1/portal/devices", {
|
|
56
|
-
method: "POST",
|
|
57
|
-
body: JSON.stringify({
|
|
58
|
-
machineName,
|
|
59
|
-
friendlyName
|
|
60
|
-
})
|
|
61
|
-
});
|
|
62
|
-
return registerResponseSchema.parse(json).device;
|
|
63
|
-
}
|
|
64
|
-
/** Short-lived token the machine presents when dialing the portal WebSocket. */
|
|
65
|
-
async function mintPortalDeviceToken(auth) {
|
|
66
|
-
const json = await portalFetch(auth, "/api/v1/portal/device-token", { method: "POST" });
|
|
67
|
-
return deviceTokenSchema.parse(json).token;
|
|
68
|
-
}
|
|
69
|
-
const forwardTargetSchema = z.object({
|
|
70
|
-
daemonOrigin: z.string().min(1),
|
|
71
|
-
token: z.string().min(1),
|
|
72
|
-
expiresInSeconds: z.number()
|
|
73
|
-
});
|
|
74
|
-
/**
|
|
75
|
-
* Everything `portal forward` needs to dial an agent's sandbox daemon through
|
|
76
|
-
* the agent-webserver edge Worker: the daemon's stable public origin and a
|
|
77
|
-
* daemon auth token (canUse-gated server-side).
|
|
78
|
-
*/
|
|
79
|
-
async function fetchForwardTarget(auth, agentId) {
|
|
80
|
-
const json = await portalFetch(auth, "/api/v1/portal/forward-target", {
|
|
81
|
-
method: "POST",
|
|
82
|
-
body: JSON.stringify({ agentId })
|
|
83
|
-
});
|
|
84
|
-
return forwardTargetSchema.parse(json);
|
|
85
|
-
}
|
|
86
|
-
async function grantPortalAccess(auth, { deviceId, agentId, conversationId }) {
|
|
87
|
-
await portalFetch(auth, `/api/v1/portal/devices/${encodeURIComponent(deviceId)}/grants`, {
|
|
88
|
-
method: "POST",
|
|
89
|
-
body: JSON.stringify({
|
|
90
|
-
agentId,
|
|
91
|
-
conversationId
|
|
92
|
-
})
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
async function revokePortalAccess(auth, { deviceId, agentId }) {
|
|
96
|
-
await portalFetch(auth, `/api/v1/portal/devices/${encodeURIComponent(deviceId)}/grants/${encodeURIComponent(agentId)}`, { method: "DELETE" });
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* The device row for a given machine identity. Matching is by `machineName`
|
|
100
|
-
* equality — the stable handle the machine registers under, not the display
|
|
101
|
-
* label.
|
|
102
|
-
*/
|
|
103
|
-
function findThisDevice(devices, machineName) {
|
|
104
|
-
return devices.find((device) => device.machineName === machineName) ?? null;
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* One-time grant migration onto the merged device. Earlier CLI builds
|
|
108
|
-
* registered a separate `<machineName>-cli` device, so a user's existing
|
|
109
|
-
* approvals hang off that row; the merged device would start with zero grants
|
|
110
|
-
* and every already-authorized agent would ask again. Copy any grant the
|
|
111
|
-
* merged device is missing (the grant endpoint upserts, so re-runs are
|
|
112
|
-
* no-ops). The legacy row is left in place — an old CLI build may still
|
|
113
|
-
* connect under it. Returns how many grants were copied.
|
|
114
|
-
*/
|
|
115
|
-
async function unifyLegacyCliGrants(auth, machineName) {
|
|
116
|
-
const { devices } = await fetchPortalDevices(auth);
|
|
117
|
-
const merged = findThisDevice(devices, machineName);
|
|
118
|
-
const legacy = findThisDevice(devices, `${machineName}-cli`);
|
|
119
|
-
if (!merged || !legacy) return 0;
|
|
120
|
-
const have = new Set(merged.grantedAgentIds);
|
|
121
|
-
const missing = legacy.grantedAgentIds.filter((id) => !have.has(id));
|
|
122
|
-
for (const agentId of missing) await grantPortalAccess(auth, {
|
|
123
|
-
deviceId: merged.id,
|
|
124
|
-
agentId,
|
|
125
|
-
conversationId: null
|
|
126
|
-
});
|
|
127
|
-
return missing.length;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
//#endregion
|
|
131
|
-
export { mintPortalDeviceToken as a, unifyLegacyCliGrants as c, grantPortalAccess as i, fetchPortalDevices as n, registerPortalDevice as o, findThisDevice as r, revokePortalAccess as s, fetchForwardTarget as t };
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import "./client-DabRpc_T.mjs";
|
|
3
|
-
import { a as runPortalDaemon, i as queryDaemonStatus, n as ensureDaemonRunning, o as startPortalDaemon, r as isDaemonListening, s as stopDaemon, t as PortalDaemon } from "./daemon-D21wQ7DI.mjs";
|
|
4
|
-
import "./api-DG5W6iwx.mjs";
|
|
5
|
-
|
|
6
|
-
export { runPortalDaemon };
|