@tiangong-ai/cli 0.0.23 → 0.0.24

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.
Files changed (40) hide show
  1. package/AGENTS.md +1 -0
  2. package/README.md +41 -32
  3. package/dist/io.d.ts +3 -0
  4. package/dist/io.js.map +1 -1
  5. package/dist/main.js +1 -0
  6. package/dist/main.js.map +1 -1
  7. package/dist/research/orchestration.js +7 -0
  8. package/dist/research/orchestration.js.map +1 -1
  9. package/dist/research/setup-command.d.ts +3 -0
  10. package/dist/research/setup-command.js +487 -0
  11. package/dist/research/setup-command.js.map +1 -0
  12. package/dist/research/workspace/broker.js +39 -4
  13. package/dist/research/workspace/broker.js.map +1 -1
  14. package/dist/research/workspace/capabilities.js +64 -0
  15. package/dist/research/workspace/capabilities.js.map +1 -1
  16. package/dist/research/workspace/constants.d.ts +3 -2
  17. package/dist/research/workspace/constants.js +19 -0
  18. package/dist/research/workspace/constants.js.map +1 -1
  19. package/dist/research/workspace/context.js +26 -1
  20. package/dist/research/workspace/context.js.map +1 -1
  21. package/dist/research/workspace/external-skills.d.ts +20 -1
  22. package/dist/research/workspace/external-skills.js +143 -2
  23. package/dist/research/workspace/external-skills.js.map +1 -1
  24. package/dist/research/workspace/runtime.js +1 -1
  25. package/dist/research/workspace/runtime.js.map +1 -1
  26. package/dist/research/workspace/setup-catalog.d.ts +161 -0
  27. package/dist/research/workspace/setup-catalog.js +601 -0
  28. package/dist/research/workspace/setup-catalog.js.map +1 -0
  29. package/dist/research/workspace/setup-wizard.d.ts +24 -0
  30. package/dist/research/workspace/setup-wizard.js +465 -0
  31. package/dist/research/workspace/setup-wizard.js.map +1 -0
  32. package/dist/research/workspace/setup.d.ts +463 -0
  33. package/dist/research/workspace/setup.js +3000 -0
  34. package/dist/research/workspace/setup.js.map +1 -0
  35. package/dist/research/workspace/storage.js +7 -0
  36. package/dist/research/workspace/storage.js.map +1 -1
  37. package/dist/research/workspace/types.d.ts +13 -1
  38. package/dist/research/workspace/workspace.js +56 -7
  39. package/dist/research/workspace/workspace.js.map +1 -1
  40. package/package.json +1 -1
@@ -0,0 +1,487 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { isAbsolute, resolve } from "node:path";
3
+ import { CliError } from "../errors.js";
4
+ import { stringifyJson, write } from "../io.js";
5
+ import { parseStrictArgs, strictBoolean, strictString } from "../strict-args.js";
6
+ import { EXTERNAL_SKILL_CONTEXT_PROFILE, EXTERNAL_SKILL_MEDIA_PROFILE, EXTERNAL_SKILL_PROFILE, } from "./workspace/external-skills.js";
7
+ import { inspectResearchSetupCatalog } from "./workspace/setup-catalog.js";
8
+ import { applyResearchSetupPlan, checkResearchSetupUpdates, createResearchSetupPlan, createResearchSetupUpgradePlan, doctorResearchSetup, inspectResearchSetupStatus, retryResearchSetup, runResearchSetupCompanion, setResearchSetupCredentialFromEnvironment, } from "./workspace/setup.js";
9
+ import { isObject, workspacePaths } from "./workspace/storage.js";
10
+ import { runResearchSetupWizard } from "./workspace/setup-wizard.js";
11
+ const COMMON_OPTIONS = { help: "boolean", json: "boolean" };
12
+ const WORKSPACE_OPTIONS = { ...COMMON_OPTIONS, workspace: "string" };
13
+ export async function runResearchSetupCommand(argv, io) {
14
+ const [action, ...rest] = argv;
15
+ if (!action || action === "wizard" || action.startsWith("-")) {
16
+ return runResearchSetupWizard(action === "wizard" ? rest : argv, io);
17
+ }
18
+ if (action === "--help" || action === "-h" || action === "help") {
19
+ write(io.stdout, researchSetupHelp());
20
+ return 0;
21
+ }
22
+ if (action === "catalog")
23
+ return runCatalog(rest, io);
24
+ if (action === "plan")
25
+ return runPlan(rest, io);
26
+ if (action === "apply")
27
+ return runApply(rest, io);
28
+ if (action === "status")
29
+ return runStatus(rest, io);
30
+ if (action === "doctor")
31
+ return runDoctor(rest, io);
32
+ if (action === "credential")
33
+ return runCredential(rest, io);
34
+ if (action === "companion")
35
+ return runCompanion(rest, io);
36
+ if (action === "retry")
37
+ return runRetry(rest, io);
38
+ if (action === "update")
39
+ return runUpdate(rest, io);
40
+ if (action === "upgrade")
41
+ return runUpgrade(rest, io);
42
+ throw new CliError(`Unknown research setup action: ${action}`, {
43
+ code: "INVALID_ARGS",
44
+ exitCode: 2,
45
+ });
46
+ }
47
+ export function researchSetupHelp() {
48
+ return `Research setup commands:
49
+ tiangong-ai research setup
50
+ Interactive, user-initiated setup Wizard (TTY required).
51
+ tiangong-ai research setup catalog [--workspace <absolute-path>] [--scope project|global] [--agents codex,claude-code] [--json]
52
+ tiangong-ai research setup plan --workspace <absolute-path> --mode smoke-test|production-research --evidence-profile none|brave-baseline|brave-context|brave-media [--skills <csv>] [--scope project|global] [--agents <csv>] [--accept-license <csv>] [--settings <absolute-json>] [--credential-env <absolute-json>] [--agent-routes <absolute-json>] [--confirm-network-downloads] [--confirm-global] [--live] [--allow-synthetic-unstructure-upload] [--agent-smoke --confirm-agent-smoke-cost] [--replace-plan] [--json]
53
+ tiangong-ai research setup apply [--plan <absolute-json>] [--workspace <absolute-path>] [--skip-doctor] [--json]
54
+ tiangong-ai research setup status [--workspace <absolute-path>] [--json]
55
+ tiangong-ai research setup doctor [--workspace <absolute-path>] [--live] [--allow-synthetic-unstructure-upload] [--agent-smoke --confirm-agent-smoke-cost] [--json]
56
+ tiangong-ai research setup credential set --id <logical-id> --from-env <name> [--workspace <absolute-path>] [--json]
57
+ tiangong-ai research setup companion run --id tiangong.document-granular-decompose --input <absolute-file> --output <absolute-new-file> [--timeout <seconds>] [--workspace <absolute-path>] [--json]
58
+ tiangong-ai research setup companion run --id tiangong.academic-paper-download (--doi <doi> | --title <exact-title> [--author <name>] [--year <yyyy>]) --out <absolute-existing-directory> [--timeout <seconds>] [--workspace <absolute-path>] [--json]
59
+ tiangong-ai research setup retry --step <recorded-step> [--clear-stale-lock --confirm-clear-stale-lock] [--workspace <absolute-path>] [--json]
60
+ tiangong-ai research setup update --check [--workspace <absolute-path>] [--json]
61
+ tiangong-ai research setup upgrade --plan --confirm-upgrade --accept-license <csv> [--workspace <absolute-path>] [--json]
62
+
63
+ Safety defaults:
64
+ Skills are never bundled or installed implicitly. Plans pin the installer,
65
+ source commits, tree hashes, destinations, licenses, and declared mutations.
66
+ Project-local copy mode is the default. Credentials are accepted only through
67
+ named owner environment variables and their values are never printed.
68
+ Every selected Skill requires its displayed license id and an explicit
69
+ network-download confirmation; an empty smoke-test plan requires neither.
70
+ `;
71
+ }
72
+ async function runCatalog(argv, io) {
73
+ const args = parseStrictArgs(argv, { ...WORKSPACE_OPTIONS, scope: "string", agents: "string" }, "research setup catalog");
74
+ if (strictBoolean(args, "help"))
75
+ return writeSetupHelp(io);
76
+ rejectPositionals(args.positionals, "research setup catalog");
77
+ const result = await inspectResearchSetupCatalog({
78
+ selectedPath: workspaceArgument(args),
79
+ scope: setupScope(strictString(args, "scope")),
80
+ agents: setupAgents(strictString(args, "agents")),
81
+ environment: io.env,
82
+ });
83
+ writeSetupJson(io, result, args);
84
+ return 0;
85
+ }
86
+ async function runPlan(argv, io) {
87
+ const args = parseStrictArgs(argv, {
88
+ ...WORKSPACE_OPTIONS,
89
+ name: "string",
90
+ mode: "string",
91
+ "evidence-profile": "string",
92
+ skills: "string",
93
+ scope: "string",
94
+ agents: "string",
95
+ "accept-license": "string",
96
+ settings: "string",
97
+ "credential-env": "string",
98
+ "agent-routes": "string",
99
+ live: "boolean",
100
+ "allow-synthetic-unstructure-upload": "boolean",
101
+ "agent-smoke": "boolean",
102
+ "confirm-network-downloads": "boolean",
103
+ "confirm-global": "boolean",
104
+ "confirm-agent-smoke-cost": "boolean",
105
+ "replace-plan": "boolean",
106
+ }, "research setup plan");
107
+ if (strictBoolean(args, "help"))
108
+ return writeSetupHelp(io);
109
+ rejectPositionals(args.positionals, "research setup plan");
110
+ const workspace = requiredAbsoluteWorkspace(args);
111
+ const mode = setupMode(strictString(args, "mode"), true);
112
+ const evidenceProfile = setupEvidenceProfile(strictString(args, "evidence-profile"), true);
113
+ const settings = await optionalStringRecord(strictString(args, "settings"), "--settings");
114
+ const credentialEnvironment = await optionalStringRecord(strictString(args, "credential-env"), "--credential-env");
115
+ const agentRoutes = await optionalAgentRoutes(strictString(args, "agent-routes"));
116
+ const name = strictString(args, "name");
117
+ const plan = await createResearchSetupPlan({
118
+ workspace,
119
+ ...(name === undefined ? {} : { name }),
120
+ mode,
121
+ evidenceProfile,
122
+ skillIds: csv(strictString(args, "skills")),
123
+ scope: setupScope(strictString(args, "scope")),
124
+ agents: setupAgents(strictString(args, "agents")),
125
+ acceptedLicenseIds: csv(strictString(args, "accept-license")),
126
+ credentialEnvironment,
127
+ settings,
128
+ agentRoutes,
129
+ liveChecks: strictBoolean(args, "live"),
130
+ allowSyntheticUnstructureUpload: strictBoolean(args, "allow-synthetic-unstructure-upload"),
131
+ agentSmoke: strictBoolean(args, "agent-smoke"),
132
+ confirmNetworkDownloads: strictBoolean(args, "confirm-network-downloads"),
133
+ confirmGlobalMutation: strictBoolean(args, "confirm-global"),
134
+ confirmAgentSmokeCost: strictBoolean(args, "confirm-agent-smoke-cost"),
135
+ replacePlan: strictBoolean(args, "replace-plan"),
136
+ environment: io.env,
137
+ });
138
+ writeSetupJson(io, plan, args);
139
+ return 0;
140
+ }
141
+ async function runApply(argv, io) {
142
+ const args = parseStrictArgs(argv, { ...WORKSPACE_OPTIONS, plan: "string", "skip-doctor": "boolean" }, "research setup apply");
143
+ if (strictBoolean(args, "help"))
144
+ return writeSetupHelp(io);
145
+ rejectPositionals(args.positionals, "research setup apply");
146
+ const planPath = strictString(args, "plan");
147
+ if (planPath && !isAbsolute(planPath)) {
148
+ throw invalidSetupArgument("--plan must be an absolute file path.");
149
+ }
150
+ const root = workspaceArgument(args);
151
+ const result = await applyResearchSetupPlan(planPath ? resolve(planPath) : workspacePaths(root).setupPlan, { environment: io.env, skipDoctor: strictBoolean(args, "skip-doctor") });
152
+ writeSetupJson(io, result, args);
153
+ return result.state.status === "blocked" ? 3 : 0;
154
+ }
155
+ async function runStatus(argv, io) {
156
+ const args = parseStrictArgs(argv, WORKSPACE_OPTIONS, "research setup status");
157
+ if (strictBoolean(args, "help"))
158
+ return writeSetupHelp(io);
159
+ rejectPositionals(args.positionals, "research setup status");
160
+ const result = await inspectResearchSetupStatus(workspaceArgument(args), io.env);
161
+ writeSetupJson(io, result, args);
162
+ return result.state.status === "blocked" ? 3 : 0;
163
+ }
164
+ async function runDoctor(argv, io) {
165
+ const args = parseStrictArgs(argv, {
166
+ ...WORKSPACE_OPTIONS,
167
+ live: "boolean",
168
+ "allow-synthetic-unstructure-upload": "boolean",
169
+ "agent-smoke": "boolean",
170
+ "confirm-agent-smoke-cost": "boolean",
171
+ }, "research setup doctor");
172
+ if (strictBoolean(args, "help"))
173
+ return writeSetupHelp(io);
174
+ rejectPositionals(args.positionals, "research setup doctor");
175
+ if (strictBoolean(args, "allow-synthetic-unstructure-upload") && !strictBoolean(args, "live")) {
176
+ throw invalidSetupArgument("Synthetic upload confirmation is valid only together with --live.");
177
+ }
178
+ if (strictBoolean(args, "agent-smoke") && !strictBoolean(args, "confirm-agent-smoke-cost")) {
179
+ throw new CliError("Agent smoke may consume provider quota and requires explicit confirmation.", {
180
+ code: "RESEARCH_SETUP_CONFIRMATION_REQUIRED",
181
+ exitCode: 2,
182
+ details: {
183
+ step: "doctor",
184
+ minimumAction: "Pass --confirm-agent-smoke-cost or omit --agent-smoke.",
185
+ },
186
+ });
187
+ }
188
+ const result = await doctorResearchSetup(workspaceArgument(args), {
189
+ live: strictBoolean(args, "live"),
190
+ allowSyntheticUnstructureUpload: strictBoolean(args, "allow-synthetic-unstructure-upload"),
191
+ agentSmoke: strictBoolean(args, "agent-smoke"),
192
+ environment: io.env,
193
+ });
194
+ writeSetupJson(io, result, args);
195
+ return result.readiness === "READY" ? 0 : 3;
196
+ }
197
+ async function runCredential(argv, io) {
198
+ const [action, ...rest] = argv;
199
+ if (action === "--help" || action === "-h")
200
+ return writeSetupHelp(io);
201
+ if (action !== "set") {
202
+ throw new CliError(`Unknown research setup credential action: ${action ?? "missing"}`, {
203
+ code: "INVALID_ARGS",
204
+ exitCode: 2,
205
+ });
206
+ }
207
+ const args = parseStrictArgs(rest, { ...WORKSPACE_OPTIONS, id: "string", "from-env": "string" }, "research setup credential set");
208
+ if (strictBoolean(args, "help"))
209
+ return writeSetupHelp(io);
210
+ rejectPositionals(args.positionals, "research setup credential set");
211
+ const credentialId = strictString(args, "id");
212
+ const environmentName = strictString(args, "from-env");
213
+ if (!credentialId || !environmentName) {
214
+ throw invalidSetupArgument("research setup credential set requires --id and --from-env.");
215
+ }
216
+ const result = await setResearchSetupCredentialFromEnvironment({
217
+ workspace: workspaceArgument(args),
218
+ credentialId,
219
+ environmentName,
220
+ environment: io.env,
221
+ });
222
+ writeSetupJson(io, result, args);
223
+ return 0;
224
+ }
225
+ async function runCompanion(argv, io) {
226
+ const [action, ...rest] = argv;
227
+ if (action === "--help" || action === "-h")
228
+ return writeSetupHelp(io);
229
+ if (action !== "run") {
230
+ throw new CliError(`Unknown research setup companion action: ${action ?? "missing"}`, {
231
+ code: "INVALID_ARGS",
232
+ exitCode: 2,
233
+ });
234
+ }
235
+ const args = parseStrictArgs(rest, {
236
+ ...WORKSPACE_OPTIONS,
237
+ id: "string",
238
+ input: "string",
239
+ output: "string",
240
+ out: "string",
241
+ doi: "string",
242
+ title: "string",
243
+ author: "string",
244
+ year: "string",
245
+ timeout: "string",
246
+ }, "research setup companion run");
247
+ if (strictBoolean(args, "help"))
248
+ return writeSetupHelp(io);
249
+ rejectPositionals(args.positionals, "research setup companion run");
250
+ const id = strictString(args, "id");
251
+ const timeoutSeconds = optionalPositiveInteger(strictString(args, "timeout"), "--timeout");
252
+ let result;
253
+ if (id === "tiangong.document-granular-decompose") {
254
+ const inputPath = strictString(args, "input");
255
+ const outputPath = strictString(args, "output");
256
+ if (!inputPath || !outputPath) {
257
+ throw invalidSetupArgument("The document companion requires --input and --output.");
258
+ }
259
+ rejectPresentOptions(args, ["out", "doi", "title", "author", "year"], id);
260
+ result = await runResearchSetupCompanion({
261
+ workspace: workspaceArgument(args),
262
+ skillId: id,
263
+ inputPath,
264
+ outputPath,
265
+ ...(timeoutSeconds === undefined ? {} : { timeoutSeconds }),
266
+ }, { environment: io.env });
267
+ }
268
+ else if (id === "tiangong.academic-paper-download") {
269
+ const outputDirectory = strictString(args, "out");
270
+ if (!outputDirectory) {
271
+ throw invalidSetupArgument("The academic-paper companion requires --out.");
272
+ }
273
+ rejectPresentOptions(args, ["input", "output"], id);
274
+ const year = optionalPositiveInteger(strictString(args, "year"), "--year");
275
+ const doi = strictString(args, "doi");
276
+ const title = strictString(args, "title");
277
+ const author = strictString(args, "author");
278
+ result = await runResearchSetupCompanion({
279
+ workspace: workspaceArgument(args),
280
+ skillId: id,
281
+ outputDirectory,
282
+ ...(doi === undefined ? {} : { doi }),
283
+ ...(title === undefined ? {} : { title }),
284
+ ...(author === undefined ? {} : { author }),
285
+ ...(year === undefined ? {} : { year }),
286
+ ...(timeoutSeconds === undefined ? {} : { timeoutSeconds }),
287
+ }, { environment: io.env });
288
+ }
289
+ else {
290
+ throw invalidSetupArgument("--id must be tiangong.document-granular-decompose or tiangong.academic-paper-download.");
291
+ }
292
+ writeSetupJson(io, result, args);
293
+ return result.status === "browser-handoff-required" ? 4 : 0;
294
+ }
295
+ async function runRetry(argv, io) {
296
+ const args = parseStrictArgs(argv, {
297
+ ...WORKSPACE_OPTIONS,
298
+ step: "string",
299
+ "clear-stale-lock": "boolean",
300
+ "confirm-clear-stale-lock": "boolean",
301
+ }, "research setup retry");
302
+ if (strictBoolean(args, "help"))
303
+ return writeSetupHelp(io);
304
+ rejectPositionals(args.positionals, "research setup retry");
305
+ const step = strictString(args, "step");
306
+ if (!step)
307
+ throw invalidSetupArgument("research setup retry requires --step.");
308
+ const clear = strictBoolean(args, "clear-stale-lock");
309
+ if (clear && !strictBoolean(args, "confirm-clear-stale-lock")) {
310
+ throw new CliError("Clearing a stale setup lock requires a second explicit confirmation.", {
311
+ code: "RESEARCH_SETUP_CONFIRMATION_REQUIRED",
312
+ exitCode: 2,
313
+ });
314
+ }
315
+ const result = await retryResearchSetup({
316
+ workspace: workspaceArgument(args),
317
+ step,
318
+ clearStaleLock: clear,
319
+ options: { environment: io.env },
320
+ });
321
+ writeSetupJson(io, result, args);
322
+ return result.state.status === "blocked" ? 3 : 0;
323
+ }
324
+ async function runUpdate(argv, io) {
325
+ const args = parseStrictArgs(argv, { ...WORKSPACE_OPTIONS, check: "boolean" }, "research setup update");
326
+ if (strictBoolean(args, "help"))
327
+ return writeSetupHelp(io);
328
+ rejectPositionals(args.positionals, "research setup update");
329
+ if (!strictBoolean(args, "check")) {
330
+ throw invalidSetupArgument("research setup update is read-only and requires --check.");
331
+ }
332
+ const result = await checkResearchSetupUpdates(workspaceArgument(args), io.env);
333
+ writeSetupJson(io, result, args);
334
+ return 0;
335
+ }
336
+ async function runUpgrade(argv, io) {
337
+ const args = parseStrictArgs(argv, {
338
+ ...WORKSPACE_OPTIONS,
339
+ plan: "boolean",
340
+ "confirm-upgrade": "boolean",
341
+ "accept-license": "string",
342
+ }, "research setup upgrade");
343
+ if (strictBoolean(args, "help"))
344
+ return writeSetupHelp(io);
345
+ rejectPositionals(args.positionals, "research setup upgrade");
346
+ if (!strictBoolean(args, "plan")) {
347
+ throw invalidSetupArgument("Upgrade is plan-only; pass --plan and review the result before apply.");
348
+ }
349
+ const result = await createResearchSetupUpgradePlan({
350
+ workspace: workspaceArgument(args),
351
+ acceptedLicenseIds: csv(strictString(args, "accept-license")),
352
+ confirmUpgrade: strictBoolean(args, "confirm-upgrade"),
353
+ environment: io.env,
354
+ });
355
+ writeSetupJson(io, result, args);
356
+ return 0;
357
+ }
358
+ function workspaceArgument(args) {
359
+ return resolve(strictString(args, "workspace") ?? process.cwd());
360
+ }
361
+ function requiredAbsoluteWorkspace(args) {
362
+ const workspace = strictString(args, "workspace");
363
+ if (!workspace || !isAbsolute(workspace)) {
364
+ throw invalidSetupArgument("research setup plan requires an absolute --workspace path.");
365
+ }
366
+ return resolve(workspace);
367
+ }
368
+ function setupScope(value) {
369
+ if (!value || value === "project")
370
+ return "project";
371
+ if (value === "global")
372
+ return value;
373
+ throw invalidSetupArgument(`Unsupported setup scope: ${value}.`);
374
+ }
375
+ function setupAgents(value) {
376
+ const values = value ? csv(value) : ["codex"];
377
+ if (values.length === 0 || values.some((agent) => agent !== "codex" && agent !== "claude-code")) {
378
+ throw invalidSetupArgument("--agents must be codex, claude-code, or both as CSV.");
379
+ }
380
+ return [...new Set(values)];
381
+ }
382
+ function setupMode(value, required) {
383
+ if (value === "smoke-test" || value === "production-research")
384
+ return value;
385
+ if (!value && !required)
386
+ return "production-research";
387
+ throw invalidSetupArgument("--mode must be smoke-test or production-research.");
388
+ }
389
+ function setupEvidenceProfile(value, required) {
390
+ if (value === "none" ||
391
+ value === EXTERNAL_SKILL_PROFILE ||
392
+ value === EXTERNAL_SKILL_CONTEXT_PROFILE ||
393
+ value === EXTERNAL_SKILL_MEDIA_PROFILE) {
394
+ return value;
395
+ }
396
+ if (value === "brave-baseline")
397
+ return EXTERNAL_SKILL_PROFILE;
398
+ if (value === "brave-context")
399
+ return EXTERNAL_SKILL_CONTEXT_PROFILE;
400
+ if (value === "brave-media")
401
+ return EXTERNAL_SKILL_MEDIA_PROFILE;
402
+ if (!value && !required)
403
+ return EXTERNAL_SKILL_CONTEXT_PROFILE;
404
+ throw invalidSetupArgument("--evidence-profile must be none, brave-baseline, brave-context, or brave-media.");
405
+ }
406
+ function csv(value) {
407
+ if (!value)
408
+ return [];
409
+ const values = value
410
+ .split(",")
411
+ .map((item) => item.trim())
412
+ .filter(Boolean);
413
+ if (new Set(values).size !== values.length) {
414
+ throw invalidSetupArgument("CSV option values must not contain duplicates.");
415
+ }
416
+ return values;
417
+ }
418
+ function optionalPositiveInteger(value, label) {
419
+ if (value === undefined)
420
+ return undefined;
421
+ if (!/^[1-9][0-9]*$/.test(value)) {
422
+ throw invalidSetupArgument(`${label} must be a positive base-10 integer.`);
423
+ }
424
+ const parsed = Number(value);
425
+ if (!Number.isSafeInteger(parsed)) {
426
+ throw invalidSetupArgument(`${label} is outside the supported integer range.`);
427
+ }
428
+ return parsed;
429
+ }
430
+ function rejectPresentOptions(args, names, skillId) {
431
+ const present = names.filter((name) => args.flags.has(name));
432
+ if (present.length) {
433
+ throw invalidSetupArgument(`${present.map((name) => `--${name}`).join(", ")} do not apply to ${skillId}.`);
434
+ }
435
+ }
436
+ async function optionalStringRecord(path, label) {
437
+ if (!path)
438
+ return {};
439
+ const value = await readAbsoluteJson(path, label);
440
+ if (!isObject(value) || Object.values(value).some((item) => typeof item !== "string")) {
441
+ throw invalidSetupArgument(`${label} must contain one JSON object of string values.`);
442
+ }
443
+ return value;
444
+ }
445
+ async function optionalAgentRoutes(path) {
446
+ if (!path)
447
+ return {};
448
+ const value = await readAbsoluteJson(path, "--agent-routes");
449
+ if (!isObject(value)) {
450
+ throw invalidSetupArgument("--agent-routes must contain one JSON object.");
451
+ }
452
+ return value;
453
+ }
454
+ async function readAbsoluteJson(path, label) {
455
+ if (!isAbsolute(path))
456
+ throw invalidSetupArgument(`${label} must be an absolute JSON path.`);
457
+ try {
458
+ return JSON.parse(await readFile(path, "utf8"));
459
+ }
460
+ catch (error) {
461
+ throw new CliError(`${label} is missing or invalid JSON.`, {
462
+ code: "RESEARCH_SETUP_INPUT_INVALID",
463
+ exitCode: 2,
464
+ details: { label, error: String(error) },
465
+ });
466
+ }
467
+ }
468
+ function rejectPositionals(positionals, command) {
469
+ if (positionals.length) {
470
+ throw new CliError(`${command} does not accept positional arguments.`, {
471
+ code: "INVALID_ARGS",
472
+ exitCode: 2,
473
+ details: { positionals },
474
+ });
475
+ }
476
+ }
477
+ function invalidSetupArgument(message) {
478
+ return new CliError(message, { code: "RESEARCH_SETUP_ARGUMENT_INVALID", exitCode: 2 });
479
+ }
480
+ function writeSetupJson(io, value, args) {
481
+ write(io.stdout, stringifyJson(value, strictBoolean(args, "json")));
482
+ }
483
+ function writeSetupHelp(io) {
484
+ write(io.stdout, researchSetupHelp());
485
+ return 0;
486
+ }
487
+ //# sourceMappingURL=setup-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-command.js","sourceRoot":"","sources":["../../src/research/setup-command.ts"],"names":[],"mappings":"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"}
@@ -101,6 +101,10 @@ async function handleMcpRequest(input) {
101
101
  description: "Logical credential ID. It is selected automatically when the capability declares exactly one credential.",
102
102
  },
103
103
  url: { type: "string" },
104
+ request_body: {
105
+ type: "object",
106
+ description: "JSON request body. Required only when the locked capability explicitly authorizes POST.",
107
+ },
104
108
  json_pointer: { type: "string" },
105
109
  item_offset: { type: "integer", minimum: 0 },
106
110
  max_items: { type: "integer", minimum: 1 },
@@ -163,6 +167,7 @@ async function fetchCandidateSource(input) {
163
167
  const capabilityId = input.arguments.capability_id;
164
168
  const credentialId = input.arguments.credential_id;
165
169
  const rawUrl = input.arguments.url;
170
+ const requestBody = input.arguments.request_body;
166
171
  const jsonPointer = input.arguments.json_pointer;
167
172
  const requestedItemOffset = input.arguments.item_offset ?? 0;
168
173
  const requestedMaxItems = input.arguments.max_items;
@@ -199,6 +204,7 @@ async function fetchCandidateSource(input) {
199
204
  const target = validateHttpsUrl(rawUrl);
200
205
  if (!capability.http)
201
206
  throw new Error(`capability has no broker HTTP policy: ${capabilityId}`);
207
+ const encodedRequestBody = validateBrokerRequestBody(capability.http.method, requestBody, capability.http.maxRequestBytes);
202
208
  const credential = credentialId
203
209
  ? capability.credentials.find((candidate) => candidate.id === credentialId)
204
210
  : capability.credentials.length === 1
@@ -223,6 +229,8 @@ async function fetchCandidateSource(input) {
223
229
  const cacheKeySha256 = sha256Text(canonicalJson({
224
230
  capabilityId,
225
231
  targetSha256: sha256Text(target.toString()),
232
+ method: capability.http.method,
233
+ requestBodySha256: encodedRequestBody ? sha256Text(encodedRequestBody) : null,
226
234
  accept: capability.http.accept,
227
235
  }));
228
236
  await appendJournalEvent(workspacePaths(input.root).journal, "capability.fetch.attempted", input.projectId, {
@@ -231,6 +239,8 @@ async function fetchCandidateSource(input) {
231
239
  capabilityId,
232
240
  credentialId: effectiveCredentialId,
233
241
  targetSha256: sha256Text(target.toString()),
242
+ method: capability.http.method,
243
+ requestBodySha256: encodedRequestBody ? sha256Text(encodedRequestBody) : null,
234
244
  cacheMode,
235
245
  cacheKeySha256,
236
246
  });
@@ -261,14 +271,17 @@ async function fetchCandidateSource(input) {
261
271
  return brokerToolResult(receipt, context.bytes, cached.contentType);
262
272
  }
263
273
  }
264
- const headers = new Headers({ Accept: capability.http.accept });
274
+ const headers = new Headers(capability.http.staticHeaders);
275
+ headers.set("Accept", capability.http.accept);
276
+ if (encodedRequestBody)
277
+ headers.set("Content-Type", "application/json");
265
278
  if (credential) {
266
279
  const value = input.credentialMap.get(credential.id);
267
280
  if (!value)
268
281
  throw new Error(`credential value is not configured: ${credential.id}`);
269
282
  headers.set(credential.headerName, `${credential.prefix}${value}`);
270
283
  }
271
- const { response, finalUrl } = await fetchWithRedirectPolicy(target, headers, capability.allowedHosts, credential?.allowedHosts);
284
+ const { response, finalUrl } = await fetchWithRedirectPolicy(target, headers, capability.allowedHosts, credential?.allowedHosts, capability.http.method, encodedRequestBody);
272
285
  const responseLimit = Math.min(capability.http.maxResponseBytes, input.workspaceResponseBytes);
273
286
  const announcedLength = Number(response.headers.get("content-length") ?? "0");
274
287
  if (response.ok && announcedLength > responseLimit)
@@ -424,21 +437,26 @@ async function appendCompletedReceipt(root, projectId, receipt, cacheKeySha256)
424
437
  cacheKeySha256,
425
438
  });
426
439
  }
427
- async function fetchWithRedirectPolicy(initialUrl, headers, capabilityHosts, credentialHosts) {
440
+ async function fetchWithRedirectPolicy(initialUrl, headers, capabilityHosts, credentialHosts, method, body) {
428
441
  let current = initialUrl;
429
442
  for (let redirects = 0; redirects <= 5; redirects += 1) {
430
443
  assertAllowedHost(current, capabilityHosts, "capability");
431
444
  if (credentialHosts)
432
445
  assertAllowedHost(current, credentialHosts, "credential");
433
446
  const response = await fetch(current, {
434
- method: "GET",
447
+ method,
435
448
  headers,
449
+ ...(body === null ? {} : { body }),
436
450
  redirect: "manual",
437
451
  signal: AbortSignal.timeout(120_000),
438
452
  });
439
453
  if (![301, 302, 303, 307, 308].includes(response.status)) {
440
454
  return { response, finalUrl: current };
441
455
  }
456
+ if (method === "POST") {
457
+ await response.body?.cancel();
458
+ throw new Error("POST capability redirects are not authorized");
459
+ }
442
460
  const location = response.headers.get("location");
443
461
  await response.body?.cancel();
444
462
  if (!location)
@@ -449,6 +467,23 @@ async function fetchWithRedirectPolicy(initialUrl, headers, capabilityHosts, cre
449
467
  }
450
468
  throw new Error("HTTPS source exceeded the redirect limit");
451
469
  }
470
+ function validateBrokerRequestBody(method, value, maxRequestBytes) {
471
+ if (method === "GET") {
472
+ if (value !== undefined)
473
+ throw new Error("GET capability does not accept request_body");
474
+ return null;
475
+ }
476
+ if (!isObject(value))
477
+ throw new Error("POST capability requires request_body as an object");
478
+ if (containsSensitiveJsonField(value)) {
479
+ throw new Error("request_body contains credential-like fields");
480
+ }
481
+ const encoded = JSON.stringify(value);
482
+ if (Buffer.byteLength(encoded, "utf8") > maxRequestBytes) {
483
+ throw new Error("request_body exceeds the capability request size limit");
484
+ }
485
+ return encoded;
486
+ }
452
487
  function assertAllowedHost(url, allowedHosts, scope) {
453
488
  if (!allowedHosts.includes(url.host)) {
454
489
  throw new Error(`target host is outside ${scope} scope: ${url.host}`);