@scp-dev/cli 0.0.0-stage → 0.1.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/LICENSE +201 -0
- package/README.md +33 -2
- package/dist/chunk-4DNQFATX.js +221 -0
- package/dist/chunk-4QYUPPGV.js +147 -0
- package/dist/chunk-6UFMKYPH.js +255 -0
- package/dist/chunk-7KP6TAJW.js +336 -0
- package/dist/chunk-DA3EB2YF.js +359 -0
- package/dist/chunk-KN7WEIE4.js +41 -0
- package/dist/chunk-N3ZROLFA.js +1182 -0
- package/dist/chunk-V5I7KEU7.js +948 -0
- package/dist/chunk-XWLF7ON2.js +757 -0
- package/dist/chunk-YYSLYVNZ.js +38 -0
- package/dist/cli.js +22 -0
- package/dist/hooks-O3UZXDYG.js +71 -0
- package/dist/install-HFBSKSTG.js +14 -0
- package/dist/login-JTRUWG6Q.js +129 -0
- package/dist/main-5CAHHN55.js +1271 -0
- package/dist/mcp-P2AZYP5G.js +72 -0
- package/dist/peek-YCOWBEPD.js +60 -0
- package/dist/relay-VVJPYVXE.js +30 -0
- package/dist/remote-3BWFISXJ.js +27 -0
- package/dist/setup-RDPVGE4H.js +39 -0
- package/dist/spawn-TQ3HOZTQ.js +74 -0
- package/dist/up-KXOYO7QV.js +37 -0
- package/package.json +38 -4
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// src/hook-fast.ts
|
|
2
|
+
var SUBAGENT = "--subagent";
|
|
3
|
+
var STDIN_CAP = 8 * 1024 * 1024;
|
|
4
|
+
var preread;
|
|
5
|
+
async function readCapped(stream) {
|
|
6
|
+
const chunks = [];
|
|
7
|
+
let size = 0;
|
|
8
|
+
for await (const chunk of stream) {
|
|
9
|
+
const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
10
|
+
const taken = bytes.subarray(0, STDIN_CAP - size);
|
|
11
|
+
chunks.push(taken);
|
|
12
|
+
size += taken.length;
|
|
13
|
+
if (size >= STDIN_CAP) break;
|
|
14
|
+
}
|
|
15
|
+
return Buffer.concat(chunks, size).toString("utf8");
|
|
16
|
+
}
|
|
17
|
+
function subagentSkip(argv, payload) {
|
|
18
|
+
return argv[0] === "hook" && argv.includes(SUBAGENT) && !/"agent(_id|Id)"/.test(payload);
|
|
19
|
+
}
|
|
20
|
+
async function subagentFastPath(argv) {
|
|
21
|
+
if (!(argv[0] === "hook" && argv.includes(SUBAGENT))) return false;
|
|
22
|
+
const payload = await readCapped(process.stdin);
|
|
23
|
+
if (subagentSkip(argv, payload)) return true;
|
|
24
|
+
preread = payload;
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
function prereadStdin() {
|
|
28
|
+
const payload = preread;
|
|
29
|
+
preread = void 0;
|
|
30
|
+
return payload;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
SUBAGENT,
|
|
35
|
+
readCapped,
|
|
36
|
+
subagentFastPath,
|
|
37
|
+
prereadStdin
|
|
38
|
+
};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
subagentFastPath
|
|
4
|
+
} from "./chunk-YYSLYVNZ.js";
|
|
5
|
+
|
|
6
|
+
// src/warnings.ts
|
|
7
|
+
function quietSqliteWarning() {
|
|
8
|
+
const emit = process.emitWarning;
|
|
9
|
+
const filtered = (warning, ...rest) => {
|
|
10
|
+
const type = typeof rest[0] === "string" ? rest[0] : rest[0]?.type;
|
|
11
|
+
if (type === "ExperimentalWarning" && /sqlite/i.test(String(warning instanceof Error ? warning.message : warning))) return;
|
|
12
|
+
emit.call(process, warning, ...rest);
|
|
13
|
+
};
|
|
14
|
+
process.emitWarning = filtered;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/cli.ts
|
|
18
|
+
if (!await subagentFastPath(process.argv.slice(2))) {
|
|
19
|
+
quietSqliteWarning();
|
|
20
|
+
const { run } = await import("./main-5CAHHN55.js");
|
|
21
|
+
await run(process.argv.slice(2));
|
|
22
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGY_HOOK_SET,
|
|
3
|
+
AGY_UNVERIFIED,
|
|
4
|
+
BLOCK_STOP,
|
|
5
|
+
DIRECTIVE_CLOSE,
|
|
6
|
+
HOOK_COMMAND,
|
|
7
|
+
NO_INJECT,
|
|
8
|
+
PARENT_EXCEPTION,
|
|
9
|
+
PUSH_WAIT,
|
|
10
|
+
PUSH_WAIT_DEFAULT_S,
|
|
11
|
+
REPLY_HOW,
|
|
12
|
+
agyHookSet,
|
|
13
|
+
claudeVersionFile,
|
|
14
|
+
hasOurHooks,
|
|
15
|
+
hookEntries,
|
|
16
|
+
hookReply,
|
|
17
|
+
injectionText,
|
|
18
|
+
introText,
|
|
19
|
+
mapHookPayload,
|
|
20
|
+
mergeHooks,
|
|
21
|
+
musePlugin,
|
|
22
|
+
nudgeText,
|
|
23
|
+
opencodePlugin,
|
|
24
|
+
pushText,
|
|
25
|
+
pushWait,
|
|
26
|
+
relayHookCommand,
|
|
27
|
+
runHook,
|
|
28
|
+
steerPrompt,
|
|
29
|
+
stopContinues,
|
|
30
|
+
subagentIntro,
|
|
31
|
+
summarize,
|
|
32
|
+
unreadPointer
|
|
33
|
+
} from "./chunk-XWLF7ON2.js";
|
|
34
|
+
import {
|
|
35
|
+
SUBAGENT
|
|
36
|
+
} from "./chunk-YYSLYVNZ.js";
|
|
37
|
+
import "./chunk-DA3EB2YF.js";
|
|
38
|
+
export {
|
|
39
|
+
AGY_HOOK_SET,
|
|
40
|
+
AGY_UNVERIFIED,
|
|
41
|
+
BLOCK_STOP,
|
|
42
|
+
DIRECTIVE_CLOSE,
|
|
43
|
+
HOOK_COMMAND,
|
|
44
|
+
NO_INJECT,
|
|
45
|
+
PARENT_EXCEPTION,
|
|
46
|
+
PUSH_WAIT,
|
|
47
|
+
PUSH_WAIT_DEFAULT_S,
|
|
48
|
+
REPLY_HOW,
|
|
49
|
+
SUBAGENT,
|
|
50
|
+
agyHookSet,
|
|
51
|
+
claudeVersionFile,
|
|
52
|
+
hasOurHooks,
|
|
53
|
+
hookEntries,
|
|
54
|
+
hookReply,
|
|
55
|
+
injectionText,
|
|
56
|
+
introText,
|
|
57
|
+
mapHookPayload,
|
|
58
|
+
mergeHooks,
|
|
59
|
+
musePlugin,
|
|
60
|
+
nudgeText,
|
|
61
|
+
opencodePlugin,
|
|
62
|
+
pushText,
|
|
63
|
+
pushWait,
|
|
64
|
+
relayHookCommand,
|
|
65
|
+
runHook,
|
|
66
|
+
steerPrompt,
|
|
67
|
+
stopContinues,
|
|
68
|
+
subagentIntro,
|
|
69
|
+
summarize,
|
|
70
|
+
unreadPointer
|
|
71
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hooksTarget,
|
|
3
|
+
installHooks,
|
|
4
|
+
mcpInstall
|
|
5
|
+
} from "./chunk-4DNQFATX.js";
|
|
6
|
+
import "./chunk-7KP6TAJW.js";
|
|
7
|
+
import "./chunk-XWLF7ON2.js";
|
|
8
|
+
import "./chunk-YYSLYVNZ.js";
|
|
9
|
+
import "./chunk-DA3EB2YF.js";
|
|
10
|
+
export {
|
|
11
|
+
hooksTarget,
|
|
12
|
+
installHooks,
|
|
13
|
+
mcpInstall
|
|
14
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import {
|
|
2
|
+
machineName
|
|
3
|
+
} from "./chunk-6UFMKYPH.js";
|
|
4
|
+
import {
|
|
5
|
+
activeRelay,
|
|
6
|
+
saveCredential
|
|
7
|
+
} from "./chunk-4QYUPPGV.js";
|
|
8
|
+
import {
|
|
9
|
+
RelayUsage
|
|
10
|
+
} from "./chunk-7KP6TAJW.js";
|
|
11
|
+
import {
|
|
12
|
+
note
|
|
13
|
+
} from "./chunk-DA3EB2YF.js";
|
|
14
|
+
|
|
15
|
+
// src/login.ts
|
|
16
|
+
import { spawn } from "child_process";
|
|
17
|
+
import { setTimeout } from "timers/promises";
|
|
18
|
+
import { CLI_NAME } from "@scp-dev/core";
|
|
19
|
+
function openBrowser(url, env = process.env) {
|
|
20
|
+
if (!/^https?:\/\//.test(url) || env.SSH_CONNECTION || env.SSH_TTY || process.platform === "linux" && !env.DISPLAY && !env.WAYLAND_DISPLAY) return false;
|
|
21
|
+
const command = env.BROWSER !== void 0 ? env.BROWSER && env.BROWSER !== "none" ? env.BROWSER : void 0 : process.platform === "darwin" ? "open" : process.platform === "linux" ? "xdg-open" : void 0;
|
|
22
|
+
if (!command) return false;
|
|
23
|
+
try {
|
|
24
|
+
const child = spawn(command, [url], { detached: true, stdio: "ignore", env });
|
|
25
|
+
child.on("error", () => {
|
|
26
|
+
});
|
|
27
|
+
child.unref();
|
|
28
|
+
return true;
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
var WHOAMI_FIELDS = ["kind", "org", "email", "role", "label", "ambient", "endpoint_labels", "expires_at"];
|
|
34
|
+
var publicWhoami = (value) => Object.fromEntries(WHOAMI_FIELDS.filter((key) => key in value).map((key) => [key, value[key]]));
|
|
35
|
+
async function responseBody(response) {
|
|
36
|
+
try {
|
|
37
|
+
const body = await response.json();
|
|
38
|
+
if (body && typeof body === "object" && !Array.isArray(body)) return body;
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
throw new Error("the relay returned an invalid login response");
|
|
42
|
+
}
|
|
43
|
+
async function login(values, env = process.env) {
|
|
44
|
+
const { url } = activeRelay(env);
|
|
45
|
+
const token = values.token !== void 0 ? values.token.trim() : env.SCP_TOKEN?.trim();
|
|
46
|
+
const done = (who) => {
|
|
47
|
+
if (values.json) console.log(JSON.stringify(publicWhoami(who)));
|
|
48
|
+
else console.log(`logged in to ${url} as ${who.email ?? "?"} in ${who.org ?? "?"}`);
|
|
49
|
+
return 0;
|
|
50
|
+
};
|
|
51
|
+
if (values.token !== void 0 && !token) throw new RelayUsage("--token needs a nonempty token");
|
|
52
|
+
if (token) {
|
|
53
|
+
let response;
|
|
54
|
+
try {
|
|
55
|
+
response = await fetch(`${url}/v1/whoami`, { headers: { Authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(3e4), redirect: "error" });
|
|
56
|
+
} catch {
|
|
57
|
+
throw new Error(`could not check the token at ${url}`);
|
|
58
|
+
}
|
|
59
|
+
if (response.status === 401) throw new Error(`that token is not valid for ${url}`);
|
|
60
|
+
if (response.status !== 200) throw new Error(`could not check the token at ${url} (HTTP ${response.status})`);
|
|
61
|
+
const who = await responseBody(response);
|
|
62
|
+
saveCredential(env, url, { token, ...metadata(who) });
|
|
63
|
+
return done(who);
|
|
64
|
+
}
|
|
65
|
+
const label = machineName(values.label, env);
|
|
66
|
+
let startResponse;
|
|
67
|
+
try {
|
|
68
|
+
startResponse = await fetch(`${url}/v1/device/code`, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
headers: { "Content-Type": "application/json" },
|
|
71
|
+
body: JSON.stringify({ label }),
|
|
72
|
+
signal: AbortSignal.timeout(3e4),
|
|
73
|
+
redirect: "error"
|
|
74
|
+
});
|
|
75
|
+
} catch {
|
|
76
|
+
throw new Error(`could not start login at ${url}`);
|
|
77
|
+
}
|
|
78
|
+
const start = await responseBody(startResponse);
|
|
79
|
+
if (!startResponse.ok)
|
|
80
|
+
throw new Error(typeof start.error_description === "string" ? start.error_description : `could not start login at ${url} (HTTP ${startResponse.status})`);
|
|
81
|
+
if (typeof start.device_code !== "string" || typeof start.user_code !== "string" || typeof start.verification_uri !== "string" || typeof start.verification_uri_complete !== "string" || typeof start.expires_in !== "number" || !Number.isFinite(start.expires_in) || start.expires_in <= 0 || typeof start.interval !== "number" || !Number.isFinite(start.interval) || start.interval < 0) {
|
|
82
|
+
throw new Error("the relay returned an invalid device code");
|
|
83
|
+
}
|
|
84
|
+
const expires = Date.now() + start.expires_in * 1e3;
|
|
85
|
+
let interval = start.interval * 1e3;
|
|
86
|
+
note(`to sign in, open ${start.verification_uri_complete}`);
|
|
87
|
+
note(`and check that it shows the code ${start.user_code}`);
|
|
88
|
+
const opened = !values["no-browser"] && openBrowser(start.verification_uri_complete, env);
|
|
89
|
+
note(opened ? "opened it in your browser; waiting for approval" : "waiting for approval");
|
|
90
|
+
while (Date.now() < expires) {
|
|
91
|
+
await setTimeout(Math.min(interval, Math.max(0, expires - Date.now())));
|
|
92
|
+
if (Date.now() >= expires) break;
|
|
93
|
+
let response;
|
|
94
|
+
let body;
|
|
95
|
+
try {
|
|
96
|
+
response = await fetch(`${url}/v1/device/token`, {
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers: { "Content-Type": "application/json" },
|
|
99
|
+
body: JSON.stringify({ device_code: start.device_code }),
|
|
100
|
+
signal: AbortSignal.timeout(Math.max(1, Math.min(3e4, expires - Date.now()))),
|
|
101
|
+
redirect: "error"
|
|
102
|
+
});
|
|
103
|
+
if (response.status >= 500) continue;
|
|
104
|
+
body = await responseBody(response);
|
|
105
|
+
} catch {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (response.ok && typeof body.access_token === "string" && body.access_token) {
|
|
109
|
+
saveCredential(env, url, { token: body.access_token, ...metadata(body), kind: "device", label: typeof body.label === "string" ? body.label : label });
|
|
110
|
+
return done({ ...body, kind: "device" });
|
|
111
|
+
}
|
|
112
|
+
if (body.error === "authorization_pending") continue;
|
|
113
|
+
if (body.error === "slow_down") {
|
|
114
|
+
interval += 5e3;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
throw new Error(
|
|
118
|
+
typeof body.error_description === "string" ? body.error_description : `login failed: ${typeof body.error === "string" ? body.error : `HTTP ${response.status}`}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
throw new Error(`the device code expired: run ${CLI_NAME} login again`);
|
|
122
|
+
}
|
|
123
|
+
function metadata(who) {
|
|
124
|
+
return Object.fromEntries(["kind", "org", "email", "label"].filter((key) => typeof who[key] === "string").map((key) => [key, who[key]]));
|
|
125
|
+
}
|
|
126
|
+
export {
|
|
127
|
+
login,
|
|
128
|
+
openBrowser
|
|
129
|
+
};
|