skydive-cli 0.2.0 → 0.3.0
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 +42 -0
- package/README.md +114 -22
- package/dist/js/bin.mjs +1261 -128
- package/dist/js/{boot-CGaXUEer.mjs → boot-HAgDg_b4.mjs} +2507 -194
- package/dist/js/{client-B3ZhhF7e.mjs → client-Dc7GZ3PG.mjs} +148 -8
- package/dist/js/client-DuwxEDG4.mjs +5 -0
- package/dist/js/daemon-Ch_MSDmQ.mjs +6 -0
- package/dist/js/daemon-Co4CtpXZ.mjs +607 -0
- package/dist/js/{theme-CuQhvqzN.mjs → install-CMYBFvR2.mjs} +256 -186
- package/dist/js/{print-BZH0b3KB.mjs → print-CPCFci60.mjs} +2 -2
- package/dist/js/{print-Bin4u16d.mjs → print-Cd-bVSEJ.mjs} +224 -10
- package/dist/js/{print-share-D7OSxvE2.mjs → print-share-7qfpQtWK.mjs} +3 -3
- package/dist/js/{raw-pty-GAvxm2ol.mjs → raw-pty-BcjbTjHJ.mjs} +1 -1
- package/dist/js/raw-pty-ChUHav4d.mjs +5 -0
- package/dist/js/{rest-CamHVOce.mjs → rest-BlN_uWmL.mjs} +150 -19
- package/dist/js/rest-CDTXCmUb.mjs +4 -0
- package/package.json +3 -1
- package/dist/js/api-CDTKq_5Q.mjs +0 -145
- package/dist/js/client-D6NAkL9e.mjs +0 -6
- package/dist/js/raw-pty-2_VA1kw_.mjs +0 -5
- package/dist/js/rest-COkLEZOB.mjs +0 -4
- package/dist/js/util-CeisaZVY.mjs +0 -15
- /package/dist/js/{client-CpEvH2Pq.mjs → client-DfcJFEbh.mjs} +0 -0
- /package/dist/js/{output-B4cW10Ph.mjs → output-DYzzdXYV.mjs} +0 -0
package/dist/js/api-CDTKq_5Q.mjs
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { t as HttpError } from "./rest-CamHVOce.mjs";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
|
|
6
|
-
//#region src/chat/portal/machine.ts
|
|
7
|
-
/**
|
|
8
|
-
* Identity this machine registers under when the CLI shares it via the portal.
|
|
9
|
-
*
|
|
10
|
-
* The `-cli` suffix / `(CLI)` label keep a CLI-shared machine a DISTINCT portal
|
|
11
|
-
* device from the same host's Skydive Desktop app. `portal_device` is unique on
|
|
12
|
-
* (org, user, machineName), and directives route to whichever socket holds the
|
|
13
|
-
* device — if the CLI and desktop registered the same name they'd share a
|
|
14
|
-
* device row and both execute every directive. Distinct names also make the
|
|
15
|
-
* grant UI unambiguous about which surface is being authorized.
|
|
16
|
-
*/
|
|
17
|
-
function machineIdentity() {
|
|
18
|
-
const host = (os.hostname() || "machine").trim().replace(/\.local$/i, "") || "machine";
|
|
19
|
-
return {
|
|
20
|
-
machineName: `${host}-cli`,
|
|
21
|
-
friendlyName: `${host} (CLI)`
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
const INHERITED_ENV = [
|
|
25
|
-
"HOME",
|
|
26
|
-
"USER",
|
|
27
|
-
"LOGNAME",
|
|
28
|
-
"SHELL",
|
|
29
|
-
"LANG",
|
|
30
|
-
"LC_ALL",
|
|
31
|
-
"TMPDIR",
|
|
32
|
-
"TERM",
|
|
33
|
-
"PATH"
|
|
34
|
-
];
|
|
35
|
-
function buildEnv(extra) {
|
|
36
|
-
const env = {};
|
|
37
|
-
for (const key of INHERITED_ENV) {
|
|
38
|
-
const value = process.env[key];
|
|
39
|
-
if (value !== void 0) env[key] = value;
|
|
40
|
-
}
|
|
41
|
-
if (extra) for (const [key, value] of Object.entries(extra)) env[key] = value;
|
|
42
|
-
return env;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Build the desktop-portal WebSocket URL from the chat origin. Mirrors the Rust
|
|
46
|
-
* desktop client: http→ws, https→wss, scheme-less defaults to wss, and the
|
|
47
|
-
* machine/label ride as query pairs (percent-encoded by URL).
|
|
48
|
-
*/
|
|
49
|
-
function portalWsUrl(appUrl, machine, label) {
|
|
50
|
-
const base = appUrl.replace(/\/+$/, "");
|
|
51
|
-
let wsBase;
|
|
52
|
-
if (base.startsWith("https://")) wsBase = `wss://${base.slice(8)}`;
|
|
53
|
-
else if (base.startsWith("http://")) wsBase = `ws://${base.slice(7)}`;
|
|
54
|
-
else wsBase = `wss://${base}`;
|
|
55
|
-
const url = new URL(`${wsBase}/api/v1/portal/desktop`);
|
|
56
|
-
url.searchParams.set("machine", machine);
|
|
57
|
-
url.searchParams.set("label", label);
|
|
58
|
-
return url.toString();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
//#endregion
|
|
62
|
-
//#region src/chat/portal/api.ts
|
|
63
|
-
/**
|
|
64
|
-
* The portal's session-authed REST surface, shared by `PortalClient` (the
|
|
65
|
-
* TUI/`portal open` connection) and the `skydive portal` management
|
|
66
|
-
* commands, so the endpoint contracts and response schemas live in exactly
|
|
67
|
-
* one place.
|
|
68
|
-
*/
|
|
69
|
-
const deviceSchema = z.object({
|
|
70
|
-
id: z.string(),
|
|
71
|
-
machineName: z.string(),
|
|
72
|
-
friendlyName: z.string(),
|
|
73
|
-
connected: z.boolean(),
|
|
74
|
-
lastSeen: z.string().nullable(),
|
|
75
|
-
grantedAgentIds: z.array(z.string())
|
|
76
|
-
});
|
|
77
|
-
const devicesResponseSchema = z.object({
|
|
78
|
-
devices: z.array(deviceSchema),
|
|
79
|
-
agents: z.array(z.object({
|
|
80
|
-
id: z.string(),
|
|
81
|
-
name: z.string()
|
|
82
|
-
}))
|
|
83
|
-
});
|
|
84
|
-
const deviceTokenSchema = z.object({ token: z.string().min(1) });
|
|
85
|
-
async function portalFetch(auth, path, init) {
|
|
86
|
-
const res = await fetch(`${auth.appUrl}${path}`, {
|
|
87
|
-
method: init.method,
|
|
88
|
-
headers: {
|
|
89
|
-
authorization: `Bearer ${auth.sessionToken}`,
|
|
90
|
-
accept: "application/json",
|
|
91
|
-
...init.body ? { "content-type": "application/json" } : {}
|
|
92
|
-
},
|
|
93
|
-
...init.body ? { body: init.body } : {}
|
|
94
|
-
});
|
|
95
|
-
if (!res.ok) {
|
|
96
|
-
const body = await res.text().catch(() => "");
|
|
97
|
-
throw new HttpError(res.status, body);
|
|
98
|
-
}
|
|
99
|
-
return res.json();
|
|
100
|
-
}
|
|
101
|
-
async function fetchPortalDevices(auth) {
|
|
102
|
-
const json = await portalFetch(auth, "/api/v1/portal/devices", { method: "GET" });
|
|
103
|
-
return devicesResponseSchema.parse(json);
|
|
104
|
-
}
|
|
105
|
-
const registerResponseSchema = z.object({ device: z.object({ id: z.string() }) });
|
|
106
|
-
/**
|
|
107
|
-
* Register this machine's device row without connecting. Connecting registers
|
|
108
|
-
* as a side effect; this covers granting an agent on a machine that has never
|
|
109
|
-
* shared yet (the grant references the device row).
|
|
110
|
-
*/
|
|
111
|
-
async function registerPortalDevice(auth, { machineName, friendlyName }) {
|
|
112
|
-
const json = await portalFetch(auth, "/api/v1/portal/devices", {
|
|
113
|
-
method: "POST",
|
|
114
|
-
body: JSON.stringify({
|
|
115
|
-
machineName,
|
|
116
|
-
friendlyName
|
|
117
|
-
})
|
|
118
|
-
});
|
|
119
|
-
return registerResponseSchema.parse(json).device;
|
|
120
|
-
}
|
|
121
|
-
/** Short-lived token the machine presents when dialing the portal WebSocket. */
|
|
122
|
-
async function mintPortalDeviceToken(auth) {
|
|
123
|
-
const json = await portalFetch(auth, "/api/v1/portal/device-token", { method: "POST" });
|
|
124
|
-
return deviceTokenSchema.parse(json).token;
|
|
125
|
-
}
|
|
126
|
-
async function grantPortalAccess(auth, { deviceId, agentId }) {
|
|
127
|
-
await portalFetch(auth, `/api/v1/portal/devices/${encodeURIComponent(deviceId)}/grants`, {
|
|
128
|
-
method: "POST",
|
|
129
|
-
body: JSON.stringify({ agentId })
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
async function revokePortalAccess(auth, { deviceId, agentId }) {
|
|
133
|
-
await portalFetch(auth, `/api/v1/portal/devices/${encodeURIComponent(deviceId)}/grants/${encodeURIComponent(agentId)}`, { method: "DELETE" });
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* The device row for a given machine identity. Matching is by `machineName`
|
|
137
|
-
* equality — the stable per-surface handle (`<host>-cli` vs the desktop's
|
|
138
|
-
* `<host>`), not the display label.
|
|
139
|
-
*/
|
|
140
|
-
function findThisDevice(devices, machineName) {
|
|
141
|
-
return devices.find((device) => device.machineName === machineName) ?? null;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
//#endregion
|
|
145
|
-
export { registerPortalDevice as a, machineIdentity as c, mintPortalDeviceToken as i, portalWsUrl as l, findThisDevice as n, revokePortalAccess as o, grantPortalAccess as r, buildEnv as s, fetchPortalDevices as t };
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
//#region src/chat/util.ts
|
|
3
|
-
/** Narrowing helper for the many `unknown` payloads the chat stream and
|
|
4
|
-
* tool inputs/outputs carry. A type predicate (not an `as` cast), so call
|
|
5
|
-
* sites can read properties without asserting. */
|
|
6
|
-
function isRecord(value) {
|
|
7
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8
|
-
}
|
|
9
|
-
/** Best-effort message from an unknown thrown value. */
|
|
10
|
-
function errorMessage(err) {
|
|
11
|
-
return err instanceof Error ? err.message : String(err);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
//#endregion
|
|
15
|
-
export { isRecord as n, errorMessage as t };
|
|
File without changes
|
|
File without changes
|