skydive-cli 0.4.1-beta.6 → 0.5.0-beta.2
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/dist/js/bin.mjs +10 -10
- package/dist/js/{boot-B48inW5D.mjs → boot-DFYFjlht.mjs} +30 -10
- package/dist/js/client-CKBQ8pft.mjs +4 -0
- package/dist/js/{client-mykp1DVb.mjs → client-Dd5sMXPv.mjs} +42 -6
- package/dist/js/{daemon-CfbpCjAw.mjs → daemon-Bq93vOIk.mjs} +115 -24
- package/dist/js/{daemon-D4ZX3wNG.mjs → daemon-DDBIumf1.mjs} +2 -2
- package/dist/js/{daemon-client-6xtta_n2.mjs → daemon-client-Bewt98dE.mjs} +24 -3
- package/dist/js/daemon-client-srAit8Fz.mjs +6 -0
- package/dist/js/{print-share-ZV88PqDu.mjs → print-share-C1LPsgTP.mjs} +5 -2
- package/dist/js/{profiler-94qrV53c.mjs → profiler-y43HVmq7.mjs} +1 -1
- package/package.json +2 -2
- package/dist/js/client-NBj0KZSP.mjs +0 -4
- package/dist/js/daemon-client-BHehCLrO.mjs +0 -6
package/dist/js/bin.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as ensureActiveOrganization, D as setActiveWorkspace, E as listWorkspaces, O as name, T as getSessionIdentity, a as installCrashHandler, k as version, t as maybeStartProfiling, u as brandHelpArt, w as getActiveWorkspaceId, x as themes } from "./profiler-
|
|
2
|
+
import { C as ensureActiveOrganization, D as setActiveWorkspace, E as listWorkspaces, O as name, T as getSessionIdentity, a as installCrashHandler, k as version, t as maybeStartProfiling, u as brandHelpArt, w as getActiveWorkspaceId, x as themes } from "./profiler-y43HVmq7.mjs";
|
|
3
3
|
import { A as getStoredApiKeyWorkspaceName, C as getLastSeenVersion, F as resolveManagementAuth, I as resolveSession, M as resolveAppUrl, N as resolveChatAuth, O as getShareMachineDefault, P as resolveConfig, R as saveConfig, S as getConfigPath, T as getPromptHistoryPath, V as setLastSeenVersion, _ as API_KEY_PREFIX, b as PREFERENCES, g as API_KEY_FAMILY_PREFIX, h as API_KEYS_URL, i as resolveAgent, j as getUpdateCheckDisabled, k as getStoredApiKeyId, v as DEFAULT_API_URL, w as getPreference, x as deleteConfig, z as saveSession } from "./print-CbayCa87.mjs";
|
|
4
4
|
import { n as printError, r as printTable, t as output } from "./output-DYzzdXYV.mjs";
|
|
5
5
|
import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
|
|
6
6
|
import { t as createRestClient } from "./rest-BY2nADw5.mjs";
|
|
7
7
|
import "./billing-blocked-2wju4gC_.mjs";
|
|
8
|
-
import { a as registerPortalDevice, c as resolveMachineIdentity, i as grantPortalAccess, n as fetchPortalDevices, o as revokePortalAccess, r as findThisDevice } from "./client-
|
|
9
|
-
import { i as queryDaemonStatus, l as PORTAL_DAEMON_FLAG, n as ensureDaemonRunning, s as stopDaemon, u as daemonPaths } from "./daemon-
|
|
8
|
+
import { a as registerPortalDevice, c as resolveMachineIdentity, i as grantPortalAccess, n as fetchPortalDevices, o as revokePortalAccess, r as findThisDevice } from "./client-Dd5sMXPv.mjs";
|
|
9
|
+
import { i as queryDaemonStatus, l as PORTAL_DAEMON_FLAG, n as ensureDaemonRunning, s as stopDaemon, u as daemonPaths } from "./daemon-Bq93vOIk.mjs";
|
|
10
10
|
import { t as SandboxStream } from "./client-Cn2af31H.mjs";
|
|
11
11
|
import { hideBin } from "yargs/helpers";
|
|
12
12
|
import yargs from "yargs";
|
|
@@ -1516,7 +1516,7 @@ const importCommand = {
|
|
|
1516
1516
|
process.exit(1);
|
|
1517
1517
|
}
|
|
1518
1518
|
}
|
|
1519
|
-
const { runChat } = await import("./boot-
|
|
1519
|
+
const { runChat } = await import("./boot-DFYFjlht.mjs");
|
|
1520
1520
|
await runChat({
|
|
1521
1521
|
appUrl,
|
|
1522
1522
|
sessionToken: session.value.sessionToken,
|
|
@@ -1543,7 +1543,7 @@ async function runImportPrintMode({ argv, appUrl }) {
|
|
|
1543
1543
|
printError(`${session.error.message} For non-interactive use, run \`skydive auth login --web\` first, or set SKYDIVE_SESSION_TOKEN.`);
|
|
1544
1544
|
process.exit(1);
|
|
1545
1545
|
}
|
|
1546
|
-
const { connectMachineShare } = await import("./print-share-
|
|
1546
|
+
const { connectMachineShare } = await import("./print-share-C1LPsgTP.mjs");
|
|
1547
1547
|
const machineShare = await connectMachineShare({
|
|
1548
1548
|
appUrl,
|
|
1549
1549
|
sessionToken: session.value.sessionToken,
|
|
@@ -1928,7 +1928,7 @@ const chatCommand = {
|
|
|
1928
1928
|
sessionToken: auth.value.token,
|
|
1929
1929
|
agentSelector: argv.agent ?? null
|
|
1930
1930
|
});
|
|
1931
|
-
const { runChat } = await import("./boot-
|
|
1931
|
+
const { runChat } = await import("./boot-DFYFjlht.mjs");
|
|
1932
1932
|
await runChat({
|
|
1933
1933
|
appUrl: auth.value.appUrl,
|
|
1934
1934
|
sessionToken: auth.value.token,
|
|
@@ -2020,7 +2020,7 @@ async function runPrintMode({ argv, appUrl }) {
|
|
|
2020
2020
|
process.exit(1);
|
|
2021
2021
|
}
|
|
2022
2022
|
} else {
|
|
2023
|
-
const { connectMachineShare } = await import("./print-share-
|
|
2023
|
+
const { connectMachineShare } = await import("./print-share-C1LPsgTP.mjs");
|
|
2024
2024
|
machineShare = await connectMachineShare({
|
|
2025
2025
|
appUrl: auth.value.appUrl,
|
|
2026
2026
|
sessionToken: auth.value.token,
|
|
@@ -2287,7 +2287,7 @@ const switchCommand = {
|
|
|
2287
2287
|
printError("The workspace picker needs the Bun runtime and it could not be set up automatically. Pass a workspace slug instead, or install Bun and retry.");
|
|
2288
2288
|
process.exit(1);
|
|
2289
2289
|
}
|
|
2290
|
-
const { runWorkspacePicker } = await import("./boot-
|
|
2290
|
+
const { runWorkspacePicker } = await import("./boot-DFYFjlht.mjs");
|
|
2291
2291
|
await runWorkspacePicker(session);
|
|
2292
2292
|
return;
|
|
2293
2293
|
}
|
|
@@ -2366,7 +2366,7 @@ const openCommand = {
|
|
|
2366
2366
|
const agent = argv.agent ? resolveAgent((await fetchPortalDevices(session)).agents, argv.agent) : null;
|
|
2367
2367
|
const cwd = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
|
|
2368
2368
|
const { machineName } = await resolveMachineIdentity();
|
|
2369
|
-
const { PortalDaemonClient } = await import("./daemon-client-
|
|
2369
|
+
const { PortalDaemonClient } = await import("./daemon-client-srAit8Fz.mjs");
|
|
2370
2370
|
let lastLine = "";
|
|
2371
2371
|
let signalConnected;
|
|
2372
2372
|
const connected = new Promise((resolve) => {
|
|
@@ -4169,7 +4169,7 @@ if (process.argv.includes(UPDATE_WORKER_FLAG)) {
|
|
|
4169
4169
|
process.exit(0);
|
|
4170
4170
|
}
|
|
4171
4171
|
if (process.argv.includes(PORTAL_DAEMON_FLAG)) {
|
|
4172
|
-
const { runPortalDaemon } = await import("./daemon-
|
|
4172
|
+
const { runPortalDaemon } = await import("./daemon-DDBIumf1.mjs");
|
|
4173
4173
|
runPortalDaemon(process.argv);
|
|
4174
4174
|
} else runCli();
|
|
4175
4175
|
function runCli() {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { D as setActiveWorkspace, E as listWorkspaces, S as themesForMode, _ as themeForMode, a as installCrashHandler, b as themeVersion, c as MARK_CELLS, d as DEFAULT_THEME_ID, f as applyTheme, g as theme, h as noColorRequested, i as writeArtifact, l as WORDMARK, m as monoTheme, n as profilingEnabled, o as buildCrashReport, p as findTheme, r as record, s as writeCrashReport, v as themeMode, w as getActiveWorkspaceId, y as themeModeFromColorFgBg } from "./profiler-
|
|
2
|
+
import { D as setActiveWorkspace, E as listWorkspaces, S as themesForMode, _ as themeForMode, a as installCrashHandler, b as themeVersion, c as MARK_CELLS, d as DEFAULT_THEME_ID, f as applyTheme, g as theme, h as noColorRequested, i as writeArtifact, l as WORDMARK, m as monoTheme, n as profilingEnabled, o as buildCrashReport, p as findTheme, r as record, s as writeCrashReport, v as themeMode, w as getActiveWorkspaceId, y as themeModeFromColorFgBg } from "./profiler-y43HVmq7.mjs";
|
|
3
3
|
import { B as saveTheme, D as getSavedTheme, E as getReviewStateDir, L as resolveWebUrl, S as getConfigPath, c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, i as resolveAgent, l as parseExternalOauthConnectParams, m as specKeyFor, p as parseConnectCard, s as MASK_CHAR, u as parseOauthConnectParams, v as DEFAULT_API_URL, y as DEFAULT_APP_URL } from "./print-CbayCa87.mjs";
|
|
4
4
|
import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
|
|
5
5
|
import { a as isRecord, i as errorMessage, n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-BY2nADw5.mjs";
|
|
6
6
|
import { i as billingBlockedOutcomeFromSendResponse } from "./billing-blocked-2wju4gC_.mjs";
|
|
7
|
-
import { t as PortalClient } from "./client-
|
|
8
|
-
import "./daemon-
|
|
7
|
+
import { t as PortalClient } from "./client-Dd5sMXPv.mjs";
|
|
8
|
+
import "./daemon-Bq93vOIk.mjs";
|
|
9
9
|
import { t as SandboxStream } from "./client-Cn2af31H.mjs";
|
|
10
|
-
import { t as PortalDaemonClient } from "./daemon-client-
|
|
10
|
+
import { t as PortalDaemonClient } from "./daemon-client-Bewt98dE.mjs";
|
|
11
11
|
import { t as runRawPtyPassthrough } from "./raw-pty-B6mAroiI.mjs";
|
|
12
12
|
import * as os$1 from "node:os";
|
|
13
13
|
import { homedir, platform, release, tmpdir } from "node:os";
|
|
@@ -109,6 +109,7 @@ const initialPortalUi = {
|
|
|
109
109
|
machineName: null,
|
|
110
110
|
error: null,
|
|
111
111
|
grantedAgentIds: [],
|
|
112
|
+
declinedAgentIds: [],
|
|
112
113
|
prompt: null
|
|
113
114
|
};
|
|
114
115
|
function initialChatTitle(screen) {
|
|
@@ -181,12 +182,13 @@ const useStore = create((set, get) => ({
|
|
|
181
182
|
chatTitle: null
|
|
182
183
|
}),
|
|
183
184
|
setPortalClient: (portalClient) => set({ portalClient }),
|
|
184
|
-
syncPortal: ({ status, machineName, error, grantedAgentIds }) => set((state) => ({ portal: {
|
|
185
|
+
syncPortal: ({ status, machineName, error, grantedAgentIds, declinedAgentIds }) => set((state) => ({ portal: {
|
|
185
186
|
...state.portal,
|
|
186
187
|
status,
|
|
187
188
|
machineName,
|
|
188
189
|
error,
|
|
189
|
-
grantedAgentIds
|
|
190
|
+
grantedAgentIds,
|
|
191
|
+
declinedAgentIds
|
|
190
192
|
} })),
|
|
191
193
|
setPortalPrompt: (prompt) => set((state) => ({ portal: {
|
|
192
194
|
...state.portal,
|
|
@@ -317,14 +319,18 @@ function createPortalClient(opts) {
|
|
|
317
319
|
if (portalDaemonDisabled()) {
|
|
318
320
|
const client = new PortalClient({
|
|
319
321
|
appUrl: opts.appUrl,
|
|
320
|
-
|
|
322
|
+
credentials: () => ({
|
|
323
|
+
sessionToken: opts.sessionToken,
|
|
324
|
+
deviceToken: null
|
|
325
|
+
}),
|
|
321
326
|
resolveCwd: () => process.cwd(),
|
|
322
327
|
onState: (state) => opts.onState({
|
|
323
328
|
status: state.status,
|
|
324
329
|
machineName: state.machineName,
|
|
325
330
|
friendlyName: state.friendlyName,
|
|
326
331
|
error: state.error,
|
|
327
|
-
grantedAgentIds: state.grantedAgentIds
|
|
332
|
+
grantedAgentIds: state.grantedAgentIds,
|
|
333
|
+
declinedAgentIds: []
|
|
328
334
|
})
|
|
329
335
|
});
|
|
330
336
|
return {
|
|
@@ -332,6 +338,9 @@ function createPortalClient(opts) {
|
|
|
332
338
|
enable: () => client.enable(),
|
|
333
339
|
disable: () => client.disable(),
|
|
334
340
|
grantAgent: (agentId) => client.grantAgent(agentId),
|
|
341
|
+
decline: (_agentId, declined) => {
|
|
342
|
+
if (declined && client.grantedAgentIds().length === 0) client.disable();
|
|
343
|
+
},
|
|
335
344
|
isGranted: (agentId) => client.isGranted(agentId),
|
|
336
345
|
bind: () => {},
|
|
337
346
|
dispose: () => client.dispose()
|
|
@@ -370,7 +379,8 @@ function usePortal({ appUrl, sessionToken, shareMachine, enabled = true }) {
|
|
|
370
379
|
status: state.status,
|
|
371
380
|
machineName: state.machineName,
|
|
372
381
|
error: state.error,
|
|
373
|
-
grantedAgentIds: state.grantedAgentIds
|
|
382
|
+
grantedAgentIds: state.grantedAgentIds,
|
|
383
|
+
declinedAgentIds: state.declinedAgentIds
|
|
374
384
|
})
|
|
375
385
|
});
|
|
376
386
|
setPortalClient(client);
|
|
@@ -11255,6 +11265,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
11255
11265
|
}
|
|
11256
11266
|
if (portal.status === "off") {
|
|
11257
11267
|
declinedRef.current.delete(agent.id);
|
|
11268
|
+
portalClient.decline(agent.id, false);
|
|
11258
11269
|
portalClient.enable();
|
|
11259
11270
|
if (conversationId) portalClient.bind(conversationId, shellSessionRef.current?.cwd ?? process.cwd());
|
|
11260
11271
|
} else {
|
|
@@ -11288,9 +11299,14 @@ function ChatScreen({ agent, conversation }) {
|
|
|
11288
11299
|
]);
|
|
11289
11300
|
const declineGrant = useCallback(() => {
|
|
11290
11301
|
declinedRef.current.add(agent.id);
|
|
11302
|
+
portalClient?.decline(agent.id, true);
|
|
11291
11303
|
setPortalPrompt(null);
|
|
11292
11304
|
useStore.getState().showToast({ message: "kept this machine private" });
|
|
11293
|
-
}, [
|
|
11305
|
+
}, [
|
|
11306
|
+
portalClient,
|
|
11307
|
+
agent.id,
|
|
11308
|
+
setPortalPrompt
|
|
11309
|
+
]);
|
|
11294
11310
|
const openPortalAccess = useCallback(() => {
|
|
11295
11311
|
if (!portalClient) {
|
|
11296
11312
|
useStore.getState().showToast({ message: useStore.getState().authKind === "api-key" ? "local access needs a browser session; run `skydive auth login --web`" : "local access is unavailable in this session" });
|
|
@@ -11299,6 +11315,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
11299
11315
|
switch (portal.status) {
|
|
11300
11316
|
case "off":
|
|
11301
11317
|
declinedRef.current.delete(agent.id);
|
|
11318
|
+
portalClient.decline(agent.id, false);
|
|
11302
11319
|
portalClient.enable();
|
|
11303
11320
|
useStore.getState().showToast({ message: "enabling local access on this machine…" });
|
|
11304
11321
|
break;
|
|
@@ -11311,6 +11328,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
11311
11328
|
break;
|
|
11312
11329
|
}
|
|
11313
11330
|
declinedRef.current.delete(agent.id);
|
|
11331
|
+
portalClient.decline(agent.id, false);
|
|
11314
11332
|
setPortalPrompt({
|
|
11315
11333
|
agentId: agent.id,
|
|
11316
11334
|
agentName: agent.name
|
|
@@ -11515,6 +11533,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
11515
11533
|
useEffect(() => {
|
|
11516
11534
|
if (!portalClient || portal.status !== "connected") return;
|
|
11517
11535
|
if (portal.grantedAgentIds.includes(agent.id)) return;
|
|
11536
|
+
if (portal.declinedAgentIds.includes(agent.id)) return;
|
|
11518
11537
|
if (declinedRef.current.has(agent.id)) return;
|
|
11519
11538
|
if (portal.prompt?.agentId === agent.id) return;
|
|
11520
11539
|
setPortalPrompt({
|
|
@@ -11532,6 +11551,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
11532
11551
|
portalClient,
|
|
11533
11552
|
portal.status,
|
|
11534
11553
|
portal.grantedAgentIds,
|
|
11554
|
+
portal.declinedAgentIds,
|
|
11535
11555
|
portal.prompt,
|
|
11536
11556
|
agent.id,
|
|
11537
11557
|
agent.name,
|
|
@@ -58,12 +58,18 @@ const INHERITED_ENV = [
|
|
|
58
58
|
"TERM",
|
|
59
59
|
"PATH"
|
|
60
60
|
];
|
|
61
|
+
const DARWIN_PATH_PREPEND = ["/opt/homebrew/bin", "/usr/local/bin"];
|
|
61
62
|
function buildEnv(extra) {
|
|
62
63
|
const env = {};
|
|
63
64
|
for (const key of INHERITED_ENV) {
|
|
64
65
|
const value = process.env[key];
|
|
65
66
|
if (value !== void 0) env[key] = value;
|
|
66
67
|
}
|
|
68
|
+
if (process.platform === "darwin") {
|
|
69
|
+
const current = env.PATH ? env.PATH.split(":") : [];
|
|
70
|
+
const missing = DARWIN_PATH_PREPEND.filter((dir) => !current.includes(dir));
|
|
71
|
+
if (missing.length > 0) env.PATH = [...missing, ...current].join(":");
|
|
72
|
+
}
|
|
67
73
|
if (extra) for (const [key, value] of Object.entries(extra)) env[key] = value;
|
|
68
74
|
return env;
|
|
69
75
|
}
|
|
@@ -430,6 +436,10 @@ var PortalClient = class {
|
|
|
430
436
|
isGranted(agentId) {
|
|
431
437
|
return this.granted.has(agentId);
|
|
432
438
|
}
|
|
439
|
+
/** Agents currently authorized on this machine (last known server state). */
|
|
440
|
+
grantedAgentIds() {
|
|
441
|
+
return [...this.granted];
|
|
442
|
+
}
|
|
433
443
|
enable() {
|
|
434
444
|
if (this.enabled || this.disposed) return;
|
|
435
445
|
this.enabled = true;
|
|
@@ -461,14 +471,36 @@ var PortalClient = class {
|
|
|
461
471
|
}
|
|
462
472
|
/** Grant one agent access to this machine (default-deny; user-initiated). */
|
|
463
473
|
async grantAgent(agentId) {
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
474
|
+
const auth = this.sessionAuth();
|
|
475
|
+
if (!auth) throw new Error("granting needs a signed-in CLI session on this machine (the desktop connection alone cannot manage grants)");
|
|
476
|
+
await grantPortalAccess(auth, {
|
|
477
|
+
deviceId: await this.ensureDeviceId(),
|
|
467
478
|
agentId
|
|
468
479
|
});
|
|
469
480
|
this.granted.add(agentId);
|
|
470
481
|
this.emit();
|
|
471
482
|
}
|
|
483
|
+
/** Session-authed REST auth, or null when only a device token is held. */
|
|
484
|
+
sessionAuth() {
|
|
485
|
+
const { sessionToken } = this.opts.credentials();
|
|
486
|
+
return sessionToken ? {
|
|
487
|
+
appUrl: this.opts.appUrl,
|
|
488
|
+
sessionToken
|
|
489
|
+
} : null;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* The bearer token for the portal WebSocket dial: minted from the session
|
|
493
|
+
* when one is held, otherwise the pre-minted device token as-is.
|
|
494
|
+
*/
|
|
495
|
+
async wsToken() {
|
|
496
|
+
const creds = this.opts.credentials();
|
|
497
|
+
if (creds.sessionToken) return mintPortalDeviceToken({
|
|
498
|
+
appUrl: this.opts.appUrl,
|
|
499
|
+
sessionToken: creds.sessionToken
|
|
500
|
+
});
|
|
501
|
+
if (creds.deviceToken) return creds.deviceToken;
|
|
502
|
+
throw new Error("no portal credentials (not signed in)");
|
|
503
|
+
}
|
|
472
504
|
setStatus(status, error = null) {
|
|
473
505
|
this.status = status;
|
|
474
506
|
this.error = error;
|
|
@@ -493,7 +525,7 @@ var PortalClient = class {
|
|
|
493
525
|
while (this.enabled && !this.disposed) {
|
|
494
526
|
this.setStatus("connecting");
|
|
495
527
|
try {
|
|
496
|
-
const token = await
|
|
528
|
+
const token = await this.wsToken();
|
|
497
529
|
await this.runConnection(token);
|
|
498
530
|
backoff = INITIAL_BACKOFF_MS;
|
|
499
531
|
} catch (err) {
|
|
@@ -547,8 +579,10 @@ var PortalClient = class {
|
|
|
547
579
|
throw new Error("this machine is not connected yet");
|
|
548
580
|
}
|
|
549
581
|
async refreshDevice() {
|
|
582
|
+
const auth = this.sessionAuth();
|
|
583
|
+
if (!auth) return;
|
|
550
584
|
try {
|
|
551
|
-
const { devices } = await fetchPortalDevices(
|
|
585
|
+
const { devices } = await fetchPortalDevices(auth);
|
|
552
586
|
const mine = findThisDevice(devices, this.machineName);
|
|
553
587
|
if (!mine) return;
|
|
554
588
|
this.deviceId = mine.id;
|
|
@@ -565,9 +599,11 @@ var PortalClient = class {
|
|
|
565
599
|
async syncDeviceState() {
|
|
566
600
|
await this.refreshDevice();
|
|
567
601
|
if (this.legacyGrantsChecked) return;
|
|
602
|
+
const auth = this.sessionAuth();
|
|
603
|
+
if (!auth) return;
|
|
568
604
|
this.legacyGrantsChecked = true;
|
|
569
605
|
try {
|
|
570
|
-
if (await unifyLegacyCliGrants(
|
|
606
|
+
if (await unifyLegacyCliGrants(auth, this.machineName) > 0) await this.refreshDevice();
|
|
571
607
|
} catch (_error) {}
|
|
572
608
|
}
|
|
573
609
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { s as isRecord, t as PortalClient } from "./client-
|
|
2
|
+
import { s as isRecord, t as PortalClient } from "./client-Dd5sMXPv.mjs";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { z } from "zod";
|
|
@@ -47,14 +47,35 @@ const LOCAL_PROTOCOL_VERSION = 1;
|
|
|
47
47
|
*/
|
|
48
48
|
const PORTAL_DAEMON_FLAG = "--skydive-internal-portal-daemon";
|
|
49
49
|
/**
|
|
50
|
+
* One daemon per machine requires one socket key per backend — but the prod
|
|
51
|
+
* backend is reachable under three hosts: the CLI's default appUrl is
|
|
52
|
+
* `https://api.skydive.com` while the desktop app carries
|
|
53
|
+
* `https://skydive.com` (and the web is on www). Hashing those raw strings
|
|
54
|
+
* would give each surface its own daemon, and with the merged machine
|
|
55
|
+
* identity both daemons would register the SAME device — the presence
|
|
56
|
+
* contention the daemon exists to prevent. Collapse the known prod aliases to
|
|
57
|
+
* the CLI's canonical form (proven for both the REST surface and the WS
|
|
58
|
+
* upgrade; the apex 301-redirects, which a WS upgrade cannot follow).
|
|
59
|
+
* Non-prod URLs (previews, localhost) pass through untouched apart from
|
|
60
|
+
* trailing-slash trimming.
|
|
61
|
+
*/
|
|
62
|
+
function canonicalPortalAppUrl(appUrl) {
|
|
63
|
+
const trimmed = appUrl.replace(/\/+$/, "");
|
|
64
|
+
const host = trimmed.replace(/^https?:\/\//, "").toLowerCase();
|
|
65
|
+
if (host === "skydive.com" || host === "www.skydive.com" || host === "api.skydive.com") return "https://api.skydive.com";
|
|
66
|
+
return trimmed;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
50
69
|
* Per-user, per-app daemon socket + state paths. Scoped by OS user and a hash
|
|
51
|
-
* of the appUrl so two logins or two deployments (prod vs a PR
|
|
52
|
-
* distinct daemons and never share a portal identity
|
|
53
|
-
*
|
|
70
|
+
* of the CANONICAL appUrl so two logins or two deployments (prod vs a PR
|
|
71
|
+
* preview) get distinct daemons and never share a portal identity — while
|
|
72
|
+
* every alias of one deployment (see {@link canonicalPortalAppUrl}) shares
|
|
73
|
+
* one. Kept under the OS temp dir because Unix socket paths are
|
|
74
|
+
* length-limited (~104 bytes on macOS).
|
|
54
75
|
*/
|
|
55
76
|
function daemonPaths(appUrl) {
|
|
56
77
|
const user = safeSegment(os.userInfo().username || "user");
|
|
57
|
-
const app = createHash("sha256").update(appUrl).digest("hex").slice(0, 12);
|
|
78
|
+
const app = createHash("sha256").update(canonicalPortalAppUrl(appUrl)).digest("hex").slice(0, 12);
|
|
58
79
|
const dir = path.join(os.tmpdir(), `skydive-portal-${user}-${app}`);
|
|
59
80
|
return {
|
|
60
81
|
dir,
|
|
@@ -67,17 +88,30 @@ function safeSegment(value) {
|
|
|
67
88
|
return value.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 24) || "user";
|
|
68
89
|
}
|
|
69
90
|
/**
|
|
70
|
-
* `hello` — a
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
91
|
+
* `hello` — a client attaches. `sessionId` is unique per client process.
|
|
92
|
+
* `token` authenticates the daemon's portal connection; `tokenKind` says what
|
|
93
|
+
* it is:
|
|
94
|
+
*
|
|
95
|
+
* - `session` — a CLI session token. The daemon mints short-lived portal
|
|
96
|
+
* device tokens from it, and can call the session-authed portal REST
|
|
97
|
+
* surface (device refresh, grants).
|
|
98
|
+
* - `device` — a pre-minted portal device token (the desktop app is handed
|
|
99
|
+
* one by the web bridge and holds no session of its own). Dials the portal
|
|
100
|
+
* WebSocket directly; the session-only REST surface is unavailable until
|
|
101
|
+
* some client supplies a session token.
|
|
102
|
+
*
|
|
103
|
+
* The daemon uses the FIRST hello's token to connect and refreshes from later
|
|
104
|
+
* hellos so a long-lived daemon can outlive the client that spawned it.
|
|
105
|
+
* `tokenKind` defaults to `session` on parse: hellos from CLI builds that
|
|
106
|
+
* predate the field are session-token clients by definition, and the default
|
|
107
|
+
* keeps them attaching to a newer daemon.
|
|
75
108
|
*/
|
|
76
109
|
const clientHelloSchema = z.object({
|
|
77
110
|
t: z.literal("hello"),
|
|
78
111
|
v: z.number(),
|
|
79
112
|
sessionId: z.string().min(1),
|
|
80
|
-
token: z.string().min(1)
|
|
113
|
+
token: z.string().min(1),
|
|
114
|
+
tokenKind: z.enum(["session", "device"]).default("session")
|
|
81
115
|
});
|
|
82
116
|
/**
|
|
83
117
|
* `bind` — associate a conversation with a working directory. Sent on chat
|
|
@@ -109,6 +143,20 @@ const clientGrantSchema = z.object({
|
|
|
109
143
|
t: z.literal("grant"),
|
|
110
144
|
agentId: z.string().min(1)
|
|
111
145
|
});
|
|
146
|
+
/**
|
|
147
|
+
* `decline` — the user answered no to an agent's request for this machine
|
|
148
|
+
* (`declined: true`), or re-opened the door for it (`declined: false`).
|
|
149
|
+
*
|
|
150
|
+
* The daemon holds this because a decline is about the MACHINE, not one
|
|
151
|
+
* terminal: every attached CLI raises its own grant prompt off the daemon's
|
|
152
|
+
* shared state, so a per-process no would be re-asked by every other open
|
|
153
|
+
* conversation and by the next `skydive` launch.
|
|
154
|
+
*/
|
|
155
|
+
const clientDeclineSchema = z.object({
|
|
156
|
+
t: z.literal("decline"),
|
|
157
|
+
agentId: z.string().min(1),
|
|
158
|
+
declined: z.boolean()
|
|
159
|
+
});
|
|
112
160
|
/** `bye` — a CLI detaches cleanly (its conversations drop from the map). */
|
|
113
161
|
const clientByeSchema = z.object({
|
|
114
162
|
t: z.literal("bye"),
|
|
@@ -133,6 +181,7 @@ const clientMessageSchema = z.discriminatedUnion("t", [
|
|
|
133
181
|
clientDisableSchema,
|
|
134
182
|
clientCwdSchema,
|
|
135
183
|
clientGrantSchema,
|
|
184
|
+
clientDeclineSchema,
|
|
136
185
|
clientByeSchema,
|
|
137
186
|
clientStatusSchema,
|
|
138
187
|
clientShutdownSchema
|
|
@@ -152,7 +201,8 @@ const daemonStateSchema = z.object({
|
|
|
152
201
|
machineName: z.string(),
|
|
153
202
|
friendlyName: z.string(),
|
|
154
203
|
error: z.string().nullable(),
|
|
155
|
-
grantedAgentIds: z.array(z.string())
|
|
204
|
+
grantedAgentIds: z.array(z.string()),
|
|
205
|
+
declinedAgentIds: z.array(z.string()).default([])
|
|
156
206
|
});
|
|
157
207
|
/** `hello_ok` — the daemon accepts an attach and reports its protocol version. */
|
|
158
208
|
const daemonHelloOkSchema = z.object({
|
|
@@ -180,7 +230,8 @@ const daemonStatusResultSchema = z.object({
|
|
|
180
230
|
machineName: z.string(),
|
|
181
231
|
friendlyName: z.string(),
|
|
182
232
|
error: z.string().nullable(),
|
|
183
|
-
grantedAgentIds: z.array(z.string())
|
|
233
|
+
grantedAgentIds: z.array(z.string()),
|
|
234
|
+
declinedAgentIds: z.array(z.string()).default([])
|
|
184
235
|
}),
|
|
185
236
|
clientCount: z.number(),
|
|
186
237
|
cwds: z.record(z.string(), z.string())
|
|
@@ -190,6 +241,12 @@ const daemonMessageSchema = z.discriminatedUnion("t", [
|
|
|
190
241
|
daemonHelloOkSchema,
|
|
191
242
|
daemonStatusResultSchema
|
|
192
243
|
]);
|
|
244
|
+
/**
|
|
245
|
+
* Encoding is the pre-parse direction, so it takes the schemas' INPUT types:
|
|
246
|
+
* fields with a `.default()` (e.g. `hello.tokenKind`) are required after a
|
|
247
|
+
* parse but optional on the wire, and a sender omitting one — an older CLI
|
|
248
|
+
* build that predates the field — must stay expressible.
|
|
249
|
+
*/
|
|
193
250
|
function encodeLine(msg) {
|
|
194
251
|
return `${JSON.stringify(msg)}\n`;
|
|
195
252
|
}
|
|
@@ -254,18 +311,21 @@ function parseDaemonMessage(line) {
|
|
|
254
311
|
/** Grace period after the last client detaches before the daemon exits. */
|
|
255
312
|
const IDLE_SHUTDOWN_MS = 3e4;
|
|
256
313
|
var PortalDaemon = class {
|
|
314
|
+
appUrl;
|
|
257
315
|
paths;
|
|
258
316
|
server = null;
|
|
259
317
|
client = null;
|
|
260
318
|
lastState = null;
|
|
261
319
|
conns = /* @__PURE__ */ new Set();
|
|
262
320
|
cwds = /* @__PURE__ */ new Map();
|
|
321
|
+
declined = /* @__PURE__ */ new Set();
|
|
263
322
|
fallbackCwd;
|
|
264
323
|
idleTimer = null;
|
|
265
324
|
sessionToken = null;
|
|
325
|
+
deviceToken = null;
|
|
266
326
|
constructor(appUrl) {
|
|
267
|
-
this.appUrl = appUrl;
|
|
268
|
-
this.paths = daemonPaths(appUrl);
|
|
327
|
+
this.appUrl = canonicalPortalAppUrl(appUrl);
|
|
328
|
+
this.paths = daemonPaths(this.appUrl);
|
|
269
329
|
this.fallbackCwd = process.env.HOME ?? process.cwd();
|
|
270
330
|
}
|
|
271
331
|
/** Start listening. Rejects if the socket is already held by another daemon. */
|
|
@@ -319,13 +379,14 @@ var PortalDaemon = class {
|
|
|
319
379
|
switch (msg.t) {
|
|
320
380
|
case "hello":
|
|
321
381
|
conn.sessionId = msg.sessionId;
|
|
322
|
-
this.
|
|
382
|
+
if (msg.tokenKind === "device") this.deviceToken = msg.token;
|
|
383
|
+
else this.sessionToken = msg.token;
|
|
323
384
|
this.ensureClient();
|
|
324
385
|
this.send(conn, {
|
|
325
386
|
t: "hello_ok",
|
|
326
387
|
v: LOCAL_PROTOCOL_VERSION
|
|
327
388
|
});
|
|
328
|
-
if (this.lastState) this.send(conn, stateMessage(this.lastState));
|
|
389
|
+
if (this.lastState) this.send(conn, stateMessage(this.lastState, this.declined));
|
|
329
390
|
return;
|
|
330
391
|
case "bind":
|
|
331
392
|
conn.conversations.add(msg.conversationId);
|
|
@@ -342,10 +403,19 @@ var PortalDaemon = class {
|
|
|
342
403
|
for (const other of this.conns) other.wantsShare = false;
|
|
343
404
|
this.client?.disable();
|
|
344
405
|
return;
|
|
406
|
+
case "decline":
|
|
407
|
+
if (msg.declined) {
|
|
408
|
+
this.declined.add(msg.agentId);
|
|
409
|
+
if ((this.lastState?.grantedAgentIds.length ?? 0) === 0) this.client?.disable();
|
|
410
|
+
} else this.declined.delete(msg.agentId);
|
|
411
|
+
this.persistState();
|
|
412
|
+
this.broadcastState();
|
|
413
|
+
return;
|
|
345
414
|
case "cwd":
|
|
346
415
|
this.fallbackCwd = msg.cwd;
|
|
347
416
|
return;
|
|
348
417
|
case "grant":
|
|
418
|
+
this.declined.delete(msg.agentId);
|
|
349
419
|
this.ensureClient();
|
|
350
420
|
this.client?.grantAgent(msg.agentId).catch((error) => {
|
|
351
421
|
this.logError("grantAgent failed", error);
|
|
@@ -375,14 +445,17 @@ var PortalDaemon = class {
|
|
|
375
445
|
}
|
|
376
446
|
/** Create the single PortalClient the first time a client needs the portal. */
|
|
377
447
|
ensureClient() {
|
|
378
|
-
if (this.client || !this.sessionToken) return;
|
|
448
|
+
if (this.client || !this.sessionToken && !this.deviceToken) return;
|
|
379
449
|
this.client = new PortalClient({
|
|
380
450
|
appUrl: this.appUrl,
|
|
381
|
-
|
|
451
|
+
credentials: () => ({
|
|
452
|
+
sessionToken: this.sessionToken,
|
|
453
|
+
deviceToken: this.deviceToken
|
|
454
|
+
}),
|
|
382
455
|
resolveCwd: (conversationId) => this.resolveCwd(conversationId),
|
|
383
456
|
onState: (state) => {
|
|
384
457
|
this.lastState = state;
|
|
385
|
-
this.
|
|
458
|
+
this.broadcastState();
|
|
386
459
|
}
|
|
387
460
|
});
|
|
388
461
|
}
|
|
@@ -400,6 +473,11 @@ var PortalDaemon = class {
|
|
|
400
473
|
broadcast(msg) {
|
|
401
474
|
for (const conn of this.conns) this.send(conn, msg);
|
|
402
475
|
}
|
|
476
|
+
/** Push the current portal state (plus the machine's declines) to every CLI. */
|
|
477
|
+
broadcastState() {
|
|
478
|
+
if (!this.lastState) return;
|
|
479
|
+
this.broadcast(stateMessage(this.lastState, this.declined));
|
|
480
|
+
}
|
|
403
481
|
armIdleTimer() {
|
|
404
482
|
this.clearIdleTimer();
|
|
405
483
|
this.idleTimer = setTimeout(() => this.shutdown(), IDLE_SHUTDOWN_MS);
|
|
@@ -417,6 +495,13 @@ var PortalDaemon = class {
|
|
|
417
495
|
const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${context}: ${message}\n`;
|
|
418
496
|
appendFile(this.paths.logPath, line).catch((_error) => {});
|
|
419
497
|
}
|
|
498
|
+
/** Close the listener without exiting the process (tests own the process). */
|
|
499
|
+
stopForTest() {
|
|
500
|
+
this.clearIdleTimer();
|
|
501
|
+
this.client?.dispose();
|
|
502
|
+
this.server?.close();
|
|
503
|
+
this.server = null;
|
|
504
|
+
}
|
|
420
505
|
shutdown() {
|
|
421
506
|
if (this.conns.size > 0) return;
|
|
422
507
|
this.client?.dispose();
|
|
@@ -436,7 +521,8 @@ var PortalDaemon = class {
|
|
|
436
521
|
machineName: portal?.machineName ?? "",
|
|
437
522
|
friendlyName: portal?.friendlyName ?? "",
|
|
438
523
|
error: portal?.error ?? null,
|
|
439
|
-
grantedAgentIds: portal?.grantedAgentIds ?? []
|
|
524
|
+
grantedAgentIds: portal?.grantedAgentIds ?? [],
|
|
525
|
+
declinedAgentIds: [...this.declined]
|
|
440
526
|
},
|
|
441
527
|
clientCount: [...this.conns].filter((c) => c.sessionId !== null).length,
|
|
442
528
|
cwds: Object.fromEntries(this.cwds)
|
|
@@ -462,13 +548,17 @@ var PortalDaemon = class {
|
|
|
462
548
|
const parsed = JSON.parse(raw);
|
|
463
549
|
if (isRecord(parsed) && parsed.version === LOCAL_PROTOCOL_VERSION && isRecord(parsed.cwds)) {
|
|
464
550
|
for (const [id, cwd] of Object.entries(parsed.cwds)) if (typeof cwd === "string") this.cwds.set(id, cwd);
|
|
551
|
+
if (Array.isArray(parsed.declined)) {
|
|
552
|
+
for (const id of parsed.declined) if (typeof id === "string") this.declined.add(id);
|
|
553
|
+
}
|
|
465
554
|
}
|
|
466
555
|
} catch (_error) {}
|
|
467
556
|
}
|
|
468
557
|
persistState() {
|
|
469
558
|
const state = {
|
|
470
559
|
version: LOCAL_PROTOCOL_VERSION,
|
|
471
|
-
cwds: Object.fromEntries(this.cwds)
|
|
560
|
+
cwds: Object.fromEntries(this.cwds),
|
|
561
|
+
declined: [...this.declined]
|
|
472
562
|
};
|
|
473
563
|
writeFile(this.paths.statePath, JSON.stringify(state)).catch((error) => {
|
|
474
564
|
this.logError("persistState failed", error);
|
|
@@ -615,14 +705,15 @@ async function stopDaemon(appUrl) {
|
|
|
615
705
|
}
|
|
616
706
|
return await isDaemonListening(socketPath) ? "failed" : "stopped";
|
|
617
707
|
}
|
|
618
|
-
function stateMessage(state) {
|
|
708
|
+
function stateMessage(state, declined) {
|
|
619
709
|
return {
|
|
620
710
|
t: "state",
|
|
621
711
|
status: state.status,
|
|
622
712
|
machineName: state.machineName,
|
|
623
713
|
friendlyName: state.friendlyName,
|
|
624
714
|
error: state.error,
|
|
625
|
-
grantedAgentIds: state.grantedAgentIds
|
|
715
|
+
grantedAgentIds: state.grantedAgentIds,
|
|
716
|
+
declinedAgentIds: [...declined]
|
|
626
717
|
};
|
|
627
718
|
}
|
|
628
719
|
function sleep(ms) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./client-
|
|
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-
|
|
2
|
+
import "./client-Dd5sMXPv.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-Bq93vOIk.mjs";
|
|
4
4
|
|
|
5
5
|
export { runPortalDaemon };
|
|
@@ -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-Bq93vOIk.mjs";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { connect } from "node:net";
|
|
5
5
|
|
|
@@ -13,6 +13,7 @@ var PortalDaemonClient = class {
|
|
|
13
13
|
fallbackCwd = null;
|
|
14
14
|
wantEnabled = false;
|
|
15
15
|
pendingGrants = /* @__PURE__ */ new Set();
|
|
16
|
+
pendingDeclines = /* @__PURE__ */ new Map();
|
|
16
17
|
grantedAgentIds = /* @__PURE__ */ new Set();
|
|
17
18
|
constructor(opts) {
|
|
18
19
|
this.opts = opts;
|
|
@@ -35,7 +36,8 @@ var PortalDaemonClient = class {
|
|
|
35
36
|
t: "hello",
|
|
36
37
|
v: LOCAL_PROTOCOL_VERSION,
|
|
37
38
|
sessionId: this.sessionId,
|
|
38
|
-
token: this.opts.sessionToken
|
|
39
|
+
token: this.opts.sessionToken,
|
|
40
|
+
tokenKind: "session"
|
|
39
41
|
});
|
|
40
42
|
if (this.wantEnabled) this.send({ t: "enable" });
|
|
41
43
|
if (this.fallbackCwd) this.send({
|
|
@@ -52,6 +54,11 @@ var PortalDaemonClient = class {
|
|
|
52
54
|
t: "grant",
|
|
53
55
|
agentId
|
|
54
56
|
});
|
|
57
|
+
for (const [agentId, declined] of this.pendingDeclines) this.send({
|
|
58
|
+
t: "decline",
|
|
59
|
+
agentId,
|
|
60
|
+
declined
|
|
61
|
+
});
|
|
55
62
|
});
|
|
56
63
|
socket.on("data", (chunk) => {
|
|
57
64
|
for (const line of parse(chunk)) {
|
|
@@ -82,7 +89,8 @@ var PortalDaemonClient = class {
|
|
|
82
89
|
machineName: msg.machineName,
|
|
83
90
|
friendlyName: msg.friendlyName,
|
|
84
91
|
error: msg.error,
|
|
85
|
-
grantedAgentIds: msg.grantedAgentIds
|
|
92
|
+
grantedAgentIds: msg.grantedAgentIds,
|
|
93
|
+
declinedAgentIds: msg.declinedAgentIds
|
|
86
94
|
});
|
|
87
95
|
}
|
|
88
96
|
}
|
|
@@ -108,6 +116,19 @@ var PortalDaemonClient = class {
|
|
|
108
116
|
this.send({ t: "disable" });
|
|
109
117
|
}
|
|
110
118
|
/**
|
|
119
|
+
* Record that the user said no (or yes again) to one agent on this machine.
|
|
120
|
+
* The daemon owns the answer, so every attached CLI stops asking — and it
|
|
121
|
+
* closes the share when nothing else is granted.
|
|
122
|
+
*/
|
|
123
|
+
decline(agentId, declined) {
|
|
124
|
+
this.pendingDeclines.set(agentId, declined);
|
|
125
|
+
this.send({
|
|
126
|
+
t: "decline",
|
|
127
|
+
agentId,
|
|
128
|
+
declined
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
111
132
|
* Set the directory an exec runs in when the daemon can't place its
|
|
112
133
|
* conversation. `skydive portal open` shares one directory and has no
|
|
113
134
|
* conversation of its own, so this is how it routes.
|
|
@@ -14,14 +14,17 @@ 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-CKBQ8pft.mjs");
|
|
18
18
|
let signalConnected;
|
|
19
19
|
const connected = new Promise((resolve) => {
|
|
20
20
|
signalConnected = resolve;
|
|
21
21
|
});
|
|
22
22
|
const machineShare = new PortalClient({
|
|
23
23
|
appUrl,
|
|
24
|
-
|
|
24
|
+
credentials: () => ({
|
|
25
|
+
sessionToken,
|
|
26
|
+
deviceToken: null
|
|
27
|
+
}),
|
|
25
28
|
resolveCwd: () => process.cwd(),
|
|
26
29
|
onState: (state) => {
|
|
27
30
|
if (state.status === "connected") signalConnected();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skydive-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-beta.2",
|
|
4
4
|
"description": "Skydive CLI — cloud agents from the command line",
|
|
5
5
|
"homepage": "https://skydive.com",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"build:binary": "node ./scripts/embed-changelog.mjs && bun scripts/build-binary.ts",
|
|
24
24
|
"test:unit": "vitest run --passWithNoTests && yarn test:tui",
|
|
25
25
|
"test:ci": "vitest run --coverage --coverage.reporter=lcovonly --reporter=default --reporter=github-actions --minWorkers=1 --maxWorkers=2 --passWithNoTests && yarn test:tui",
|
|
26
|
-
"test:tui": "
|
|
26
|
+
"test:tui": "node scripts/run-tui-tests.mjs",
|
|
27
27
|
"render:frames": "bun scripts/render-frames.tsx",
|
|
28
28
|
"render:send-errors": "bun scripts/render-send-errors.tsx",
|
|
29
29
|
"typecheck": "tsgo --noEmit"
|