@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.
Files changed (80) hide show
  1. package/.output/cli.js +20743 -16945
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-DQHjxFO6.js → CompareDrawer-B3_TLTJh.js} +1 -1
  4. package/.output/public/assets/{InspectorPet-D1A4505B.js → InspectorPet-Cpn6hv4P.js} +1 -1
  5. package/.output/public/assets/{ProxyViewerContainer-BhpIgfBi.js → ProxyViewerContainer-BEAhuGOJ.js} +27 -27
  6. package/.output/public/assets/{ReplayDialog-C5vmbecv.js → ReplayDialog-B3EKVdJk.js} +1 -1
  7. package/.output/public/assets/{RequestAnatomy-CPGNB8lk.js → RequestAnatomy-COOQjyUC.js} +1 -1
  8. package/.output/public/assets/{ResponseView-DUfrajCm.js → ResponseView-DBCjkwye.js} +1 -1
  9. package/.output/public/assets/{StreamingChunkSequence-nbeCQqOz.js → StreamingChunkSequence-CuXhDgMo.js} +1 -1
  10. package/.output/public/assets/{_sessionId-mw3aA8Bp.js → _sessionId-BJlM3nmt.js} +1 -1
  11. package/.output/public/assets/{_sessionId-CDhRgzf6.js → _sessionId-zbgYl7ZJ.js} +1 -1
  12. package/.output/public/assets/{index-B2eoeT-Z.js → index-BIK4bPTv.js} +1 -1
  13. package/.output/public/assets/{index-C6qZwYlR.js → index-BZo_8koU.js} +15 -15
  14. package/.output/public/assets/{index-CX7_rf7o.js → index-D2Qw6Ri2.js} +1 -1
  15. package/.output/public/assets/{index-BWjeqxbk.css → index-ZWschkCh.css} +1 -1
  16. package/.output/public/assets/{index-BfRhTvOd.js → index-xF_NY8Yb.js} +5 -5
  17. package/.output/public/assets/{json-viewer-BLmF1sOZ.js → json-viewer-B-Ao6pwW.js} +1 -1
  18. package/.output/public/assets/{jszip.min-C9rCFoU3.js → jszip.min-B_rfAUCe.js} +1 -1
  19. package/.output/server/_libs/modelcontextprotocol__core.mjs +25 -25
  20. package/.output/server/_libs/modelcontextprotocol__server.mjs +2 -2
  21. package/.output/server/_libs/zod.mjs +6 -6
  22. package/.output/server/{_sessionId-U54BAqdp.mjs → _sessionId-CJ99gZFV.mjs} +3 -1
  23. package/.output/server/{_sessionId-E_Dmopc-.mjs → _sessionId-DEjScBpK.mjs} +5 -3
  24. package/.output/server/_ssr/{CompareDrawer-CksatPed.mjs → CompareDrawer-DtOIMeQB.mjs} +4 -2
  25. package/.output/server/_ssr/{InspectorPet-DNc4GQ7z.mjs → InspectorPet-CXc4oCBQ.mjs} +3 -1
  26. package/.output/server/_ssr/{ProxyViewerContainer-B02Ey0p_.mjs → ProxyViewerContainer-DVPlwfkt.mjs} +52 -27
  27. package/.output/server/_ssr/{ReplayDialog-DdMvHJF1.mjs → ReplayDialog-BQEA95qC.mjs} +6 -4
  28. package/.output/server/_ssr/{RequestAnatomy--vh2Z8__.mjs → RequestAnatomy-BMyd8B7Q.mjs} +4 -2
  29. package/.output/server/_ssr/{ResponseView-Dhxc-cKC.mjs → ResponseView-qrTaEIM8.mjs} +4 -2
  30. package/.output/server/_ssr/{StreamingChunkSequence-BG0EQmaD.mjs → StreamingChunkSequence-BFTL8mIy.mjs} +5 -3
  31. package/.output/server/_ssr/{index-lcfulIXa.mjs → index-Bcj1Hcy4.mjs} +4 -2
  32. package/.output/server/_ssr/{index-D0At9nfd.mjs → index-DW-M7XUG.mjs} +3 -1
  33. package/.output/server/_ssr/index.mjs +4 -3
  34. package/.output/server/_ssr/{json-viewer-My1J1_5R.mjs → json-viewer-DiI0Yjsn.mjs} +4 -2
  35. package/.output/server/_ssr/publicBasePathContext-5264L3rX.mjs +148 -0
  36. package/.output/server/_ssr/{router-DWcUFPR3.mjs → router-BJ_ltLim.mjs} +2432 -475
  37. package/.output/server/_ssr/start-C6MjPjxp.mjs +57 -0
  38. package/.output/server/_tanstack-start-manifest_v-C6pXXX9R.mjs +4 -0
  39. package/.output/server/index.mjs +92 -92
  40. package/.output/workers/logFinalizer.worker.js +4 -0
  41. package/.output/workers/sessionWorkerEntry.js +4 -0
  42. package/README.md +221 -25
  43. package/docs/instance-control.openapi.yaml +306 -0
  44. package/package.json +4 -1
  45. package/src/cli/alias.ts +440 -0
  46. package/src/cli/instance.ts +1037 -0
  47. package/src/cli/instanceArgs.ts +266 -0
  48. package/src/cli/instanceControl.ts +183 -0
  49. package/src/cli/instanceModel.ts +275 -0
  50. package/src/cli/instanceRegistry.ts +740 -0
  51. package/src/cli/onboard.ts +123 -35
  52. package/src/cli/onboardTarget.ts +179 -0
  53. package/src/cli/templates/codex-skill-onboard.ts +76 -28
  54. package/src/cli/templates/skill-onboard.ts +93 -59
  55. package/src/cli.ts +154 -16
  56. package/src/components/ProxyViewer.tsx +13 -5
  57. package/src/components/providers/SettingsDialog.tsx +13 -8
  58. package/src/lib/basePath.ts +8 -2
  59. package/src/lib/codingAgentEndpoints.ts +16 -0
  60. package/src/lib/instanceContract.ts +220 -0
  61. package/src/lib/managedInstance.ts +104 -0
  62. package/src/lib/publicBasePathContext.ts +107 -0
  63. package/src/mcp/instanceHandlers.ts +149 -0
  64. package/src/mcp/loopback.ts +16 -5
  65. package/src/mcp/mode.ts +25 -0
  66. package/src/mcp/server.ts +258 -18
  67. package/src/mcp/toolHandlers.ts +2 -2
  68. package/src/proxy/identityProxy.ts +655 -36
  69. package/src/proxy/runtimeShutdown.ts +73 -1
  70. package/src/proxy/sessionInfo.ts +5 -4
  71. package/src/routes/api/-instances.ts +109 -0
  72. package/src/routes/api/instances.$name.connection.ts +12 -0
  73. package/src/routes/api/instances.$name.restart.ts +23 -0
  74. package/src/routes/api/instances.$name.start.ts +18 -0
  75. package/src/routes/api/instances.$name.stop.ts +23 -0
  76. package/src/routes/api/instances.$name.ts +12 -0
  77. package/src/routes/api/instances.ts +16 -0
  78. package/src/start.ts +10 -0
  79. package/.output/server/_ssr/start-HYkvq4Ni.mjs +0 -4
  80. package/.output/server/_tanstack-start-manifest_v-B2chR8ef.mjs +0 -4
@@ -0,0 +1,275 @@
1
+ import { randomBytes, randomUUID } from "node:crypto";
2
+ import { isAbsolute } from "node:path";
3
+ import { z } from "zod";
4
+ import { normalizeBasePath } from "../lib/basePath";
5
+ import { MANAGED_INSTANCE_CONTROL_PATH } from "../lib/managedInstance";
6
+ import { CaptureModeSchema, type CaptureMode } from "../lib/runtimeConfig";
7
+
8
+ export const INSTANCE_RECORD_SCHEMA_VERSION = 1;
9
+ export const INSTANCE_OUTPUT_SCHEMA_VERSION = 1;
10
+ export const MAX_INSTANCE_NAME_LENGTH = 64;
11
+
12
+ const WINDOWS_DEVICE_NAME = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/u;
13
+ const INSTANCE_NAME_CHARACTERS = /^[a-z0-9._-]+$/u;
14
+
15
+ export type InstanceRegistryErrorCode =
16
+ | "data_dir_conflict"
17
+ | "data_dir_invalid"
18
+ | "lock_not_owned"
19
+ | "lock_timeout"
20
+ | "name_invalid"
21
+ | "port_conflict"
22
+ | "port_exhausted"
23
+ | "port_invalid"
24
+ | "record_invalid"
25
+ | "registry_io";
26
+
27
+ export type InstanceRegistryError = {
28
+ code: InstanceRegistryErrorCode;
29
+ message: string;
30
+ };
31
+
32
+ export type InstanceRegistryResult<T> =
33
+ | { kind: "success"; value: T }
34
+ | { kind: "error"; error: InstanceRegistryError };
35
+
36
+ export function instanceRegistrySuccess<T>(value: T): InstanceRegistryResult<T> {
37
+ return { kind: "success", value };
38
+ }
39
+
40
+ export function instanceRegistryError<T>(
41
+ code: InstanceRegistryErrorCode,
42
+ message: string,
43
+ ): InstanceRegistryResult<T> {
44
+ return { kind: "error", error: { code, message } };
45
+ }
46
+
47
+ /**
48
+ * Normalize a user-facing instance name to its case-insensitive Windows key.
49
+ * Validation happens before callers create a registry directory.
50
+ */
51
+ export function validateInstanceName(rawName: string): InstanceRegistryResult<string> {
52
+ const normalized = rawName.toLowerCase();
53
+ const invalid =
54
+ rawName.length === 0 ||
55
+ rawName !== rawName.trim() ||
56
+ normalized.length > MAX_INSTANCE_NAME_LENGTH ||
57
+ normalized === "." ||
58
+ normalized === ".." ||
59
+ normalized === "default" ||
60
+ normalized.endsWith(".") ||
61
+ normalized.endsWith(" ") ||
62
+ !INSTANCE_NAME_CHARACTERS.test(normalized) ||
63
+ WINDOWS_DEVICE_NAME.test(normalized);
64
+
65
+ return invalid
66
+ ? instanceRegistryError(
67
+ "name_invalid",
68
+ `Invalid instance name ${JSON.stringify(rawName)}. Use 1-${String(MAX_INSTANCE_NAME_LENGTH)} letters, digits, dots, hyphens, or underscores; "default" and Windows device names are reserved.`,
69
+ )
70
+ : instanceRegistrySuccess(normalized);
71
+ }
72
+
73
+ export function normalizeInstanceBasePath(
74
+ rawBasePath: string | undefined,
75
+ ): InstanceRegistryResult<string> {
76
+ const value = rawBasePath ?? "";
77
+ const trimmed = value.trim();
78
+ const hasControlCharacter = [...trimmed].some((character) => {
79
+ const codePoint = character.codePointAt(0) ?? 0;
80
+ return codePoint < 32 || codePoint === 127;
81
+ });
82
+ if (
83
+ trimmed.length > 0 &&
84
+ (!trimmed.startsWith("/") ||
85
+ trimmed.includes("?") ||
86
+ trimmed.includes("#") ||
87
+ trimmed.includes("\\") ||
88
+ hasControlCharacter)
89
+ ) {
90
+ return instanceRegistryError(
91
+ "record_invalid",
92
+ "Instance Base Path must be an absolute URL path without a query, hash, backslash, or control character.",
93
+ );
94
+ }
95
+ const normalized = normalizeBasePath(trimmed);
96
+ const reservedControlRoot = MANAGED_INSTANCE_CONTROL_PATH.replace(/\/instance$/u, "");
97
+ if (normalized === reservedControlRoot || normalized.startsWith(`${reservedControlRoot}/`)) {
98
+ return instanceRegistryError(
99
+ "record_invalid",
100
+ `Instance Base Path ${normalized} collides with the reserved Agent Inspector control namespace.`,
101
+ );
102
+ }
103
+ return instanceRegistrySuccess(normalized);
104
+ }
105
+
106
+ const NormalizedInstanceNameSchema = z.string().refine((name) => {
107
+ const result = validateInstanceName(name);
108
+ return result.kind === "success" && result.value === name;
109
+ });
110
+
111
+ const NormalizedBasePathSchema = z.string().refine((basePath) => {
112
+ const result = normalizeInstanceBasePath(basePath);
113
+ return result.kind === "success" && result.value === basePath;
114
+ });
115
+
116
+ const PortSchema = z.number().int().min(1).max(65_535);
117
+ const TimestampSchema = z.string().datetime();
118
+ const IdentifierSchema = z.string().min(8).max(128);
119
+
120
+ export const InstanceLaunchSchema = z
121
+ .object({
122
+ launchId: IdentifierSchema,
123
+ controlToken: z.string().min(32).max(256),
124
+ startedAt: TimestampSchema,
125
+ })
126
+ .strict();
127
+
128
+ export const InstanceRecordSchema = z
129
+ .object({
130
+ schemaVersion: z.literal(INSTANCE_RECORD_SCHEMA_VERSION),
131
+ name: NormalizedInstanceNameSchema,
132
+ instanceId: IdentifierSchema,
133
+ createdAt: TimestampSchema,
134
+ updatedAt: TimestampSchema,
135
+ host: z.string().trim().min(1).max(255),
136
+ publicPort: PortSchema,
137
+ upstreamPort: PortSchema,
138
+ basePath: NormalizedBasePathSchema,
139
+ captureMode: CaptureModeSchema,
140
+ dataDir: z
141
+ .string()
142
+ .min(1)
143
+ .refine((path) => isAbsolute(path)),
144
+ launch: InstanceLaunchSchema,
145
+ })
146
+ .strict()
147
+ .superRefine((record, context) => {
148
+ if (record.publicPort === record.upstreamPort) {
149
+ context.addIssue({
150
+ code: "custom",
151
+ message: "Public and upstream ports must be different.",
152
+ path: ["upstreamPort"],
153
+ });
154
+ }
155
+ });
156
+
157
+ export type InstanceLaunch = z.infer<typeof InstanceLaunchSchema>;
158
+ export type InstanceRecord = z.infer<typeof InstanceRecordSchema>;
159
+
160
+ export type InstanceRecordOutput = {
161
+ schemaVersion: typeof INSTANCE_OUTPUT_SCHEMA_VERSION;
162
+ name: string;
163
+ instanceId: string;
164
+ createdAt: string;
165
+ updatedAt: string;
166
+ host: string;
167
+ publicPort: number;
168
+ upstreamPort: number;
169
+ basePath: string;
170
+ captureMode: CaptureMode;
171
+ dataDir: string;
172
+ launch: {
173
+ launchId: string;
174
+ startedAt: string;
175
+ };
176
+ };
177
+
178
+ export type CreateInstanceRecordInput = {
179
+ name: string;
180
+ host: string;
181
+ publicPort: number;
182
+ upstreamPort: number;
183
+ basePath?: string;
184
+ captureMode: CaptureMode;
185
+ dataDir: string;
186
+ };
187
+
188
+ export type InstanceIdentityFactory = {
189
+ now?: () => Date;
190
+ createInstanceId?: () => string;
191
+ createLaunchId?: () => string;
192
+ createControlToken?: () => string;
193
+ };
194
+
195
+ function createLaunch(factory: InstanceIdentityFactory, now: Date): InstanceLaunch {
196
+ return {
197
+ launchId: factory.createLaunchId?.() ?? randomUUID(),
198
+ controlToken: factory.createControlToken?.() ?? randomBytes(32).toString("base64url"),
199
+ startedAt: now.toISOString(),
200
+ };
201
+ }
202
+
203
+ export function createInstanceRecord(
204
+ input: CreateInstanceRecordInput,
205
+ factory: InstanceIdentityFactory = {},
206
+ ): InstanceRegistryResult<InstanceRecord> {
207
+ const name = validateInstanceName(input.name);
208
+ if (name.kind === "error") return name;
209
+ const basePath = normalizeInstanceBasePath(input.basePath);
210
+ if (basePath.kind === "error") return basePath;
211
+
212
+ const now = factory.now?.() ?? new Date();
213
+ const timestamp = now.toISOString();
214
+ const candidate: unknown = {
215
+ schemaVersion: INSTANCE_RECORD_SCHEMA_VERSION,
216
+ name: name.value,
217
+ instanceId: factory.createInstanceId?.() ?? randomUUID(),
218
+ createdAt: timestamp,
219
+ updatedAt: timestamp,
220
+ host: input.host.trim(),
221
+ publicPort: input.publicPort,
222
+ upstreamPort: input.upstreamPort,
223
+ basePath: basePath.value,
224
+ captureMode: input.captureMode,
225
+ dataDir: input.dataDir,
226
+ launch: createLaunch(factory, now),
227
+ };
228
+ const parsed = InstanceRecordSchema.safeParse(candidate);
229
+ return parsed.success
230
+ ? instanceRegistrySuccess(parsed.data)
231
+ : instanceRegistryError(
232
+ "record_invalid",
233
+ `Cannot create instance record: ${parsed.error.issues[0]?.message ?? "invalid saved configuration"}`,
234
+ );
235
+ }
236
+
237
+ export function rotateInstanceLaunch(
238
+ record: InstanceRecord,
239
+ factory: InstanceIdentityFactory = {},
240
+ ): InstanceRegistryResult<InstanceRecord> {
241
+ const now = factory.now?.() ?? new Date();
242
+ const candidate: unknown = {
243
+ ...record,
244
+ updatedAt: now.toISOString(),
245
+ launch: createLaunch(factory, now),
246
+ };
247
+ const parsed = InstanceRecordSchema.safeParse(candidate);
248
+ return parsed.success
249
+ ? instanceRegistrySuccess(parsed.data)
250
+ : instanceRegistryError(
251
+ "record_invalid",
252
+ `Cannot rotate instance launch: ${parsed.error.issues[0]?.message ?? "invalid saved configuration"}`,
253
+ );
254
+ }
255
+
256
+ /** A deliberate allow-list prevents control credentials from reaching CLI JSON output. */
257
+ export function toInstanceRecordOutput(record: InstanceRecord): InstanceRecordOutput {
258
+ return {
259
+ schemaVersion: INSTANCE_OUTPUT_SCHEMA_VERSION,
260
+ name: record.name,
261
+ instanceId: record.instanceId,
262
+ createdAt: record.createdAt,
263
+ updatedAt: record.updatedAt,
264
+ host: record.host,
265
+ publicPort: record.publicPort,
266
+ upstreamPort: record.upstreamPort,
267
+ basePath: record.basePath,
268
+ captureMode: record.captureMode,
269
+ dataDir: record.dataDir,
270
+ launch: {
271
+ launchId: record.launch.launchId,
272
+ startedAt: record.launch.startedAt,
273
+ },
274
+ };
275
+ }