requestshield 0.1.4 → 0.1.5
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/README.md +211 -13
- package/package.json +16 -10
- package/skills/requestshield/SKILL.md +307 -307
- package/skills/requestshield/assets/AGENTS.codex.md +62 -62
- package/skills/requestshield/references/backend-java-core.md +128 -128
- package/skills/requestshield/references/backend-spring-boot.md +145 -145
- package/skills/requestshield/references/browser-manual.md +210 -210
- package/skills/requestshield/references/browser-seamless.md +164 -164
- package/skills/requestshield/references/cli.md +183 -182
- package/skills/requestshield/references/integration-planning.md +389 -389
- package/skills/requestshield/references/troubleshooting.md +118 -118
- package/src/agent-detector.mjs +102 -74
- package/src/api-client.mjs +100 -5
- package/src/args.mjs +182 -79
- package/src/cli.mjs +255 -51
- package/src/commands/agent-setup.mjs +185 -185
- package/src/commands/apps-get.mjs +64 -0
- package/src/commands/apps-list.mjs +90 -0
- package/src/commands/billing-get.mjs +110 -0
- package/src/commands/challenge-volume.mjs +81 -0
- package/src/commands/contract.mjs +106 -0
- package/src/config.mjs +8 -0
- package/src/main.mjs +24 -24
package/src/args.mjs
CHANGED
|
@@ -1,85 +1,188 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
import { CliError } from "./errors.mjs";
|
|
4
|
-
|
|
5
|
-
const HELP = `RequestShield CLI
|
|
6
|
-
|
|
7
|
-
Usage:
|
|
8
|
-
requestshield signin
|
|
9
|
-
requestshield keys create [--yes]
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { CliError } from "./errors.mjs";
|
|
4
|
+
|
|
5
|
+
const HELP = `RequestShield CLI
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
requestshield signin
|
|
9
|
+
requestshield keys create [--yes]
|
|
10
|
+
requestshield contract
|
|
11
|
+
requestshield apps list [--json]
|
|
12
|
+
requestshield apps get <app-key>
|
|
13
|
+
requestshield challenge volume <app-key> [--from <time>] [--to <time>] [--granularity <value>]
|
|
14
|
+
requestshield get billing <app-key>
|
|
10
15
|
requestshield agent setup [--force]
|
|
11
16
|
requestshield agent setup --codex [--force]
|
|
12
17
|
requestshield agent setup --claude [--force]
|
|
13
18
|
requestshield update check
|
|
14
19
|
requestshield --help
|
|
15
20
|
requestshield --version`;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @typedef {{ command: "help", help: string }
|
|
19
|
-
* | { command: "version" }
|
|
20
|
-
* | { command: "update-check" }
|
|
21
|
-
* | { command: "signin" }
|
|
22
|
-
* | { command: "keys-create", yes: boolean }
|
|
23
|
-
* | { command: "
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
/** @
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {{ command: "help", help: string }
|
|
24
|
+
* | { command: "version" }
|
|
25
|
+
* | { command: "update-check" }
|
|
26
|
+
* | { command: "signin" }
|
|
27
|
+
* | { command: "keys-create", yes: boolean }
|
|
28
|
+
* | { command: "contract" }
|
|
29
|
+
* | { command: "apps-list", json: boolean }
|
|
30
|
+
* | { command: "apps-get", appKey: string }
|
|
31
|
+
* | { command: "challenge-volume", appKey: string, from?: string, to?: string, granularity?: string }
|
|
32
|
+
* | { command: "billing-get", appKey: string }
|
|
33
|
+
* | { command: "agent-setup", agent?: "codex" | "claude", force: boolean }} ParsedArgs
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/** @param {string[]} argv @returns {ParsedArgs} */
|
|
37
|
+
export function parseArgs(argv) {
|
|
38
|
+
if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
|
|
39
|
+
return { command: "help", help: HELP };
|
|
40
|
+
}
|
|
41
|
+
if (argv.length === 1 && (argv[0] === "--version" || argv[0] === "-v" || argv[0] === "-V")) {
|
|
42
|
+
return { command: "version" };
|
|
43
|
+
}
|
|
44
|
+
if (argv[0] === "update" && argv[1] === "check") {
|
|
45
|
+
assertOnly(argv.slice(2), new Set());
|
|
46
|
+
return { command: "update-check" };
|
|
47
|
+
}
|
|
48
|
+
if (argv[0] === "signin") {
|
|
49
|
+
assertOnly(argv.slice(1), new Set());
|
|
50
|
+
return { command: "signin" };
|
|
51
|
+
}
|
|
52
|
+
if (argv[0] === "keys" && argv[1] === "create") {
|
|
53
|
+
assertOnly(argv.slice(2), new Set(["--yes"]));
|
|
54
|
+
return { command: "keys-create", yes: argv.includes("--yes") };
|
|
55
|
+
}
|
|
56
|
+
if (argv[0] === "contract") {
|
|
57
|
+
assertOnly(argv.slice(1), new Set());
|
|
58
|
+
return { command: "contract" };
|
|
59
|
+
}
|
|
60
|
+
if (argv[0] === "apps" && argv[1] === "list") {
|
|
61
|
+
assertOnly(argv.slice(2), new Set(["--json"]));
|
|
62
|
+
return { command: "apps-list", json: argv.includes("--json") };
|
|
63
|
+
}
|
|
64
|
+
if (argv[0] === "apps" && argv[1] === "get") {
|
|
65
|
+
return {
|
|
66
|
+
command: "apps-get",
|
|
67
|
+
appKey: parseAppKey(argv, "Usage: requestshield apps get <app-key>"),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (argv[0] === "challenge" && argv[1] === "volume") {
|
|
71
|
+
return parseChallengeVolume(argv);
|
|
72
|
+
}
|
|
73
|
+
if (argv[0] === "get" && argv[1] === "billing") {
|
|
74
|
+
return {
|
|
75
|
+
command: "billing-get",
|
|
76
|
+
appKey: parseAppKey(argv, "Usage: requestshield get billing <app-key>"),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (argv[0] === "agent" && argv[1] === "setup") {
|
|
80
|
+
const rest = argv.slice(2);
|
|
81
|
+
/** @type {"codex" | "claude" | undefined} */
|
|
82
|
+
let agent;
|
|
83
|
+
let force = false;
|
|
84
|
+
for (let index = 0; index < rest.length; index++) {
|
|
85
|
+
const arg = rest[index];
|
|
86
|
+
if (arg === "--force") {
|
|
87
|
+
force = true;
|
|
88
|
+
} else if (arg === "--codex" || arg === "--claude") {
|
|
89
|
+
if (agent !== undefined) {
|
|
90
|
+
throw new CliError("Only one of --codex or --claude may be provided", {
|
|
91
|
+
code: "INVALID_AGENT",
|
|
92
|
+
exitCode: 2,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
agent = arg === "--codex" ? "codex" : "claude";
|
|
96
|
+
} else {
|
|
97
|
+
throw new CliError(`Unknown option: ${arg}`, { exitCode: 2 });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
command: "agent-setup",
|
|
102
|
+
...(agent ? { agent } : {}),
|
|
103
|
+
force,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
throw new CliError(`Unknown command.\n\n${HELP}`, { exitCode: 2 });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** @param {string[]} args @param {Set<string>} allowed */
|
|
110
|
+
function assertOnly(args, allowed) {
|
|
111
|
+
for (const arg of args) {
|
|
112
|
+
if (!allowed.has(arg)) {
|
|
113
|
+
throw new CliError(`Unknown option: ${arg}`, { exitCode: 2 });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** @param {string[]} argv @param {string} usage */
|
|
119
|
+
function parseAppKey(argv, usage) {
|
|
120
|
+
if (argv.length !== 3) throw new CliError(usage, { exitCode: 2 });
|
|
121
|
+
return validateAppKey(argv[2]);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** @param {string[]} argv @returns {Extract<ParsedArgs, {command: "challenge-volume"}>} */
|
|
125
|
+
function parseChallengeVolume(argv) {
|
|
126
|
+
const usage = "Usage: requestshield challenge volume <app-key> [--from <time>] [--to <time>] [--granularity <value>]";
|
|
127
|
+
if (argv.length < 3) throw new CliError(usage, { exitCode: 2 });
|
|
128
|
+
const appKey = validateAppKey(argv[2]);
|
|
129
|
+
/** @type {{ from?: string, to?: string, granularity?: string }} */
|
|
130
|
+
const options = {};
|
|
131
|
+
/** @type {Map<string, "from" | "to" | "granularity">} */
|
|
132
|
+
const optionNames = new Map([
|
|
133
|
+
["--from", "from"],
|
|
134
|
+
["--to", "to"],
|
|
135
|
+
["--granularity", "granularity"],
|
|
136
|
+
]);
|
|
137
|
+
for (let index = 3; index < argv.length; index += 2) {
|
|
138
|
+
const option = argv[index];
|
|
139
|
+
const property = optionNames.get(option);
|
|
140
|
+
if (!property) throw new CliError(`Unknown option: ${option}`, { exitCode: 2 });
|
|
141
|
+
const value = argv[index + 1];
|
|
142
|
+
if (!value || value.startsWith("--")) {
|
|
143
|
+
throw new CliError(`${option} requires a value`, { exitCode: 2 });
|
|
144
|
+
}
|
|
145
|
+
if (options[property] !== undefined) {
|
|
146
|
+
throw new CliError(`${option} may only be provided once`, { exitCode: 2 });
|
|
147
|
+
}
|
|
148
|
+
if ((property === "from" || property === "to") && !isIsoTime(value)) {
|
|
149
|
+
throw new CliError(`${option} must be an ISO-8601 timestamp`, {
|
|
150
|
+
code: "INVALID_TIME",
|
|
151
|
+
exitCode: 2,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
if (property === "granularity" && !/^[a-z][a-z0-9_-]{0,31}$/.test(value)) {
|
|
155
|
+
throw new CliError(
|
|
156
|
+
"--granularity must be 1-32 lowercase letters, numbers, '_' or '-'",
|
|
157
|
+
{ code: "INVALID_GRANULARITY", exitCode: 2 },
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
options[property] = value;
|
|
161
|
+
}
|
|
162
|
+
if (options.from && options.to && Date.parse(options.from) > Date.parse(options.to)) {
|
|
163
|
+
throw new CliError("--from must not be later than --to", {
|
|
164
|
+
code: "INVALID_TIME_RANGE",
|
|
165
|
+
exitCode: 2,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return { command: "challenge-volume", appKey, ...options };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** @param {string} appKey */
|
|
172
|
+
function validateAppKey(appKey) {
|
|
173
|
+
if (!/^[A-Za-z0-9._~-]{1,128}$/.test(appKey)) {
|
|
174
|
+
throw new CliError(
|
|
175
|
+
"App Key must be 1-128 characters using letters, numbers, '.', '_', '~', or '-'",
|
|
176
|
+
{ code: "INVALID_APP_KEY", exitCode: 2 },
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
return appKey;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** @param {string} value */
|
|
183
|
+
function isIsoTime(value) {
|
|
184
|
+
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:\d{2})$/.test(value)
|
|
185
|
+
&& Number.isFinite(Date.parse(value));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export { HELP };
|
package/src/cli.mjs
CHANGED
|
@@ -1,51 +1,255 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// @ts-check
|
|
3
|
-
|
|
4
|
-
import { parseArgs } from "./args.mjs";
|
|
5
|
-
import { ManagementApiClient } from "./api-client.mjs";
|
|
6
|
-
import { SessionStore } from "./session-store.mjs";
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-check
|
|
3
|
+
|
|
4
|
+
import { parseArgs } from "./args.mjs";
|
|
5
|
+
import { ManagementApiClient } from "./api-client.mjs";
|
|
6
|
+
import { SessionStore } from "./session-store.mjs";
|
|
7
|
+
|
|
8
|
+
import { signin } from "./commands/signin.mjs";
|
|
9
|
+
import { createKeys } from "./commands/keys-create.mjs";
|
|
10
|
+
import { showIntegrationContract } from "./commands/contract.mjs";
|
|
11
|
+
import { listApps } from "./commands/apps-list.mjs";
|
|
12
|
+
import { getApp } from "./commands/apps-get.mjs";
|
|
13
|
+
import { showChallengeVolume } from "./commands/challenge-volume.mjs";
|
|
14
|
+
import { showBilling } from "./commands/billing-get.mjs";
|
|
15
|
+
import { setupAgent } from "./commands/agent-setup.mjs";
|
|
16
|
+
import { checkForUpdate } from "./commands/update-check.mjs";
|
|
17
|
+
|
|
18
|
+
import packageJson from "../package.json" with { type: "json" };
|
|
19
|
+
import { DEFAULT_MANAGEMENT_API_URL } from "./config.mjs";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {{
|
|
23
|
+
* log: (message: string) => void,
|
|
24
|
+
* api?: ManagementApiClient,
|
|
25
|
+
* sessions?: SessionStore,
|
|
26
|
+
* env?: NodeJS.ProcessEnv,
|
|
27
|
+
* homeDir?: string,
|
|
28
|
+
* sourceDir?: string,
|
|
29
|
+
* executablePath?: string,
|
|
30
|
+
* detectAgents?: () => Promise<Array<"codex" | "claude">>,
|
|
31
|
+
* selectAgent?: (
|
|
32
|
+
* detected: Array<"codex" | "claude">
|
|
33
|
+
* ) => Promise<"codex" | "claude">,
|
|
34
|
+
* wait?: (milliseconds: number) => Promise<unknown>,
|
|
35
|
+
* confirm?: () => Promise<boolean>,
|
|
36
|
+
* fetchImpl?: typeof fetch,
|
|
37
|
+
* confirmUpdate?: () => Promise<boolean>,
|
|
38
|
+
* installLatest?: (
|
|
39
|
+
* packageName: string,
|
|
40
|
+
* version: string
|
|
41
|
+
* ) => Promise<void>
|
|
42
|
+
* }} CommandContext
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {CommandContext & {
|
|
47
|
+
* api: ManagementApiClient,
|
|
48
|
+
* sessions: SessionStore
|
|
49
|
+
* }} AuthenticatedCommandContext
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @typedef {{
|
|
54
|
+
* requiresApiSession: boolean,
|
|
55
|
+
* handler: (
|
|
56
|
+
* parsed: any,
|
|
57
|
+
* context: CommandContext
|
|
58
|
+
* ) => unknown | Promise<unknown>
|
|
59
|
+
* }} CommandHandler
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Put every CLI command handler here.
|
|
64
|
+
*
|
|
65
|
+
* @type {Record<string, CommandHandler>}
|
|
66
|
+
*/
|
|
67
|
+
const COMMAND_HANDLERS = {
|
|
68
|
+
"help": {
|
|
69
|
+
requiresApiSession: false,
|
|
70
|
+
handler: (parsed, context) =>
|
|
71
|
+
context.log(parsed.help),
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
"version": {
|
|
75
|
+
requiresApiSession: false,
|
|
76
|
+
handler: (_parsed, context) =>
|
|
77
|
+
context.log(`requestshield ${packageJson.version}`),
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
"update-check": {
|
|
81
|
+
requiresApiSession: false,
|
|
82
|
+
handler: (_parsed, context) =>
|
|
83
|
+
checkForUpdate({
|
|
84
|
+
currentVersion: packageJson.version,
|
|
85
|
+
packageName: packageJson.name,
|
|
86
|
+
log: context.log,
|
|
87
|
+
fetchImpl: context.fetchImpl,
|
|
88
|
+
confirm: context.confirmUpdate,
|
|
89
|
+
install: context.installLatest,
|
|
90
|
+
}),
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
"agent-setup": {
|
|
94
|
+
requiresApiSession: false,
|
|
95
|
+
handler: (parsed, context) =>
|
|
96
|
+
setupAgent(parsed, context),
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
"signin": {
|
|
100
|
+
requiresApiSession: true,
|
|
101
|
+
handler: (_parsed, context) =>
|
|
102
|
+
signin(requireApiSession(context)),
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
"keys-create": {
|
|
106
|
+
requiresApiSession: true,
|
|
107
|
+
handler: (parsed, context) =>
|
|
108
|
+
createKeys(parsed, requireApiSession(context)),
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
"contract": {
|
|
112
|
+
requiresApiSession: true,
|
|
113
|
+
handler: (_parsed, context) =>
|
|
114
|
+
showIntegrationContract(requireApiSession(context)),
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
"apps-list": {
|
|
118
|
+
requiresApiSession: true,
|
|
119
|
+
handler: (parsed, context) =>
|
|
120
|
+
listApps(parsed, requireApiSession(context)),
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
"apps-get": {
|
|
124
|
+
requiresApiSession: true,
|
|
125
|
+
handler: (parsed, context) =>
|
|
126
|
+
getApp(parsed, requireApiSession(context)),
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
"challenge-volume": {
|
|
130
|
+
requiresApiSession: true,
|
|
131
|
+
handler: (parsed, context) =>
|
|
132
|
+
showChallengeVolume(parsed, requireApiSession(context)),
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
"billing-get": {
|
|
136
|
+
requiresApiSession: true,
|
|
137
|
+
handler: (parsed, context) =>
|
|
138
|
+
showBilling(parsed, requireApiSession(context)),
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Narrow the shared context before an authenticated handler can use it.
|
|
144
|
+
*
|
|
145
|
+
* @param {CommandContext} context
|
|
146
|
+
* @returns {AuthenticatedCommandContext}
|
|
147
|
+
*/
|
|
148
|
+
function requireApiSession(context) {
|
|
149
|
+
if (!context.api || !context.sessions) {
|
|
150
|
+
throw new Error("Authenticated command context was not initialized");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
...context,
|
|
155
|
+
api: context.api,
|
|
156
|
+
sessions: context.sessions,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @param {string[]} argv
|
|
162
|
+
* @param {{
|
|
163
|
+
* log?: (message: string) => void,
|
|
164
|
+
* api?: ManagementApiClient,
|
|
165
|
+
* sessions?: SessionStore,
|
|
166
|
+
* env?: NodeJS.ProcessEnv,
|
|
167
|
+
* homeDir?: string,
|
|
168
|
+
* sourceDir?: string,
|
|
169
|
+
* executablePath?: string,
|
|
170
|
+
* detectAgents?: () => Promise<Array<"codex" | "claude">>,
|
|
171
|
+
* selectAgent?: (
|
|
172
|
+
* detected: Array<"codex" | "claude">
|
|
173
|
+
* ) => Promise<"codex" | "claude">,
|
|
174
|
+
* wait?: (milliseconds: number) => Promise<unknown>,
|
|
175
|
+
* confirm?: () => Promise<boolean>,
|
|
176
|
+
* fetchImpl?: typeof fetch,
|
|
177
|
+
* confirmUpdate?: () => Promise<boolean>,
|
|
178
|
+
* installLatest?: (
|
|
179
|
+
* packageName: string,
|
|
180
|
+
* version: string
|
|
181
|
+
* ) => Promise<void>
|
|
182
|
+
* }} [deps]
|
|
183
|
+
*/
|
|
184
|
+
export async function run(argv, deps = {}) {
|
|
185
|
+
const parsed = parseArgs(argv);
|
|
186
|
+
|
|
187
|
+
const log =
|
|
188
|
+
deps.log ??
|
|
189
|
+
((message) => console.log(message));
|
|
190
|
+
|
|
191
|
+
/*
|
|
192
|
+
* Find the requested command.
|
|
193
|
+
*/
|
|
194
|
+
const command = COMMAND_HANDLERS[parsed.command];
|
|
195
|
+
|
|
196
|
+
if (!command) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
`Unsupported command: ${String(parsed.command)}`
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/*
|
|
203
|
+
* Dependencies available to every command.
|
|
204
|
+
*/
|
|
205
|
+
/** @type {CommandContext} */
|
|
206
|
+
const context = {
|
|
207
|
+
...deps,
|
|
208
|
+
log,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/*
|
|
212
|
+
* Only initialize the Management API and session
|
|
213
|
+
* for commands that require them.
|
|
214
|
+
*/
|
|
215
|
+
if (command.requiresApiSession) {
|
|
216
|
+
const env =
|
|
217
|
+
deps.env ??
|
|
218
|
+
process.env;
|
|
219
|
+
|
|
220
|
+
const api =
|
|
221
|
+
deps.api ??
|
|
222
|
+
new ManagementApiClient({
|
|
223
|
+
baseUrl:
|
|
224
|
+
env.REQUESTSHIELD_API_URL ??
|
|
225
|
+
DEFAULT_MANAGEMENT_API_URL,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const sessions =
|
|
229
|
+
deps.sessions ??
|
|
230
|
+
new SessionStore({
|
|
231
|
+
env,
|
|
232
|
+
homeDir: deps.homeDir,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const authenticatedContext = {
|
|
236
|
+
...context,
|
|
237
|
+
env,
|
|
238
|
+
api,
|
|
239
|
+
sessions,
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
return command.handler(
|
|
243
|
+
parsed,
|
|
244
|
+
authenticatedContext
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/*
|
|
249
|
+
* Execute commands that do not need an API session.
|
|
250
|
+
*/
|
|
251
|
+
return command.handler(
|
|
252
|
+
parsed,
|
|
253
|
+
context
|
|
254
|
+
);
|
|
255
|
+
}
|