@tonyclaw/agent-inspector 3.0.44 → 3.0.45
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/.output/cli.js +20743 -16945
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-DQHjxFO6.js → CompareDrawer-B3_TLTJh.js} +1 -1
- package/.output/public/assets/{InspectorPet-D1A4505B.js → InspectorPet-Cpn6hv4P.js} +1 -1
- package/.output/public/assets/{ProxyViewerContainer-BhpIgfBi.js → ProxyViewerContainer-BEAhuGOJ.js} +27 -27
- package/.output/public/assets/{ReplayDialog-C5vmbecv.js → ReplayDialog-B3EKVdJk.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-CPGNB8lk.js → RequestAnatomy-COOQjyUC.js} +1 -1
- package/.output/public/assets/{ResponseView-DUfrajCm.js → ResponseView-DBCjkwye.js} +1 -1
- package/.output/public/assets/{StreamingChunkSequence-nbeCQqOz.js → StreamingChunkSequence-CuXhDgMo.js} +1 -1
- package/.output/public/assets/{_sessionId-mw3aA8Bp.js → _sessionId-BJlM3nmt.js} +1 -1
- package/.output/public/assets/{_sessionId-CDhRgzf6.js → _sessionId-zbgYl7ZJ.js} +1 -1
- package/.output/public/assets/{index-B2eoeT-Z.js → index-BIK4bPTv.js} +1 -1
- package/.output/public/assets/{index-C6qZwYlR.js → index-BZo_8koU.js} +15 -15
- package/.output/public/assets/{index-CX7_rf7o.js → index-D2Qw6Ri2.js} +1 -1
- package/.output/public/assets/{index-BWjeqxbk.css → index-ZWschkCh.css} +1 -1
- package/.output/public/assets/{index-BfRhTvOd.js → index-xF_NY8Yb.js} +5 -5
- package/.output/public/assets/{json-viewer-BLmF1sOZ.js → json-viewer-B-Ao6pwW.js} +1 -1
- package/.output/public/assets/{jszip.min-C9rCFoU3.js → jszip.min-B_rfAUCe.js} +1 -1
- package/.output/server/_libs/modelcontextprotocol__core.mjs +25 -25
- package/.output/server/_libs/modelcontextprotocol__server.mjs +2 -2
- package/.output/server/_libs/zod.mjs +6 -6
- package/.output/server/{_sessionId-U54BAqdp.mjs → _sessionId-CJ99gZFV.mjs} +3 -1
- package/.output/server/{_sessionId-E_Dmopc-.mjs → _sessionId-DEjScBpK.mjs} +5 -3
- package/.output/server/_ssr/{CompareDrawer-CksatPed.mjs → CompareDrawer-DtOIMeQB.mjs} +4 -2
- package/.output/server/_ssr/{InspectorPet-DNc4GQ7z.mjs → InspectorPet-CXc4oCBQ.mjs} +3 -1
- package/.output/server/_ssr/{ProxyViewerContainer-B02Ey0p_.mjs → ProxyViewerContainer-DVPlwfkt.mjs} +52 -27
- package/.output/server/_ssr/{ReplayDialog-DdMvHJF1.mjs → ReplayDialog-BQEA95qC.mjs} +6 -4
- package/.output/server/_ssr/{RequestAnatomy--vh2Z8__.mjs → RequestAnatomy-BMyd8B7Q.mjs} +4 -2
- package/.output/server/_ssr/{ResponseView-Dhxc-cKC.mjs → ResponseView-qrTaEIM8.mjs} +4 -2
- package/.output/server/_ssr/{StreamingChunkSequence-BG0EQmaD.mjs → StreamingChunkSequence-BFTL8mIy.mjs} +5 -3
- package/.output/server/_ssr/{index-lcfulIXa.mjs → index-Bcj1Hcy4.mjs} +4 -2
- package/.output/server/_ssr/{index-D0At9nfd.mjs → index-DW-M7XUG.mjs} +3 -1
- package/.output/server/_ssr/index.mjs +4 -3
- package/.output/server/_ssr/{json-viewer-My1J1_5R.mjs → json-viewer-DiI0Yjsn.mjs} +4 -2
- package/.output/server/_ssr/publicBasePathContext-5264L3rX.mjs +148 -0
- package/.output/server/_ssr/{router-DWcUFPR3.mjs → router-BJ_ltLim.mjs} +2432 -475
- package/.output/server/_ssr/start-C6MjPjxp.mjs +57 -0
- package/.output/server/_tanstack-start-manifest_v-C6pXXX9R.mjs +4 -0
- package/.output/server/index.mjs +92 -92
- package/.output/workers/logFinalizer.worker.js +4 -0
- package/.output/workers/sessionWorkerEntry.js +4 -0
- package/README.md +221 -25
- package/docs/instance-control.openapi.yaml +306 -0
- package/package.json +4 -1
- package/src/cli/alias.ts +440 -0
- package/src/cli/instance.ts +1037 -0
- package/src/cli/instanceArgs.ts +266 -0
- package/src/cli/instanceControl.ts +183 -0
- package/src/cli/instanceModel.ts +275 -0
- package/src/cli/instanceRegistry.ts +740 -0
- package/src/cli/onboard.ts +123 -35
- package/src/cli/onboardTarget.ts +179 -0
- package/src/cli/templates/codex-skill-onboard.ts +76 -28
- package/src/cli/templates/skill-onboard.ts +93 -59
- package/src/cli.ts +154 -16
- package/src/components/ProxyViewer.tsx +13 -5
- package/src/components/providers/SettingsDialog.tsx +13 -8
- package/src/lib/basePath.ts +8 -2
- package/src/lib/codingAgentEndpoints.ts +16 -0
- package/src/lib/instanceContract.ts +220 -0
- package/src/lib/managedInstance.ts +104 -0
- package/src/lib/publicBasePathContext.ts +107 -0
- package/src/mcp/instanceHandlers.ts +149 -0
- package/src/mcp/loopback.ts +16 -5
- package/src/mcp/mode.ts +25 -0
- package/src/mcp/server.ts +258 -18
- package/src/mcp/toolHandlers.ts +2 -2
- package/src/proxy/identityProxy.ts +655 -36
- package/src/proxy/runtimeShutdown.ts +73 -1
- package/src/proxy/sessionInfo.ts +5 -4
- package/src/routes/api/-instances.ts +109 -0
- package/src/routes/api/instances.$name.connection.ts +12 -0
- package/src/routes/api/instances.$name.restart.ts +23 -0
- package/src/routes/api/instances.$name.start.ts +18 -0
- package/src/routes/api/instances.$name.stop.ts +23 -0
- package/src/routes/api/instances.$name.ts +12 -0
- package/src/routes/api/instances.ts +16 -0
- package/src/start.ts +10 -0
- package/.output/server/_ssr/start-HYkvq4Ni.mjs +0 -4
- package/.output/server/_tanstack-start-manifest_v-B2chR8ef.mjs +0 -4
package/src/cli/onboard.ts
CHANGED
|
@@ -26,6 +26,7 @@ import { fileURLToPath } from "node:url";
|
|
|
26
26
|
|
|
27
27
|
import { appendBasePathToOrigin, getConfiguredBasePath } from "../lib/basePath.js";
|
|
28
28
|
import { detectAll, detectFirst } from "./detect-tools.js";
|
|
29
|
+
import { type OnboardTarget, resolveOnboardTarget } from "./onboardTarget.js";
|
|
29
30
|
import { renderCommandOnboard } from "./templates/command-onboard.js";
|
|
30
31
|
import {
|
|
31
32
|
renderCodexSkillOnboard,
|
|
@@ -38,7 +39,11 @@ const __dirname = dirname(__filename);
|
|
|
38
39
|
|
|
39
40
|
const DEFAULT_PORT = 9527;
|
|
40
41
|
const DEFAULT_ORIGIN = `http://localhost:${DEFAULT_PORT}`;
|
|
41
|
-
const DEFAULT_MCP_URL =
|
|
42
|
+
const DEFAULT_MCP_URL = appendBasePathToOrigin(
|
|
43
|
+
DEFAULT_ORIGIN,
|
|
44
|
+
"/api/mcp",
|
|
45
|
+
getConfiguredBasePath({}),
|
|
46
|
+
);
|
|
42
47
|
const LEGACY_MCP_URL = "http://localhost:9527/api/mcp";
|
|
43
48
|
const SKILL_DIR_NAME = "agent-inspector-onboard";
|
|
44
49
|
const SKILL_FILE_NAME = "SKILL.md";
|
|
@@ -65,12 +70,15 @@ export type OnboardFlags = {
|
|
|
65
70
|
opencodeOnly: boolean;
|
|
66
71
|
opencodeConfig: string | null;
|
|
67
72
|
opencodeTransport: AgentMcpTransport;
|
|
68
|
-
opencodeMcpUrl: string;
|
|
73
|
+
opencodeMcpUrl: string | null;
|
|
69
74
|
mimo: boolean;
|
|
70
75
|
mimoOnly: boolean;
|
|
71
76
|
mimoConfig: string | null;
|
|
72
77
|
mimoTransport: AgentMcpTransport;
|
|
73
|
-
mimoMcpUrl: string;
|
|
78
|
+
mimoMcpUrl: string | null;
|
|
79
|
+
instanceName: string | null;
|
|
80
|
+
baseUrl: string | null;
|
|
81
|
+
mcpName: string | null;
|
|
74
82
|
uninstall: boolean;
|
|
75
83
|
status: boolean;
|
|
76
84
|
json: boolean;
|
|
@@ -88,6 +96,7 @@ type AgentMcpConfigTarget = {
|
|
|
88
96
|
configPath: string;
|
|
89
97
|
transport: AgentMcpTransport;
|
|
90
98
|
endpoint: string;
|
|
99
|
+
mcpName: string;
|
|
91
100
|
setupAction: string;
|
|
92
101
|
forceAction: string;
|
|
93
102
|
verifyCommand: string;
|
|
@@ -102,6 +111,7 @@ type AgentMcpConfigStatus = {
|
|
|
102
111
|
state: AgentMcpConfigState;
|
|
103
112
|
transport: AgentMcpStatusTransport;
|
|
104
113
|
endpoint: string;
|
|
114
|
+
mcpName: string;
|
|
105
115
|
action: string;
|
|
106
116
|
forceAction: string;
|
|
107
117
|
verifyCommand: string;
|
|
@@ -183,11 +193,6 @@ function actionForStatus(label: string, state: GeneratedFileState): string {
|
|
|
183
193
|
}
|
|
184
194
|
|
|
185
195
|
function parseFlags(argv: readonly string[]): OnboardFlags {
|
|
186
|
-
const defaultMcpUrl = appendBasePathToOrigin(
|
|
187
|
-
DEFAULT_ORIGIN,
|
|
188
|
-
"/api/mcp",
|
|
189
|
-
getConfiguredBasePath(process.env),
|
|
190
|
-
);
|
|
191
196
|
const flags: OnboardFlags = {
|
|
192
197
|
force: false,
|
|
193
198
|
dryRun: false,
|
|
@@ -199,12 +204,15 @@ function parseFlags(argv: readonly string[]): OnboardFlags {
|
|
|
199
204
|
opencodeOnly: false,
|
|
200
205
|
opencodeConfig: null,
|
|
201
206
|
opencodeTransport: "local",
|
|
202
|
-
opencodeMcpUrl:
|
|
207
|
+
opencodeMcpUrl: null,
|
|
203
208
|
mimo: false,
|
|
204
209
|
mimoOnly: false,
|
|
205
210
|
mimoConfig: null,
|
|
206
211
|
mimoTransport: "local",
|
|
207
|
-
mimoMcpUrl:
|
|
212
|
+
mimoMcpUrl: null,
|
|
213
|
+
instanceName: null,
|
|
214
|
+
baseUrl: null,
|
|
215
|
+
mcpName: null,
|
|
208
216
|
uninstall: false,
|
|
209
217
|
status: false,
|
|
210
218
|
json: false,
|
|
@@ -332,6 +340,36 @@ function parseFlags(argv: readonly string[]): OnboardFlags {
|
|
|
332
340
|
i++;
|
|
333
341
|
break;
|
|
334
342
|
}
|
|
343
|
+
case "--instance": {
|
|
344
|
+
const next = argv[i + 1];
|
|
345
|
+
if (next === undefined || next.trim().length === 0) {
|
|
346
|
+
process.stderr.write("agent-inspector onboard: --instance requires a name\n");
|
|
347
|
+
process.exit(2);
|
|
348
|
+
}
|
|
349
|
+
flags.instanceName = next;
|
|
350
|
+
i++;
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
case "--base-url": {
|
|
354
|
+
const next = argv[i + 1];
|
|
355
|
+
if (next === undefined || next.trim().length === 0) {
|
|
356
|
+
process.stderr.write("agent-inspector onboard: --base-url requires a URL\n");
|
|
357
|
+
process.exit(2);
|
|
358
|
+
}
|
|
359
|
+
flags.baseUrl = next;
|
|
360
|
+
i++;
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
case "--mcp-name": {
|
|
364
|
+
const next = argv[i + 1];
|
|
365
|
+
if (next === undefined || next.trim().length === 0) {
|
|
366
|
+
process.stderr.write("agent-inspector onboard: --mcp-name requires a name\n");
|
|
367
|
+
process.exit(2);
|
|
368
|
+
}
|
|
369
|
+
flags.mcpName = next;
|
|
370
|
+
i++;
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
335
373
|
case "--uninstall":
|
|
336
374
|
flags.uninstall = true;
|
|
337
375
|
break;
|
|
@@ -401,6 +439,9 @@ Options:
|
|
|
401
439
|
--mimo-transport <local|remote>
|
|
402
440
|
MiMo Code MCP transport to write (default: local)
|
|
403
441
|
--mimo-mcp-url <url> Agent Inspector MCP endpoint for MiMo Code
|
|
442
|
+
--instance <name> Target a registered named Inspector instance
|
|
443
|
+
--base-url <url> Target an explicit credential-free Inspector Base URL
|
|
444
|
+
--mcp-name <name> MCP config key (named default: agent-inspector-<instance>)
|
|
404
445
|
--uninstall Remove generated files whose version matches this package
|
|
405
446
|
--status Show installed onboarding file versions and suggested action
|
|
406
447
|
--json Emit machine-readable JSON with --status
|
|
@@ -487,34 +528,49 @@ function resolveMiMoConfigPath(flags: OnboardFlags): string {
|
|
|
487
528
|
return jsoncPath;
|
|
488
529
|
}
|
|
489
530
|
|
|
490
|
-
function
|
|
531
|
+
function onboardSelectionArguments(flags: OnboardFlags, target: OnboardTarget): string {
|
|
532
|
+
const args: string[] = [];
|
|
533
|
+
if (target.kind === "instance" && target.instanceName !== null) {
|
|
534
|
+
args.push("--instance", target.instanceName);
|
|
535
|
+
} else if (target.kind === "base-url") {
|
|
536
|
+
args.push("--base-url", target.baseUrl);
|
|
537
|
+
}
|
|
538
|
+
if (flags.mcpName !== null) args.push("--mcp-name", target.mcpName);
|
|
539
|
+
return args.length === 0 ? "" : ` ${args.join(" ")}`;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function openCodeTarget(flags: OnboardFlags, onboardTarget: OnboardTarget): AgentMcpConfigTarget {
|
|
543
|
+
const setupAction = `agent-inspector onboard --opencode-only${onboardSelectionArguments(flags, onboardTarget)}`;
|
|
491
544
|
return {
|
|
492
545
|
label: "OpenCode",
|
|
493
546
|
configLabel: "OpenCode MCP config",
|
|
494
547
|
configPath: resolveOpenCodeConfigPath(flags),
|
|
495
548
|
transport: flags.opencodeTransport,
|
|
496
|
-
endpoint: flags.opencodeMcpUrl,
|
|
497
|
-
|
|
498
|
-
|
|
549
|
+
endpoint: flags.opencodeMcpUrl ?? onboardTarget.mcpUrl,
|
|
550
|
+
mcpName: onboardTarget.mcpName,
|
|
551
|
+
setupAction,
|
|
552
|
+
forceAction: `${setupAction} --force`,
|
|
499
553
|
verifyCommand: "opencode mcp list",
|
|
500
|
-
invalidAction:
|
|
501
|
-
configObjectName:
|
|
554
|
+
invalidAction: `fix OpenCode JSON/JSONC, then rerun ${setupAction}`,
|
|
555
|
+
configObjectName: `mcp.${onboardTarget.mcpName}`,
|
|
502
556
|
defaultSchema: "https://opencode.ai/config.json",
|
|
503
557
|
};
|
|
504
558
|
}
|
|
505
559
|
|
|
506
|
-
function miMoTarget(flags: OnboardFlags): AgentMcpConfigTarget {
|
|
560
|
+
function miMoTarget(flags: OnboardFlags, onboardTarget: OnboardTarget): AgentMcpConfigTarget {
|
|
561
|
+
const setupAction = `agent-inspector onboard --mimo-only${onboardSelectionArguments(flags, onboardTarget)}`;
|
|
507
562
|
return {
|
|
508
563
|
label: "MiMo Code",
|
|
509
564
|
configLabel: "MiMo Code MCP config",
|
|
510
565
|
configPath: resolveMiMoConfigPath(flags),
|
|
511
566
|
transport: flags.mimoTransport,
|
|
512
|
-
endpoint: flags.mimoMcpUrl,
|
|
513
|
-
|
|
514
|
-
|
|
567
|
+
endpoint: flags.mimoMcpUrl ?? onboardTarget.mcpUrl,
|
|
568
|
+
mcpName: onboardTarget.mcpName,
|
|
569
|
+
setupAction,
|
|
570
|
+
forceAction: `${setupAction} --force`,
|
|
515
571
|
verifyCommand: "mimo mcp list",
|
|
516
|
-
invalidAction:
|
|
517
|
-
configObjectName:
|
|
572
|
+
invalidAction: `fix MiMo Code JSON/JSONC, then rerun ${setupAction}`,
|
|
573
|
+
configObjectName: `mcp.${onboardTarget.mcpName}`,
|
|
518
574
|
defaultSchema: "https://mimo.xiaomi.com/mimocode/config.json",
|
|
519
575
|
};
|
|
520
576
|
}
|
|
@@ -731,6 +787,7 @@ function makeAgentStatus(
|
|
|
731
787
|
state,
|
|
732
788
|
transport,
|
|
733
789
|
endpoint: target.endpoint,
|
|
790
|
+
mcpName: target.mcpName,
|
|
734
791
|
action: target.setupAction,
|
|
735
792
|
forceAction: target.forceAction,
|
|
736
793
|
verifyCommand: target.verifyCommand,
|
|
@@ -743,12 +800,12 @@ function makeAgentStatus(
|
|
|
743
800
|
};
|
|
744
801
|
}
|
|
745
802
|
|
|
746
|
-
function getAgentEntry(config: Record<string, unknown
|
|
803
|
+
function getAgentEntry(config: Record<string, unknown>, mcpName: string): unknown {
|
|
747
804
|
const mcp = config["mcp"];
|
|
748
805
|
if (!isObject(mcp)) {
|
|
749
806
|
return undefined;
|
|
750
807
|
}
|
|
751
|
-
return mcp[
|
|
808
|
+
return mcp[mcpName];
|
|
752
809
|
}
|
|
753
810
|
|
|
754
811
|
function buildAgentConfig(
|
|
@@ -768,7 +825,7 @@ function buildAgentConfig(
|
|
|
768
825
|
|
|
769
826
|
const currentMcp = config["mcp"];
|
|
770
827
|
const nextMcp: Record<string, unknown> = isObject(currentMcp) ? { ...currentMcp } : {};
|
|
771
|
-
nextMcp[
|
|
828
|
+
nextMcp[target.mcpName] = buildAgentMcpEntry(target.transport, target.endpoint);
|
|
772
829
|
nextConfig["mcp"] = nextMcp;
|
|
773
830
|
return nextConfig;
|
|
774
831
|
}
|
|
@@ -777,7 +834,10 @@ function formatAgentConfig(config: Record<string, unknown>): string {
|
|
|
777
834
|
return `${JSON.stringify(config, null, 2)}\n`;
|
|
778
835
|
}
|
|
779
836
|
|
|
780
|
-
function removeAgentEntry(
|
|
837
|
+
function removeAgentEntry(
|
|
838
|
+
config: Record<string, unknown>,
|
|
839
|
+
mcpName: string,
|
|
840
|
+
): Record<string, unknown> {
|
|
781
841
|
const nextConfig: Record<string, unknown> = {};
|
|
782
842
|
for (const [key, value] of Object.entries(config)) {
|
|
783
843
|
if (key !== "mcp") {
|
|
@@ -792,7 +852,7 @@ function removeAgentEntry(config: Record<string, unknown>): Record<string, unkno
|
|
|
792
852
|
|
|
793
853
|
const nextMcp: Record<string, unknown> = {};
|
|
794
854
|
for (const [key, value] of Object.entries(currentMcp)) {
|
|
795
|
-
if (key !==
|
|
855
|
+
if (key !== mcpName) {
|
|
796
856
|
nextMcp[key] = value;
|
|
797
857
|
}
|
|
798
858
|
}
|
|
@@ -809,7 +869,7 @@ function readAgentStatus(target: AgentMcpConfigTarget): AgentMcpConfigStatus {
|
|
|
809
869
|
return makeAgentStatus(target, "invalid", "unknown", read.message);
|
|
810
870
|
}
|
|
811
871
|
|
|
812
|
-
const entry = getAgentEntry(read.config);
|
|
872
|
+
const entry = getAgentEntry(read.config, target.mcpName);
|
|
813
873
|
if (entry === undefined) {
|
|
814
874
|
const detail = read.exists
|
|
815
875
|
? `${target.label} config has no ${target.configObjectName}`
|
|
@@ -861,12 +921,14 @@ function buildAgentPlan(
|
|
|
861
921
|
}
|
|
862
922
|
|
|
863
923
|
const status = readAgentStatus(target);
|
|
864
|
-
const entry = getAgentEntry(read.config);
|
|
924
|
+
const entry = getAgentEntry(read.config, target.mcpName);
|
|
865
925
|
const nextBody = formatAgentConfig(buildAgentConfig(read.config, target));
|
|
866
926
|
|
|
867
927
|
if (uninstall) {
|
|
868
928
|
const shouldRemove = entry !== undefined && isKnownAgentEntry(entry, target.endpoint, true);
|
|
869
|
-
const removeBody = shouldRemove
|
|
929
|
+
const removeBody = shouldRemove
|
|
930
|
+
? formatAgentConfig(removeAgentEntry(read.config, target.mcpName))
|
|
931
|
+
: null;
|
|
870
932
|
return {
|
|
871
933
|
status,
|
|
872
934
|
body: removeBody,
|
|
@@ -1129,14 +1191,21 @@ function buildPlannedFiles(
|
|
|
1129
1191
|
flags: OnboardFlags,
|
|
1130
1192
|
targets: OnboardTargets,
|
|
1131
1193
|
version: string,
|
|
1194
|
+
onboardTarget: OnboardTarget,
|
|
1132
1195
|
): PlannedFile[] {
|
|
1133
1196
|
const installClaude = !flags.codexOnly && !hasOnlyAgentMcpTarget(flags);
|
|
1134
1197
|
const installCodex = !flags.skipCodexSkill && !hasOnlyAgentMcpTarget(flags);
|
|
1135
1198
|
const detectedSummary = installClaude ? buildDetectedSummary() : "";
|
|
1199
|
+
const targetArguments = onboardSelectionArguments(flags, onboardTarget);
|
|
1136
1200
|
const claudeSkillBody = installClaude
|
|
1137
1201
|
? renderSkillOnboard({
|
|
1138
1202
|
version,
|
|
1139
|
-
port:
|
|
1203
|
+
port: onboardTarget.port,
|
|
1204
|
+
baseUrl: onboardTarget.baseUrl,
|
|
1205
|
+
mcpName: onboardTarget.mcpName,
|
|
1206
|
+
targetKind: onboardTarget.kind,
|
|
1207
|
+
instanceName: onboardTarget.instanceName,
|
|
1208
|
+
onboardTargetArguments: targetArguments,
|
|
1140
1209
|
detectedSummary,
|
|
1141
1210
|
})
|
|
1142
1211
|
: "";
|
|
@@ -1144,7 +1213,12 @@ function buildPlannedFiles(
|
|
|
1144
1213
|
const codexSkillBody = installCodex
|
|
1145
1214
|
? renderCodexSkillOnboard({
|
|
1146
1215
|
version,
|
|
1147
|
-
port:
|
|
1216
|
+
port: onboardTarget.port,
|
|
1217
|
+
baseUrl: onboardTarget.baseUrl,
|
|
1218
|
+
mcpName: onboardTarget.mcpName,
|
|
1219
|
+
targetKind: onboardTarget.kind,
|
|
1220
|
+
instanceName: onboardTarget.instanceName,
|
|
1221
|
+
onboardTargetArguments: targetArguments,
|
|
1148
1222
|
})
|
|
1149
1223
|
: "";
|
|
1150
1224
|
|
|
@@ -1348,6 +1422,7 @@ function runStatus(
|
|
|
1348
1422
|
process.stdout.write(`${agentStatus.label}: ${agentStatus.state}`);
|
|
1349
1423
|
process.stdout.write(`, transport ${agentStatus.transport}\n`);
|
|
1350
1424
|
process.stdout.write(` ${agentStatus.path}\n`);
|
|
1425
|
+
process.stdout.write(` MCP key: ${agentStatus.mcpName}\n`);
|
|
1351
1426
|
process.stdout.write(` Endpoint: ${agentStatus.endpoint}\n`);
|
|
1352
1427
|
process.stdout.write(` Detail: ${agentStatus.detail}\n`);
|
|
1353
1428
|
process.stdout.write(` Action: ${agentStatus.action}\n`);
|
|
@@ -1371,16 +1446,28 @@ export function runOnboard(argv: readonly string[]): Promise<number> {
|
|
|
1371
1446
|
|
|
1372
1447
|
function runOnboardSync(argv: readonly string[]): number {
|
|
1373
1448
|
const flags = parseFlags(argv);
|
|
1449
|
+
const resolvedTarget = resolveOnboardTarget({
|
|
1450
|
+
instanceName: flags.instanceName,
|
|
1451
|
+
baseUrl: flags.baseUrl,
|
|
1452
|
+
mcpName: flags.mcpName,
|
|
1453
|
+
});
|
|
1454
|
+
if (!resolvedTarget.ok) {
|
|
1455
|
+
process.stderr.write(`agent-inspector onboard: ${resolvedTarget.message}\n`);
|
|
1456
|
+
return 2;
|
|
1457
|
+
}
|
|
1458
|
+
const onboardTarget = resolvedTarget.target;
|
|
1374
1459
|
const targets = resolveTargets(flags);
|
|
1375
1460
|
const version = readPackageVersion();
|
|
1376
|
-
const plannedFiles = buildPlannedFiles(flags, targets, version);
|
|
1461
|
+
const plannedFiles = buildPlannedFiles(flags, targets, version, onboardTarget);
|
|
1377
1462
|
const enabledFiles = plannedFiles.filter((file) => file.enabled);
|
|
1378
1463
|
const agentPlans: AgentMcpConfigPlan[] = [];
|
|
1379
1464
|
if (isOpenCodeEnabled(flags)) {
|
|
1380
|
-
agentPlans.push(
|
|
1465
|
+
agentPlans.push(
|
|
1466
|
+
buildAgentPlan(openCodeTarget(flags, onboardTarget), flags.uninstall, flags.force),
|
|
1467
|
+
);
|
|
1381
1468
|
}
|
|
1382
1469
|
if (isMiMoEnabled(flags)) {
|
|
1383
|
-
agentPlans.push(buildAgentPlan(miMoTarget(flags), flags.uninstall, flags.force));
|
|
1470
|
+
agentPlans.push(buildAgentPlan(miMoTarget(flags, onboardTarget), flags.uninstall, flags.force));
|
|
1384
1471
|
}
|
|
1385
1472
|
const agentStatuses = agentPlans.map((plan) => plan.status);
|
|
1386
1473
|
|
|
@@ -1411,6 +1498,7 @@ function runOnboardSync(argv: readonly string[]): number {
|
|
|
1411
1498
|
for (const plan of agentPlans) {
|
|
1412
1499
|
process.stdout.write(`${plan.status.label}: ${plan.status.path} (${plan.status.state})\n`);
|
|
1413
1500
|
process.stdout.write(` Transport: ${plan.status.transport}\n`);
|
|
1501
|
+
process.stdout.write(` MCP key: ${plan.status.mcpName}\n`);
|
|
1414
1502
|
process.stdout.write(` Endpoint: ${plan.status.endpoint}\n`);
|
|
1415
1503
|
process.stdout.write(` Action: ${plan.status.action}\n`);
|
|
1416
1504
|
if (plan.blockedMessage !== null) {
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { appendBasePathToOrigin, getConfiguredBasePath } from "../lib/basePath.js";
|
|
2
|
+
import { getInstancesRoot, readInstanceRecord, validateInstanceName } from "./instanceRegistry.js";
|
|
3
|
+
import { urlForHost } from "./networkHints.js";
|
|
4
|
+
|
|
5
|
+
const DEFAULT_PORT = 9527;
|
|
6
|
+
const DEFAULT_ORIGIN = `http://localhost:${String(DEFAULT_PORT)}`;
|
|
7
|
+
const MCP_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
8
|
+
|
|
9
|
+
type OnboardTargetEnv = Readonly<Record<string, string | undefined>>;
|
|
10
|
+
|
|
11
|
+
export type OnboardTargetKind = "default" | "instance" | "base-url";
|
|
12
|
+
|
|
13
|
+
export type OnboardTarget = {
|
|
14
|
+
kind: OnboardTargetKind;
|
|
15
|
+
instanceName: string | null;
|
|
16
|
+
mcpName: string;
|
|
17
|
+
port: number;
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
apiUrl: string;
|
|
20
|
+
healthUrl: string;
|
|
21
|
+
anthropicProxyUrl: string;
|
|
22
|
+
openAiProxyUrl: string;
|
|
23
|
+
mcpUrl: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type OnboardTargetOptions = {
|
|
27
|
+
instanceName: string | null;
|
|
28
|
+
baseUrl: string | null;
|
|
29
|
+
mcpName: string | null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type OnboardTargetResult =
|
|
33
|
+
| { ok: true; target: OnboardTarget }
|
|
34
|
+
| { ok: false; message: string };
|
|
35
|
+
|
|
36
|
+
function targetFromBase(
|
|
37
|
+
kind: OnboardTargetKind,
|
|
38
|
+
instanceName: string | null,
|
|
39
|
+
mcpName: string,
|
|
40
|
+
port: number,
|
|
41
|
+
baseUrl: string,
|
|
42
|
+
): OnboardTarget {
|
|
43
|
+
return {
|
|
44
|
+
kind,
|
|
45
|
+
instanceName,
|
|
46
|
+
mcpName,
|
|
47
|
+
port,
|
|
48
|
+
baseUrl,
|
|
49
|
+
apiUrl: `${baseUrl}/api`,
|
|
50
|
+
healthUrl: `${baseUrl}/api/health`,
|
|
51
|
+
anthropicProxyUrl: `${baseUrl}/proxy`,
|
|
52
|
+
openAiProxyUrl: `${baseUrl}/proxy/v1`,
|
|
53
|
+
mcpUrl: `${baseUrl}/api/mcp`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function validateMcpName(rawName: string): string | null {
|
|
58
|
+
const name = rawName.trim();
|
|
59
|
+
return name === rawName && MCP_NAME_PATTERN.test(name) ? name : null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function normalizeExplicitBaseUrl(rawBaseUrl: string): { baseUrl: string; port: number } | null {
|
|
63
|
+
const candidate = rawBaseUrl.trim();
|
|
64
|
+
if (
|
|
65
|
+
candidate.length === 0 ||
|
|
66
|
+
candidate !== rawBaseUrl ||
|
|
67
|
+
candidate.includes("?") ||
|
|
68
|
+
candidate.includes("#")
|
|
69
|
+
) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const parsed = new URL(candidate);
|
|
75
|
+
if (
|
|
76
|
+
(parsed.protocol !== "http:" && parsed.protocol !== "https:") ||
|
|
77
|
+
parsed.hostname.length === 0 ||
|
|
78
|
+
parsed.username.length > 0 ||
|
|
79
|
+
parsed.password.length > 0
|
|
80
|
+
) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const pathname = parsed.pathname.replace(/\/+$/u, "");
|
|
84
|
+
const baseUrl = `${parsed.origin}${pathname === "" ? "" : pathname}`;
|
|
85
|
+
const defaultPort = parsed.protocol === "https:" ? 443 : 80;
|
|
86
|
+
const port = parsed.port === "" ? defaultPort : Number(parsed.port);
|
|
87
|
+
return Number.isInteger(port) && port >= 1 && port <= 65_535 ? { baseUrl, port } : null;
|
|
88
|
+
} catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function resolveOnboardTarget(
|
|
94
|
+
options: OnboardTargetOptions,
|
|
95
|
+
env: OnboardTargetEnv = process.env,
|
|
96
|
+
): OnboardTargetResult {
|
|
97
|
+
if (options.instanceName !== null && options.baseUrl !== null) {
|
|
98
|
+
return {
|
|
99
|
+
ok: false,
|
|
100
|
+
message: "--instance and --base-url are mutually exclusive",
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const explicitMcpName = options.mcpName === null ? null : validateMcpName(options.mcpName);
|
|
105
|
+
if (options.mcpName !== null && explicitMcpName === null) {
|
|
106
|
+
return {
|
|
107
|
+
ok: false,
|
|
108
|
+
message:
|
|
109
|
+
"--mcp-name must be 1-128 letters, digits, dots, hyphens, or underscores and start with a letter or digit",
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (options.baseUrl !== null) {
|
|
114
|
+
const explicit = normalizeExplicitBaseUrl(options.baseUrl);
|
|
115
|
+
if (explicit === null) {
|
|
116
|
+
return {
|
|
117
|
+
ok: false,
|
|
118
|
+
message:
|
|
119
|
+
"--base-url must be a credential-free HTTP or HTTPS URL without a query or fragment",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
ok: true,
|
|
124
|
+
target: targetFromBase(
|
|
125
|
+
"base-url",
|
|
126
|
+
null,
|
|
127
|
+
explicitMcpName ?? "agent-inspector",
|
|
128
|
+
explicit.port,
|
|
129
|
+
explicit.baseUrl,
|
|
130
|
+
),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (options.instanceName !== null) {
|
|
135
|
+
const validatedName = validateInstanceName(options.instanceName);
|
|
136
|
+
if (validatedName.kind === "error") {
|
|
137
|
+
return { ok: false, message: validatedName.error.message };
|
|
138
|
+
}
|
|
139
|
+
const record = readInstanceRecord(getInstancesRoot(env), validatedName.value);
|
|
140
|
+
if (record.kind === "error") {
|
|
141
|
+
return { ok: false, message: record.error.message };
|
|
142
|
+
}
|
|
143
|
+
if (record.value === null) {
|
|
144
|
+
return {
|
|
145
|
+
ok: false,
|
|
146
|
+
message: `Named instance ${validatedName.value} does not exist`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const origin = urlForHost(record.value.publicPort, record.value.host);
|
|
150
|
+
const baseUrl = appendBasePathToOrigin(origin, "/", record.value.basePath);
|
|
151
|
+
return {
|
|
152
|
+
ok: true,
|
|
153
|
+
target: targetFromBase(
|
|
154
|
+
"instance",
|
|
155
|
+
record.value.name,
|
|
156
|
+
explicitMcpName ?? `agent-inspector-${record.value.name}`,
|
|
157
|
+
record.value.publicPort,
|
|
158
|
+
baseUrl,
|
|
159
|
+
),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const baseUrl = appendBasePathToOrigin(DEFAULT_ORIGIN, "/", getConfiguredBasePath(env));
|
|
164
|
+
return {
|
|
165
|
+
ok: true,
|
|
166
|
+
target: targetFromBase(
|
|
167
|
+
"default",
|
|
168
|
+
null,
|
|
169
|
+
explicitMcpName ?? "agent-inspector",
|
|
170
|
+
DEFAULT_PORT,
|
|
171
|
+
baseUrl,
|
|
172
|
+
),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export const onboardTargetForTests = {
|
|
177
|
+
normalizeExplicitBaseUrl,
|
|
178
|
+
validateMcpName,
|
|
179
|
+
};
|