skydive-cli 0.5.0-beta.5 → 0.5.0-beta.52
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 +12 -0
- package/README.md +60 -12
- package/dist/js/api-TwLD7ibI.mjs +315 -0
- package/dist/js/{billing-blocked-Dgu5-oDy.mjs → billing-blocked-SE6tySLd.mjs} +1 -1
- package/dist/js/bin.mjs +658 -279
- package/dist/js/{boot-Brwh0it_.mjs → boot-D_VFIRJj.mjs} +3556 -995
- package/dist/js/chunk-BbwQpWto.mjs +33 -0
- package/dist/js/{client-Cn2af31H.mjs → client-Btq6bMzX.mjs} +108 -2
- package/dist/js/client-CkPQG8M1.mjs +5 -0
- package/dist/js/client-aFwHzCPG.mjs +963 -0
- package/dist/js/daemon-CORAITjj.mjs +7 -0
- package/dist/js/{daemon-client-DcuD4v12.mjs → daemon-client-Cfi66Xy9.mjs} +12 -8
- package/dist/js/daemon-client-DJPW9cRp.mjs +8 -0
- package/dist/js/{daemon-BhArnzeW.mjs → daemon-uzpOdRPL.mjs} +135 -10
- package/dist/js/dist-CRtjM7ba.mjs +1750 -0
- package/dist/js/forward-Ct8Vd2WW.mjs +208 -0
- package/dist/js/{profiler-LLaIFZgn.mjs → install-BBplm-Zp.mjs} +529 -215
- package/dist/js/launcher.mjs +49 -0
- package/dist/js/localhost-cert-Bn-UBUmj.mjs +67 -0
- package/dist/js/{print-ba_0hiV9.mjs → print-Cuao6LN6.mjs} +3 -3
- package/dist/js/{print-CbayCa87.mjs → print-DR6Gas-M.mjs} +291 -39
- package/dist/js/{print-share-uwUG16Ov.mjs → print-share-BypBUrNh.mjs} +9 -3
- package/dist/js/raw-pty-Ci2qFR9F.mjs +5 -0
- package/dist/js/{raw-pty-B6mAroiI.mjs → raw-pty-D5PhKZSl.mjs} +1 -1
- package/dist/js/rest-CJkBP2Jz.mjs +6 -0
- package/dist/js/{rest-BY2nADw5.mjs → rest-DkuT5_oX.mjs} +117 -28
- package/dist/js/tls-cert-CLgSQALB.mjs +4 -0
- package/dist/js/tls-cert-CV-pwxVN.mjs +67 -0
- package/package.json +11 -4
- package/dist/js/client-DZstLQ_1.mjs +0 -4
- package/dist/js/client-Dd5sMXPv.mjs +0 -620
- package/dist/js/daemon-CjzrUvXx.mjs +0 -5
- package/dist/js/daemon-client-D8bNXTxu.mjs +0 -6
- package/dist/js/raw-pty-3EkG-jjH.mjs +0 -5
- package/dist/js/rest-DADJh0bi.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.5.0-beta.52";
|
|
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,6 +23,7 @@ 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
|
/**
|
|
23
29
|
* Fullest renderable text for a thrown value. `HttpError.message` clips the
|
|
@@ -104,7 +110,8 @@ const MAX_STREAM_RECONNECTS = 5;
|
|
|
104
110
|
function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
105
111
|
const baseHeaders = {
|
|
106
112
|
authorization: `Bearer ${sessionToken}`,
|
|
107
|
-
accept: "application/json"
|
|
113
|
+
accept: "application/json",
|
|
114
|
+
[CLI_VERSION_HEADER]: version
|
|
108
115
|
};
|
|
109
116
|
if (workspaceId) baseHeaders["x-workspace-id"] = workspaceId;
|
|
110
117
|
async function get(path, schema) {
|
|
@@ -124,6 +131,13 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
124
131
|
if (!res.ok) throw new HttpError(res.status, await res.text().catch(() => ""));
|
|
125
132
|
return schema.parse(await res.json());
|
|
126
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* The agent PATCH is a merge: only the keys present are written, so each
|
|
136
|
+
* caller sends exactly the settings it means to change.
|
|
137
|
+
*/
|
|
138
|
+
function patchAgent(agentId, body) {
|
|
139
|
+
return post(`/api/v1/agents/${encodeURIComponent(agentId)}`, body, updateAgentResponseSchema, "PATCH");
|
|
140
|
+
}
|
|
127
141
|
async function del(path) {
|
|
128
142
|
const res = await fetch(`${appUrl}${path}`, {
|
|
129
143
|
method: "DELETE",
|
|
@@ -140,7 +154,8 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
140
154
|
try {
|
|
141
155
|
const headers = {
|
|
142
156
|
authorization: `Bearer ${sessionToken}`,
|
|
143
|
-
accept: "text/event-stream"
|
|
157
|
+
accept: "text/event-stream",
|
|
158
|
+
[CLI_VERSION_HEADER]: version
|
|
144
159
|
};
|
|
145
160
|
if (workspaceId) headers["x-workspace-id"] = workspaceId;
|
|
146
161
|
if (lastEventId) headers["last-event-id"] = lastEventId;
|
|
@@ -193,13 +208,14 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
193
208
|
}
|
|
194
209
|
};
|
|
195
210
|
return {
|
|
196
|
-
listAgents: async ({ scope, onPage }) => {
|
|
211
|
+
listAgents: async ({ scope, onPage, limit }) => {
|
|
197
212
|
const all = [];
|
|
198
213
|
let cursor;
|
|
199
|
-
const maxAgents = 2e3;
|
|
214
|
+
const maxAgents = limit ?? 2e3;
|
|
215
|
+
const pageSize = Math.min(100, maxAgents);
|
|
200
216
|
do {
|
|
201
217
|
const params = new URLSearchParams({
|
|
202
|
-
limit:
|
|
218
|
+
limit: String(pageSize),
|
|
203
219
|
scope,
|
|
204
220
|
sort: "mine_first_usage",
|
|
205
221
|
includeStats: "false"
|
|
@@ -213,16 +229,23 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
213
229
|
return all;
|
|
214
230
|
},
|
|
215
231
|
createAgent: async ({ name }) => {
|
|
216
|
-
const { agent } = await post("/api/v1/agents", { name }, createAgentResponseSchema);
|
|
232
|
+
const { agent } = await post("/api/v1/agents", name == null ? {} : { name }, createAgentResponseSchema);
|
|
217
233
|
return agent;
|
|
218
234
|
},
|
|
219
|
-
|
|
220
|
-
const
|
|
235
|
+
createOnboardingConversation: async ({ agentId, projectDir, os }) => {
|
|
236
|
+
const result = await post(`/api/v1/agents/${encodeURIComponent(agentId)}/onboarding-conversation`, {
|
|
237
|
+
projectDir,
|
|
238
|
+
os
|
|
239
|
+
}, onboardingConversationResponseSchema);
|
|
221
240
|
return {
|
|
222
|
-
|
|
223
|
-
|
|
241
|
+
conversationId: result.conversationId,
|
|
242
|
+
runId: result.runId
|
|
224
243
|
};
|
|
225
244
|
},
|
|
245
|
+
getAgent: async ({ agentId }) => {
|
|
246
|
+
const { agent } = await get(`/api/v1/agents/${encodeURIComponent(agentId)}`, getAgentResponseSchema);
|
|
247
|
+
return agent;
|
|
248
|
+
},
|
|
226
249
|
suggestAgentIdentity: async () => {
|
|
227
250
|
const { suggestion } = await get("/api/v1/agents/suggest", suggestAgentResponseSchema);
|
|
228
251
|
return suggestion;
|
|
@@ -235,9 +258,31 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
235
258
|
const { models } = await get("/api/v1/models", listModelsResponseSchema);
|
|
236
259
|
return models;
|
|
237
260
|
},
|
|
238
|
-
|
|
239
|
-
const {
|
|
240
|
-
|
|
261
|
+
listModelEfforts: async () => {
|
|
262
|
+
const { modelConfigOverrides } = await get("/api/v1/models/preferred", modelEffortPreferencesResponseSchema);
|
|
263
|
+
const efforts = {};
|
|
264
|
+
for (const [modelId, config] of Object.entries(modelConfigOverrides ?? {})) if (config.thinkingLevel) efforts[modelId] = config.thinkingLevel;
|
|
265
|
+
return efforts;
|
|
266
|
+
},
|
|
267
|
+
setModelEffort: async ({ modelId, thinkingLevel }) => {
|
|
268
|
+
await post("/api/v1/models/config", {
|
|
269
|
+
modelId,
|
|
270
|
+
config: { thinkingLevel }
|
|
271
|
+
}, z.unknown(), "PATCH");
|
|
272
|
+
},
|
|
273
|
+
updateAgentModel: async ({ agentId, model, thinkingLevel }) => {
|
|
274
|
+
const { agent } = await patchAgent(agentId, {
|
|
275
|
+
model,
|
|
276
|
+
thinkingLevel
|
|
277
|
+
});
|
|
278
|
+
return {
|
|
279
|
+
model: agent.model ?? null,
|
|
280
|
+
thinkingLevel: agent.thinkingLevel ?? null
|
|
281
|
+
};
|
|
282
|
+
},
|
|
283
|
+
updateAgentEffort: async ({ agentId, thinkingLevel }) => {
|
|
284
|
+
const { agent } = await patchAgent(agentId, { thinkingLevel });
|
|
285
|
+
return { thinkingLevel: agent.thinkingLevel ?? null };
|
|
241
286
|
},
|
|
242
287
|
listConversations: async ({ agentId, limit, channels, archived, query, onPage }) => {
|
|
243
288
|
const trimmedQuery = query?.trim();
|
|
@@ -262,9 +307,17 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
262
307
|
} while (cursor && all.length < maxConversations);
|
|
263
308
|
return limit ? all.slice(0, limit) : all;
|
|
264
309
|
},
|
|
265
|
-
listMessages: async ({ conversationId }) => {
|
|
266
|
-
const
|
|
267
|
-
|
|
310
|
+
listMessages: async ({ conversationId, limit, before }) => {
|
|
311
|
+
const params = new URLSearchParams();
|
|
312
|
+
if (limit !== void 0) params.set("limit", String(limit));
|
|
313
|
+
if (before !== void 0) params.set("before", before);
|
|
314
|
+
const query = params.toString();
|
|
315
|
+
const { messages, hasMore, nextBefore } = await get(`/api/v1/conversations/${encodeURIComponent(conversationId)}/messages${query ? `?${query}` : ""}`, listMessagesResponseSchema);
|
|
316
|
+
return {
|
|
317
|
+
messages,
|
|
318
|
+
hasMore: hasMore ?? false,
|
|
319
|
+
nextBefore: nextBefore ?? null
|
|
320
|
+
};
|
|
268
321
|
},
|
|
269
322
|
listWorkspaceFiles: async ({ agentId }) => {
|
|
270
323
|
const { files } = await get(`/api/v1/workspace-files?${new URLSearchParams({ agentId }).toString()}`, listWorkspaceFilesResponseSchema);
|
|
@@ -291,6 +344,13 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
291
344
|
const { result } = await get(`/api/v1/trpc/v2.subagentTasks.list?input=${encodeURIComponent(JSON.stringify({ conversationId }))}`, subagentTasksResponseSchema);
|
|
292
345
|
return new Map(result.data.subagentTasks.map((task) => [task.id, task]));
|
|
293
346
|
},
|
|
347
|
+
computeLiveStats: async (agentId) => {
|
|
348
|
+
const { stats } = await get(`/api/v1/compute/live-stats?agentId=${encodeURIComponent(agentId)}`, liveStatsResponseSchema);
|
|
349
|
+
return stats;
|
|
350
|
+
},
|
|
351
|
+
restartAgent: async (agentId) => {
|
|
352
|
+
await post(`/api/v1/agents/${encodeURIComponent(agentId)}/restart`, {}, z.object({ ok: z.boolean() }));
|
|
353
|
+
},
|
|
294
354
|
uploadAttachment: async ({ agentId, fileName, mediaType, data }) => {
|
|
295
355
|
const size = data.byteLength;
|
|
296
356
|
const presign = await post("/api/v1/attachments/presign", {
|
|
@@ -331,6 +391,9 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
331
391
|
setConversationArchived: async ({ conversationId, archived }) => {
|
|
332
392
|
await post(`/api/v1/conversations/${encodeURIComponent(conversationId)}/archive`, { archived }, z.object({ archived: z.boolean() }));
|
|
333
393
|
},
|
|
394
|
+
markConversationRead: async ({ conversationId }) => {
|
|
395
|
+
await post(`/api/v1/conversations/${encodeURIComponent(conversationId)}/read`, {}, z.object({ read: z.boolean() }));
|
|
396
|
+
},
|
|
334
397
|
renameConversation: async ({ conversationId, title }) => {
|
|
335
398
|
await post(`/api/v1/conversations/${encodeURIComponent(conversationId)}`, { title }, z.object({ conversation: z.object({ id: z.string() }) }), "PATCH");
|
|
336
399
|
},
|
|
@@ -352,6 +415,7 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
352
415
|
const { authorizationUrl } = await post("/api/v1/external-oauth/connect", input, externalOauthConnectResponseSchema);
|
|
353
416
|
return { authorizationUrl: authorizationUrl ?? null };
|
|
354
417
|
},
|
|
418
|
+
decideComputeRequest: async ({ requestId, decision }) => post(`/api/v1/compute-requests/${encodeURIComponent(requestId)}/decision`, { decision }, computeDecisionResponseSchema),
|
|
355
419
|
fulfillCredential: async ({ url, body }) => {
|
|
356
420
|
const target = new URL(url, appUrl).toString();
|
|
357
421
|
const res = await fetch(target, {
|
|
@@ -385,6 +449,7 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
385
449
|
headers: {
|
|
386
450
|
authorization: `Bearer ${sessionToken}`,
|
|
387
451
|
accept: "text/event-stream",
|
|
452
|
+
[CLI_VERSION_HEADER]: version,
|
|
388
453
|
...workspaceId ? { "x-workspace-id": workspaceId } : {}
|
|
389
454
|
},
|
|
390
455
|
signal
|
|
@@ -436,27 +501,35 @@ const agentSummarySchema = z.object({
|
|
|
436
501
|
createdAt: z.string(),
|
|
437
502
|
creatorName: z.string().nullable().optional(),
|
|
438
503
|
model: z.string().nullable().optional(),
|
|
439
|
-
modelLocked: z.boolean().optional()
|
|
504
|
+
modelLocked: z.boolean().optional(),
|
|
505
|
+
thinkingLevel: z.string().nullable().optional()
|
|
440
506
|
});
|
|
441
507
|
const platformModelSchema = z.object({
|
|
442
508
|
id: z.string(),
|
|
443
509
|
displayName: z.string(),
|
|
444
510
|
providerDisplay: z.string().optional(),
|
|
445
511
|
reasoning: z.boolean().optional(),
|
|
446
|
-
compliant: z.boolean().optional()
|
|
512
|
+
compliant: z.boolean().optional(),
|
|
513
|
+
thinkingLevels: z.array(z.string()).optional(),
|
|
514
|
+
thinkingLevelLabels: z.record(z.string(), z.string()).optional()
|
|
447
515
|
}).passthrough();
|
|
448
516
|
const listModelsResponseSchema = z.object({ models: z.array(platformModelSchema) });
|
|
449
|
-
const
|
|
517
|
+
const modelEffortPreferencesResponseSchema = z.object({ modelConfigOverrides: z.record(z.string(), z.object({ thinkingLevel: z.string().optional() })).optional() });
|
|
518
|
+
const updateAgentResponseSchema = z.object({ agent: z.object({
|
|
519
|
+
model: z.string().nullable().optional(),
|
|
520
|
+
thinkingLevel: z.string().nullable().optional()
|
|
521
|
+
}).passthrough() });
|
|
450
522
|
const listAgentsResponseSchema = z.object({
|
|
451
523
|
agents: z.array(agentSummarySchema),
|
|
452
524
|
nextCursor: z.string().nullable().optional(),
|
|
453
525
|
totalCount: z.number().nullable().optional()
|
|
454
526
|
});
|
|
455
527
|
const createAgentResponseSchema = z.object({ agent: agentSummarySchema });
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
})
|
|
528
|
+
const onboardingConversationResponseSchema = z.object({
|
|
529
|
+
conversationId: z.string(),
|
|
530
|
+
runId: z.string().nullable()
|
|
531
|
+
});
|
|
532
|
+
const getAgentResponseSchema = z.object({ agent: agentSummarySchema });
|
|
460
533
|
const agentSuggestionSchema = z.object({ name: z.string() });
|
|
461
534
|
const suggestAgentResponseSchema = z.object({ suggestion: agentSuggestionSchema.nullable() });
|
|
462
535
|
const conversationAgentSchema = z.object({
|
|
@@ -474,6 +547,7 @@ const conversationSummarySchema = z.object({
|
|
|
474
547
|
channel: z.string().nullable(),
|
|
475
548
|
channelLabel: z.string().nullable(),
|
|
476
549
|
viewerArchivedAt: z.string().nullable().optional(),
|
|
550
|
+
unread: z.boolean().optional(),
|
|
477
551
|
agent: conversationAgentSchema,
|
|
478
552
|
agents: z.array(conversationAgentSchema).optional()
|
|
479
553
|
});
|
|
@@ -535,7 +609,20 @@ const subagentTaskSnapshotSchema = z.object({
|
|
|
535
609
|
completedAt: z.string().nullable()
|
|
536
610
|
});
|
|
537
611
|
const subagentTasksResponseSchema = z.object({ result: z.object({ data: z.object({ subagentTasks: z.array(subagentTaskSnapshotSchema) }) }) });
|
|
538
|
-
const
|
|
612
|
+
const liveStatsResponseSchema = z.object({ stats: z.object({
|
|
613
|
+
sampledAt: z.number(),
|
|
614
|
+
cpuUsedPct: z.number(),
|
|
615
|
+
cpuCount: z.number(),
|
|
616
|
+
memUsedBytes: z.number(),
|
|
617
|
+
memTotalBytes: z.number(),
|
|
618
|
+
diskUsedBytes: z.number(),
|
|
619
|
+
diskTotalBytes: z.number()
|
|
620
|
+
}).nullable() });
|
|
621
|
+
const listMessagesResponseSchema = z.object({
|
|
622
|
+
messages: z.array(uiMessageSchema),
|
|
623
|
+
hasMore: z.boolean().optional(),
|
|
624
|
+
nextBefore: z.string().nullish()
|
|
625
|
+
});
|
|
539
626
|
const workspaceFileSchema = z.object({
|
|
540
627
|
id: z.string(),
|
|
541
628
|
path: z.string(),
|
|
@@ -569,6 +656,7 @@ const finalizeResponseSchema = z.object({
|
|
|
569
656
|
});
|
|
570
657
|
const oauthConnectResponseSchema = z.object({ connectLink: z.string() }).passthrough();
|
|
571
658
|
const externalOauthConnectResponseSchema = z.object({ authorizationUrl: z.string().optional() }).passthrough();
|
|
659
|
+
const computeDecisionResponseSchema = z.object({ status: z.string() }).passthrough();
|
|
572
660
|
const runStreamEventSchema = z.union([z.object({
|
|
573
661
|
kind: z.literal("chunk"),
|
|
574
662
|
chunk: z.record(z.unknown())
|
|
@@ -589,7 +677,8 @@ const conversationStreamEventSchema = z.discriminatedUnion("kind", [
|
|
|
589
677
|
kind: z.literal("agent"),
|
|
590
678
|
id: z.string(),
|
|
591
679
|
model: z.string().nullable(),
|
|
592
|
-
modelLocked: z.boolean()
|
|
680
|
+
modelLocked: z.boolean(),
|
|
681
|
+
thinkingLevel: z.string().nullish()
|
|
593
682
|
}),
|
|
594
683
|
z.object({
|
|
595
684
|
kind: z.literal("run"),
|
|
@@ -607,4 +696,4 @@ const conversationStreamEventSchema = z.discriminatedUnion("kind", [
|
|
|
607
696
|
]);
|
|
608
697
|
|
|
609
698
|
//#endregion
|
|
610
|
-
export { isRecord as a, errorMessage as i, errorDetail as n, sendErrorMessage as r, createRestClient as t };
|
|
699
|
+
export { isRecord as a, errorMessage as i, errorDetail as n, name as o, sendErrorMessage as r, version as s, createRestClient as t };
|
|
@@ -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.5.0-beta.
|
|
3
|
+
"version": "0.5.0-beta.52",
|
|
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",
|
|
@@ -41,7 +42,6 @@
|
|
|
41
42
|
"react-devtools-core": "^7.0.1",
|
|
42
43
|
"safe-stable-stringify": "^2.3.1",
|
|
43
44
|
"semver": "^7.7.4",
|
|
44
|
-
"shell-quote": "1.9.0",
|
|
45
45
|
"web-tree-sitter": "0.25.10",
|
|
46
46
|
"ws": "^8.21.0",
|
|
47
47
|
"yargs": "^17.7.2",
|
|
@@ -66,5 +66,12 @@
|
|
|
66
66
|
"engines": {
|
|
67
67
|
"bun": ">=1.3.14",
|
|
68
68
|
"node": ">=20.0.0"
|
|
69
|
+
},
|
|
70
|
+
"//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.",
|
|
71
|
+
"optionalDependencies": {
|
|
72
|
+
"skydive-cli-darwin-arm64": "0.5.0-beta.52",
|
|
73
|
+
"skydive-cli-darwin-x64": "0.5.0-beta.52",
|
|
74
|
+
"skydive-cli-linux-x64": "0.5.0-beta.52",
|
|
75
|
+
"skydive-cli-linux-arm64": "0.5.0-beta.52"
|
|
69
76
|
}
|
|
70
77
|
}
|