skydive-cli 0.5.0-beta.2 → 0.5.0-beta.21
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 +2 -0
- package/README.md +15 -4
- package/dist/js/api-DG5W6iwx.mjs +131 -0
- package/dist/js/bin.mjs +105 -52
- package/dist/js/{boot-DFYFjlht.mjs → boot-DcQhJadu.mjs} +1181 -192
- package/dist/js/{client-Dd5sMXPv.mjs → client-DbqRBquD.mjs} +206 -133
- package/dist/js/client-hH2PJL8y.mjs +5 -0
- package/dist/js/{daemon-DDBIumf1.mjs → daemon-C6tZH1dO.mjs} +3 -2
- package/dist/js/{daemon-Bq93vOIk.mjs → daemon-Dj9tGT12.mjs} +93 -7
- package/dist/js/{daemon-client-Bewt98dE.mjs → daemon-client-BNK77jX_.mjs} +12 -8
- package/dist/js/daemon-client-CgpMgM8Q.mjs +7 -0
- package/dist/js/forward-18QoL5dO.mjs +68 -0
- package/dist/js/{print-ba_0hiV9.mjs → print-C8DHKrJu.mjs} +2 -2
- package/dist/js/{print-CbayCa87.mjs → print-OvYyj6Uk.mjs} +106 -30
- package/dist/js/{print-share-C1LPsgTP.mjs → print-share-BH9gvCls.mjs} +3 -1
- package/dist/js/{profiler-y43HVmq7.mjs → profiler-Bl25cyUp.mjs} +39 -4
- package/dist/js/{raw-pty-B6mAroiI.mjs → raw-pty-DY4KelZW.mjs} +1 -1
- package/dist/js/raw-pty-DmdUf4_w.mjs +5 -0
- package/dist/js/{rest-DADJh0bi.mjs → rest-DW05dMMc.mjs} +1 -1
- package/dist/js/{rest-BY2nADw5.mjs → rest-Dx6b-Nq_.mjs} +25 -12
- package/package.json +1 -1
- package/dist/js/client-CKBQ8pft.mjs +0 -4
- package/dist/js/daemon-client-srAit8Fz.mjs +0 -6
- package/dist/js/raw-pty-3EkG-jjH.mjs +0 -5
- /package/dist/js/{client-Cn2af31H.mjs → client-c4c5MmgN.mjs} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { c as LOCAL_PROTOCOL_VERSION, d as encodeLine, f as makeLineParser, n as ensureDaemonRunning, p as parseDaemonMessage, u as daemonPaths } from "./daemon-
|
|
2
|
+
import { c as LOCAL_PROTOCOL_VERSION, d as encodeLine, f as makeLineParser, n as ensureDaemonRunning, p as parseDaemonMessage, u as daemonPaths } from "./daemon-Dj9tGT12.mjs";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { connect } from "node:net";
|
|
5
5
|
|
|
@@ -12,7 +12,7 @@ var PortalDaemonClient = class {
|
|
|
12
12
|
lastBind = null;
|
|
13
13
|
fallbackCwd = null;
|
|
14
14
|
wantEnabled = false;
|
|
15
|
-
pendingGrants = /* @__PURE__ */ new
|
|
15
|
+
pendingGrants = /* @__PURE__ */ new Map();
|
|
16
16
|
pendingDeclines = /* @__PURE__ */ new Map();
|
|
17
17
|
grantedAgentIds = /* @__PURE__ */ new Set();
|
|
18
18
|
constructor(opts) {
|
|
@@ -50,9 +50,10 @@ var PortalDaemonClient = class {
|
|
|
50
50
|
conversationId: this.lastBind.conversationId,
|
|
51
51
|
cwd: this.lastBind.cwd
|
|
52
52
|
});
|
|
53
|
-
for (const agentId of this.pendingGrants) this.send({
|
|
53
|
+
for (const [agentId, conversationId] of this.pendingGrants) this.send({
|
|
54
54
|
t: "grant",
|
|
55
|
-
agentId
|
|
55
|
+
agentId,
|
|
56
|
+
conversationId
|
|
56
57
|
});
|
|
57
58
|
for (const [agentId, declined] of this.pendingDeclines) this.send({
|
|
58
59
|
t: "decline",
|
|
@@ -157,13 +158,16 @@ var PortalDaemonClient = class {
|
|
|
157
158
|
* Authorize one agent to run commands on this machine. Resolves once the
|
|
158
159
|
* request is sent to the daemon (the daemon performs the grant and pushes the
|
|
159
160
|
* updated state); kept async so it's a drop-in for the old in-process client's
|
|
160
|
-
* awaited `grantAgent`.
|
|
161
|
+
* awaited `grantAgent`. `conversationId` names the conversation whose run
|
|
162
|
+
* asked, so the grant can wake the waiting agent; null when the grant is not
|
|
163
|
+
* answering an in-chat request.
|
|
161
164
|
*/
|
|
162
|
-
grantAgent(agentId) {
|
|
163
|
-
this.pendingGrants.
|
|
165
|
+
grantAgent(agentId, conversationId) {
|
|
166
|
+
this.pendingGrants.set(agentId, conversationId);
|
|
164
167
|
this.send({
|
|
165
168
|
t: "grant",
|
|
166
|
-
agentId
|
|
169
|
+
agentId,
|
|
170
|
+
conversationId
|
|
167
171
|
});
|
|
168
172
|
return Promise.resolve();
|
|
169
173
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { t as fetchForwardTarget } from "./api-DG5W6iwx.mjs";
|
|
3
|
+
import net from "node:net";
|
|
4
|
+
import { WebSocket, createWebSocketStream } from "ws";
|
|
5
|
+
|
|
6
|
+
//#region src/chat/portal/forward.ts
|
|
7
|
+
const TARGET_REFRESH_SAFETY_MS = 12e4;
|
|
8
|
+
/**
|
|
9
|
+
* The reverse portal's client half: listen on the local machine's loopback and
|
|
10
|
+
* pipe each TCP connection to the agent sandbox's daemon (`/portal/tcp`),
|
|
11
|
+
* which pipes to the sandbox's own loopback. The daemon is reached through the
|
|
12
|
+
* agent-webserver edge Worker (sandboxes have public ingress disabled; the
|
|
13
|
+
* Worker owns boot-resolution and injects the sandbox edge-auth token), so a
|
|
14
|
+
* sandbox recycle just costs the next connection a cold-start wait rather
|
|
15
|
+
* than invalidating the forward.
|
|
16
|
+
*/
|
|
17
|
+
async function startForward({ auth, agentId, localPort, targetPort, log }) {
|
|
18
|
+
let target = await fetchForwardTarget(auth, agentId);
|
|
19
|
+
let mintedAt = Date.now();
|
|
20
|
+
async function freshTarget() {
|
|
21
|
+
const ttlMs = target.expiresInSeconds * 1e3;
|
|
22
|
+
if (Date.now() - mintedAt > ttlMs - TARGET_REFRESH_SAFETY_MS) {
|
|
23
|
+
target = await fetchForwardTarget(auth, agentId);
|
|
24
|
+
mintedAt = Date.now();
|
|
25
|
+
}
|
|
26
|
+
return target;
|
|
27
|
+
}
|
|
28
|
+
const server = net.createServer((sock) => {
|
|
29
|
+
sock.pause();
|
|
30
|
+
(async () => {
|
|
31
|
+
let resolved;
|
|
32
|
+
try {
|
|
33
|
+
resolved = await freshTarget();
|
|
34
|
+
} catch (err) {
|
|
35
|
+
log(`forward: token refresh failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
36
|
+
sock.destroy();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const ws = new WebSocket(`${resolved.daemonOrigin.replace(/^http/, "ws")}/portal/tcp?port=${targetPort}`, { headers: { authorization: `Bearer ${resolved.token}` } });
|
|
40
|
+
ws.on("open", () => {
|
|
41
|
+
const stream = createWebSocketStream(ws);
|
|
42
|
+
stream.on("error", () => sock.destroy());
|
|
43
|
+
sock.on("error", () => stream.destroy());
|
|
44
|
+
sock.pipe(stream).pipe(sock);
|
|
45
|
+
sock.resume();
|
|
46
|
+
});
|
|
47
|
+
ws.on("error", (err) => {
|
|
48
|
+
log(`forward: tunnel connect failed: ${err.message}`);
|
|
49
|
+
sock.destroy();
|
|
50
|
+
});
|
|
51
|
+
})();
|
|
52
|
+
});
|
|
53
|
+
await new Promise((resolve, reject) => {
|
|
54
|
+
server.once("error", reject);
|
|
55
|
+
server.listen(localPort, "127.0.0.1", () => {
|
|
56
|
+
server.removeListener("error", reject);
|
|
57
|
+
resolve();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
const addr = server.address();
|
|
61
|
+
return {
|
|
62
|
+
port: addr && typeof addr === "object" ? addr.port : localPort,
|
|
63
|
+
close: () => new Promise((resolve) => server.close(() => resolve()))
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
//#endregion
|
|
68
|
+
export { startForward };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-
|
|
3
|
-
import "./rest-
|
|
2
|
+
import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-OvYyj6Uk.mjs";
|
|
3
|
+
import "./rest-Dx6b-Nq_.mjs";
|
|
4
4
|
import "./billing-blocked-2wju4gC_.mjs";
|
|
5
5
|
|
|
6
6
|
export { messageGet, readStdin, resolveAgent, runPrint };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
|
|
3
|
-
import { a as isRecord, i as errorMessage, r as sendErrorMessage, t as createRestClient } from "./rest-
|
|
3
|
+
import { a as isRecord, i as errorMessage, r as sendErrorMessage, t as createRestClient } from "./rest-Dx6b-Nq_.mjs";
|
|
4
4
|
import { i as billingBlockedOutcomeFromSendResponse, n as BillingBlockedError } from "./billing-blocked-2wju4gC_.mjs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import Conf from "conf";
|
|
@@ -9,7 +9,7 @@ import stableStringify from "safe-stable-stringify";
|
|
|
9
9
|
|
|
10
10
|
//#region src/config.ts
|
|
11
11
|
/** Default host for the public management API (`/v1`, API-key auth). */
|
|
12
|
-
const DEFAULT_API_URL = "https://api.skydive.com";
|
|
12
|
+
const DEFAULT_API_URL = typeof SKYDIVE_BUILD_API_URL === "string" ? SKYDIVE_BUILD_API_URL : "https://api.skydive.com";
|
|
13
13
|
/**
|
|
14
14
|
* Default origin for the interactive chat client (`skydive chat`).
|
|
15
15
|
*
|
|
@@ -20,9 +20,9 @@ const DEFAULT_API_URL = "https://api.skydive.com";
|
|
|
20
20
|
* `DEFAULT_API_URL`; override with `--api-url` / `SKYDIVE_APP_URL` for local
|
|
21
21
|
* dev or while the DNS record is still being provisioned.
|
|
22
22
|
*/
|
|
23
|
-
const DEFAULT_APP_URL =
|
|
23
|
+
const DEFAULT_APP_URL = DEFAULT_API_URL;
|
|
24
24
|
/** Web front door, for pages opened in the user's browser. */
|
|
25
|
-
const DEFAULT_WEB_URL = "https://skydive.com";
|
|
25
|
+
const DEFAULT_WEB_URL = typeof SKYDIVE_BUILD_WEB_URL === "string" ? SKYDIVE_BUILD_WEB_URL : "https://skydive.com";
|
|
26
26
|
function resolveWebUrl(appUrl) {
|
|
27
27
|
if (appUrl == null) return appUrl;
|
|
28
28
|
return appUrl === DEFAULT_APP_URL ? DEFAULT_WEB_URL : appUrl;
|
|
@@ -213,6 +213,32 @@ function saveUpdateCheck(value) {
|
|
|
213
213
|
}
|
|
214
214
|
store.set("updateCheck", value);
|
|
215
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Agent a bare `skydive chat` (no --agent/--resume) opens a new conversation
|
|
218
|
+
* with. Any selector `--agent` accepts works: an id, or a unique
|
|
219
|
+
* case-insensitive slug/name, resolved against the active workspace's roster
|
|
220
|
+
* at launch. Null when unset (the launch falls back to the agent picker).
|
|
221
|
+
* A hand-edited blank value counts as unset rather than as a selector no
|
|
222
|
+
* agent could ever match.
|
|
223
|
+
*/
|
|
224
|
+
function getDefaultAgent() {
|
|
225
|
+
const value = store.get("defaultAgent")?.trim();
|
|
226
|
+
return value ? value : null;
|
|
227
|
+
}
|
|
228
|
+
function saveDefaultAgent(value) {
|
|
229
|
+
store.set("defaultAgent", value);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Remember the agent the chat TUI just opened a conversation with, so the
|
|
233
|
+
* next bare `skydive chat` returns to it (last used wins). Records the id —
|
|
234
|
+
* stable across renames, unlike a slug/name selector. Skips the write when
|
|
235
|
+
* the value already matches: this runs on every chat-screen entry, and an
|
|
236
|
+
* unchanged default shouldn't rewrite config.json.
|
|
237
|
+
*/
|
|
238
|
+
function recordDefaultAgent(agentId) {
|
|
239
|
+
if (getDefaultAgent() === agentId) return;
|
|
240
|
+
store.set("defaultAgent", agentId);
|
|
241
|
+
}
|
|
216
242
|
function parseBoolean(raw) {
|
|
217
243
|
const v = raw.trim().toLowerCase();
|
|
218
244
|
if ([
|
|
@@ -229,25 +255,40 @@ function parseBoolean(raw) {
|
|
|
229
255
|
].includes(v)) return ok(false);
|
|
230
256
|
return err(`Expected a boolean (true/false), got "${raw}".`);
|
|
231
257
|
}
|
|
232
|
-
const PREFERENCES = [
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
},
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
258
|
+
const PREFERENCES = [
|
|
259
|
+
{
|
|
260
|
+
key: "shareMachineDefault",
|
|
261
|
+
type: "boolean",
|
|
262
|
+
describe: "Share this machine over the portal on `skydive chat` launch, as if --share-machine were passed. Default false.",
|
|
263
|
+
read: () => getShareMachineDefault(),
|
|
264
|
+
isSet: () => store.has("shareMachineDefault"),
|
|
265
|
+
set: (raw) => parseBoolean(raw).map(saveShareMachineDefault),
|
|
266
|
+
clear: () => store.delete("shareMachineDefault")
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
key: "updateCheck",
|
|
270
|
+
type: "boolean",
|
|
271
|
+
describe: "Run the daily background update check and its \"Update available\" notice. Set false to disable. Default true.",
|
|
272
|
+
read: () => !getUpdateCheckDisabled(),
|
|
273
|
+
isSet: () => store.has("updateCheck"),
|
|
274
|
+
set: (raw) => parseBoolean(raw).map(saveUpdateCheck),
|
|
275
|
+
clear: () => store.delete("updateCheck")
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
key: "defaultAgent",
|
|
279
|
+
type: "string",
|
|
280
|
+
describe: "Agent (id, slug, or name) a bare `skydive chat` opens a new conversation with, skipping the agent picker. Unset by default.",
|
|
281
|
+
read: () => getDefaultAgent(),
|
|
282
|
+
isSet: () => getDefaultAgent() !== null,
|
|
283
|
+
set: (raw) => {
|
|
284
|
+
const value = raw.trim();
|
|
285
|
+
if (!value) return err("Expected an agent id, slug, or name (use `config unset defaultAgent` to clear it).");
|
|
286
|
+
saveDefaultAgent(value);
|
|
287
|
+
return ok(void 0);
|
|
288
|
+
},
|
|
289
|
+
clear: () => store.delete("defaultAgent")
|
|
290
|
+
}
|
|
291
|
+
];
|
|
251
292
|
function getPreference(key) {
|
|
252
293
|
return PREFERENCES.find((p) => p.key === key);
|
|
253
294
|
}
|
|
@@ -282,14 +323,15 @@ function parseButton(element) {
|
|
|
282
323
|
const params = isRecord(press.params) ? press.params : {};
|
|
283
324
|
const primary = props.variant === "primary";
|
|
284
325
|
if (press.action === "approve_portal_access") {
|
|
285
|
-
const agentId = params.agentId;
|
|
286
|
-
if (
|
|
326
|
+
const agentId = optionalString(params.agentId);
|
|
327
|
+
if (!agentId) return null;
|
|
287
328
|
return {
|
|
288
329
|
label,
|
|
289
330
|
action: {
|
|
290
331
|
kind: "grant_portal",
|
|
291
332
|
agentId,
|
|
292
|
-
deviceId:
|
|
333
|
+
deviceId: optionalString(params.deviceId),
|
|
334
|
+
conversationId: optionalString(params.conversationId)
|
|
293
335
|
},
|
|
294
336
|
primary
|
|
295
337
|
};
|
|
@@ -323,12 +365,46 @@ function parseButton(element) {
|
|
|
323
365
|
function specKeyFor(spec) {
|
|
324
366
|
return stableStringify(spec) ?? crypto.randomUUID();
|
|
325
367
|
}
|
|
368
|
+
/**
|
|
369
|
+
* The `platform portal request` consent card. The server emits one
|
|
370
|
+
* DesktopHandoffCard spec for every surface (api routes/portal.ts): the web
|
|
371
|
+
* renders it as the "continue in the Skydive desktop app" handoff, but this
|
|
372
|
+
* terminal's own portal daemon can provide the machine, so here it maps onto
|
|
373
|
+
* the existing grant_portal approval instead of a pointer to the app. Without
|
|
374
|
+
* this the spec is not a Card, parses to null, and the request is silently
|
|
375
|
+
* dropped — `platform portal request` looks desktop-only from the CLI
|
|
376
|
+
* (ANY-6521).
|
|
377
|
+
*/
|
|
378
|
+
function parseDesktopHandoffCard(rootEl) {
|
|
379
|
+
const props = isRecord(rootEl.props) ? rootEl.props : {};
|
|
380
|
+
const agentId = optionalString(props.agentId);
|
|
381
|
+
if (!agentId) return null;
|
|
382
|
+
const agentName = optionalString(props.agentName) ?? "This agent";
|
|
383
|
+
return {
|
|
384
|
+
title: `Let ${agentName} use your computer?`,
|
|
385
|
+
subtitle: null,
|
|
386
|
+
description: `Approving shares this machine with ${agentName} while you're signed in. Revoke anytime: skydive portal revoke --agent "${agentName}"`,
|
|
387
|
+
fields: [],
|
|
388
|
+
button: {
|
|
389
|
+
label: "Approve",
|
|
390
|
+
action: {
|
|
391
|
+
kind: "grant_portal",
|
|
392
|
+
agentId,
|
|
393
|
+
deviceId: optionalString(props.deviceId),
|
|
394
|
+
conversationId: optionalString(props.conversationId)
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
state: {}
|
|
398
|
+
};
|
|
399
|
+
}
|
|
326
400
|
function parseConnectCard(spec) {
|
|
327
401
|
if (!isRecord(spec)) return null;
|
|
328
402
|
const { root, elements } = spec;
|
|
329
403
|
if (typeof root !== "string" || !isRecord(elements)) return null;
|
|
330
404
|
const rootEl = elements[root];
|
|
331
|
-
if (!isRecord(rootEl)
|
|
405
|
+
if (!isRecord(rootEl)) return null;
|
|
406
|
+
if (rootEl.type === "DesktopHandoffCard") return parseDesktopHandoffCard(rootEl);
|
|
407
|
+
if (rootEl.type !== "Card") return null;
|
|
332
408
|
const rootProps = isRecord(rootEl.props) ? rootEl.props : {};
|
|
333
409
|
const title = optionalString(rootProps.title);
|
|
334
410
|
if (!title) return null;
|
|
@@ -572,7 +648,7 @@ async function runPrint({ appUrl, sessionToken, prompt, agentSelector, conversat
|
|
|
572
648
|
onPage: null
|
|
573
649
|
}), agentSelector);
|
|
574
650
|
if (machineShare) {
|
|
575
|
-
if (!machineShare.isGranted(agent.id) && grantTargetAgent) await machineShare.grantAgent(agent.id);
|
|
651
|
+
if (!machineShare.isGranted(agent.id) && grantTargetAgent) await machineShare.grantAgent(agent.id, null);
|
|
576
652
|
if (machineShare.isGranted(agent.id)) console.error(`portal: shared this machine with ${agent.name} for this run (grant persists until revoked)`);
|
|
577
653
|
else console.error(`portal: this machine is shared (shareMachineDefault), but ${agent.name} has no grant. Approve its request, or run \`skydive portal grant --agent ${agent.name}\`.`);
|
|
578
654
|
}
|
|
@@ -657,7 +733,7 @@ async function collectRunText({ client, appUrl, target, onText, messageIdForHint
|
|
|
657
733
|
const onEvent = (event) => {
|
|
658
734
|
if (event.kind === "finished") {
|
|
659
735
|
if (event.outcome) billingBlocked = event.outcome;
|
|
660
|
-
if (event.error) streamError = event.error;
|
|
736
|
+
if (event.error && streamError === null) streamError = event.error;
|
|
661
737
|
return;
|
|
662
738
|
}
|
|
663
739
|
const chunk = event.chunk;
|
|
@@ -760,4 +836,4 @@ async function readStdin() {
|
|
|
760
836
|
}
|
|
761
837
|
|
|
762
838
|
//#endregion
|
|
763
|
-
export {
|
|
839
|
+
export { getStoredApiKeyId as A, saveConfig as B, getDefaultAgent as C, getReviewStateDir as D, getPromptHistoryPath as E, resolveChatAuth as F, saveTheme as H, resolveConfig as I, resolveManagementAuth as L, getUpdateCheckDisabled as M, recordDefaultAgent as N, getSavedTheme as O, resolveAppUrl as P, resolveSession as R, getConfigPath as S, getPreference as T, setLastSeenVersion as U, saveSession as V, API_KEY_PREFIX as _, runPrint as a, PREFERENCES as b, cardActionErrorMessage as c, reconcileMaskedInput as d, resolveConnectUrl as f, API_KEY_FAMILY_PREFIX as g, API_KEYS_URL as h, resolveAgent as i, getStoredApiKeyWorkspaceName as j, getShareMachineDefault as k, parseExternalOauthConnectParams as l, specKeyFor as m, messageGet as n, toPrintError as o, parseConnectCard as p, readStdin as r, MASK_CHAR as s, collectRunText as t, parseOauthConnectParams as u, DEFAULT_API_URL as v, getLastSeenVersion as w, deleteConfig as x, DEFAULT_APP_URL as y, resolveWebUrl as z };
|
|
@@ -14,7 +14,7 @@ import { n as printError } from "./output-DYzzdXYV.mjs";
|
|
|
14
14
|
* the reply (and to --json).
|
|
15
15
|
*/
|
|
16
16
|
async function connectMachineShare({ appUrl, sessionToken, timeoutHint }) {
|
|
17
|
-
const { PortalClient } = await import("./client-
|
|
17
|
+
const { PortalClient } = await import("./client-hH2PJL8y.mjs");
|
|
18
18
|
let signalConnected;
|
|
19
19
|
const connected = new Promise((resolve) => {
|
|
20
20
|
signalConnected = resolve;
|
|
@@ -26,6 +26,8 @@ async function connectMachineShare({ appUrl, sessionToken, timeoutHint }) {
|
|
|
26
26
|
deviceToken: null
|
|
27
27
|
}),
|
|
28
28
|
resolveCwd: () => process.cwd(),
|
|
29
|
+
persistedMachineName: null,
|
|
30
|
+
onMachineName: () => {},
|
|
29
31
|
onState: (state) => {
|
|
30
32
|
if (state.status === "connected") signalConnected();
|
|
31
33
|
if (state.status === "error") console.error(`portal: connection error: ${state.error ?? "unknown"}; retrying`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { S as getConfigPath } from "./print-
|
|
2
|
+
import { S as getConfigPath } from "./print-OvYyj6Uk.mjs";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { err, ok } from "neverthrow";
|
|
@@ -8,7 +8,7 @@ import fs from "node:fs";
|
|
|
8
8
|
|
|
9
9
|
//#region package.json
|
|
10
10
|
var name = "skydive-cli";
|
|
11
|
-
var version$1 = "0.5.0-beta.
|
|
11
|
+
var version$1 = "0.5.0-beta.21";
|
|
12
12
|
|
|
13
13
|
//#endregion
|
|
14
14
|
//#region src/auth/organization.ts
|
|
@@ -125,6 +125,30 @@ async function ensureActiveOrganization({ appUrl, sessionToken }) {
|
|
|
125
125
|
});
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/chat/import-seed.ts
|
|
130
|
+
/** Map `process.platform` to the OS family used by the seed prompts. Every
|
|
131
|
+
* non-`win32` platform Node runs on (darwin, linux, the BSDs) is POSIX. */
|
|
132
|
+
function machineOsFromPlatform(platform) {
|
|
133
|
+
return platform === "win32" ? "windows" : "posix";
|
|
134
|
+
}
|
|
135
|
+
/** One clause describing the machine's shell/paths so the agent's discovery
|
|
136
|
+
* sweep uses the right conventions instead of guessing. */
|
|
137
|
+
function osHint(os) {
|
|
138
|
+
return os === "windows" ? "This machine is Windows, so use PowerShell and Windows paths (%USERPROFILE%, backslashes)." : "This machine is POSIX (macOS/Linux), so use a POSIX shell and paths (~, forward slashes).";
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The first message of an explicit `skydive import` conversation, sent as the
|
|
142
|
+
* user. Frames the migration and points the agent at its import-config skill.
|
|
143
|
+
*/
|
|
144
|
+
function buildImportSeedPrompt(projectDir, os) {
|
|
145
|
+
return [
|
|
146
|
+
"I'm migrating from another coding agent. Import my setup from this machine.",
|
|
147
|
+
"",
|
|
148
|
+
`Use your import-config skill. I ran this from \`${projectDir}\`, so start there and in my home directory. ${osHint(os)} Don't assume one tool — do the discovery sweep so you catch whatever I actually use (Claude Code, Cursor, Codex, Gemini CLI, Copilot, Windsurf, Cline, OpenCode, Aider, and any nested AGENTS.md). Show me the plan first: everything you found, what you'll bring over, where it lands in you, and anything you're leaving out (credentials especially). Then wait for my OK before committing anything.`
|
|
149
|
+
].join("\n");
|
|
150
|
+
}
|
|
151
|
+
|
|
128
152
|
//#endregion
|
|
129
153
|
//#region src/chat/tui/theme.ts
|
|
130
154
|
const tokyonight = {
|
|
@@ -907,6 +931,17 @@ const WORDMARK = [
|
|
|
907
931
|
"███████║██║ ██╗ ██║ ██████╔╝██║ ╚████╔╝ ███████╗",
|
|
908
932
|
"╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚══════╝"
|
|
909
933
|
];
|
|
934
|
+
/** Columns between the pinwheel and the wordmark. */
|
|
935
|
+
const SPLASH_GAP = 3;
|
|
936
|
+
/** Columns the full mark + wordmark occupies (no chrome). */
|
|
937
|
+
const SPLASH_WIDTH = MARK_WIDTH + SPLASH_GAP + WORDMARK.reduce((max, line) => Math.max(max, line.length), 0);
|
|
938
|
+
/** Extra columns around the splash: `skydive --help` indents by 2, the TUI
|
|
939
|
+
* app pads 1 on each side. Same number, so one threshold covers both. */
|
|
940
|
+
const SPLASH_CHROME = 2;
|
|
941
|
+
/** Whether the full splash fits on one line of `columns` without wrapping. */
|
|
942
|
+
function splashFitsWidth(columns) {
|
|
943
|
+
return columns >= SPLASH_WIDTH + SPLASH_CHROME;
|
|
944
|
+
}
|
|
910
945
|
const RESET = "\x1B[0m";
|
|
911
946
|
function hexToRgb(hex) {
|
|
912
947
|
const n = Number.parseInt(hex.slice(1), 16);
|
|
@@ -933,7 +968,7 @@ function markLinesAnsi() {
|
|
|
933
968
|
function brandHelpArt(stream = process.stdout) {
|
|
934
969
|
if (!stream.isTTY) return "";
|
|
935
970
|
const truecolor = (typeof stream.getColorDepth === "function" ? stream.getColorDepth() : 1) >= 24;
|
|
936
|
-
if ((stream.columns ?? 80)
|
|
971
|
+
if (!splashFitsWidth(stream.columns ?? 80)) return truecolor ? `\n ${sgr("✦", BRAND_ACCENT)} Skydive\n` : "\n ✦ Skydive\n";
|
|
937
972
|
if (!truecolor) return `\n${WORDMARK.map((l) => ` ${l}`).join("\n")}\n`;
|
|
938
973
|
const mark = markLinesAnsi();
|
|
939
974
|
const word = [...WORDMARK];
|
|
@@ -1264,4 +1299,4 @@ function maybeStartProfiling(argv, cliVersion) {
|
|
|
1264
1299
|
}
|
|
1265
1300
|
|
|
1266
1301
|
//#endregion
|
|
1267
|
-
export {
|
|
1302
|
+
export { setActiveWorkspace as A, themesForMode as C, getActiveWorkspaceId as D, ensureActiveOrganization as E, version$1 as M, getSessionIdentity as O, themes as S, machineOsFromPlatform as T, theme as _, installCrashHandler as a, themeModeFromColorFgBg as b, MARK_CELLS as c, splashFitsWidth as d, DEFAULT_THEME_ID as f, noColorRequested as g, monoTheme as h, writeArtifact as i, name as j, listWorkspaces as k, WORDMARK as l, findTheme as m, profilingEnabled as n, buildCrashReport as o, applyTheme as p, record as r, writeCrashReport as s, maybeStartProfiling as t, brandHelpArt as u, themeForMode as v, buildImportSeedPrompt as w, themeVersion as x, themeMode as y };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
|
|
3
|
-
import { n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-
|
|
3
|
+
import { n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-Dx6b-Nq_.mjs";
|
|
4
4
|
import "./billing-blocked-2wju4gC_.mjs";
|
|
5
5
|
|
|
6
6
|
export { createRestClient };
|
|
@@ -193,13 +193,14 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
193
193
|
}
|
|
194
194
|
};
|
|
195
195
|
return {
|
|
196
|
-
listAgents: async ({ scope, onPage }) => {
|
|
196
|
+
listAgents: async ({ scope, onPage, limit }) => {
|
|
197
197
|
const all = [];
|
|
198
198
|
let cursor;
|
|
199
|
-
const maxAgents = 2e3;
|
|
199
|
+
const maxAgents = limit ?? 2e3;
|
|
200
|
+
const pageSize = Math.min(100, maxAgents);
|
|
200
201
|
do {
|
|
201
202
|
const params = new URLSearchParams({
|
|
202
|
-
limit:
|
|
203
|
+
limit: String(pageSize),
|
|
203
204
|
scope,
|
|
204
205
|
sort: "mine_first_usage",
|
|
205
206
|
includeStats: "false"
|
|
@@ -213,16 +214,23 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
213
214
|
return all;
|
|
214
215
|
},
|
|
215
216
|
createAgent: async ({ name }) => {
|
|
216
|
-
const { agent } = await post("/api/v1/agents", { name }, createAgentResponseSchema);
|
|
217
|
+
const { agent } = await post("/api/v1/agents", name == null ? {} : { name }, createAgentResponseSchema);
|
|
217
218
|
return agent;
|
|
218
219
|
},
|
|
219
|
-
|
|
220
|
-
const
|
|
220
|
+
createOnboardingConversation: async ({ agentId, projectDir, os }) => {
|
|
221
|
+
const result = await post(`/api/v1/agents/${encodeURIComponent(agentId)}/onboarding-conversation`, {
|
|
222
|
+
projectDir,
|
|
223
|
+
os
|
|
224
|
+
}, onboardingConversationResponseSchema);
|
|
221
225
|
return {
|
|
222
|
-
|
|
223
|
-
|
|
226
|
+
conversationId: result.conversationId,
|
|
227
|
+
runId: result.runId
|
|
224
228
|
};
|
|
225
229
|
},
|
|
230
|
+
getAgent: async ({ agentId }) => {
|
|
231
|
+
const { agent } = await get(`/api/v1/agents/${encodeURIComponent(agentId)}`, getAgentResponseSchema);
|
|
232
|
+
return agent;
|
|
233
|
+
},
|
|
226
234
|
suggestAgentIdentity: async () => {
|
|
227
235
|
const { suggestion } = await get("/api/v1/agents/suggest", suggestAgentResponseSchema);
|
|
228
236
|
return suggestion;
|
|
@@ -331,6 +339,9 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
|
|
|
331
339
|
setConversationArchived: async ({ conversationId, archived }) => {
|
|
332
340
|
await post(`/api/v1/conversations/${encodeURIComponent(conversationId)}/archive`, { archived }, z.object({ archived: z.boolean() }));
|
|
333
341
|
},
|
|
342
|
+
markConversationRead: async ({ conversationId }) => {
|
|
343
|
+
await post(`/api/v1/conversations/${encodeURIComponent(conversationId)}/read`, {}, z.object({ read: z.boolean() }));
|
|
344
|
+
},
|
|
334
345
|
renameConversation: async ({ conversationId, title }) => {
|
|
335
346
|
await post(`/api/v1/conversations/${encodeURIComponent(conversationId)}`, { title }, z.object({ conversation: z.object({ id: z.string() }) }), "PATCH");
|
|
336
347
|
},
|
|
@@ -453,10 +464,11 @@ const listAgentsResponseSchema = z.object({
|
|
|
453
464
|
totalCount: z.number().nullable().optional()
|
|
454
465
|
});
|
|
455
466
|
const createAgentResponseSchema = z.object({ agent: agentSummarySchema });
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
})
|
|
467
|
+
const onboardingConversationResponseSchema = z.object({
|
|
468
|
+
conversationId: z.string(),
|
|
469
|
+
runId: z.string().nullable()
|
|
470
|
+
});
|
|
471
|
+
const getAgentResponseSchema = z.object({ agent: agentSummarySchema });
|
|
460
472
|
const agentSuggestionSchema = z.object({ name: z.string() });
|
|
461
473
|
const suggestAgentResponseSchema = z.object({ suggestion: agentSuggestionSchema.nullable() });
|
|
462
474
|
const conversationAgentSchema = z.object({
|
|
@@ -474,6 +486,7 @@ const conversationSummarySchema = z.object({
|
|
|
474
486
|
channel: z.string().nullable(),
|
|
475
487
|
channelLabel: z.string().nullable(),
|
|
476
488
|
viewerArchivedAt: z.string().nullable().optional(),
|
|
489
|
+
unread: z.boolean().optional(),
|
|
477
490
|
agent: conversationAgentSchema,
|
|
478
491
|
agents: z.array(conversationAgentSchema).optional()
|
|
479
492
|
});
|
package/package.json
CHANGED
|
File without changes
|