@spotpatch/dev-server 0.6.0 → 0.7.0
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/dist/index.cjs +429 -244
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -3
- package/dist/index.d.ts +30 -3
- package/dist/index.js +388 -189
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2256,6 +2256,9 @@ import { access as access2, lstat as lstat3, readFile as readFile2, realpath as
|
|
|
2256
2256
|
import { createRequire } from "module";
|
|
2257
2257
|
import path3 from "path";
|
|
2258
2258
|
import { promisify } from "util";
|
|
2259
|
+
import {
|
|
2260
|
+
DEFAULT_AGENT_LIMITS as DEFAULT_AGENT_LIMITS2
|
|
2261
|
+
} from "@spotpatch/shared";
|
|
2259
2262
|
var execFileAsync = promisify(execFile);
|
|
2260
2263
|
var TYPESCRIPT_CHECK_ID = "spotpatch-typecheck";
|
|
2261
2264
|
var TYPESCRIPT_CHECK_LABEL = "TypeScript";
|
|
@@ -2323,6 +2326,15 @@ async function resolveTypeScriptCli(appRoot) {
|
|
|
2323
2326
|
function portableRelativePath(from, to) {
|
|
2324
2327
|
return path3.relative(from, to).split(path3.sep).join("/");
|
|
2325
2328
|
}
|
|
2329
|
+
function hasRequiredCheck(checks) {
|
|
2330
|
+
return Object.values(checks).some((check) => check.required);
|
|
2331
|
+
}
|
|
2332
|
+
function availableCheckId(checks, preferred) {
|
|
2333
|
+
if (checks[preferred] === void 0) return preferred;
|
|
2334
|
+
let suffix = 2;
|
|
2335
|
+
while (checks[`${preferred}-${String(suffix)}`] !== void 0) suffix += 1;
|
|
2336
|
+
return `${preferred}-${String(suffix)}`;
|
|
2337
|
+
}
|
|
2326
2338
|
async function discoverProjectValidationCheck(options) {
|
|
2327
2339
|
const appRoot = await realpath2(options.appRoot);
|
|
2328
2340
|
const tsconfigPath = path3.join(appRoot, "tsconfig.json");
|
|
@@ -2351,6 +2363,8 @@ async function discoverProjectValidationCheck(options) {
|
|
|
2351
2363
|
"--noEmit",
|
|
2352
2364
|
"--pretty",
|
|
2353
2365
|
"false",
|
|
2366
|
+
"--incremental",
|
|
2367
|
+
"false",
|
|
2354
2368
|
"--project",
|
|
2355
2369
|
projectPath
|
|
2356
2370
|
]),
|
|
@@ -2358,21 +2372,30 @@ async function discoverProjectValidationCheck(options) {
|
|
|
2358
2372
|
timeoutMs: options.timeoutMs
|
|
2359
2373
|
});
|
|
2360
2374
|
}
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2375
|
+
async function resolveProjectValidationChecks(options) {
|
|
2376
|
+
if (hasRequiredCheck(options.checks)) return options.checks;
|
|
2377
|
+
const discovered = await discoverProjectValidationCheck(options);
|
|
2378
|
+
if (discovered === void 0) return options.checks;
|
|
2379
|
+
const id = availableCheckId(options.checks, discovered.id);
|
|
2380
|
+
return Object.freeze({
|
|
2381
|
+
...options.checks,
|
|
2382
|
+
[id]: Object.freeze({ ...discovered, id })
|
|
2383
|
+
});
|
|
2365
2384
|
}
|
|
2366
|
-
function
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2385
|
+
async function resolveManagedExecutionValidation(options) {
|
|
2386
|
+
const checks = options.ai === false ? Object.freeze({}) : options.ai.execution.checks;
|
|
2387
|
+
const limits = options.ai === false ? DEFAULT_AGENT_LIMITS2 : options.ai.execution.limits;
|
|
2388
|
+
return Object.freeze({
|
|
2389
|
+
checks: await resolveProjectValidationChecks({
|
|
2390
|
+
appRoot: options.appRoot,
|
|
2391
|
+
checks,
|
|
2392
|
+
timeoutMs: limits.checkTimeoutMs
|
|
2393
|
+
}),
|
|
2394
|
+
limits
|
|
2395
|
+
});
|
|
2375
2396
|
}
|
|
2397
|
+
|
|
2398
|
+
// src/project-options.ts
|
|
2376
2399
|
async function resolveProjectOptions(input) {
|
|
2377
2400
|
const userOptions = input.options ?? {};
|
|
2378
2401
|
const resolved = resolveOptions(userOptions, input.environmentAi);
|
|
@@ -2384,22 +2407,15 @@ async function resolveProjectOptions(input) {
|
|
|
2384
2407
|
"SpotPatch trustedFastMode cannot be combined with applyMode auto."
|
|
2385
2408
|
);
|
|
2386
2409
|
}
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
);
|
|
2397
|
-
}
|
|
2398
|
-
const id = availableCheckId(checks, discovered.id);
|
|
2399
|
-
checks = Object.freeze({
|
|
2400
|
-
...checks,
|
|
2401
|
-
[id]: Object.freeze({ ...discovered, id })
|
|
2402
|
-
});
|
|
2410
|
+
const checks = await resolveProjectValidationChecks({
|
|
2411
|
+
appRoot: input.appRoot,
|
|
2412
|
+
checks: resolved.ai.execution.checks,
|
|
2413
|
+
timeoutMs: resolved.ai.execution.limits.checkTimeoutMs
|
|
2414
|
+
});
|
|
2415
|
+
if (!Object.values(checks).some((check) => check.required)) {
|
|
2416
|
+
throw new RangeError(
|
|
2417
|
+
"SpotPatch trustedFastMode requires a configured required check or a local TypeScript project with tsconfig.json."
|
|
2418
|
+
);
|
|
2403
2419
|
}
|
|
2404
2420
|
const ai = Object.freeze({
|
|
2405
2421
|
...resolved.ai,
|
|
@@ -2480,10 +2496,10 @@ function createSourceRegistry(options = {}) {
|
|
|
2480
2496
|
|
|
2481
2497
|
// src/server/middleware.ts
|
|
2482
2498
|
import {
|
|
2483
|
-
ERROR_CODES as
|
|
2499
|
+
ERROR_CODES as ERROR_CODES16,
|
|
2484
2500
|
SPOTPATCH_API_BASE,
|
|
2485
|
-
SPOTPATCH_ENDPOINTS as
|
|
2486
|
-
SpotPatchError as
|
|
2501
|
+
SPOTPATCH_ENDPOINTS as SPOTPATCH_ENDPOINTS4,
|
|
2502
|
+
SpotPatchError as SpotPatchError16,
|
|
2487
2503
|
openEditorRequestSchema,
|
|
2488
2504
|
sourceContextRequestSchema
|
|
2489
2505
|
} from "@spotpatch/shared";
|
|
@@ -3061,11 +3077,171 @@ async function handleExternalHandoffBrowserRequest(request, response, route, opt
|
|
|
3061
3077
|
writeSuccess(response, result.replayed ? 200 : 201, result);
|
|
3062
3078
|
}
|
|
3063
3079
|
|
|
3064
|
-
// src/
|
|
3080
|
+
// src/external-agent/browser-http.ts
|
|
3065
3081
|
import {
|
|
3066
3082
|
ERROR_CODES as ERROR_CODES11,
|
|
3083
|
+
EXTERNAL_AGENT_CONTROL_LIMITS,
|
|
3067
3084
|
SPOTPATCH_ENDPOINTS as SPOTPATCH_ENDPOINTS2,
|
|
3068
3085
|
SpotPatchError as SpotPatchError11,
|
|
3086
|
+
externalAgentControlCancelRequestSchema,
|
|
3087
|
+
externalAgentControlConnectRequestSchema,
|
|
3088
|
+
externalAgentControlDisconnectRequestSchema,
|
|
3089
|
+
externalAgentControlStatusRequestSchema,
|
|
3090
|
+
externalAgentControlStatusSchema,
|
|
3091
|
+
externalAgentEventSchema,
|
|
3092
|
+
externalAgentEventsRequestSchema,
|
|
3093
|
+
externalAgentManagedResultSchema,
|
|
3094
|
+
externalAgentResultRequestSchema
|
|
3095
|
+
} from "@spotpatch/shared";
|
|
3096
|
+
function matchExternalAgentBrowserPath(path9) {
|
|
3097
|
+
if (path9 === SPOTPATCH_ENDPOINTS2.externalAgentControlStatus) return "status";
|
|
3098
|
+
if (path9 === SPOTPATCH_ENDPOINTS2.externalAgentControlConnect) return "connect";
|
|
3099
|
+
if (path9 === SPOTPATCH_ENDPOINTS2.externalAgentControlDisconnect) {
|
|
3100
|
+
return "disconnect";
|
|
3101
|
+
}
|
|
3102
|
+
if (path9 === SPOTPATCH_ENDPOINTS2.externalAgentControlCancel) return "cancel";
|
|
3103
|
+
if (path9 === SPOTPATCH_ENDPOINTS2.externalAgentEvents) return "events";
|
|
3104
|
+
if (path9 === SPOTPATCH_ENDPOINTS2.externalAgentResult) return "result";
|
|
3105
|
+
return void 0;
|
|
3106
|
+
}
|
|
3107
|
+
function writeEvent(response, event) {
|
|
3108
|
+
response.write(`${JSON.stringify(externalAgentEventSchema.parse(event))}
|
|
3109
|
+
`);
|
|
3110
|
+
}
|
|
3111
|
+
function createExternalAgentBrowserController(port) {
|
|
3112
|
+
const streams = /* @__PURE__ */ new Set();
|
|
3113
|
+
let disposed = false;
|
|
3114
|
+
const handleEvents = async (request, response) => {
|
|
3115
|
+
const parsed = externalAgentEventsRequestSchema.safeParse(
|
|
3116
|
+
await readJsonRequestBody(request)
|
|
3117
|
+
);
|
|
3118
|
+
if (!parsed.success) {
|
|
3119
|
+
throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
|
|
3120
|
+
}
|
|
3121
|
+
if (disposed || streams.size >= EXTERNAL_AGENT_CONTROL_LIMITS.maximumEventSubscribers) {
|
|
3122
|
+
throw new SpotPatchError11(ERROR_CODES11.BRIDGE_BUSY);
|
|
3123
|
+
}
|
|
3124
|
+
response.statusCode = 200;
|
|
3125
|
+
response.setHeader("Cache-Control", "no-store");
|
|
3126
|
+
response.setHeader("Content-Type", "application/x-ndjson; charset=utf-8");
|
|
3127
|
+
response.setHeader("X-Content-Type-Options", "nosniff");
|
|
3128
|
+
streams.add(response);
|
|
3129
|
+
const initial = externalAgentControlStatusSchema.parse(port.getStatus());
|
|
3130
|
+
writeEvent(response, { type: "status", data: initial });
|
|
3131
|
+
const unsubscribe = port.subscribe((status) => {
|
|
3132
|
+
if (!response.destroyed && status.sequence > (parsed.data.afterSequence ?? -1)) {
|
|
3133
|
+
writeEvent(response, { type: "status", data: status });
|
|
3134
|
+
}
|
|
3135
|
+
});
|
|
3136
|
+
const heartbeat = setInterval(() => {
|
|
3137
|
+
if (!response.destroyed) {
|
|
3138
|
+
const status = port.getStatus();
|
|
3139
|
+
writeEvent(response, {
|
|
3140
|
+
type: "heartbeat",
|
|
3141
|
+
sequence: status.sequence,
|
|
3142
|
+
emittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3143
|
+
});
|
|
3144
|
+
}
|
|
3145
|
+
}, EXTERNAL_AGENT_CONTROL_LIMITS.eventHeartbeatMs);
|
|
3146
|
+
heartbeat.unref();
|
|
3147
|
+
await new Promise((resolve) => {
|
|
3148
|
+
const close = () => {
|
|
3149
|
+
response.off("close", close);
|
|
3150
|
+
clearInterval(heartbeat);
|
|
3151
|
+
unsubscribe();
|
|
3152
|
+
streams.delete(response);
|
|
3153
|
+
resolve();
|
|
3154
|
+
};
|
|
3155
|
+
response.once("close", close);
|
|
3156
|
+
});
|
|
3157
|
+
};
|
|
3158
|
+
return Object.freeze({
|
|
3159
|
+
async handle(request, response, route, writeSuccess) {
|
|
3160
|
+
if (request.method !== "POST" || disposed) {
|
|
3161
|
+
throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
|
|
3162
|
+
}
|
|
3163
|
+
if (route === "events") {
|
|
3164
|
+
await handleEvents(request, response);
|
|
3165
|
+
return;
|
|
3166
|
+
}
|
|
3167
|
+
if (route === "status") {
|
|
3168
|
+
const parsed2 = externalAgentControlStatusRequestSchema.safeParse(
|
|
3169
|
+
await readJsonRequestBody(request)
|
|
3170
|
+
);
|
|
3171
|
+
if (!parsed2.success) throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
|
|
3172
|
+
writeSuccess(
|
|
3173
|
+
response,
|
|
3174
|
+
200,
|
|
3175
|
+
externalAgentControlStatusSchema.parse(port.getStatus())
|
|
3176
|
+
);
|
|
3177
|
+
return;
|
|
3178
|
+
}
|
|
3179
|
+
if (route === "connect") {
|
|
3180
|
+
const parsed2 = externalAgentControlConnectRequestSchema.safeParse(
|
|
3181
|
+
await readJsonRequestBody(request)
|
|
3182
|
+
);
|
|
3183
|
+
if (!parsed2.success) throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
|
|
3184
|
+
const controller = new AbortController();
|
|
3185
|
+
response.once("close", () => {
|
|
3186
|
+
if (!response.writableEnded) controller.abort("browser-disconnected");
|
|
3187
|
+
});
|
|
3188
|
+
writeSuccess(
|
|
3189
|
+
response,
|
|
3190
|
+
200,
|
|
3191
|
+
externalAgentControlStatusSchema.parse(
|
|
3192
|
+
await port.connect(parsed2.data, controller.signal)
|
|
3193
|
+
)
|
|
3194
|
+
);
|
|
3195
|
+
return;
|
|
3196
|
+
}
|
|
3197
|
+
if (route === "disconnect") {
|
|
3198
|
+
const parsed2 = externalAgentControlDisconnectRequestSchema.safeParse(
|
|
3199
|
+
await readJsonRequestBody(request)
|
|
3200
|
+
);
|
|
3201
|
+
if (!parsed2.success) throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
|
|
3202
|
+
writeSuccess(
|
|
3203
|
+
response,
|
|
3204
|
+
200,
|
|
3205
|
+
externalAgentControlStatusSchema.parse(await port.disconnect(parsed2.data))
|
|
3206
|
+
);
|
|
3207
|
+
return;
|
|
3208
|
+
}
|
|
3209
|
+
if (route === "cancel") {
|
|
3210
|
+
const parsed2 = externalAgentControlCancelRequestSchema.safeParse(
|
|
3211
|
+
await readJsonRequestBody(request)
|
|
3212
|
+
);
|
|
3213
|
+
if (!parsed2.success) throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
|
|
3214
|
+
writeSuccess(
|
|
3215
|
+
response,
|
|
3216
|
+
200,
|
|
3217
|
+
externalAgentControlStatusSchema.parse(await port.cancel(parsed2.data))
|
|
3218
|
+
);
|
|
3219
|
+
return;
|
|
3220
|
+
}
|
|
3221
|
+
const parsed = externalAgentResultRequestSchema.safeParse(
|
|
3222
|
+
await readJsonRequestBody(request)
|
|
3223
|
+
);
|
|
3224
|
+
if (!parsed.success) throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
|
|
3225
|
+
const result = port.getResult(parsed.data.revision);
|
|
3226
|
+
if (result === void 0) {
|
|
3227
|
+
throw new SpotPatchError11(ERROR_CODES11.HANDOFF_NOT_FOUND);
|
|
3228
|
+
}
|
|
3229
|
+
writeSuccess(response, 200, externalAgentManagedResultSchema.parse(result));
|
|
3230
|
+
},
|
|
3231
|
+
dispose() {
|
|
3232
|
+
if (disposed) return;
|
|
3233
|
+
disposed = true;
|
|
3234
|
+
for (const response of streams) response.end();
|
|
3235
|
+
streams.clear();
|
|
3236
|
+
}
|
|
3237
|
+
});
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
// src/server/agent-http.ts
|
|
3241
|
+
import {
|
|
3242
|
+
ERROR_CODES as ERROR_CODES12,
|
|
3243
|
+
SPOTPATCH_ENDPOINTS as SPOTPATCH_ENDPOINTS3,
|
|
3244
|
+
SpotPatchError as SpotPatchError12,
|
|
3069
3245
|
agentCapabilityRequestSchema,
|
|
3070
3246
|
agentJobActionRequestSchema,
|
|
3071
3247
|
agentJobCreateRequestSchema,
|
|
@@ -3110,16 +3286,16 @@ var EVENT_STREAM_END_STATUSES = /* @__PURE__ */ new Set([
|
|
|
3110
3286
|
"failed"
|
|
3111
3287
|
]);
|
|
3112
3288
|
function matchAgentRequestPath(path9) {
|
|
3113
|
-
if (path9 ===
|
|
3289
|
+
if (path9 === SPOTPATCH_ENDPOINTS3.agentCapability) {
|
|
3114
3290
|
return Object.freeze({ kind: "capability" });
|
|
3115
3291
|
}
|
|
3116
|
-
if (path9 ===
|
|
3292
|
+
if (path9 === SPOTPATCH_ENDPOINTS3.agentWorkspaceHealth) {
|
|
3117
3293
|
return Object.freeze({ kind: "workspace-health" });
|
|
3118
3294
|
}
|
|
3119
|
-
if (path9 ===
|
|
3295
|
+
if (path9 === SPOTPATCH_ENDPOINTS3.agentJobs) {
|
|
3120
3296
|
return Object.freeze({ kind: "create-job" });
|
|
3121
3297
|
}
|
|
3122
|
-
const prefix = `${
|
|
3298
|
+
const prefix = `${SPOTPATCH_ENDPOINTS3.agentJobs}/`;
|
|
3123
3299
|
if (!path9.startsWith(prefix)) {
|
|
3124
3300
|
return void 0;
|
|
3125
3301
|
}
|
|
@@ -3137,7 +3313,7 @@ function matchAgentRequestPath(path9) {
|
|
|
3137
3313
|
}
|
|
3138
3314
|
function requireAgentManager(options) {
|
|
3139
3315
|
if (options.agentManager === void 0 || options.options.ai === false) {
|
|
3140
|
-
throw new
|
|
3316
|
+
throw new SpotPatchError12(ERROR_CODES12.AI_DISABLED);
|
|
3141
3317
|
}
|
|
3142
3318
|
return options.agentManager;
|
|
3143
3319
|
}
|
|
@@ -3190,13 +3366,13 @@ function streamAgentJobEvents(response, manager, jobId) {
|
|
|
3190
3366
|
}
|
|
3191
3367
|
async function handleCapability(request, response, options, writeSuccess) {
|
|
3192
3368
|
if (request.method !== "POST") {
|
|
3193
|
-
throw new
|
|
3369
|
+
throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
|
|
3194
3370
|
}
|
|
3195
3371
|
const parsed = agentCapabilityRequestSchema.safeParse(
|
|
3196
3372
|
await readJsonRequestBody(request)
|
|
3197
3373
|
);
|
|
3198
3374
|
if (!parsed.success) {
|
|
3199
|
-
throw new
|
|
3375
|
+
throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
|
|
3200
3376
|
}
|
|
3201
3377
|
const controller = new AbortController();
|
|
3202
3378
|
const abort = () => {
|
|
@@ -3215,13 +3391,13 @@ async function handleCapability(request, response, options, writeSuccess) {
|
|
|
3215
3391
|
}
|
|
3216
3392
|
async function handleCreateJob(request, response, options, writeSuccess) {
|
|
3217
3393
|
if (request.method !== "POST") {
|
|
3218
|
-
throw new
|
|
3394
|
+
throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
|
|
3219
3395
|
}
|
|
3220
3396
|
const parsed = agentJobCreateRequestSchema.safeParse(
|
|
3221
3397
|
await readJsonRequestBody(request, MAX_AGENT_REQUEST_BODY_BYTES)
|
|
3222
3398
|
);
|
|
3223
3399
|
if (!parsed.success) {
|
|
3224
|
-
throw new
|
|
3400
|
+
throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
|
|
3225
3401
|
}
|
|
3226
3402
|
const authorizedRequest = await authorizeAgentJobRequest({
|
|
3227
3403
|
request: parsed.data,
|
|
@@ -3234,13 +3410,13 @@ async function handleCreateJob(request, response, options, writeSuccess) {
|
|
|
3234
3410
|
}
|
|
3235
3411
|
async function handleWorkspaceHealth(request, response, options, writeSuccess) {
|
|
3236
3412
|
if (request.method !== "POST") {
|
|
3237
|
-
throw new
|
|
3413
|
+
throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
|
|
3238
3414
|
}
|
|
3239
3415
|
const parsed = agentWorkspaceHealthRequestSchema.safeParse(
|
|
3240
3416
|
await readJsonRequestBody(request)
|
|
3241
3417
|
);
|
|
3242
3418
|
if (!parsed.success) {
|
|
3243
|
-
throw new
|
|
3419
|
+
throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
|
|
3244
3420
|
}
|
|
3245
3421
|
const controller = new AbortController();
|
|
3246
3422
|
const abort = () => {
|
|
@@ -3257,13 +3433,13 @@ async function handleWorkspaceHealth(request, response, options, writeSuccess) {
|
|
|
3257
3433
|
async function handleJobAction(request, response, options, route, writeSuccess) {
|
|
3258
3434
|
const manager = requireAgentManager(options);
|
|
3259
3435
|
if (request.method !== "POST") {
|
|
3260
|
-
throw new
|
|
3436
|
+
throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
|
|
3261
3437
|
}
|
|
3262
3438
|
const parsed = agentJobActionRequestSchema.safeParse(
|
|
3263
3439
|
await readJsonRequestBody(request)
|
|
3264
3440
|
);
|
|
3265
3441
|
if (!parsed.success) {
|
|
3266
|
-
throw new
|
|
3442
|
+
throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
|
|
3267
3443
|
}
|
|
3268
3444
|
if (route.action === "events") {
|
|
3269
3445
|
streamAgentJobEvents(response, manager, route.jobId);
|
|
@@ -3384,8 +3560,8 @@ import {
|
|
|
3384
3560
|
} from "@spotpatch/analyzer";
|
|
3385
3561
|
import {
|
|
3386
3562
|
DATA_FLOW_SCHEMA_VERSION,
|
|
3387
|
-
ERROR_CODES as
|
|
3388
|
-
SpotPatchError as
|
|
3563
|
+
ERROR_CODES as ERROR_CODES13,
|
|
3564
|
+
SpotPatchError as SpotPatchError13,
|
|
3389
3565
|
dataFlowComponentReportRequestSchema,
|
|
3390
3566
|
dataFlowPageReportRequestSchema,
|
|
3391
3567
|
limitDataFlowReportCollections
|
|
@@ -3404,7 +3580,7 @@ function limitDataFlowReportToBytes(report, maximumBytes) {
|
|
|
3404
3580
|
truncatedBy: "bytes"
|
|
3405
3581
|
});
|
|
3406
3582
|
if (envelopeBytes(limited) > maximumBytes) {
|
|
3407
|
-
throw new
|
|
3583
|
+
throw new SpotPatchError13(ERROR_CODES13.INTERNAL_ERROR);
|
|
3408
3584
|
}
|
|
3409
3585
|
for (let maximumDependencies = 1; maximumDependencies <= structurallyLimited.dependencies.length; maximumDependencies += 1) {
|
|
3410
3586
|
const candidate = limitDataFlowReportCollections(structurallyLimited, {
|
|
@@ -3433,7 +3609,7 @@ async function analyzeTarget(request, analyzer, options) {
|
|
|
3433
3609
|
request.componentSourceId
|
|
3434
3610
|
);
|
|
3435
3611
|
if (anchor?.sourceVersion !== request.sourceVersion) {
|
|
3436
|
-
throw new
|
|
3612
|
+
throw new SpotPatchError13(ERROR_CODES13.DATA_FLOW_SOURCE_STALE);
|
|
3437
3613
|
}
|
|
3438
3614
|
return anchor;
|
|
3439
3615
|
}
|
|
@@ -3450,7 +3626,7 @@ async function analyzeTarget(request, analyzer, options) {
|
|
|
3450
3626
|
column: resolvedRequest.column
|
|
3451
3627
|
});
|
|
3452
3628
|
if (resolvedRequest.sourceVersion !== void 0 && resolvedRequest.sourceVersion !== report.component.source.sourceVersion) {
|
|
3453
|
-
throw new
|
|
3629
|
+
throw new SpotPatchError13(ERROR_CODES13.DATA_FLOW_SOURCE_STALE);
|
|
3454
3630
|
}
|
|
3455
3631
|
return limitDataFlowReportToBytes(
|
|
3456
3632
|
report,
|
|
@@ -3459,7 +3635,7 @@ async function analyzeTarget(request, analyzer, options) {
|
|
|
3459
3635
|
}
|
|
3460
3636
|
function requireAnalyzer(analyzer) {
|
|
3461
3637
|
if (analyzer === void 0) {
|
|
3462
|
-
throw new
|
|
3638
|
+
throw new SpotPatchError13(ERROR_CODES13.DATA_FLOW_DISABLED);
|
|
3463
3639
|
}
|
|
3464
3640
|
return analyzer;
|
|
3465
3641
|
}
|
|
@@ -3471,7 +3647,7 @@ async function handleComponentDataFlowReport(request, analyzer, options) {
|
|
|
3471
3647
|
)
|
|
3472
3648
|
);
|
|
3473
3649
|
if (!parsed.success) {
|
|
3474
|
-
throw new
|
|
3650
|
+
throw new SpotPatchError13(ERROR_CODES13.INVALID_REQUEST);
|
|
3475
3651
|
}
|
|
3476
3652
|
return analyzeTarget(parsed.data, requireAnalyzer(analyzer), options);
|
|
3477
3653
|
}
|
|
@@ -3483,7 +3659,7 @@ async function handlePageDataFlowReport(request, analyzer, options) {
|
|
|
3483
3659
|
)
|
|
3484
3660
|
);
|
|
3485
3661
|
if (!parsed.success) {
|
|
3486
|
-
throw new
|
|
3662
|
+
throw new SpotPatchError13(ERROR_CODES13.INVALID_REQUEST);
|
|
3487
3663
|
}
|
|
3488
3664
|
const activeAnalyzer = requireAnalyzer(analyzer);
|
|
3489
3665
|
const componentReports = await Promise.all(
|
|
@@ -3556,7 +3732,7 @@ async function handlePageDataFlowReport(request, analyzer, options) {
|
|
|
3556
3732
|
// src/server/request-security.ts
|
|
3557
3733
|
import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
3558
3734
|
import { isIP } from "net";
|
|
3559
|
-
import { ERROR_CODES as
|
|
3735
|
+
import { ERROR_CODES as ERROR_CODES14, SPOTPATCH_TOKEN_HEADER, SpotPatchError as SpotPatchError14 } from "@spotpatch/shared";
|
|
3560
3736
|
function getSingleHeader(request, name) {
|
|
3561
3737
|
const value = request.headers[name.toLowerCase()];
|
|
3562
3738
|
return Array.isArray(value) ? value[0] : value;
|
|
@@ -3603,32 +3779,32 @@ function parseOrigin(value) {
|
|
|
3603
3779
|
function assertRequestAuthorized(request, options) {
|
|
3604
3780
|
const actualToken = getSingleHeader(request, SPOTPATCH_TOKEN_HEADER);
|
|
3605
3781
|
if (!tokensMatch2(actualToken, options.sessionToken)) {
|
|
3606
|
-
throw new
|
|
3782
|
+
throw new SpotPatchError14(ERROR_CODES14.INVALID_TOKEN);
|
|
3607
3783
|
}
|
|
3608
3784
|
const hostHeader = getSingleHeader(request, "host");
|
|
3609
3785
|
const originHeader = getSingleHeader(request, "origin");
|
|
3610
3786
|
const host = hostHeader === void 0 ? void 0 : parseHost(hostHeader);
|
|
3611
3787
|
const origin = originHeader === void 0 ? void 0 : parseOrigin(originHeader);
|
|
3612
3788
|
if (host === void 0 || origin === void 0) {
|
|
3613
|
-
throw new
|
|
3789
|
+
throw new SpotPatchError14(ERROR_CODES14.ORIGIN_NOT_ALLOWED);
|
|
3614
3790
|
}
|
|
3615
3791
|
const hostIsLoopback = isLoopbackHostname(host.hostname);
|
|
3616
3792
|
const originIsLoopback = isLoopbackHostname(origin.hostname);
|
|
3617
3793
|
if (!options.allowLan) {
|
|
3618
3794
|
if (!hostIsLoopback || !originIsLoopback) {
|
|
3619
|
-
throw new
|
|
3795
|
+
throw new SpotPatchError14(ERROR_CODES14.ORIGIN_NOT_ALLOWED);
|
|
3620
3796
|
}
|
|
3621
3797
|
return;
|
|
3622
3798
|
}
|
|
3623
3799
|
if (!originIsLoopback && origin.host.toLowerCase() !== host.host.toLowerCase()) {
|
|
3624
|
-
throw new
|
|
3800
|
+
throw new SpotPatchError14(ERROR_CODES14.ORIGIN_NOT_ALLOWED);
|
|
3625
3801
|
}
|
|
3626
3802
|
}
|
|
3627
3803
|
|
|
3628
3804
|
// src/server/runtime-bootstrap.ts
|
|
3629
3805
|
import {
|
|
3630
|
-
ERROR_CODES as
|
|
3631
|
-
SpotPatchError as
|
|
3806
|
+
ERROR_CODES as ERROR_CODES15,
|
|
3807
|
+
SpotPatchError as SpotPatchError15,
|
|
3632
3808
|
runtimeBootstrapRequestSchema,
|
|
3633
3809
|
runtimeConfigSchema
|
|
3634
3810
|
} from "@spotpatch/shared";
|
|
@@ -3658,7 +3834,7 @@ function resolveRuntimeBootstrapOptions(options) {
|
|
|
3658
3834
|
function assertRuntimeBootstrapRequest(request, expectedOrigin) {
|
|
3659
3835
|
const contentType = getSingleHeader2(request, "content-type")?.split(";", 1)[0]?.trim().toLowerCase();
|
|
3660
3836
|
if (request.method !== "POST" || contentType !== "application/json") {
|
|
3661
|
-
throw new
|
|
3837
|
+
throw new SpotPatchError15(ERROR_CODES15.INVALID_REQUEST);
|
|
3662
3838
|
}
|
|
3663
3839
|
const host = getSingleHeader2(request, "host");
|
|
3664
3840
|
let hostIsLoopback = false;
|
|
@@ -3670,7 +3846,7 @@ function assertRuntimeBootstrapRequest(request, expectedOrigin) {
|
|
|
3670
3846
|
}
|
|
3671
3847
|
}
|
|
3672
3848
|
if (!hostIsLoopback || getSingleHeader2(request, "origin") !== expectedOrigin || getSingleHeader2(request, "sec-fetch-site") !== "same-origin") {
|
|
3673
|
-
throw new
|
|
3849
|
+
throw new SpotPatchError15(ERROR_CODES15.ORIGIN_NOT_ALLOWED);
|
|
3674
3850
|
}
|
|
3675
3851
|
}
|
|
3676
3852
|
async function readRuntimeBootstrap(request, options) {
|
|
@@ -3679,123 +3855,123 @@ async function readRuntimeBootstrap(request, options) {
|
|
|
3679
3855
|
await readJsonRequestBody(request)
|
|
3680
3856
|
);
|
|
3681
3857
|
if (!parsedBody.success) {
|
|
3682
|
-
throw new
|
|
3858
|
+
throw new SpotPatchError15(ERROR_CODES15.INVALID_REQUEST);
|
|
3683
3859
|
}
|
|
3684
3860
|
return options.runtimeConfig;
|
|
3685
3861
|
}
|
|
3686
3862
|
|
|
3687
3863
|
// src/server/middleware.ts
|
|
3688
3864
|
var STATUS_BY_ERROR = Object.freeze({
|
|
3689
|
-
[
|
|
3690
|
-
[
|
|
3691
|
-
[
|
|
3692
|
-
[
|
|
3693
|
-
[
|
|
3694
|
-
[
|
|
3695
|
-
[
|
|
3696
|
-
[
|
|
3697
|
-
[
|
|
3698
|
-
[
|
|
3699
|
-
[
|
|
3700
|
-
[
|
|
3701
|
-
[
|
|
3702
|
-
[
|
|
3703
|
-
[
|
|
3704
|
-
[
|
|
3705
|
-
[
|
|
3706
|
-
[
|
|
3707
|
-
[
|
|
3708
|
-
[
|
|
3709
|
-
[
|
|
3710
|
-
[
|
|
3711
|
-
[
|
|
3712
|
-
[
|
|
3713
|
-
[
|
|
3714
|
-
[
|
|
3715
|
-
[
|
|
3716
|
-
[
|
|
3717
|
-
[
|
|
3718
|
-
[
|
|
3719
|
-
[
|
|
3720
|
-
[
|
|
3721
|
-
[
|
|
3722
|
-
[
|
|
3723
|
-
[
|
|
3724
|
-
[
|
|
3725
|
-
[
|
|
3726
|
-
[
|
|
3727
|
-
[
|
|
3728
|
-
[
|
|
3729
|
-
[
|
|
3730
|
-
[
|
|
3731
|
-
[
|
|
3732
|
-
[
|
|
3733
|
-
[
|
|
3734
|
-
[
|
|
3735
|
-
[
|
|
3736
|
-
[
|
|
3737
|
-
[
|
|
3738
|
-
[
|
|
3739
|
-
[
|
|
3740
|
-
[
|
|
3741
|
-
[
|
|
3742
|
-
[
|
|
3865
|
+
[ERROR_CODES16.INVALID_REQUEST]: 400,
|
|
3866
|
+
[ERROR_CODES16.INVALID_TOKEN]: 401,
|
|
3867
|
+
[ERROR_CODES16.ORIGIN_NOT_ALLOWED]: 403,
|
|
3868
|
+
[ERROR_CODES16.SOURCE_NOT_FOUND]: 404,
|
|
3869
|
+
[ERROR_CODES16.SOURCE_OUTSIDE_ROOT]: 403,
|
|
3870
|
+
[ERROR_CODES16.SOURCE_TOO_LARGE]: 413,
|
|
3871
|
+
[ERROR_CODES16.EDITOR_OPEN_FAILED]: 500,
|
|
3872
|
+
[ERROR_CODES16.DATA_FLOW_DISABLED]: 404,
|
|
3873
|
+
[ERROR_CODES16.DATA_FLOW_SOURCE_STALE]: 409,
|
|
3874
|
+
[ERROR_CODES16.DATA_FLOW_ANALYSIS_CANCELLED]: 409,
|
|
3875
|
+
[ERROR_CODES16.AI_DISABLED]: 404,
|
|
3876
|
+
[ERROR_CODES16.PROVIDER_NOT_CONFIGURED]: 503,
|
|
3877
|
+
[ERROR_CODES16.PROVIDER_AUTH_FAILED]: 502,
|
|
3878
|
+
[ERROR_CODES16.PROVIDER_PROTOCOL_UNSUPPORTED]: 502,
|
|
3879
|
+
[ERROR_CODES16.MODEL_NOT_ALLOWED]: 400,
|
|
3880
|
+
[ERROR_CODES16.MODEL_TOOL_CALL_UNSUPPORTED]: 422,
|
|
3881
|
+
[ERROR_CODES16.PROVIDER_RATE_LIMITED]: 429,
|
|
3882
|
+
[ERROR_CODES16.AGENT_BUSY]: 409,
|
|
3883
|
+
[ERROR_CODES16.AGENT_LIMIT_EXCEEDED]: 413,
|
|
3884
|
+
[ERROR_CODES16.AGENT_CANCELLED]: 409,
|
|
3885
|
+
[ERROR_CODES16.EXTERNAL_HANDOFF_DISABLED]: 404,
|
|
3886
|
+
[ERROR_CODES16.EXTERNAL_HANDOFF_UNAVAILABLE]: 503,
|
|
3887
|
+
[ERROR_CODES16.HANDOFF_VALIDATION_FAILED]: 422,
|
|
3888
|
+
[ERROR_CODES16.HANDOFF_SOURCE_STALE]: 409,
|
|
3889
|
+
[ERROR_CODES16.HANDOFF_NOT_FOUND]: 404,
|
|
3890
|
+
[ERROR_CODES16.HANDOFF_EXPIRED]: 410,
|
|
3891
|
+
[ERROR_CODES16.HANDOFF_CURSOR_INVALID]: 409,
|
|
3892
|
+
[ERROR_CODES16.HANDOFF_RESPONSE_TOO_LARGE]: 413,
|
|
3893
|
+
[ERROR_CODES16.BRIDGE_UNAUTHORIZED]: 401,
|
|
3894
|
+
[ERROR_CODES16.BRIDGE_PROTOCOL_MISMATCH]: 409,
|
|
3895
|
+
[ERROR_CODES16.BRIDGE_BUSY]: 429,
|
|
3896
|
+
[ERROR_CODES16.EXTERNAL_AGENT_BUSY]: 409,
|
|
3897
|
+
[ERROR_CODES16.ACTIVE_ADAPTER_CONFLICT]: 409,
|
|
3898
|
+
[ERROR_CODES16.ACTIVE_ADAPTER_LEASE_INVALID]: 409,
|
|
3899
|
+
[ERROR_CODES16.ACTIVE_DISPATCH_INVALID]: 409,
|
|
3900
|
+
[ERROR_CODES16.SESSION_NOT_FOUND]: 404,
|
|
3901
|
+
[ERROR_CODES16.SESSION_AMBIGUOUS]: 409,
|
|
3902
|
+
[ERROR_CODES16.SESSION_CLOSED]: 410,
|
|
3903
|
+
[ERROR_CODES16.WORKTREE_DIRTY]: 409,
|
|
3904
|
+
[ERROR_CODES16.WORKTREE_NOT_REPOSITORY]: 409,
|
|
3905
|
+
[ERROR_CODES16.WORKTREE_OPERATION_IN_PROGRESS]: 409,
|
|
3906
|
+
[ERROR_CODES16.WORKTREE_CONFLICTED]: 409,
|
|
3907
|
+
[ERROR_CODES16.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: 413,
|
|
3908
|
+
[ERROR_CODES16.WORKTREE_UNTRACKED_UNSUPPORTED]: 409,
|
|
3909
|
+
[ERROR_CODES16.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: 409,
|
|
3910
|
+
[ERROR_CODES16.TOOL_DENIED]: 403,
|
|
3911
|
+
[ERROR_CODES16.TOOL_INPUT_INVALID]: 422,
|
|
3912
|
+
[ERROR_CODES16.TOOL_ARGUMENTS_INVALID]: 422,
|
|
3913
|
+
[ERROR_CODES16.TOOL_CALL_ID_CONFLICT]: 422,
|
|
3914
|
+
[ERROR_CODES16.TOOL_PATH_DENIED]: 403,
|
|
3915
|
+
[ERROR_CODES16.PATCH_REJECTED]: 422,
|
|
3916
|
+
[ERROR_CODES16.VALIDATION_FAILED]: 422,
|
|
3917
|
+
[ERROR_CODES16.APPLY_CONFLICT]: 409,
|
|
3918
|
+
[ERROR_CODES16.INTERNAL_ERROR]: 500
|
|
3743
3919
|
});
|
|
3744
3920
|
var PUBLIC_MESSAGES = Object.freeze({
|
|
3745
|
-
[
|
|
3746
|
-
[
|
|
3747
|
-
[
|
|
3748
|
-
[
|
|
3749
|
-
[
|
|
3750
|
-
[
|
|
3751
|
-
[
|
|
3752
|
-
[
|
|
3753
|
-
[
|
|
3754
|
-
[
|
|
3755
|
-
[
|
|
3756
|
-
[
|
|
3757
|
-
[
|
|
3758
|
-
[
|
|
3759
|
-
[
|
|
3760
|
-
[
|
|
3761
|
-
[
|
|
3762
|
-
[
|
|
3763
|
-
[
|
|
3764
|
-
[
|
|
3765
|
-
[
|
|
3766
|
-
[
|
|
3767
|
-
[
|
|
3768
|
-
[
|
|
3769
|
-
[
|
|
3770
|
-
[
|
|
3771
|
-
[
|
|
3772
|
-
[
|
|
3773
|
-
[
|
|
3774
|
-
[
|
|
3775
|
-
[
|
|
3776
|
-
[
|
|
3777
|
-
[
|
|
3778
|
-
[
|
|
3779
|
-
[
|
|
3780
|
-
[
|
|
3781
|
-
[
|
|
3782
|
-
[
|
|
3783
|
-
[
|
|
3784
|
-
[
|
|
3785
|
-
[
|
|
3786
|
-
[
|
|
3787
|
-
[
|
|
3788
|
-
[
|
|
3789
|
-
[
|
|
3790
|
-
[
|
|
3791
|
-
[
|
|
3792
|
-
[
|
|
3793
|
-
[
|
|
3794
|
-
[
|
|
3795
|
-
[
|
|
3796
|
-
[
|
|
3797
|
-
[
|
|
3798
|
-
[
|
|
3921
|
+
[ERROR_CODES16.INVALID_REQUEST]: "The request is invalid.",
|
|
3922
|
+
[ERROR_CODES16.INVALID_TOKEN]: "The session token is invalid.",
|
|
3923
|
+
[ERROR_CODES16.ORIGIN_NOT_ALLOWED]: "The request origin is not allowed.",
|
|
3924
|
+
[ERROR_CODES16.SOURCE_NOT_FOUND]: "The source file is unavailable.",
|
|
3925
|
+
[ERROR_CODES16.SOURCE_OUTSIDE_ROOT]: "The source file is outside the project root.",
|
|
3926
|
+
[ERROR_CODES16.SOURCE_TOO_LARGE]: "The source file exceeds the size limit.",
|
|
3927
|
+
[ERROR_CODES16.EDITOR_OPEN_FAILED]: "The editor request could not be started.",
|
|
3928
|
+
[ERROR_CODES16.DATA_FLOW_DISABLED]: "Component data-flow analysis is not enabled.",
|
|
3929
|
+
[ERROR_CODES16.DATA_FLOW_SOURCE_STALE]: "The selected source version is stale.",
|
|
3930
|
+
[ERROR_CODES16.DATA_FLOW_ANALYSIS_CANCELLED]: "The data-flow analysis was cancelled.",
|
|
3931
|
+
[ERROR_CODES16.AI_DISABLED]: "AI execution is not enabled.",
|
|
3932
|
+
[ERROR_CODES16.PROVIDER_NOT_CONFIGURED]: "The AI provider is unavailable.",
|
|
3933
|
+
[ERROR_CODES16.PROVIDER_AUTH_FAILED]: "The AI provider rejected authentication.",
|
|
3934
|
+
[ERROR_CODES16.PROVIDER_PROTOCOL_UNSUPPORTED]: "The AI provider protocol is unsupported.",
|
|
3935
|
+
[ERROR_CODES16.MODEL_NOT_ALLOWED]: "The selected model is not allowed.",
|
|
3936
|
+
[ERROR_CODES16.MODEL_TOOL_CALL_UNSUPPORTED]: "The selected model cannot run SpotPatch tools.",
|
|
3937
|
+
[ERROR_CODES16.PROVIDER_RATE_LIMITED]: "The AI provider is rate limited.",
|
|
3938
|
+
[ERROR_CODES16.AGENT_BUSY]: "Another Agent job is already running.",
|
|
3939
|
+
[ERROR_CODES16.AGENT_LIMIT_EXCEEDED]: "The Agent job exceeded a safety limit.",
|
|
3940
|
+
[ERROR_CODES16.AGENT_CANCELLED]: "The Agent job was cancelled.",
|
|
3941
|
+
[ERROR_CODES16.EXTERNAL_HANDOFF_DISABLED]: "External Agent handoff is not enabled.",
|
|
3942
|
+
[ERROR_CODES16.EXTERNAL_HANDOFF_UNAVAILABLE]: "External Agent handoff is temporarily unavailable.",
|
|
3943
|
+
[ERROR_CODES16.HANDOFF_VALIDATION_FAILED]: "The handoff content is invalid.",
|
|
3944
|
+
[ERROR_CODES16.HANDOFF_SOURCE_STALE]: "The selected source is stale.",
|
|
3945
|
+
[ERROR_CODES16.HANDOFF_NOT_FOUND]: "No current handoff is available.",
|
|
3946
|
+
[ERROR_CODES16.HANDOFF_EXPIRED]: "The handoff has expired.",
|
|
3947
|
+
[ERROR_CODES16.HANDOFF_CURSOR_INVALID]: "The handoff cursor is invalid.",
|
|
3948
|
+
[ERROR_CODES16.HANDOFF_RESPONSE_TOO_LARGE]: "The handoff exceeds the size limit.",
|
|
3949
|
+
[ERROR_CODES16.BRIDGE_UNAUTHORIZED]: "The local bridge request is unauthorized.",
|
|
3950
|
+
[ERROR_CODES16.BRIDGE_PROTOCOL_MISMATCH]: "The local bridge protocol is incompatible.",
|
|
3951
|
+
[ERROR_CODES16.BRIDGE_BUSY]: "The local bridge is busy.",
|
|
3952
|
+
[ERROR_CODES16.EXTERNAL_AGENT_BUSY]: "The connected external Agent is busy.",
|
|
3953
|
+
[ERROR_CODES16.ACTIVE_ADAPTER_CONFLICT]: "Another active Agent adapter is connected.",
|
|
3954
|
+
[ERROR_CODES16.ACTIVE_ADAPTER_LEASE_INVALID]: "The active Agent adapter lease is invalid.",
|
|
3955
|
+
[ERROR_CODES16.ACTIVE_DISPATCH_INVALID]: "The active Agent dispatch transition is invalid.",
|
|
3956
|
+
[ERROR_CODES16.SESSION_NOT_FOUND]: "No active SpotPatch session was found.",
|
|
3957
|
+
[ERROR_CODES16.SESSION_AMBIGUOUS]: "More than one SpotPatch session matches.",
|
|
3958
|
+
[ERROR_CODES16.SESSION_CLOSED]: "The SpotPatch session has closed.",
|
|
3959
|
+
[ERROR_CODES16.WORKTREE_DIRTY]: "Local changes require explicit inclusion consent.",
|
|
3960
|
+
[ERROR_CODES16.WORKTREE_NOT_REPOSITORY]: "The project root is not a Git repository.",
|
|
3961
|
+
[ERROR_CODES16.WORKTREE_OPERATION_IN_PROGRESS]: "A Git operation is currently in progress.",
|
|
3962
|
+
[ERROR_CODES16.WORKTREE_CONFLICTED]: "The local workspace contains unresolved merge conflicts.",
|
|
3963
|
+
[ERROR_CODES16.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: "The local workspace exceeds the safe isolation size limit.",
|
|
3964
|
+
[ERROR_CODES16.WORKTREE_UNTRACKED_UNSUPPORTED]: "An untracked path cannot be isolated safely.",
|
|
3965
|
+
[ERROR_CODES16.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: "The local workspace state cannot be isolated safely.",
|
|
3966
|
+
[ERROR_CODES16.TOOL_DENIED]: "The Agent tool request was denied.",
|
|
3967
|
+
[ERROR_CODES16.TOOL_INPUT_INVALID]: "The Agent tool input was invalid.",
|
|
3968
|
+
[ERROR_CODES16.TOOL_ARGUMENTS_INVALID]: "The Agent tool arguments are invalid.",
|
|
3969
|
+
[ERROR_CODES16.TOOL_CALL_ID_CONFLICT]: "A tool call ID conflicts within one Agent turn.",
|
|
3970
|
+
[ERROR_CODES16.TOOL_PATH_DENIED]: "The Agent tool path was denied.",
|
|
3971
|
+
[ERROR_CODES16.PATCH_REJECTED]: "The proposed patch was rejected.",
|
|
3972
|
+
[ERROR_CODES16.VALIDATION_FAILED]: "The proposed change failed validation.",
|
|
3973
|
+
[ERROR_CODES16.APPLY_CONFLICT]: "The change conflicts with the current worktree.",
|
|
3974
|
+
[ERROR_CODES16.INTERNAL_ERROR]: "The request could not be completed."
|
|
3799
3975
|
});
|
|
3800
3976
|
function writeJson2(response, status, payload) {
|
|
3801
3977
|
response.statusCode = status;
|
|
@@ -3804,11 +3980,11 @@ function writeJson2(response, status, payload) {
|
|
|
3804
3980
|
response.end(JSON.stringify(payload));
|
|
3805
3981
|
}
|
|
3806
3982
|
function asSpotPatchError(error) {
|
|
3807
|
-
return error instanceof
|
|
3983
|
+
return error instanceof SpotPatchError16 ? error : new SpotPatchError16(ERROR_CODES16.INTERNAL_ERROR, void 0, { cause: error });
|
|
3808
3984
|
}
|
|
3809
3985
|
function writeError(response, error, logger) {
|
|
3810
3986
|
const normalized = asSpotPatchError(error);
|
|
3811
|
-
if (normalized.code ===
|
|
3987
|
+
if (normalized.code === ERROR_CODES16.INTERNAL_ERROR) {
|
|
3812
3988
|
logger?.warn("[spotpatch:server] Internal request failure.");
|
|
3813
3989
|
}
|
|
3814
3990
|
writeJson2(response, STATUS_BY_ERROR[normalized.code], {
|
|
@@ -3831,7 +4007,7 @@ async function handleSourceContext(request, options) {
|
|
|
3831
4007
|
await readJsonRequestBody(request)
|
|
3832
4008
|
);
|
|
3833
4009
|
if (!parsed.success) {
|
|
3834
|
-
throw new
|
|
4010
|
+
throw new SpotPatchError16(ERROR_CODES16.INVALID_REQUEST);
|
|
3835
4011
|
}
|
|
3836
4012
|
return readSourceContext({
|
|
3837
4013
|
request: parsed.data,
|
|
@@ -3844,7 +4020,7 @@ async function handleSourceContext(request, options) {
|
|
|
3844
4020
|
async function handleOpenEditor(request, options) {
|
|
3845
4021
|
const parsed = openEditorRequestSchema.safeParse(await readJsonRequestBody(request));
|
|
3846
4022
|
if (!parsed.success) {
|
|
3847
|
-
throw new
|
|
4023
|
+
throw new SpotPatchError16(ERROR_CODES16.INVALID_REQUEST);
|
|
3848
4024
|
}
|
|
3849
4025
|
const body = parsed.data;
|
|
3850
4026
|
const sourcePath = await resolveSourceFile({
|
|
@@ -3861,7 +4037,7 @@ async function handleOpenEditor(request, options) {
|
|
|
3861
4037
|
options.logger?.warn(
|
|
3862
4038
|
`[spotpatch:server] ${options.options.editor === "auto" ? "The detected editor" : options.options.editor} rejected an editor request.`
|
|
3863
4039
|
);
|
|
3864
|
-
throw new
|
|
4040
|
+
throw new SpotPatchError16(ERROR_CODES16.EDITOR_OPEN_FAILED, void 0, {
|
|
3865
4041
|
cause: error
|
|
3866
4042
|
});
|
|
3867
4043
|
}
|
|
@@ -3869,16 +4045,18 @@ async function handleOpenEditor(request, options) {
|
|
|
3869
4045
|
function createSpotPatchMiddleware(options) {
|
|
3870
4046
|
const bootstrap = options.bootstrap === void 0 ? void 0 : resolveRuntimeBootstrapOptions(options.bootstrap);
|
|
3871
4047
|
const dataFlowAnalyzer = createDataFlowAnalyzer(options);
|
|
3872
|
-
|
|
4048
|
+
const externalAgentController = options.externalAgentControl === void 0 ? void 0 : createExternalAgentBrowserController(options.externalAgentControl);
|
|
4049
|
+
const middleware = (request, response, next) => {
|
|
3873
4050
|
const path9 = requestPath(request);
|
|
3874
4051
|
const agentRoute = matchAgentRequestPath(path9);
|
|
4052
|
+
const externalAgentRoute = matchExternalAgentBrowserPath(path9);
|
|
3875
4053
|
const externalHandoffRoute = matchExternalHandoffBrowserPath(path9);
|
|
3876
|
-
if (path9 !==
|
|
4054
|
+
if (path9 !== SPOTPATCH_ENDPOINTS4.sourceContext && path9 !== SPOTPATCH_ENDPOINTS4.openEditor && path9 !== SPOTPATCH_ENDPOINTS4.dataFlowComponentReport && path9 !== SPOTPATCH_ENDPOINTS4.dataFlowPageReport && agentRoute === void 0 && externalAgentRoute === void 0 && externalHandoffRoute === void 0 && !path9.startsWith(`${SPOTPATCH_API_BASE}/`)) {
|
|
3877
4055
|
next();
|
|
3878
4056
|
return;
|
|
3879
4057
|
}
|
|
3880
4058
|
const handle = async () => {
|
|
3881
|
-
if (path9 ===
|
|
4059
|
+
if (path9 === SPOTPATCH_ENDPOINTS4.bootstrap && bootstrap !== void 0) {
|
|
3882
4060
|
const data = await readRuntimeBootstrap(
|
|
3883
4061
|
request,
|
|
3884
4062
|
bootstrap
|
|
@@ -3890,25 +4068,25 @@ function createSpotPatchMiddleware(options) {
|
|
|
3890
4068
|
allowLan: options.options.allowLan,
|
|
3891
4069
|
sessionToken: options.session.token
|
|
3892
4070
|
});
|
|
3893
|
-
if (path9 ===
|
|
4071
|
+
if (path9 === SPOTPATCH_ENDPOINTS4.sourceContext) {
|
|
3894
4072
|
if (request.method !== "POST") {
|
|
3895
|
-
throw new
|
|
4073
|
+
throw new SpotPatchError16(ERROR_CODES16.INVALID_REQUEST);
|
|
3896
4074
|
}
|
|
3897
4075
|
const data = await handleSourceContext(request, options);
|
|
3898
4076
|
writeJson2(response, 200, { ok: true, data });
|
|
3899
4077
|
return;
|
|
3900
4078
|
}
|
|
3901
|
-
if (path9 ===
|
|
4079
|
+
if (path9 === SPOTPATCH_ENDPOINTS4.openEditor) {
|
|
3902
4080
|
if (request.method !== "POST") {
|
|
3903
|
-
throw new
|
|
4081
|
+
throw new SpotPatchError16(ERROR_CODES16.INVALID_REQUEST);
|
|
3904
4082
|
}
|
|
3905
4083
|
const data = await handleOpenEditor(request, options);
|
|
3906
4084
|
writeJson2(response, 200, { ok: true, data });
|
|
3907
4085
|
return;
|
|
3908
4086
|
}
|
|
3909
|
-
if (path9 ===
|
|
4087
|
+
if (path9 === SPOTPATCH_ENDPOINTS4.dataFlowComponentReport) {
|
|
3910
4088
|
if (request.method !== "POST") {
|
|
3911
|
-
throw new
|
|
4089
|
+
throw new SpotPatchError16(ERROR_CODES16.INVALID_REQUEST);
|
|
3912
4090
|
}
|
|
3913
4091
|
const data = await handleComponentDataFlowReport(
|
|
3914
4092
|
request,
|
|
@@ -3918,14 +4096,28 @@ function createSpotPatchMiddleware(options) {
|
|
|
3918
4096
|
writeJson2(response, 200, { ok: true, data });
|
|
3919
4097
|
return;
|
|
3920
4098
|
}
|
|
3921
|
-
if (path9 ===
|
|
4099
|
+
if (path9 === SPOTPATCH_ENDPOINTS4.dataFlowPageReport) {
|
|
3922
4100
|
if (request.method !== "POST") {
|
|
3923
|
-
throw new
|
|
4101
|
+
throw new SpotPatchError16(ERROR_CODES16.INVALID_REQUEST);
|
|
3924
4102
|
}
|
|
3925
4103
|
const data = await handlePageDataFlowReport(request, dataFlowAnalyzer, options);
|
|
3926
4104
|
writeJson2(response, 200, { ok: true, data });
|
|
3927
4105
|
return;
|
|
3928
4106
|
}
|
|
4107
|
+
if (externalAgentRoute !== void 0) {
|
|
4108
|
+
if (!options.options.externalAgent.enabled || externalAgentController === void 0) {
|
|
4109
|
+
throw new SpotPatchError16(ERROR_CODES16.EXTERNAL_HANDOFF_DISABLED);
|
|
4110
|
+
}
|
|
4111
|
+
await externalAgentController.handle(
|
|
4112
|
+
request,
|
|
4113
|
+
response,
|
|
4114
|
+
externalAgentRoute,
|
|
4115
|
+
(target, status, data) => {
|
|
4116
|
+
writeJson2(target, status, { ok: true, data });
|
|
4117
|
+
}
|
|
4118
|
+
);
|
|
4119
|
+
return;
|
|
4120
|
+
}
|
|
3929
4121
|
if (externalHandoffRoute !== void 0) {
|
|
3930
4122
|
await handleExternalHandoffBrowserRequest(
|
|
3931
4123
|
request,
|
|
@@ -3944,7 +4136,7 @@ function createSpotPatchMiddleware(options) {
|
|
|
3944
4136
|
return;
|
|
3945
4137
|
}
|
|
3946
4138
|
if (agentRoute === void 0) {
|
|
3947
|
-
throw new
|
|
4139
|
+
throw new SpotPatchError16(ERROR_CODES16.INVALID_REQUEST);
|
|
3948
4140
|
}
|
|
3949
4141
|
await handleAgentRequest(
|
|
3950
4142
|
request,
|
|
@@ -3960,6 +4152,11 @@ function createSpotPatchMiddleware(options) {
|
|
|
3960
4152
|
writeError(response, error, options.logger);
|
|
3961
4153
|
});
|
|
3962
4154
|
};
|
|
4155
|
+
return Object.assign(middleware, {
|
|
4156
|
+
dispose() {
|
|
4157
|
+
externalAgentController?.dispose();
|
|
4158
|
+
}
|
|
4159
|
+
});
|
|
3963
4160
|
}
|
|
3964
4161
|
|
|
3965
4162
|
// src/server/source-registration.ts
|
|
@@ -4291,8 +4488,10 @@ export {
|
|
|
4291
4488
|
readRuntimeBootstrap,
|
|
4292
4489
|
resolveCredentialEnvironment,
|
|
4293
4490
|
resolveEnvironmentAiConfiguration,
|
|
4491
|
+
resolveManagedExecutionValidation,
|
|
4294
4492
|
resolveOptions,
|
|
4295
4493
|
resolveProjectOptions,
|
|
4494
|
+
resolveProjectValidationChecks,
|
|
4296
4495
|
resolveRuntimeBootstrapOptions,
|
|
4297
4496
|
serializeResolvedSpotPatchOptions
|
|
4298
4497
|
};
|