skydive-cli 0.2.0-beta.613 → 0.2.0-beta.618
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 +134 -26
- package/dist/js/{boot-BnaFkGl4.mjs → boot-CaWHIM4R.mjs} +234 -20
- package/dist/js/client-CKlIpyAb.mjs +5 -0
- package/dist/js/{client-l3OrGKaC.mjs → client-DT9bWi1D.mjs} +148 -8
- package/dist/js/daemon-CqhMYzpN.mjs +6 -0
- package/dist/js/daemon-DfMgVlYE.mjs +607 -0
- package/dist/js/{install-D1uhYunw.mjs → install-BhPuzBxW.mjs} +3 -189
- package/dist/js/{print-6Bm51XhR.mjs → print-CNIvmpqW.mjs} +194 -4
- package/dist/js/{print-qXJymvuF.mjs → print-nefugWMx.mjs} +2 -2
- package/dist/js/{print-share-DE_dkZX7.mjs → print-share-D8Nn1qFq.mjs} +3 -3
- package/dist/js/{raw-pty-Dbi2kb9v.mjs → raw-pty-BcjbTjHJ.mjs} +1 -1
- package/dist/js/raw-pty-ChUHav4d.mjs +5 -0
- package/dist/js/{rest-C3RySrPa.mjs → rest-CBROy0L-.mjs} +1 -1
- package/package.json +1 -1
- package/dist/js/api-BGWcPuik.mjs +0 -145
- package/dist/js/client-BRfmlbgI.mjs +0 -6
- package/dist/js/raw-pty-pspO57gT.mjs +0 -5
- /package/dist/js/{client-BVOAwU8M.mjs → client-DfcJFEbh.mjs} +0 -0
- /package/dist/js/{output-B4cW10Ph.mjs → output-DYzzdXYV.mjs} +0 -0
- /package/dist/js/{rest-DMpigwmN.mjs → rest-Q5yl_b2-.mjs} +0 -0
|
@@ -1,200 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { S as getConfigPath } from "./print-CNIvmpqW.mjs";
|
|
2
3
|
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
|
-
import Conf from "conf";
|
|
5
5
|
import { err, ok } from "neverthrow";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import fs from "node:fs";
|
|
8
8
|
|
|
9
9
|
//#region package.json
|
|
10
10
|
var name = "skydive-cli";
|
|
11
|
-
var version$1 = "0.2.0-beta.
|
|
12
|
-
|
|
13
|
-
//#endregion
|
|
14
|
-
//#region src/config.ts
|
|
15
|
-
/** Default host for the public management API (`/v1`, API-key auth). */
|
|
16
|
-
const DEFAULT_API_URL = "https://api.skydive.com";
|
|
17
|
-
/**
|
|
18
|
-
* Default origin for the interactive chat client (`skydive chat`).
|
|
19
|
-
*
|
|
20
|
-
* The API host that serves better-auth (`/api/auth/*`) and the internal tRPC
|
|
21
|
-
* API (`/api/v1/trpc`) that chat streams over. We target the API host
|
|
22
|
-
* directly (not the web front door) because chat opens a WebSocket and
|
|
23
|
-
* authenticates with a bearer token on the upgrade request. Same host as
|
|
24
|
-
* `DEFAULT_API_URL`; override with `--api-url` / `SKYDIVE_APP_URL` for local
|
|
25
|
-
* dev or while the DNS record is still being provisioned.
|
|
26
|
-
*/
|
|
27
|
-
const DEFAULT_APP_URL = "https://api.skydive.com";
|
|
28
|
-
/** Web front door, for pages opened in the user's browser. */
|
|
29
|
-
const DEFAULT_WEB_URL = "https://skydive.com";
|
|
30
|
-
/**
|
|
31
|
-
* Origin for browser-facing links (e.g. opening a conversation's web page).
|
|
32
|
-
* The app origin is the API host, which serves no web UI in production, so
|
|
33
|
-
* map the default to the web front door. Overridden origins (local dev,
|
|
34
|
-
* previews) serve both and pass through unchanged.
|
|
35
|
-
*/
|
|
36
|
-
function resolveWebUrl(appUrl) {
|
|
37
|
-
return appUrl === DEFAULT_APP_URL ? DEFAULT_WEB_URL : appUrl;
|
|
38
|
-
}
|
|
39
|
-
/** Prefix on workspace-scoped Skydive API keys. Kept in sync with the API's
|
|
40
|
-
* `API_KEY_PREFIX` (`apps/anyone/api/src/lib/api-key.ts`); the CLI is a
|
|
41
|
-
* standalone published package so it can't import the backend constant. */
|
|
42
|
-
const API_KEY_PREFIX = "sky_live_";
|
|
43
|
-
/**
|
|
44
|
-
* Common prefix across all Skydive API key kinds — `sky_live_…` workspace
|
|
45
|
-
* keys today, `sky_user_…` account keys when ANY-5105 lands. The CLI only
|
|
46
|
-
* sanity-checks the family on `--api-key`; the server authoritatively rejects
|
|
47
|
-
* a kind that can't drive a given route, with a clearer message than the
|
|
48
|
-
* client could produce.
|
|
49
|
-
*/
|
|
50
|
-
const API_KEY_FAMILY_PREFIX = "sky_";
|
|
51
|
-
/** Where users mint and copy API keys. Shown in the login prompt. */
|
|
52
|
-
const API_KEYS_URL = "skydive.com/settings/account";
|
|
53
|
-
const store = new Conf({
|
|
54
|
-
projectName: process.env["SKYDIVE_CONFIG_NAME"] ?? "skydive",
|
|
55
|
-
projectSuffix: "",
|
|
56
|
-
configFileMode: 384
|
|
57
|
-
});
|
|
58
|
-
function resolveConfig(opts) {
|
|
59
|
-
const apiKey = process.env["SKYDIVE_API_KEY"] ?? store.get("apiKey");
|
|
60
|
-
const apiUrl = process.env["SKYDIVE_API_URL"] ?? opts.apiUrl ?? store.get("apiUrl") ?? DEFAULT_API_URL;
|
|
61
|
-
if (!apiKey) return err({ message: "Not authenticated. Run `skydive auth login` first." });
|
|
62
|
-
return ok({
|
|
63
|
-
apiKey,
|
|
64
|
-
apiUrl
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Resolve the bearer credential for the management API (`agents` / `keys` /
|
|
69
|
-
* `secrets`). The server's `/v1` gate accepts either an API key or the
|
|
70
|
-
* device-flow session bearer, so both work — but only one of them tracks the
|
|
71
|
-
* active workspace.
|
|
72
|
-
*
|
|
73
|
-
* An API key is pinned server-side to the organization that minted it and
|
|
74
|
-
* ignores the workspace header by design, so it can never follow `skydive
|
|
75
|
-
* workspace switch`. A key is also a strictly narrower credential than its
|
|
76
|
-
* owner's session. So the session wins whenever there is one, and a key is
|
|
77
|
-
* what's left for machines that never ran an interactive login.
|
|
78
|
-
*
|
|
79
|
-
* `SKYDIVE_SESSION_TOKEN=` (empty) suppresses session auth for one
|
|
80
|
-
* invocation, to drive a specific organization's key while signed in.
|
|
81
|
-
*/
|
|
82
|
-
function resolveManagementAuth(opts) {
|
|
83
|
-
const session = resolveSession({ appUrl: opts.apiUrl });
|
|
84
|
-
if (session.isOk()) return ok({
|
|
85
|
-
token: session.value.sessionToken,
|
|
86
|
-
apiUrl: session.value.appUrl,
|
|
87
|
-
kind: "session",
|
|
88
|
-
pinnedWorkspaceName: null
|
|
89
|
-
});
|
|
90
|
-
const envKey = process.env["SKYDIVE_API_KEY"];
|
|
91
|
-
const storedKey = store.get("apiKey");
|
|
92
|
-
const apiKey = envKey ?? storedKey;
|
|
93
|
-
if (apiKey) return ok({
|
|
94
|
-
token: apiKey,
|
|
95
|
-
apiUrl: process.env["SKYDIVE_API_URL"] ?? opts.apiUrl ?? store.get("apiUrl") ?? DEFAULT_API_URL,
|
|
96
|
-
kind: "api-key",
|
|
97
|
-
pinnedWorkspaceName: !envKey && storedKey ? store.get("apiKeyWorkspaceName") ?? null : null
|
|
98
|
-
});
|
|
99
|
-
return err({ message: "Not authenticated. Run `skydive auth login`." });
|
|
100
|
-
}
|
|
101
|
-
function saveConfig(config) {
|
|
102
|
-
store.set("apiKey", config.apiKey);
|
|
103
|
-
store.set("apiUrl", config.apiUrl);
|
|
104
|
-
if (config.apiKeyId) store.set("apiKeyId", config.apiKeyId);
|
|
105
|
-
else store.delete("apiKeyId");
|
|
106
|
-
if (config.workspaceName) store.set("apiKeyWorkspaceName", config.workspaceName);
|
|
107
|
-
else store.delete("apiKeyWorkspaceName");
|
|
108
|
-
}
|
|
109
|
-
/** Server-side id of the auto-minted key, if login minted one. */
|
|
110
|
-
function getStoredApiKeyId() {
|
|
111
|
-
return store.get("apiKeyId") ?? null;
|
|
112
|
-
}
|
|
113
|
-
/** Workspace the auto-minted key is pinned to, if login recorded one. */
|
|
114
|
-
function getStoredApiKeyWorkspaceName() {
|
|
115
|
-
return store.get("apiKeyWorkspaceName") ?? null;
|
|
116
|
-
}
|
|
117
|
-
function deleteConfig() {
|
|
118
|
-
store.clear();
|
|
119
|
-
}
|
|
120
|
-
function getConfigPath() {
|
|
121
|
-
return store.path;
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Where the chat TUI persists its prompt history (up-arrow recall). Kept
|
|
125
|
-
* beside the config file so all CLI state lives in one directory.
|
|
126
|
-
*/
|
|
127
|
-
function getLastSeenVersion() {
|
|
128
|
-
return store.get("lastSeenVersion");
|
|
129
|
-
}
|
|
130
|
-
function setLastSeenVersion(version) {
|
|
131
|
-
store.set("lastSeenVersion", version);
|
|
132
|
-
}
|
|
133
|
-
function getPromptHistoryPath() {
|
|
134
|
-
return path.join(path.dirname(store.path), "prompt-history.jsonl");
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Where the chat TUI persists pending review comments — one JSON file per
|
|
138
|
-
* conversation, so a pending comment survives conversation switches and
|
|
139
|
-
* process death. Kept beside the config file like prompt history.
|
|
140
|
-
*/
|
|
141
|
-
function getReviewStateDir() {
|
|
142
|
-
return path.join(path.dirname(store.path), "review");
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Resolve the chat/auth origin. Precedence: `SKYDIVE_APP_URL` env > explicit
|
|
146
|
-
* `--api-url` style override > stored value > `SKYDIVE_API_URL` env >
|
|
147
|
-
* `DEFAULT_APP_URL`.
|
|
148
|
-
*
|
|
149
|
-
* The `SKYDIVE_API_URL` fallback matters for previews: the device/`--web`
|
|
150
|
-
* flow and chat hit the same api service as the management API, so pointing
|
|
151
|
-
* `SKYDIVE_API_URL` at a preview stack is enough — you don't also have to set
|
|
152
|
-
* `SKYDIVE_APP_URL`. Otherwise auth would silently fall through to prod
|
|
153
|
-
* (`DEFAULT_APP_URL`) and hand back a prod verification URL.
|
|
154
|
-
*/
|
|
155
|
-
function resolveAppUrl(opts) {
|
|
156
|
-
return process.env["SKYDIVE_APP_URL"] ?? opts.appUrl ?? store.get("appUrl") ?? process.env["SKYDIVE_API_URL"] ?? DEFAULT_APP_URL;
|
|
157
|
-
}
|
|
158
|
-
function resolveSession(opts) {
|
|
159
|
-
const sessionToken = process.env["SKYDIVE_SESSION_TOKEN"] ?? store.get("sessionToken");
|
|
160
|
-
const appUrl = resolveAppUrl(opts);
|
|
161
|
-
if (!sessionToken) return err({ message: "Not signed in for chat. Run `skydive chat` to sign in." });
|
|
162
|
-
return ok({
|
|
163
|
-
sessionToken,
|
|
164
|
-
appUrl
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
function saveSession(session) {
|
|
168
|
-
store.set("sessionToken", session.sessionToken);
|
|
169
|
-
store.set("sessionObtainedAt", (/* @__PURE__ */ new Date()).toISOString());
|
|
170
|
-
store.set("appUrl", session.appUrl);
|
|
171
|
-
}
|
|
172
|
-
function getSavedTheme(mode) {
|
|
173
|
-
return store.get(mode === "dark" ? "themeDark" : "themeLight");
|
|
174
|
-
}
|
|
175
|
-
function saveTheme(mode, themeId) {
|
|
176
|
-
store.set(mode === "dark" ? "themeDark" : "themeLight", themeId);
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Whether `skydive chat` should enable portal machine sharing on launch
|
|
180
|
-
* without `--share-machine`. Set by hand-editing `shareMachineDefault` in
|
|
181
|
-
* config.json — deliberately no CLI command (kept off the API surface).
|
|
182
|
-
* Sharing only makes the machine reachable — agents still need a
|
|
183
|
-
* (persistent) grant to run anything, so this default skips the per-session
|
|
184
|
-
* enable step, not the consent step.
|
|
185
|
-
*/
|
|
186
|
-
function getShareMachineDefault() {
|
|
187
|
-
return store.get("shareMachineDefault") ?? false;
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Whether the update check is disabled via config.json (`"updateCheck":
|
|
191
|
-
* false`). The persistent counterpart to the SKYDIVE_NO_UPDATE_CHECK /
|
|
192
|
-
* NO_UPDATE_NOTIFIER env opt-outs; like shareMachineDefault, set by editing
|
|
193
|
-
* config.json — deliberately no CLI command.
|
|
194
|
-
*/
|
|
195
|
-
function getUpdateCheckDisabled() {
|
|
196
|
-
return store.get("updateCheck") === false;
|
|
197
|
-
}
|
|
11
|
+
var version$1 = "0.2.0-beta.618";
|
|
198
12
|
|
|
199
13
|
//#endregion
|
|
200
14
|
//#region src/auth/organization.ts
|
|
@@ -1243,4 +1057,4 @@ function printFatalNotice(file, memory) {
|
|
|
1243
1057
|
}
|
|
1244
1058
|
|
|
1245
1059
|
//#endregion
|
|
1246
|
-
export {
|
|
1060
|
+
export { setActiveWorkspace as C, listWorkspaces as S, version$1 as T, themes as _, WORDMARK as a, getActiveWorkspaceId as b, applyTheme as c, noColorRequested as d, theme as f, themeVersion as g, themeModeFromColorFgBg as h, MARK_CELLS as i, findTheme as l, themeMode as m, buildCrashReport as n, brandHelpArt as o, themeForMode as p, writeCrashReport as r, DEFAULT_THEME_ID as s, installCrashHandler as t, monoTheme as u, themesForMode as v, name as w, getSessionIdentity as x, ensureActiveOrganization as y };
|
|
@@ -1,7 +1,191 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as errorMessage, i as sendErrorMessage, n as createRestClient, o as isRecord, t as HttpError } from "./rest-
|
|
2
|
+
import { a as errorMessage, i as sendErrorMessage, n as createRestClient, o as isRecord, t as HttpError } from "./rest-Q5yl_b2-.mjs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import Conf from "conf";
|
|
5
|
+
import { err, ok } from "neverthrow";
|
|
3
6
|
import stableStringify from "safe-stable-stringify";
|
|
4
7
|
|
|
8
|
+
//#region src/config.ts
|
|
9
|
+
/** Default host for the public management API (`/v1`, API-key auth). */
|
|
10
|
+
const DEFAULT_API_URL = "https://api.skydive.com";
|
|
11
|
+
/**
|
|
12
|
+
* Default origin for the interactive chat client (`skydive chat`).
|
|
13
|
+
*
|
|
14
|
+
* The API host that serves better-auth (`/api/auth/*`) and the internal tRPC
|
|
15
|
+
* API (`/api/v1/trpc`) that chat streams over. We target the API host
|
|
16
|
+
* directly (not the web front door) because chat opens a WebSocket and
|
|
17
|
+
* authenticates with a bearer token on the upgrade request. Same host as
|
|
18
|
+
* `DEFAULT_API_URL`; override with `--api-url` / `SKYDIVE_APP_URL` for local
|
|
19
|
+
* dev or while the DNS record is still being provisioned.
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_APP_URL = "https://api.skydive.com";
|
|
22
|
+
/** Web front door, for pages opened in the user's browser. */
|
|
23
|
+
const DEFAULT_WEB_URL = "https://skydive.com";
|
|
24
|
+
function resolveWebUrl(appUrl) {
|
|
25
|
+
if (appUrl == null) return appUrl;
|
|
26
|
+
return appUrl === DEFAULT_APP_URL ? DEFAULT_WEB_URL : appUrl;
|
|
27
|
+
}
|
|
28
|
+
/** Prefix on workspace-scoped Skydive API keys. Kept in sync with the API's
|
|
29
|
+
* `API_KEY_PREFIX` (`apps/anyone/api/src/lib/api-key.ts`); the CLI is a
|
|
30
|
+
* standalone published package so it can't import the backend constant. */
|
|
31
|
+
const API_KEY_PREFIX = "sky_live_";
|
|
32
|
+
/**
|
|
33
|
+
* Common prefix across all Skydive API key kinds — `sky_live_…` workspace
|
|
34
|
+
* keys today, `sky_user_…` account keys when ANY-5105 lands. The CLI only
|
|
35
|
+
* sanity-checks the family on `--api-key`; the server authoritatively rejects
|
|
36
|
+
* a kind that can't drive a given route, with a clearer message than the
|
|
37
|
+
* client could produce.
|
|
38
|
+
*/
|
|
39
|
+
const API_KEY_FAMILY_PREFIX = "sky_";
|
|
40
|
+
/** Where users mint and copy API keys. Shown in the login prompt. */
|
|
41
|
+
const API_KEYS_URL = "skydive.com/settings/account";
|
|
42
|
+
const store = new Conf({
|
|
43
|
+
projectName: process.env["SKYDIVE_CONFIG_NAME"] ?? "skydive",
|
|
44
|
+
projectSuffix: "",
|
|
45
|
+
configFileMode: 384
|
|
46
|
+
});
|
|
47
|
+
function resolveConfig(opts) {
|
|
48
|
+
const apiKey = process.env["SKYDIVE_API_KEY"] ?? store.get("apiKey");
|
|
49
|
+
const apiUrl = process.env["SKYDIVE_API_URL"] ?? opts.apiUrl ?? store.get("apiUrl") ?? DEFAULT_API_URL;
|
|
50
|
+
if (!apiKey) return err({ message: "Not authenticated. Run `skydive auth login` first." });
|
|
51
|
+
return ok({
|
|
52
|
+
apiKey,
|
|
53
|
+
apiUrl
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Resolve the bearer credential for the management API (`agents` / `keys` /
|
|
58
|
+
* `secrets`). The server's `/v1` gate accepts either an API key or the
|
|
59
|
+
* device-flow session bearer, so both work — but only one of them tracks the
|
|
60
|
+
* active workspace.
|
|
61
|
+
*
|
|
62
|
+
* An API key is pinned server-side to the organization that minted it and
|
|
63
|
+
* ignores the workspace header by design, so it can never follow `skydive
|
|
64
|
+
* workspace switch`. A key is also a strictly narrower credential than its
|
|
65
|
+
* owner's session. So the session wins whenever there is one, and a key is
|
|
66
|
+
* what's left for machines that never ran an interactive login.
|
|
67
|
+
*
|
|
68
|
+
* `SKYDIVE_SESSION_TOKEN=` (empty) suppresses session auth for one
|
|
69
|
+
* invocation, to drive a specific organization's key while signed in.
|
|
70
|
+
*/
|
|
71
|
+
function resolveManagementAuth(opts) {
|
|
72
|
+
const session = resolveSession({ appUrl: opts.apiUrl });
|
|
73
|
+
if (session.isOk()) return ok({
|
|
74
|
+
token: session.value.sessionToken,
|
|
75
|
+
apiUrl: session.value.appUrl,
|
|
76
|
+
kind: "session",
|
|
77
|
+
pinnedWorkspaceName: null
|
|
78
|
+
});
|
|
79
|
+
const envKey = process.env["SKYDIVE_API_KEY"];
|
|
80
|
+
const storedKey = store.get("apiKey");
|
|
81
|
+
const apiKey = envKey ?? storedKey;
|
|
82
|
+
if (apiKey) return ok({
|
|
83
|
+
token: apiKey,
|
|
84
|
+
apiUrl: process.env["SKYDIVE_API_URL"] ?? opts.apiUrl ?? store.get("apiUrl") ?? DEFAULT_API_URL,
|
|
85
|
+
kind: "api-key",
|
|
86
|
+
pinnedWorkspaceName: !envKey && storedKey ? store.get("apiKeyWorkspaceName") ?? null : null
|
|
87
|
+
});
|
|
88
|
+
return err({ message: "Not authenticated. Run `skydive auth login`." });
|
|
89
|
+
}
|
|
90
|
+
function saveConfig(config) {
|
|
91
|
+
store.set("apiKey", config.apiKey);
|
|
92
|
+
store.set("apiUrl", config.apiUrl);
|
|
93
|
+
if (config.apiKeyId) store.set("apiKeyId", config.apiKeyId);
|
|
94
|
+
else store.delete("apiKeyId");
|
|
95
|
+
if (config.workspaceName) store.set("apiKeyWorkspaceName", config.workspaceName);
|
|
96
|
+
else store.delete("apiKeyWorkspaceName");
|
|
97
|
+
}
|
|
98
|
+
/** Server-side id of the auto-minted key, if login minted one. */
|
|
99
|
+
function getStoredApiKeyId() {
|
|
100
|
+
return store.get("apiKeyId") ?? null;
|
|
101
|
+
}
|
|
102
|
+
/** Workspace the auto-minted key is pinned to, if login recorded one. */
|
|
103
|
+
function getStoredApiKeyWorkspaceName() {
|
|
104
|
+
return store.get("apiKeyWorkspaceName") ?? null;
|
|
105
|
+
}
|
|
106
|
+
function deleteConfig() {
|
|
107
|
+
store.clear();
|
|
108
|
+
}
|
|
109
|
+
function getConfigPath() {
|
|
110
|
+
return store.path;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Where the chat TUI persists its prompt history (up-arrow recall). Kept
|
|
114
|
+
* beside the config file so all CLI state lives in one directory.
|
|
115
|
+
*/
|
|
116
|
+
function getLastSeenVersion() {
|
|
117
|
+
return store.get("lastSeenVersion");
|
|
118
|
+
}
|
|
119
|
+
function setLastSeenVersion(version) {
|
|
120
|
+
store.set("lastSeenVersion", version);
|
|
121
|
+
}
|
|
122
|
+
function getPromptHistoryPath() {
|
|
123
|
+
return path.join(path.dirname(store.path), "prompt-history.jsonl");
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Where the chat TUI persists pending review comments — one JSON file per
|
|
127
|
+
* conversation, so a pending comment survives conversation switches and
|
|
128
|
+
* process death. Kept beside the config file like prompt history.
|
|
129
|
+
*/
|
|
130
|
+
function getReviewStateDir() {
|
|
131
|
+
return path.join(path.dirname(store.path), "review");
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Resolve the chat/auth origin. Precedence: `SKYDIVE_APP_URL` env > explicit
|
|
135
|
+
* `--api-url` style override > stored value > `SKYDIVE_API_URL` env >
|
|
136
|
+
* `DEFAULT_APP_URL`.
|
|
137
|
+
*
|
|
138
|
+
* The `SKYDIVE_API_URL` fallback matters for previews: the device/`--web`
|
|
139
|
+
* flow and chat hit the same api service as the management API, so pointing
|
|
140
|
+
* `SKYDIVE_API_URL` at a preview stack is enough — you don't also have to set
|
|
141
|
+
* `SKYDIVE_APP_URL`. Otherwise auth would silently fall through to prod
|
|
142
|
+
* (`DEFAULT_APP_URL`) and hand back a prod verification URL.
|
|
143
|
+
*/
|
|
144
|
+
function resolveAppUrl(opts) {
|
|
145
|
+
return process.env["SKYDIVE_APP_URL"] ?? opts.appUrl ?? store.get("appUrl") ?? process.env["SKYDIVE_API_URL"] ?? DEFAULT_APP_URL;
|
|
146
|
+
}
|
|
147
|
+
function resolveSession(opts) {
|
|
148
|
+
const sessionToken = process.env["SKYDIVE_SESSION_TOKEN"] ?? store.get("sessionToken");
|
|
149
|
+
const appUrl = resolveAppUrl(opts);
|
|
150
|
+
if (!sessionToken) return err({ message: "Not signed in for chat. Run `skydive chat` to sign in." });
|
|
151
|
+
return ok({
|
|
152
|
+
sessionToken,
|
|
153
|
+
appUrl
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function saveSession(session) {
|
|
157
|
+
store.set("sessionToken", session.sessionToken);
|
|
158
|
+
store.set("sessionObtainedAt", (/* @__PURE__ */ new Date()).toISOString());
|
|
159
|
+
store.set("appUrl", session.appUrl);
|
|
160
|
+
}
|
|
161
|
+
function getSavedTheme(mode) {
|
|
162
|
+
return store.get(mode === "dark" ? "themeDark" : "themeLight");
|
|
163
|
+
}
|
|
164
|
+
function saveTheme(mode, themeId) {
|
|
165
|
+
store.set(mode === "dark" ? "themeDark" : "themeLight", themeId);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Whether `skydive chat` should enable portal machine sharing on launch
|
|
169
|
+
* without `--share-machine`. Set by hand-editing `shareMachineDefault` in
|
|
170
|
+
* config.json — deliberately no CLI command (kept off the API surface).
|
|
171
|
+
* Sharing only makes the machine reachable — agents still need a
|
|
172
|
+
* (persistent) grant to run anything, so this default skips the per-session
|
|
173
|
+
* enable step, not the consent step.
|
|
174
|
+
*/
|
|
175
|
+
function getShareMachineDefault() {
|
|
176
|
+
return store.get("shareMachineDefault") ?? false;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Whether the update check is disabled via config.json (`"updateCheck":
|
|
180
|
+
* false`). The persistent counterpart to the SKYDIVE_NO_UPDATE_CHECK /
|
|
181
|
+
* NO_UPDATE_NOTIFIER env opt-outs; like shareMachineDefault, set by editing
|
|
182
|
+
* config.json — deliberately no CLI command.
|
|
183
|
+
*/
|
|
184
|
+
function getUpdateCheckDisabled() {
|
|
185
|
+
return store.get("updateCheck") === false;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
//#endregion
|
|
5
189
|
//#region src/chat/tui/chat/card.ts
|
|
6
190
|
const urlActionKinds = [
|
|
7
191
|
"open_oauth",
|
|
@@ -227,14 +411,20 @@ function summarizeConnectCard(card, appUrl) {
|
|
|
227
411
|
const act = button.action;
|
|
228
412
|
switch (act.kind) {
|
|
229
413
|
case "open_oauth":
|
|
230
|
-
case "open_external_oauth":
|
|
231
|
-
case "open_github_app": return {
|
|
414
|
+
case "open_external_oauth": return {
|
|
232
415
|
...base,
|
|
233
416
|
action: {
|
|
234
417
|
kind: "open_url",
|
|
235
418
|
url: resolveConnectUrl(act.url, appUrl)
|
|
236
419
|
}
|
|
237
420
|
};
|
|
421
|
+
case "open_github_app": return {
|
|
422
|
+
...base,
|
|
423
|
+
action: {
|
|
424
|
+
kind: "open_url",
|
|
425
|
+
url: resolveConnectUrl(act.url, resolveWebUrl(appUrl))
|
|
426
|
+
}
|
|
427
|
+
};
|
|
238
428
|
case "submit_credential": return {
|
|
239
429
|
...base,
|
|
240
430
|
action: {
|
|
@@ -487,4 +677,4 @@ async function readStdin() {
|
|
|
487
677
|
}
|
|
488
678
|
|
|
489
679
|
//#endregion
|
|
490
|
-
export { runPrint as a, cardActionErrorMessage as c, reconcileMaskedInput as d, resolveConnectUrl as f, resolveAgent as i, 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 };
|
|
680
|
+
export { getUpdateCheckDisabled as A, getLastSeenVersion as C, getShareMachineDefault as D, getSavedTheme as E, resolveWebUrl as F, saveConfig as I, saveSession as L, resolveConfig as M, resolveManagementAuth as N, getStoredApiKeyId as O, resolveSession as P, saveTheme as R, getConfigPath as S, getReviewStateDir as T, API_KEY_PREFIX as _, runPrint as a, DEFAULT_WEB_URL 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, resolveAppUrl as j, getStoredApiKeyWorkspaceName 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, getPromptHistoryPath as w, deleteConfig as x, DEFAULT_APP_URL as y, setLastSeenVersion as z };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./
|
|
3
|
-
import
|
|
2
|
+
import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-CNIvmpqW.mjs";
|
|
3
|
+
import "./rest-Q5yl_b2-.mjs";
|
|
4
4
|
|
|
5
5
|
export { messageGet, readStdin, resolveAgent, runPrint };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as printError } from "./output-
|
|
2
|
+
import { n as printError } from "./output-DYzzdXYV.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/chat/print-share.ts
|
|
5
5
|
/**
|
|
@@ -14,7 +14,7 @@ import { n as printError } from "./output-B4cW10Ph.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-CKlIpyAb.mjs");
|
|
18
18
|
let signalConnected;
|
|
19
19
|
const connected = new Promise((resolve) => {
|
|
20
20
|
signalConnected = resolve;
|
|
@@ -22,7 +22,7 @@ async function connectMachineShare({ appUrl, sessionToken, timeoutHint }) {
|
|
|
22
22
|
const machineShare = new PortalClient({
|
|
23
23
|
appUrl,
|
|
24
24
|
sessionToken,
|
|
25
|
-
|
|
25
|
+
resolveCwd: () => process.cwd(),
|
|
26
26
|
onState: (state) => {
|
|
27
27
|
if (state.status === "connected") signalConnected();
|
|
28
28
|
if (state.status === "error") console.error(`portal: connection error: ${state.error ?? "unknown"} — retrying`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as sendErrorMessage, n as createRestClient, r as errorDetail, t as HttpError } from "./rest-
|
|
2
|
+
import { i as sendErrorMessage, n as createRestClient, r as errorDetail, t as HttpError } from "./rest-Q5yl_b2-.mjs";
|
|
3
3
|
|
|
4
4
|
export { createRestClient };
|
package/package.json
CHANGED
package/dist/js/api-BGWcPuik.mjs
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { t as HttpError } from "./rest-DMpigwmN.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 };
|
|
File without changes
|
|
File without changes
|
|
File without changes
|