@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.cjs
CHANGED
|
@@ -51,8 +51,10 @@ __export(index_exports, {
|
|
|
51
51
|
readRuntimeBootstrap: () => readRuntimeBootstrap,
|
|
52
52
|
resolveCredentialEnvironment: () => resolveCredentialEnvironment,
|
|
53
53
|
resolveEnvironmentAiConfiguration: () => resolveEnvironmentAiConfiguration,
|
|
54
|
+
resolveManagedExecutionValidation: () => resolveManagedExecutionValidation,
|
|
54
55
|
resolveOptions: () => resolveOptions,
|
|
55
56
|
resolveProjectOptions: () => resolveProjectOptions,
|
|
57
|
+
resolveProjectValidationChecks: () => resolveProjectValidationChecks,
|
|
56
58
|
resolveRuntimeBootstrapOptions: () => resolveRuntimeBootstrapOptions,
|
|
57
59
|
serializeResolvedSpotPatchOptions: () => serializeResolvedSpotPatchOptions
|
|
58
60
|
});
|
|
@@ -2250,6 +2252,7 @@ var import_promises3 = require("fs/promises");
|
|
|
2250
2252
|
var import_node_module = require("module");
|
|
2251
2253
|
var import_node_path3 = __toESM(require("path"), 1);
|
|
2252
2254
|
var import_node_util = require("util");
|
|
2255
|
+
var import_shared9 = require("@spotpatch/shared");
|
|
2253
2256
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
2254
2257
|
var TYPESCRIPT_CHECK_ID = "spotpatch-typecheck";
|
|
2255
2258
|
var TYPESCRIPT_CHECK_LABEL = "TypeScript";
|
|
@@ -2317,6 +2320,15 @@ async function resolveTypeScriptCli(appRoot) {
|
|
|
2317
2320
|
function portableRelativePath(from, to) {
|
|
2318
2321
|
return import_node_path3.default.relative(from, to).split(import_node_path3.default.sep).join("/");
|
|
2319
2322
|
}
|
|
2323
|
+
function hasRequiredCheck(checks) {
|
|
2324
|
+
return Object.values(checks).some((check) => check.required);
|
|
2325
|
+
}
|
|
2326
|
+
function availableCheckId(checks, preferred) {
|
|
2327
|
+
if (checks[preferred] === void 0) return preferred;
|
|
2328
|
+
let suffix = 2;
|
|
2329
|
+
while (checks[`${preferred}-${String(suffix)}`] !== void 0) suffix += 1;
|
|
2330
|
+
return `${preferred}-${String(suffix)}`;
|
|
2331
|
+
}
|
|
2320
2332
|
async function discoverProjectValidationCheck(options) {
|
|
2321
2333
|
const appRoot = await (0, import_promises3.realpath)(options.appRoot);
|
|
2322
2334
|
const tsconfigPath = import_node_path3.default.join(appRoot, "tsconfig.json");
|
|
@@ -2345,6 +2357,8 @@ async function discoverProjectValidationCheck(options) {
|
|
|
2345
2357
|
"--noEmit",
|
|
2346
2358
|
"--pretty",
|
|
2347
2359
|
"false",
|
|
2360
|
+
"--incremental",
|
|
2361
|
+
"false",
|
|
2348
2362
|
"--project",
|
|
2349
2363
|
projectPath
|
|
2350
2364
|
]),
|
|
@@ -2352,21 +2366,30 @@ async function discoverProjectValidationCheck(options) {
|
|
|
2352
2366
|
timeoutMs: options.timeoutMs
|
|
2353
2367
|
});
|
|
2354
2368
|
}
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2369
|
+
async function resolveProjectValidationChecks(options) {
|
|
2370
|
+
if (hasRequiredCheck(options.checks)) return options.checks;
|
|
2371
|
+
const discovered = await discoverProjectValidationCheck(options);
|
|
2372
|
+
if (discovered === void 0) return options.checks;
|
|
2373
|
+
const id = availableCheckId(options.checks, discovered.id);
|
|
2374
|
+
return Object.freeze({
|
|
2375
|
+
...options.checks,
|
|
2376
|
+
[id]: Object.freeze({ ...discovered, id })
|
|
2377
|
+
});
|
|
2359
2378
|
}
|
|
2360
|
-
function
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2379
|
+
async function resolveManagedExecutionValidation(options) {
|
|
2380
|
+
const checks = options.ai === false ? Object.freeze({}) : options.ai.execution.checks;
|
|
2381
|
+
const limits = options.ai === false ? import_shared9.DEFAULT_AGENT_LIMITS : options.ai.execution.limits;
|
|
2382
|
+
return Object.freeze({
|
|
2383
|
+
checks: await resolveProjectValidationChecks({
|
|
2384
|
+
appRoot: options.appRoot,
|
|
2385
|
+
checks,
|
|
2386
|
+
timeoutMs: limits.checkTimeoutMs
|
|
2387
|
+
}),
|
|
2388
|
+
limits
|
|
2389
|
+
});
|
|
2369
2390
|
}
|
|
2391
|
+
|
|
2392
|
+
// src/project-options.ts
|
|
2370
2393
|
async function resolveProjectOptions(input) {
|
|
2371
2394
|
const userOptions = input.options ?? {};
|
|
2372
2395
|
const resolved = resolveOptions(userOptions, input.environmentAi);
|
|
@@ -2378,22 +2401,15 @@ async function resolveProjectOptions(input) {
|
|
|
2378
2401
|
"SpotPatch trustedFastMode cannot be combined with applyMode auto."
|
|
2379
2402
|
);
|
|
2380
2403
|
}
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
);
|
|
2391
|
-
}
|
|
2392
|
-
const id = availableCheckId(checks, discovered.id);
|
|
2393
|
-
checks = Object.freeze({
|
|
2394
|
-
...checks,
|
|
2395
|
-
[id]: Object.freeze({ ...discovered, id })
|
|
2396
|
-
});
|
|
2404
|
+
const checks = await resolveProjectValidationChecks({
|
|
2405
|
+
appRoot: input.appRoot,
|
|
2406
|
+
checks: resolved.ai.execution.checks,
|
|
2407
|
+
timeoutMs: resolved.ai.execution.limits.checkTimeoutMs
|
|
2408
|
+
});
|
|
2409
|
+
if (!Object.values(checks).some((check) => check.required)) {
|
|
2410
|
+
throw new RangeError(
|
|
2411
|
+
"SpotPatch trustedFastMode requires a configured required check or a local TypeScript project with tsconfig.json."
|
|
2412
|
+
);
|
|
2397
2413
|
}
|
|
2398
2414
|
const ai = Object.freeze({
|
|
2399
2415
|
...resolved.ai,
|
|
@@ -2473,20 +2489,20 @@ function createSourceRegistry(options = {}) {
|
|
|
2473
2489
|
}
|
|
2474
2490
|
|
|
2475
2491
|
// src/server/middleware.ts
|
|
2476
|
-
var
|
|
2492
|
+
var import_shared20 = require("@spotpatch/shared");
|
|
2477
2493
|
|
|
2478
2494
|
// src/external-handoff/browser-http.ts
|
|
2479
|
-
var
|
|
2495
|
+
var import_shared13 = require("@spotpatch/shared");
|
|
2480
2496
|
|
|
2481
2497
|
// src/server/annotation-authorizer.ts
|
|
2482
2498
|
var import_promises6 = require("fs/promises");
|
|
2483
2499
|
var import_node_path7 = __toESM(require("path"), 1);
|
|
2484
|
-
var
|
|
2500
|
+
var import_shared12 = require("@spotpatch/shared");
|
|
2485
2501
|
|
|
2486
2502
|
// src/server/source-context.ts
|
|
2487
2503
|
var import_promises5 = require("fs/promises");
|
|
2488
2504
|
var import_node_path6 = __toESM(require("path"), 1);
|
|
2489
|
-
var
|
|
2505
|
+
var import_shared11 = require("@spotpatch/shared");
|
|
2490
2506
|
|
|
2491
2507
|
// src/server/extract-code-context.ts
|
|
2492
2508
|
var import_oxc_parser = require("oxc-parser");
|
|
@@ -2717,7 +2733,7 @@ function extractCodeContext(options) {
|
|
|
2717
2733
|
// src/server/source-file.ts
|
|
2718
2734
|
var import_promises4 = require("fs/promises");
|
|
2719
2735
|
var import_node_path5 = __toESM(require("path"), 1);
|
|
2720
|
-
var
|
|
2736
|
+
var import_shared10 = require("@spotpatch/shared");
|
|
2721
2737
|
var ALLOWED_EXTENSIONS = /* @__PURE__ */ new Set([".jsx", ".tsx"]);
|
|
2722
2738
|
function isMissingFileError(error) {
|
|
2723
2739
|
return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
|
|
@@ -2732,7 +2748,7 @@ async function assertInsideRoot(root, candidate) {
|
|
|
2732
2748
|
]);
|
|
2733
2749
|
} catch (error) {
|
|
2734
2750
|
if (isMissingFileError(error)) {
|
|
2735
|
-
throw new
|
|
2751
|
+
throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.SOURCE_NOT_FOUND, void 0, {
|
|
2736
2752
|
cause: error
|
|
2737
2753
|
});
|
|
2738
2754
|
}
|
|
@@ -2741,35 +2757,35 @@ async function assertInsideRoot(root, candidate) {
|
|
|
2741
2757
|
const relative = import_node_path5.default.relative(realRoot, realCandidate);
|
|
2742
2758
|
const outside = relative.startsWith(`..${import_node_path5.default.sep}`) || relative === ".." || import_node_path5.default.isAbsolute(relative);
|
|
2743
2759
|
if (outside) {
|
|
2744
|
-
throw new
|
|
2760
|
+
throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.SOURCE_OUTSIDE_ROOT);
|
|
2745
2761
|
}
|
|
2746
2762
|
return realCandidate;
|
|
2747
2763
|
}
|
|
2748
2764
|
async function resolveSourceFile(options) {
|
|
2749
2765
|
const registeredPath = options.registry.resolve(options.fileId);
|
|
2750
2766
|
if (registeredPath === void 0) {
|
|
2751
|
-
throw new
|
|
2767
|
+
throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.SOURCE_NOT_FOUND);
|
|
2752
2768
|
}
|
|
2753
2769
|
const sourcePath = await assertInsideRoot(options.root, registeredPath);
|
|
2754
2770
|
if (!ALLOWED_EXTENSIONS.has(import_node_path5.default.extname(sourcePath).toLowerCase())) {
|
|
2755
|
-
throw new
|
|
2771
|
+
throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.SOURCE_NOT_FOUND);
|
|
2756
2772
|
}
|
|
2757
2773
|
let sourceStat;
|
|
2758
2774
|
try {
|
|
2759
2775
|
sourceStat = await (0, import_promises4.stat)(sourcePath);
|
|
2760
2776
|
} catch (error) {
|
|
2761
2777
|
if (isMissingFileError(error)) {
|
|
2762
|
-
throw new
|
|
2778
|
+
throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.SOURCE_NOT_FOUND, void 0, {
|
|
2763
2779
|
cause: error
|
|
2764
2780
|
});
|
|
2765
2781
|
}
|
|
2766
2782
|
throw error;
|
|
2767
2783
|
}
|
|
2768
2784
|
if (!sourceStat.isFile()) {
|
|
2769
|
-
throw new
|
|
2785
|
+
throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.SOURCE_NOT_FOUND);
|
|
2770
2786
|
}
|
|
2771
2787
|
if (sourceStat.size > MAX_SOURCE_FILE_BYTES) {
|
|
2772
|
-
throw new
|
|
2788
|
+
throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.SOURCE_TOO_LARGE);
|
|
2773
2789
|
}
|
|
2774
2790
|
return sourcePath;
|
|
2775
2791
|
}
|
|
@@ -2789,7 +2805,7 @@ async function readSourceContext(options) {
|
|
|
2789
2805
|
source = await (0, import_promises5.readFile)(sourcePath, "utf8");
|
|
2790
2806
|
} catch (error) {
|
|
2791
2807
|
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
|
2792
|
-
throw new
|
|
2808
|
+
throw new import_shared11.SpotPatchError(import_shared11.ERROR_CODES.SOURCE_NOT_FOUND, void 0, {
|
|
2793
2809
|
cause: error
|
|
2794
2810
|
});
|
|
2795
2811
|
}
|
|
@@ -2797,7 +2813,7 @@ async function readSourceContext(options) {
|
|
|
2797
2813
|
}
|
|
2798
2814
|
const lines = source.split(/\r?\n/);
|
|
2799
2815
|
if (options.request.line > lines.length) {
|
|
2800
|
-
throw new
|
|
2816
|
+
throw new import_shared11.SpotPatchError(import_shared11.ERROR_CODES.INVALID_REQUEST);
|
|
2801
2817
|
}
|
|
2802
2818
|
const extension = import_node_path6.default.extname(sourcePath).toLowerCase();
|
|
2803
2819
|
return extractCodeContext({
|
|
@@ -2815,9 +2831,9 @@ async function readSourceContext(options) {
|
|
|
2815
2831
|
// src/server/annotation-authorizer.ts
|
|
2816
2832
|
function sanitizePageContext(page) {
|
|
2817
2833
|
return Object.freeze({
|
|
2818
|
-
url: (0,
|
|
2819
|
-
pathname: (0,
|
|
2820
|
-
title: (0,
|
|
2834
|
+
url: (0, import_shared12.sanitizeUrl)(page.url, page.url),
|
|
2835
|
+
pathname: (0, import_shared12.redactSensitiveText)(page.pathname),
|
|
2836
|
+
title: (0, import_shared12.redactSensitiveText)(page.title),
|
|
2821
2837
|
viewportWidth: page.viewportWidth,
|
|
2822
2838
|
viewportHeight: page.viewportHeight,
|
|
2823
2839
|
devicePixelRatio: page.devicePixelRatio
|
|
@@ -2836,18 +2852,18 @@ function compactSourceRef(source) {
|
|
|
2836
2852
|
async function authorizeSourceRef(source, registry, root) {
|
|
2837
2853
|
const markerOrigin = source.origin === "jsx-host" || source.origin === "dom-ancestor";
|
|
2838
2854
|
if (markerOrigin && (source.fileId === void 0 || source.line === void 0 || source.column === void 0)) {
|
|
2839
|
-
throw new
|
|
2855
|
+
throw new import_shared12.SpotPatchError(import_shared12.ERROR_CODES.INVALID_REQUEST);
|
|
2840
2856
|
}
|
|
2841
2857
|
if (source.fileId === void 0) {
|
|
2842
2858
|
if (source.origin === "none" && source.relativePath !== void 0) {
|
|
2843
|
-
throw new
|
|
2859
|
+
throw new import_shared12.SpotPatchError(import_shared12.ERROR_CODES.INVALID_REQUEST);
|
|
2844
2860
|
}
|
|
2845
2861
|
return compactSourceRef(source);
|
|
2846
2862
|
}
|
|
2847
2863
|
const sourcePath = await resolveSourceFile({ fileId: source.fileId, registry, root });
|
|
2848
2864
|
const relativePath = import_node_path7.default.relative(await (0, import_promises6.realpath)(root), sourcePath).split(import_node_path7.default.sep).join("/");
|
|
2849
2865
|
if (source.relativePath !== void 0 && source.relativePath !== relativePath) {
|
|
2850
|
-
throw new
|
|
2866
|
+
throw new import_shared12.SpotPatchError(import_shared12.ERROR_CODES.INVALID_REQUEST);
|
|
2851
2867
|
}
|
|
2852
2868
|
return Object.freeze({ ...compactSourceRef(source), relativePath });
|
|
2853
2869
|
}
|
|
@@ -2883,7 +2899,7 @@ async function authorizeTarget(target, input) {
|
|
|
2883
2899
|
maxLines: input.options.budget.maxCodeLines
|
|
2884
2900
|
});
|
|
2885
2901
|
if (marker === void 0 && target.code !== void 0) {
|
|
2886
|
-
throw new
|
|
2902
|
+
throw new import_shared12.SpotPatchError(import_shared12.ERROR_CODES.INVALID_REQUEST);
|
|
2887
2903
|
}
|
|
2888
2904
|
const code = marker === void 0 ? void 0 : await readSourceContext({
|
|
2889
2905
|
request: marker,
|
|
@@ -2893,7 +2909,7 @@ async function authorizeTarget(target, input) {
|
|
|
2893
2909
|
maxLines: input.options.budget.maxCodeLines
|
|
2894
2910
|
});
|
|
2895
2911
|
if (target.code !== void 0 && target.code.relativePath !== code?.relativePath) {
|
|
2896
|
-
throw new
|
|
2912
|
+
throw new import_shared12.SpotPatchError(import_shared12.ERROR_CODES.INVALID_REQUEST);
|
|
2897
2913
|
}
|
|
2898
2914
|
return Object.freeze({
|
|
2899
2915
|
instruction: target.instruction,
|
|
@@ -2928,11 +2944,11 @@ async function authorizeTarget(target, input) {
|
|
|
2928
2944
|
async function authorizeAnnotation(input) {
|
|
2929
2945
|
const requestedTargets = input.annotation.targets;
|
|
2930
2946
|
if (requestedTargets.length > input.options.maxTargets) {
|
|
2931
|
-
throw new
|
|
2947
|
+
throw new import_shared12.SpotPatchError(import_shared12.ERROR_CODES.INVALID_REQUEST);
|
|
2932
2948
|
}
|
|
2933
2949
|
const identities = requestedTargets.map(targetIdentity);
|
|
2934
2950
|
if (new Set(identities).size !== identities.length) {
|
|
2935
|
-
throw new
|
|
2951
|
+
throw new import_shared12.SpotPatchError(import_shared12.ERROR_CODES.INVALID_REQUEST);
|
|
2936
2952
|
}
|
|
2937
2953
|
const targets = Object.freeze(
|
|
2938
2954
|
await Promise.all(requestedTargets.map((target) => authorizeTarget(target, input)))
|
|
@@ -2949,29 +2965,29 @@ async function authorizeAnnotation(input) {
|
|
|
2949
2965
|
|
|
2950
2966
|
// src/external-handoff/browser-http.ts
|
|
2951
2967
|
function matchExternalHandoffBrowserPath(path9) {
|
|
2952
|
-
if (path9 ===
|
|
2953
|
-
if (path9 ===
|
|
2954
|
-
if (path9 ===
|
|
2955
|
-
if (path9 ===
|
|
2968
|
+
if (path9 === import_shared13.SPOTPATCH_ENDPOINTS.externalHandoffCapability) return "capability";
|
|
2969
|
+
if (path9 === import_shared13.SPOTPATCH_ENDPOINTS.externalHandoffPublish) return "publish";
|
|
2970
|
+
if (path9 === import_shared13.SPOTPATCH_ENDPOINTS.externalHandoffStatus) return "status";
|
|
2971
|
+
if (path9 === import_shared13.SPOTPATCH_ENDPOINTS.externalHandoffResolveDelivery) {
|
|
2956
2972
|
return "resolve-delivery";
|
|
2957
2973
|
}
|
|
2958
2974
|
return void 0;
|
|
2959
2975
|
}
|
|
2960
2976
|
function requireService(options) {
|
|
2961
2977
|
if (!options.options.externalAgent.enabled || options.service === void 0) {
|
|
2962
|
-
throw new
|
|
2978
|
+
throw new import_shared13.SpotPatchError(import_shared13.ERROR_CODES.EXTERNAL_HANDOFF_DISABLED);
|
|
2963
2979
|
}
|
|
2964
2980
|
return options.service;
|
|
2965
2981
|
}
|
|
2966
2982
|
function remapAuthorizationError(error) {
|
|
2967
|
-
if (error instanceof
|
|
2968
|
-
if (error.code ===
|
|
2969
|
-
throw new
|
|
2983
|
+
if (error instanceof import_shared13.SpotPatchError) {
|
|
2984
|
+
if (error.code === import_shared13.ERROR_CODES.SOURCE_NOT_FOUND || error.code === import_shared13.ERROR_CODES.SOURCE_OUTSIDE_ROOT || error.code === import_shared13.ERROR_CODES.SOURCE_TOO_LARGE) {
|
|
2985
|
+
throw new import_shared13.SpotPatchError(import_shared13.ERROR_CODES.HANDOFF_SOURCE_STALE, void 0, {
|
|
2970
2986
|
cause: error
|
|
2971
2987
|
});
|
|
2972
2988
|
}
|
|
2973
|
-
if (error.code ===
|
|
2974
|
-
throw new
|
|
2989
|
+
if (error.code === import_shared13.ERROR_CODES.INVALID_REQUEST) {
|
|
2990
|
+
throw new import_shared13.SpotPatchError(import_shared13.ERROR_CODES.HANDOFF_VALIDATION_FAILED, void 0, {
|
|
2975
2991
|
cause: error
|
|
2976
2992
|
});
|
|
2977
2993
|
}
|
|
@@ -2980,38 +2996,38 @@ function remapAuthorizationError(error) {
|
|
|
2980
2996
|
}
|
|
2981
2997
|
async function handleExternalHandoffBrowserRequest(request, response, route, options, writeSuccess) {
|
|
2982
2998
|
if (request.method !== "POST") {
|
|
2983
|
-
throw new
|
|
2999
|
+
throw new import_shared13.SpotPatchError(import_shared13.ERROR_CODES.INVALID_REQUEST);
|
|
2984
3000
|
}
|
|
2985
3001
|
const service = requireService(options);
|
|
2986
3002
|
if (route === "capability") {
|
|
2987
|
-
const parsed2 =
|
|
3003
|
+
const parsed2 = import_shared13.externalHandoffCapabilityRequestSchema.safeParse(
|
|
2988
3004
|
await readJsonRequestBody(request)
|
|
2989
3005
|
);
|
|
2990
|
-
if (!parsed2.success) throw new
|
|
3006
|
+
if (!parsed2.success) throw new import_shared13.SpotPatchError(import_shared13.ERROR_CODES.INVALID_REQUEST);
|
|
2991
3007
|
writeSuccess(response, 200, service.capability());
|
|
2992
3008
|
return;
|
|
2993
3009
|
}
|
|
2994
3010
|
if (route === "status") {
|
|
2995
|
-
const parsed2 =
|
|
3011
|
+
const parsed2 = import_shared13.externalHandoffStatusRequestSchema.safeParse(
|
|
2996
3012
|
await readJsonRequestBody(request)
|
|
2997
3013
|
);
|
|
2998
|
-
if (!parsed2.success) throw new
|
|
3014
|
+
if (!parsed2.success) throw new import_shared13.SpotPatchError(import_shared13.ERROR_CODES.INVALID_REQUEST);
|
|
2999
3015
|
writeSuccess(response, 200, service.status(parsed2.data.cursor));
|
|
3000
3016
|
return;
|
|
3001
3017
|
}
|
|
3002
3018
|
if (route === "resolve-delivery") {
|
|
3003
|
-
const parsed2 =
|
|
3019
|
+
const parsed2 = import_shared13.externalHandoffResolveDeliveryRequestSchema.safeParse(
|
|
3004
3020
|
await readJsonRequestBody(request)
|
|
3005
3021
|
);
|
|
3006
|
-
if (!parsed2.success) throw new
|
|
3022
|
+
if (!parsed2.success) throw new import_shared13.SpotPatchError(import_shared13.ERROR_CODES.INVALID_REQUEST);
|
|
3007
3023
|
writeSuccess(response, 200, service.resolveDelivery(parsed2.data.cursor));
|
|
3008
3024
|
return;
|
|
3009
3025
|
}
|
|
3010
|
-
const parsed =
|
|
3011
|
-
await readJsonRequestBody(request,
|
|
3026
|
+
const parsed = import_shared13.externalHandoffPublishRequestSchema.safeParse(
|
|
3027
|
+
await readJsonRequestBody(request, import_shared13.EXTERNAL_HANDOFF_LIMITS.maximumPublishBodyBytes)
|
|
3012
3028
|
);
|
|
3013
3029
|
if (!parsed.success) {
|
|
3014
|
-
throw new
|
|
3030
|
+
throw new import_shared13.SpotPatchError(import_shared13.ERROR_CODES.HANDOFF_VALIDATION_FAILED);
|
|
3015
3031
|
}
|
|
3016
3032
|
const result = await service.publish(parsed.data, async (annotation) => {
|
|
3017
3033
|
try {
|
|
@@ -3028,11 +3044,157 @@ async function handleExternalHandoffBrowserRequest(request, response, route, opt
|
|
|
3028
3044
|
writeSuccess(response, result.replayed ? 200 : 201, result);
|
|
3029
3045
|
}
|
|
3030
3046
|
|
|
3031
|
-
// src/
|
|
3047
|
+
// src/external-agent/browser-http.ts
|
|
3032
3048
|
var import_shared14 = require("@spotpatch/shared");
|
|
3049
|
+
function matchExternalAgentBrowserPath(path9) {
|
|
3050
|
+
if (path9 === import_shared14.SPOTPATCH_ENDPOINTS.externalAgentControlStatus) return "status";
|
|
3051
|
+
if (path9 === import_shared14.SPOTPATCH_ENDPOINTS.externalAgentControlConnect) return "connect";
|
|
3052
|
+
if (path9 === import_shared14.SPOTPATCH_ENDPOINTS.externalAgentControlDisconnect) {
|
|
3053
|
+
return "disconnect";
|
|
3054
|
+
}
|
|
3055
|
+
if (path9 === import_shared14.SPOTPATCH_ENDPOINTS.externalAgentControlCancel) return "cancel";
|
|
3056
|
+
if (path9 === import_shared14.SPOTPATCH_ENDPOINTS.externalAgentEvents) return "events";
|
|
3057
|
+
if (path9 === import_shared14.SPOTPATCH_ENDPOINTS.externalAgentResult) return "result";
|
|
3058
|
+
return void 0;
|
|
3059
|
+
}
|
|
3060
|
+
function writeEvent(response, event) {
|
|
3061
|
+
response.write(`${JSON.stringify(import_shared14.externalAgentEventSchema.parse(event))}
|
|
3062
|
+
`);
|
|
3063
|
+
}
|
|
3064
|
+
function createExternalAgentBrowserController(port) {
|
|
3065
|
+
const streams = /* @__PURE__ */ new Set();
|
|
3066
|
+
let disposed = false;
|
|
3067
|
+
const handleEvents = async (request, response) => {
|
|
3068
|
+
const parsed = import_shared14.externalAgentEventsRequestSchema.safeParse(
|
|
3069
|
+
await readJsonRequestBody(request)
|
|
3070
|
+
);
|
|
3071
|
+
if (!parsed.success) {
|
|
3072
|
+
throw new import_shared14.SpotPatchError(import_shared14.ERROR_CODES.INVALID_REQUEST);
|
|
3073
|
+
}
|
|
3074
|
+
if (disposed || streams.size >= import_shared14.EXTERNAL_AGENT_CONTROL_LIMITS.maximumEventSubscribers) {
|
|
3075
|
+
throw new import_shared14.SpotPatchError(import_shared14.ERROR_CODES.BRIDGE_BUSY);
|
|
3076
|
+
}
|
|
3077
|
+
response.statusCode = 200;
|
|
3078
|
+
response.setHeader("Cache-Control", "no-store");
|
|
3079
|
+
response.setHeader("Content-Type", "application/x-ndjson; charset=utf-8");
|
|
3080
|
+
response.setHeader("X-Content-Type-Options", "nosniff");
|
|
3081
|
+
streams.add(response);
|
|
3082
|
+
const initial = import_shared14.externalAgentControlStatusSchema.parse(port.getStatus());
|
|
3083
|
+
writeEvent(response, { type: "status", data: initial });
|
|
3084
|
+
const unsubscribe = port.subscribe((status) => {
|
|
3085
|
+
if (!response.destroyed && status.sequence > (parsed.data.afterSequence ?? -1)) {
|
|
3086
|
+
writeEvent(response, { type: "status", data: status });
|
|
3087
|
+
}
|
|
3088
|
+
});
|
|
3089
|
+
const heartbeat = setInterval(() => {
|
|
3090
|
+
if (!response.destroyed) {
|
|
3091
|
+
const status = port.getStatus();
|
|
3092
|
+
writeEvent(response, {
|
|
3093
|
+
type: "heartbeat",
|
|
3094
|
+
sequence: status.sequence,
|
|
3095
|
+
emittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3096
|
+
});
|
|
3097
|
+
}
|
|
3098
|
+
}, import_shared14.EXTERNAL_AGENT_CONTROL_LIMITS.eventHeartbeatMs);
|
|
3099
|
+
heartbeat.unref();
|
|
3100
|
+
await new Promise((resolve) => {
|
|
3101
|
+
const close = () => {
|
|
3102
|
+
response.off("close", close);
|
|
3103
|
+
clearInterval(heartbeat);
|
|
3104
|
+
unsubscribe();
|
|
3105
|
+
streams.delete(response);
|
|
3106
|
+
resolve();
|
|
3107
|
+
};
|
|
3108
|
+
response.once("close", close);
|
|
3109
|
+
});
|
|
3110
|
+
};
|
|
3111
|
+
return Object.freeze({
|
|
3112
|
+
async handle(request, response, route, writeSuccess) {
|
|
3113
|
+
if (request.method !== "POST" || disposed) {
|
|
3114
|
+
throw new import_shared14.SpotPatchError(import_shared14.ERROR_CODES.INVALID_REQUEST);
|
|
3115
|
+
}
|
|
3116
|
+
if (route === "events") {
|
|
3117
|
+
await handleEvents(request, response);
|
|
3118
|
+
return;
|
|
3119
|
+
}
|
|
3120
|
+
if (route === "status") {
|
|
3121
|
+
const parsed2 = import_shared14.externalAgentControlStatusRequestSchema.safeParse(
|
|
3122
|
+
await readJsonRequestBody(request)
|
|
3123
|
+
);
|
|
3124
|
+
if (!parsed2.success) throw new import_shared14.SpotPatchError(import_shared14.ERROR_CODES.INVALID_REQUEST);
|
|
3125
|
+
writeSuccess(
|
|
3126
|
+
response,
|
|
3127
|
+
200,
|
|
3128
|
+
import_shared14.externalAgentControlStatusSchema.parse(port.getStatus())
|
|
3129
|
+
);
|
|
3130
|
+
return;
|
|
3131
|
+
}
|
|
3132
|
+
if (route === "connect") {
|
|
3133
|
+
const parsed2 = import_shared14.externalAgentControlConnectRequestSchema.safeParse(
|
|
3134
|
+
await readJsonRequestBody(request)
|
|
3135
|
+
);
|
|
3136
|
+
if (!parsed2.success) throw new import_shared14.SpotPatchError(import_shared14.ERROR_CODES.INVALID_REQUEST);
|
|
3137
|
+
const controller = new AbortController();
|
|
3138
|
+
response.once("close", () => {
|
|
3139
|
+
if (!response.writableEnded) controller.abort("browser-disconnected");
|
|
3140
|
+
});
|
|
3141
|
+
writeSuccess(
|
|
3142
|
+
response,
|
|
3143
|
+
200,
|
|
3144
|
+
import_shared14.externalAgentControlStatusSchema.parse(
|
|
3145
|
+
await port.connect(parsed2.data, controller.signal)
|
|
3146
|
+
)
|
|
3147
|
+
);
|
|
3148
|
+
return;
|
|
3149
|
+
}
|
|
3150
|
+
if (route === "disconnect") {
|
|
3151
|
+
const parsed2 = import_shared14.externalAgentControlDisconnectRequestSchema.safeParse(
|
|
3152
|
+
await readJsonRequestBody(request)
|
|
3153
|
+
);
|
|
3154
|
+
if (!parsed2.success) throw new import_shared14.SpotPatchError(import_shared14.ERROR_CODES.INVALID_REQUEST);
|
|
3155
|
+
writeSuccess(
|
|
3156
|
+
response,
|
|
3157
|
+
200,
|
|
3158
|
+
import_shared14.externalAgentControlStatusSchema.parse(await port.disconnect(parsed2.data))
|
|
3159
|
+
);
|
|
3160
|
+
return;
|
|
3161
|
+
}
|
|
3162
|
+
if (route === "cancel") {
|
|
3163
|
+
const parsed2 = import_shared14.externalAgentControlCancelRequestSchema.safeParse(
|
|
3164
|
+
await readJsonRequestBody(request)
|
|
3165
|
+
);
|
|
3166
|
+
if (!parsed2.success) throw new import_shared14.SpotPatchError(import_shared14.ERROR_CODES.INVALID_REQUEST);
|
|
3167
|
+
writeSuccess(
|
|
3168
|
+
response,
|
|
3169
|
+
200,
|
|
3170
|
+
import_shared14.externalAgentControlStatusSchema.parse(await port.cancel(parsed2.data))
|
|
3171
|
+
);
|
|
3172
|
+
return;
|
|
3173
|
+
}
|
|
3174
|
+
const parsed = import_shared14.externalAgentResultRequestSchema.safeParse(
|
|
3175
|
+
await readJsonRequestBody(request)
|
|
3176
|
+
);
|
|
3177
|
+
if (!parsed.success) throw new import_shared14.SpotPatchError(import_shared14.ERROR_CODES.INVALID_REQUEST);
|
|
3178
|
+
const result = port.getResult(parsed.data.revision);
|
|
3179
|
+
if (result === void 0) {
|
|
3180
|
+
throw new import_shared14.SpotPatchError(import_shared14.ERROR_CODES.HANDOFF_NOT_FOUND);
|
|
3181
|
+
}
|
|
3182
|
+
writeSuccess(response, 200, import_shared14.externalAgentManagedResultSchema.parse(result));
|
|
3183
|
+
},
|
|
3184
|
+
dispose() {
|
|
3185
|
+
if (disposed) return;
|
|
3186
|
+
disposed = true;
|
|
3187
|
+
for (const response of streams) response.end();
|
|
3188
|
+
streams.clear();
|
|
3189
|
+
}
|
|
3190
|
+
});
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
// src/server/agent-http.ts
|
|
3194
|
+
var import_shared16 = require("@spotpatch/shared");
|
|
3033
3195
|
|
|
3034
3196
|
// src/server/agent-request.ts
|
|
3035
|
-
var
|
|
3197
|
+
var import_shared15 = require("@spotpatch/shared");
|
|
3036
3198
|
async function authorizeAgentJobRequest(input) {
|
|
3037
3199
|
const annotation = await authorizeAnnotation({
|
|
3038
3200
|
annotation: input.request.annotation,
|
|
@@ -3069,16 +3231,16 @@ var EVENT_STREAM_END_STATUSES = /* @__PURE__ */ new Set([
|
|
|
3069
3231
|
"failed"
|
|
3070
3232
|
]);
|
|
3071
3233
|
function matchAgentRequestPath(path9) {
|
|
3072
|
-
if (path9 ===
|
|
3234
|
+
if (path9 === import_shared16.SPOTPATCH_ENDPOINTS.agentCapability) {
|
|
3073
3235
|
return Object.freeze({ kind: "capability" });
|
|
3074
3236
|
}
|
|
3075
|
-
if (path9 ===
|
|
3237
|
+
if (path9 === import_shared16.SPOTPATCH_ENDPOINTS.agentWorkspaceHealth) {
|
|
3076
3238
|
return Object.freeze({ kind: "workspace-health" });
|
|
3077
3239
|
}
|
|
3078
|
-
if (path9 ===
|
|
3240
|
+
if (path9 === import_shared16.SPOTPATCH_ENDPOINTS.agentJobs) {
|
|
3079
3241
|
return Object.freeze({ kind: "create-job" });
|
|
3080
3242
|
}
|
|
3081
|
-
const prefix = `${
|
|
3243
|
+
const prefix = `${import_shared16.SPOTPATCH_ENDPOINTS.agentJobs}/`;
|
|
3082
3244
|
if (!path9.startsWith(prefix)) {
|
|
3083
3245
|
return void 0;
|
|
3084
3246
|
}
|
|
@@ -3096,7 +3258,7 @@ function matchAgentRequestPath(path9) {
|
|
|
3096
3258
|
}
|
|
3097
3259
|
function requireAgentManager(options) {
|
|
3098
3260
|
if (options.agentManager === void 0 || options.options.ai === false) {
|
|
3099
|
-
throw new
|
|
3261
|
+
throw new import_shared16.SpotPatchError(import_shared16.ERROR_CODES.AI_DISABLED);
|
|
3100
3262
|
}
|
|
3101
3263
|
return options.agentManager;
|
|
3102
3264
|
}
|
|
@@ -3149,13 +3311,13 @@ function streamAgentJobEvents(response, manager, jobId) {
|
|
|
3149
3311
|
}
|
|
3150
3312
|
async function handleCapability(request, response, options, writeSuccess) {
|
|
3151
3313
|
if (request.method !== "POST") {
|
|
3152
|
-
throw new
|
|
3314
|
+
throw new import_shared16.SpotPatchError(import_shared16.ERROR_CODES.INVALID_REQUEST);
|
|
3153
3315
|
}
|
|
3154
|
-
const parsed =
|
|
3316
|
+
const parsed = import_shared16.agentCapabilityRequestSchema.safeParse(
|
|
3155
3317
|
await readJsonRequestBody(request)
|
|
3156
3318
|
);
|
|
3157
3319
|
if (!parsed.success) {
|
|
3158
|
-
throw new
|
|
3320
|
+
throw new import_shared16.SpotPatchError(import_shared16.ERROR_CODES.INVALID_REQUEST);
|
|
3159
3321
|
}
|
|
3160
3322
|
const controller = new AbortController();
|
|
3161
3323
|
const abort = () => {
|
|
@@ -3174,13 +3336,13 @@ async function handleCapability(request, response, options, writeSuccess) {
|
|
|
3174
3336
|
}
|
|
3175
3337
|
async function handleCreateJob(request, response, options, writeSuccess) {
|
|
3176
3338
|
if (request.method !== "POST") {
|
|
3177
|
-
throw new
|
|
3339
|
+
throw new import_shared16.SpotPatchError(import_shared16.ERROR_CODES.INVALID_REQUEST);
|
|
3178
3340
|
}
|
|
3179
|
-
const parsed =
|
|
3341
|
+
const parsed = import_shared16.agentJobCreateRequestSchema.safeParse(
|
|
3180
3342
|
await readJsonRequestBody(request, MAX_AGENT_REQUEST_BODY_BYTES)
|
|
3181
3343
|
);
|
|
3182
3344
|
if (!parsed.success) {
|
|
3183
|
-
throw new
|
|
3345
|
+
throw new import_shared16.SpotPatchError(import_shared16.ERROR_CODES.INVALID_REQUEST);
|
|
3184
3346
|
}
|
|
3185
3347
|
const authorizedRequest = await authorizeAgentJobRequest({
|
|
3186
3348
|
request: parsed.data,
|
|
@@ -3193,13 +3355,13 @@ async function handleCreateJob(request, response, options, writeSuccess) {
|
|
|
3193
3355
|
}
|
|
3194
3356
|
async function handleWorkspaceHealth(request, response, options, writeSuccess) {
|
|
3195
3357
|
if (request.method !== "POST") {
|
|
3196
|
-
throw new
|
|
3358
|
+
throw new import_shared16.SpotPatchError(import_shared16.ERROR_CODES.INVALID_REQUEST);
|
|
3197
3359
|
}
|
|
3198
|
-
const parsed =
|
|
3360
|
+
const parsed = import_shared16.agentWorkspaceHealthRequestSchema.safeParse(
|
|
3199
3361
|
await readJsonRequestBody(request)
|
|
3200
3362
|
);
|
|
3201
3363
|
if (!parsed.success) {
|
|
3202
|
-
throw new
|
|
3364
|
+
throw new import_shared16.SpotPatchError(import_shared16.ERROR_CODES.INVALID_REQUEST);
|
|
3203
3365
|
}
|
|
3204
3366
|
const controller = new AbortController();
|
|
3205
3367
|
const abort = () => {
|
|
@@ -3216,13 +3378,13 @@ async function handleWorkspaceHealth(request, response, options, writeSuccess) {
|
|
|
3216
3378
|
async function handleJobAction(request, response, options, route, writeSuccess) {
|
|
3217
3379
|
const manager = requireAgentManager(options);
|
|
3218
3380
|
if (request.method !== "POST") {
|
|
3219
|
-
throw new
|
|
3381
|
+
throw new import_shared16.SpotPatchError(import_shared16.ERROR_CODES.INVALID_REQUEST);
|
|
3220
3382
|
}
|
|
3221
|
-
const parsed =
|
|
3383
|
+
const parsed = import_shared16.agentJobActionRequestSchema.safeParse(
|
|
3222
3384
|
await readJsonRequestBody(request)
|
|
3223
3385
|
);
|
|
3224
3386
|
if (!parsed.success) {
|
|
3225
|
-
throw new
|
|
3387
|
+
throw new import_shared16.SpotPatchError(import_shared16.ERROR_CODES.INVALID_REQUEST);
|
|
3226
3388
|
}
|
|
3227
3389
|
if (route.action === "events") {
|
|
3228
3390
|
streamAgentJobEvents(response, manager, route.jobId);
|
|
@@ -3339,25 +3501,25 @@ var launchConfiguredEditor = createEditorLauncher();
|
|
|
3339
3501
|
// src/server/data-flow-http.ts
|
|
3340
3502
|
var import_node_crypto9 = require("crypto");
|
|
3341
3503
|
var import_analyzer = require("@spotpatch/analyzer");
|
|
3342
|
-
var
|
|
3504
|
+
var import_shared17 = require("@spotpatch/shared");
|
|
3343
3505
|
function envelopeBytes(report) {
|
|
3344
3506
|
return Buffer.byteLength(JSON.stringify({ ok: true, data: report }), "utf8");
|
|
3345
3507
|
}
|
|
3346
3508
|
function limitDataFlowReportToBytes(report, maximumBytes) {
|
|
3347
|
-
const structurallyLimited = (0,
|
|
3509
|
+
const structurallyLimited = (0, import_shared17.limitDataFlowReportCollections)(report);
|
|
3348
3510
|
if (envelopeBytes(structurallyLimited) <= maximumBytes) {
|
|
3349
3511
|
return structurallyLimited;
|
|
3350
3512
|
}
|
|
3351
|
-
let limited = (0,
|
|
3513
|
+
let limited = (0, import_shared17.limitDataFlowReportCollections)(structurallyLimited, {
|
|
3352
3514
|
forceTruncation: true,
|
|
3353
3515
|
maximumDependencies: 0,
|
|
3354
3516
|
truncatedBy: "bytes"
|
|
3355
3517
|
});
|
|
3356
3518
|
if (envelopeBytes(limited) > maximumBytes) {
|
|
3357
|
-
throw new
|
|
3519
|
+
throw new import_shared17.SpotPatchError(import_shared17.ERROR_CODES.INTERNAL_ERROR);
|
|
3358
3520
|
}
|
|
3359
3521
|
for (let maximumDependencies = 1; maximumDependencies <= structurallyLimited.dependencies.length; maximumDependencies += 1) {
|
|
3360
|
-
const candidate = (0,
|
|
3522
|
+
const candidate = (0, import_shared17.limitDataFlowReportCollections)(structurallyLimited, {
|
|
3361
3523
|
forceTruncation: true,
|
|
3362
3524
|
maximumDependencies,
|
|
3363
3525
|
truncatedBy: "bytes"
|
|
@@ -3383,7 +3545,7 @@ async function analyzeTarget(request, analyzer, options) {
|
|
|
3383
3545
|
request.componentSourceId
|
|
3384
3546
|
);
|
|
3385
3547
|
if (anchor?.sourceVersion !== request.sourceVersion) {
|
|
3386
|
-
throw new
|
|
3548
|
+
throw new import_shared17.SpotPatchError(import_shared17.ERROR_CODES.DATA_FLOW_SOURCE_STALE);
|
|
3387
3549
|
}
|
|
3388
3550
|
return anchor;
|
|
3389
3551
|
}
|
|
@@ -3400,7 +3562,7 @@ async function analyzeTarget(request, analyzer, options) {
|
|
|
3400
3562
|
column: resolvedRequest.column
|
|
3401
3563
|
});
|
|
3402
3564
|
if (resolvedRequest.sourceVersion !== void 0 && resolvedRequest.sourceVersion !== report.component.source.sourceVersion) {
|
|
3403
|
-
throw new
|
|
3565
|
+
throw new import_shared17.SpotPatchError(import_shared17.ERROR_CODES.DATA_FLOW_SOURCE_STALE);
|
|
3404
3566
|
}
|
|
3405
3567
|
return limitDataFlowReportToBytes(
|
|
3406
3568
|
report,
|
|
@@ -3409,31 +3571,31 @@ async function analyzeTarget(request, analyzer, options) {
|
|
|
3409
3571
|
}
|
|
3410
3572
|
function requireAnalyzer(analyzer) {
|
|
3411
3573
|
if (analyzer === void 0) {
|
|
3412
|
-
throw new
|
|
3574
|
+
throw new import_shared17.SpotPatchError(import_shared17.ERROR_CODES.DATA_FLOW_DISABLED);
|
|
3413
3575
|
}
|
|
3414
3576
|
return analyzer;
|
|
3415
3577
|
}
|
|
3416
3578
|
async function handleComponentDataFlowReport(request, analyzer, options) {
|
|
3417
|
-
const parsed =
|
|
3579
|
+
const parsed = import_shared17.dataFlowComponentReportRequestSchema.safeParse(
|
|
3418
3580
|
await readJsonRequestBody(
|
|
3419
3581
|
request,
|
|
3420
3582
|
options.options.dataFlow.limits.protocolRequestMaxBytes
|
|
3421
3583
|
)
|
|
3422
3584
|
);
|
|
3423
3585
|
if (!parsed.success) {
|
|
3424
|
-
throw new
|
|
3586
|
+
throw new import_shared17.SpotPatchError(import_shared17.ERROR_CODES.INVALID_REQUEST);
|
|
3425
3587
|
}
|
|
3426
3588
|
return analyzeTarget(parsed.data, requireAnalyzer(analyzer), options);
|
|
3427
3589
|
}
|
|
3428
3590
|
async function handlePageDataFlowReport(request, analyzer, options) {
|
|
3429
|
-
const parsed =
|
|
3591
|
+
const parsed = import_shared17.dataFlowPageReportRequestSchema.safeParse(
|
|
3430
3592
|
await readJsonRequestBody(
|
|
3431
3593
|
request,
|
|
3432
3594
|
options.options.dataFlow.limits.protocolRequestMaxBytes
|
|
3433
3595
|
)
|
|
3434
3596
|
);
|
|
3435
3597
|
if (!parsed.success) {
|
|
3436
|
-
throw new
|
|
3598
|
+
throw new import_shared17.SpotPatchError(import_shared17.ERROR_CODES.INVALID_REQUEST);
|
|
3437
3599
|
}
|
|
3438
3600
|
const activeAnalyzer = requireAnalyzer(analyzer);
|
|
3439
3601
|
const componentReports = await Promise.all(
|
|
@@ -3460,7 +3622,7 @@ async function handlePageDataFlowReport(request, analyzer, options) {
|
|
|
3460
3622
|
const reportId = `page_${(0, import_node_crypto9.createHash)("sha256").update(componentReports.map((report2) => report2.reportId).join("\0")).digest("base64url").slice(0, 22)}`;
|
|
3461
3623
|
const complete = componentReports.every((report2) => report2.completeness.complete);
|
|
3462
3624
|
const report = Object.freeze({
|
|
3463
|
-
schemaVersion:
|
|
3625
|
+
schemaVersion: import_shared17.DATA_FLOW_SCHEMA_VERSION,
|
|
3464
3626
|
reportId,
|
|
3465
3627
|
baseline: Object.freeze({
|
|
3466
3628
|
registryEpoch: options.session.id,
|
|
@@ -3506,7 +3668,7 @@ async function handlePageDataFlowReport(request, analyzer, options) {
|
|
|
3506
3668
|
// src/server/request-security.ts
|
|
3507
3669
|
var import_node_crypto10 = require("crypto");
|
|
3508
3670
|
var import_node_net = require("net");
|
|
3509
|
-
var
|
|
3671
|
+
var import_shared18 = require("@spotpatch/shared");
|
|
3510
3672
|
function getSingleHeader(request, name) {
|
|
3511
3673
|
const value = request.headers[name.toLowerCase()];
|
|
3512
3674
|
return Array.isArray(value) ? value[0] : value;
|
|
@@ -3551,32 +3713,32 @@ function parseOrigin(value) {
|
|
|
3551
3713
|
}
|
|
3552
3714
|
}
|
|
3553
3715
|
function assertRequestAuthorized(request, options) {
|
|
3554
|
-
const actualToken = getSingleHeader(request,
|
|
3716
|
+
const actualToken = getSingleHeader(request, import_shared18.SPOTPATCH_TOKEN_HEADER);
|
|
3555
3717
|
if (!tokensMatch2(actualToken, options.sessionToken)) {
|
|
3556
|
-
throw new
|
|
3718
|
+
throw new import_shared18.SpotPatchError(import_shared18.ERROR_CODES.INVALID_TOKEN);
|
|
3557
3719
|
}
|
|
3558
3720
|
const hostHeader = getSingleHeader(request, "host");
|
|
3559
3721
|
const originHeader = getSingleHeader(request, "origin");
|
|
3560
3722
|
const host = hostHeader === void 0 ? void 0 : parseHost(hostHeader);
|
|
3561
3723
|
const origin = originHeader === void 0 ? void 0 : parseOrigin(originHeader);
|
|
3562
3724
|
if (host === void 0 || origin === void 0) {
|
|
3563
|
-
throw new
|
|
3725
|
+
throw new import_shared18.SpotPatchError(import_shared18.ERROR_CODES.ORIGIN_NOT_ALLOWED);
|
|
3564
3726
|
}
|
|
3565
3727
|
const hostIsLoopback = isLoopbackHostname(host.hostname);
|
|
3566
3728
|
const originIsLoopback = isLoopbackHostname(origin.hostname);
|
|
3567
3729
|
if (!options.allowLan) {
|
|
3568
3730
|
if (!hostIsLoopback || !originIsLoopback) {
|
|
3569
|
-
throw new
|
|
3731
|
+
throw new import_shared18.SpotPatchError(import_shared18.ERROR_CODES.ORIGIN_NOT_ALLOWED);
|
|
3570
3732
|
}
|
|
3571
3733
|
return;
|
|
3572
3734
|
}
|
|
3573
3735
|
if (!originIsLoopback && origin.host.toLowerCase() !== host.host.toLowerCase()) {
|
|
3574
|
-
throw new
|
|
3736
|
+
throw new import_shared18.SpotPatchError(import_shared18.ERROR_CODES.ORIGIN_NOT_ALLOWED);
|
|
3575
3737
|
}
|
|
3576
3738
|
}
|
|
3577
3739
|
|
|
3578
3740
|
// src/server/runtime-bootstrap.ts
|
|
3579
|
-
var
|
|
3741
|
+
var import_shared19 = require("@spotpatch/shared");
|
|
3580
3742
|
function getSingleHeader2(request, name) {
|
|
3581
3743
|
const value = request.headers[name.toLowerCase()];
|
|
3582
3744
|
return Array.isArray(value) ? value[0] : value;
|
|
@@ -3591,7 +3753,7 @@ function resolveRuntimeBootstrapOptions(options) {
|
|
|
3591
3753
|
if (expectedOrigin.origin !== options.expectedOrigin || expectedOrigin.protocol !== "http:" || !isLoopbackHostname(expectedOrigin.hostname)) {
|
|
3592
3754
|
throw new TypeError("The SpotPatch bootstrap origin must be a loopback origin.");
|
|
3593
3755
|
}
|
|
3594
|
-
const parsedConfig =
|
|
3756
|
+
const parsedConfig = import_shared19.runtimeConfigSchema.safeParse(options.runtimeConfig);
|
|
3595
3757
|
if (!parsedConfig.success) {
|
|
3596
3758
|
throw new TypeError("The SpotPatch Runtime configuration is invalid.");
|
|
3597
3759
|
}
|
|
@@ -3603,7 +3765,7 @@ function resolveRuntimeBootstrapOptions(options) {
|
|
|
3603
3765
|
function assertRuntimeBootstrapRequest(request, expectedOrigin) {
|
|
3604
3766
|
const contentType = getSingleHeader2(request, "content-type")?.split(";", 1)[0]?.trim().toLowerCase();
|
|
3605
3767
|
if (request.method !== "POST" || contentType !== "application/json") {
|
|
3606
|
-
throw new
|
|
3768
|
+
throw new import_shared19.SpotPatchError(import_shared19.ERROR_CODES.INVALID_REQUEST);
|
|
3607
3769
|
}
|
|
3608
3770
|
const host = getSingleHeader2(request, "host");
|
|
3609
3771
|
let hostIsLoopback = false;
|
|
@@ -3615,132 +3777,132 @@ function assertRuntimeBootstrapRequest(request, expectedOrigin) {
|
|
|
3615
3777
|
}
|
|
3616
3778
|
}
|
|
3617
3779
|
if (!hostIsLoopback || getSingleHeader2(request, "origin") !== expectedOrigin || getSingleHeader2(request, "sec-fetch-site") !== "same-origin") {
|
|
3618
|
-
throw new
|
|
3780
|
+
throw new import_shared19.SpotPatchError(import_shared19.ERROR_CODES.ORIGIN_NOT_ALLOWED);
|
|
3619
3781
|
}
|
|
3620
3782
|
}
|
|
3621
3783
|
async function readRuntimeBootstrap(request, options) {
|
|
3622
3784
|
assertRuntimeBootstrapRequest(request, options.expectedOrigin);
|
|
3623
|
-
const parsedBody =
|
|
3785
|
+
const parsedBody = import_shared19.runtimeBootstrapRequestSchema.safeParse(
|
|
3624
3786
|
await readJsonRequestBody(request)
|
|
3625
3787
|
);
|
|
3626
3788
|
if (!parsedBody.success) {
|
|
3627
|
-
throw new
|
|
3789
|
+
throw new import_shared19.SpotPatchError(import_shared19.ERROR_CODES.INVALID_REQUEST);
|
|
3628
3790
|
}
|
|
3629
3791
|
return options.runtimeConfig;
|
|
3630
3792
|
}
|
|
3631
3793
|
|
|
3632
3794
|
// src/server/middleware.ts
|
|
3633
3795
|
var STATUS_BY_ERROR = Object.freeze({
|
|
3634
|
-
[
|
|
3635
|
-
[
|
|
3636
|
-
[
|
|
3637
|
-
[
|
|
3638
|
-
[
|
|
3639
|
-
[
|
|
3640
|
-
[
|
|
3641
|
-
[
|
|
3642
|
-
[
|
|
3643
|
-
[
|
|
3644
|
-
[
|
|
3645
|
-
[
|
|
3646
|
-
[
|
|
3647
|
-
[
|
|
3648
|
-
[
|
|
3649
|
-
[
|
|
3650
|
-
[
|
|
3651
|
-
[
|
|
3652
|
-
[
|
|
3653
|
-
[
|
|
3654
|
-
[
|
|
3655
|
-
[
|
|
3656
|
-
[
|
|
3657
|
-
[
|
|
3658
|
-
[
|
|
3659
|
-
[
|
|
3660
|
-
[
|
|
3661
|
-
[
|
|
3662
|
-
[
|
|
3663
|
-
[
|
|
3664
|
-
[
|
|
3665
|
-
[
|
|
3666
|
-
[
|
|
3667
|
-
[
|
|
3668
|
-
[
|
|
3669
|
-
[
|
|
3670
|
-
[
|
|
3671
|
-
[
|
|
3672
|
-
[
|
|
3673
|
-
[
|
|
3674
|
-
[
|
|
3675
|
-
[
|
|
3676
|
-
[
|
|
3677
|
-
[
|
|
3678
|
-
[
|
|
3679
|
-
[
|
|
3680
|
-
[
|
|
3681
|
-
[
|
|
3682
|
-
[
|
|
3683
|
-
[
|
|
3684
|
-
[
|
|
3685
|
-
[
|
|
3686
|
-
[
|
|
3687
|
-
[
|
|
3796
|
+
[import_shared20.ERROR_CODES.INVALID_REQUEST]: 400,
|
|
3797
|
+
[import_shared20.ERROR_CODES.INVALID_TOKEN]: 401,
|
|
3798
|
+
[import_shared20.ERROR_CODES.ORIGIN_NOT_ALLOWED]: 403,
|
|
3799
|
+
[import_shared20.ERROR_CODES.SOURCE_NOT_FOUND]: 404,
|
|
3800
|
+
[import_shared20.ERROR_CODES.SOURCE_OUTSIDE_ROOT]: 403,
|
|
3801
|
+
[import_shared20.ERROR_CODES.SOURCE_TOO_LARGE]: 413,
|
|
3802
|
+
[import_shared20.ERROR_CODES.EDITOR_OPEN_FAILED]: 500,
|
|
3803
|
+
[import_shared20.ERROR_CODES.DATA_FLOW_DISABLED]: 404,
|
|
3804
|
+
[import_shared20.ERROR_CODES.DATA_FLOW_SOURCE_STALE]: 409,
|
|
3805
|
+
[import_shared20.ERROR_CODES.DATA_FLOW_ANALYSIS_CANCELLED]: 409,
|
|
3806
|
+
[import_shared20.ERROR_CODES.AI_DISABLED]: 404,
|
|
3807
|
+
[import_shared20.ERROR_CODES.PROVIDER_NOT_CONFIGURED]: 503,
|
|
3808
|
+
[import_shared20.ERROR_CODES.PROVIDER_AUTH_FAILED]: 502,
|
|
3809
|
+
[import_shared20.ERROR_CODES.PROVIDER_PROTOCOL_UNSUPPORTED]: 502,
|
|
3810
|
+
[import_shared20.ERROR_CODES.MODEL_NOT_ALLOWED]: 400,
|
|
3811
|
+
[import_shared20.ERROR_CODES.MODEL_TOOL_CALL_UNSUPPORTED]: 422,
|
|
3812
|
+
[import_shared20.ERROR_CODES.PROVIDER_RATE_LIMITED]: 429,
|
|
3813
|
+
[import_shared20.ERROR_CODES.AGENT_BUSY]: 409,
|
|
3814
|
+
[import_shared20.ERROR_CODES.AGENT_LIMIT_EXCEEDED]: 413,
|
|
3815
|
+
[import_shared20.ERROR_CODES.AGENT_CANCELLED]: 409,
|
|
3816
|
+
[import_shared20.ERROR_CODES.EXTERNAL_HANDOFF_DISABLED]: 404,
|
|
3817
|
+
[import_shared20.ERROR_CODES.EXTERNAL_HANDOFF_UNAVAILABLE]: 503,
|
|
3818
|
+
[import_shared20.ERROR_CODES.HANDOFF_VALIDATION_FAILED]: 422,
|
|
3819
|
+
[import_shared20.ERROR_CODES.HANDOFF_SOURCE_STALE]: 409,
|
|
3820
|
+
[import_shared20.ERROR_CODES.HANDOFF_NOT_FOUND]: 404,
|
|
3821
|
+
[import_shared20.ERROR_CODES.HANDOFF_EXPIRED]: 410,
|
|
3822
|
+
[import_shared20.ERROR_CODES.HANDOFF_CURSOR_INVALID]: 409,
|
|
3823
|
+
[import_shared20.ERROR_CODES.HANDOFF_RESPONSE_TOO_LARGE]: 413,
|
|
3824
|
+
[import_shared20.ERROR_CODES.BRIDGE_UNAUTHORIZED]: 401,
|
|
3825
|
+
[import_shared20.ERROR_CODES.BRIDGE_PROTOCOL_MISMATCH]: 409,
|
|
3826
|
+
[import_shared20.ERROR_CODES.BRIDGE_BUSY]: 429,
|
|
3827
|
+
[import_shared20.ERROR_CODES.EXTERNAL_AGENT_BUSY]: 409,
|
|
3828
|
+
[import_shared20.ERROR_CODES.ACTIVE_ADAPTER_CONFLICT]: 409,
|
|
3829
|
+
[import_shared20.ERROR_CODES.ACTIVE_ADAPTER_LEASE_INVALID]: 409,
|
|
3830
|
+
[import_shared20.ERROR_CODES.ACTIVE_DISPATCH_INVALID]: 409,
|
|
3831
|
+
[import_shared20.ERROR_CODES.SESSION_NOT_FOUND]: 404,
|
|
3832
|
+
[import_shared20.ERROR_CODES.SESSION_AMBIGUOUS]: 409,
|
|
3833
|
+
[import_shared20.ERROR_CODES.SESSION_CLOSED]: 410,
|
|
3834
|
+
[import_shared20.ERROR_CODES.WORKTREE_DIRTY]: 409,
|
|
3835
|
+
[import_shared20.ERROR_CODES.WORKTREE_NOT_REPOSITORY]: 409,
|
|
3836
|
+
[import_shared20.ERROR_CODES.WORKTREE_OPERATION_IN_PROGRESS]: 409,
|
|
3837
|
+
[import_shared20.ERROR_CODES.WORKTREE_CONFLICTED]: 409,
|
|
3838
|
+
[import_shared20.ERROR_CODES.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: 413,
|
|
3839
|
+
[import_shared20.ERROR_CODES.WORKTREE_UNTRACKED_UNSUPPORTED]: 409,
|
|
3840
|
+
[import_shared20.ERROR_CODES.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: 409,
|
|
3841
|
+
[import_shared20.ERROR_CODES.TOOL_DENIED]: 403,
|
|
3842
|
+
[import_shared20.ERROR_CODES.TOOL_INPUT_INVALID]: 422,
|
|
3843
|
+
[import_shared20.ERROR_CODES.TOOL_ARGUMENTS_INVALID]: 422,
|
|
3844
|
+
[import_shared20.ERROR_CODES.TOOL_CALL_ID_CONFLICT]: 422,
|
|
3845
|
+
[import_shared20.ERROR_CODES.TOOL_PATH_DENIED]: 403,
|
|
3846
|
+
[import_shared20.ERROR_CODES.PATCH_REJECTED]: 422,
|
|
3847
|
+
[import_shared20.ERROR_CODES.VALIDATION_FAILED]: 422,
|
|
3848
|
+
[import_shared20.ERROR_CODES.APPLY_CONFLICT]: 409,
|
|
3849
|
+
[import_shared20.ERROR_CODES.INTERNAL_ERROR]: 500
|
|
3688
3850
|
});
|
|
3689
3851
|
var PUBLIC_MESSAGES = Object.freeze({
|
|
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
|
-
[
|
|
3743
|
-
[
|
|
3852
|
+
[import_shared20.ERROR_CODES.INVALID_REQUEST]: "The request is invalid.",
|
|
3853
|
+
[import_shared20.ERROR_CODES.INVALID_TOKEN]: "The session token is invalid.",
|
|
3854
|
+
[import_shared20.ERROR_CODES.ORIGIN_NOT_ALLOWED]: "The request origin is not allowed.",
|
|
3855
|
+
[import_shared20.ERROR_CODES.SOURCE_NOT_FOUND]: "The source file is unavailable.",
|
|
3856
|
+
[import_shared20.ERROR_CODES.SOURCE_OUTSIDE_ROOT]: "The source file is outside the project root.",
|
|
3857
|
+
[import_shared20.ERROR_CODES.SOURCE_TOO_LARGE]: "The source file exceeds the size limit.",
|
|
3858
|
+
[import_shared20.ERROR_CODES.EDITOR_OPEN_FAILED]: "The editor request could not be started.",
|
|
3859
|
+
[import_shared20.ERROR_CODES.DATA_FLOW_DISABLED]: "Component data-flow analysis is not enabled.",
|
|
3860
|
+
[import_shared20.ERROR_CODES.DATA_FLOW_SOURCE_STALE]: "The selected source version is stale.",
|
|
3861
|
+
[import_shared20.ERROR_CODES.DATA_FLOW_ANALYSIS_CANCELLED]: "The data-flow analysis was cancelled.",
|
|
3862
|
+
[import_shared20.ERROR_CODES.AI_DISABLED]: "AI execution is not enabled.",
|
|
3863
|
+
[import_shared20.ERROR_CODES.PROVIDER_NOT_CONFIGURED]: "The AI provider is unavailable.",
|
|
3864
|
+
[import_shared20.ERROR_CODES.PROVIDER_AUTH_FAILED]: "The AI provider rejected authentication.",
|
|
3865
|
+
[import_shared20.ERROR_CODES.PROVIDER_PROTOCOL_UNSUPPORTED]: "The AI provider protocol is unsupported.",
|
|
3866
|
+
[import_shared20.ERROR_CODES.MODEL_NOT_ALLOWED]: "The selected model is not allowed.",
|
|
3867
|
+
[import_shared20.ERROR_CODES.MODEL_TOOL_CALL_UNSUPPORTED]: "The selected model cannot run SpotPatch tools.",
|
|
3868
|
+
[import_shared20.ERROR_CODES.PROVIDER_RATE_LIMITED]: "The AI provider is rate limited.",
|
|
3869
|
+
[import_shared20.ERROR_CODES.AGENT_BUSY]: "Another Agent job is already running.",
|
|
3870
|
+
[import_shared20.ERROR_CODES.AGENT_LIMIT_EXCEEDED]: "The Agent job exceeded a safety limit.",
|
|
3871
|
+
[import_shared20.ERROR_CODES.AGENT_CANCELLED]: "The Agent job was cancelled.",
|
|
3872
|
+
[import_shared20.ERROR_CODES.EXTERNAL_HANDOFF_DISABLED]: "External Agent handoff is not enabled.",
|
|
3873
|
+
[import_shared20.ERROR_CODES.EXTERNAL_HANDOFF_UNAVAILABLE]: "External Agent handoff is temporarily unavailable.",
|
|
3874
|
+
[import_shared20.ERROR_CODES.HANDOFF_VALIDATION_FAILED]: "The handoff content is invalid.",
|
|
3875
|
+
[import_shared20.ERROR_CODES.HANDOFF_SOURCE_STALE]: "The selected source is stale.",
|
|
3876
|
+
[import_shared20.ERROR_CODES.HANDOFF_NOT_FOUND]: "No current handoff is available.",
|
|
3877
|
+
[import_shared20.ERROR_CODES.HANDOFF_EXPIRED]: "The handoff has expired.",
|
|
3878
|
+
[import_shared20.ERROR_CODES.HANDOFF_CURSOR_INVALID]: "The handoff cursor is invalid.",
|
|
3879
|
+
[import_shared20.ERROR_CODES.HANDOFF_RESPONSE_TOO_LARGE]: "The handoff exceeds the size limit.",
|
|
3880
|
+
[import_shared20.ERROR_CODES.BRIDGE_UNAUTHORIZED]: "The local bridge request is unauthorized.",
|
|
3881
|
+
[import_shared20.ERROR_CODES.BRIDGE_PROTOCOL_MISMATCH]: "The local bridge protocol is incompatible.",
|
|
3882
|
+
[import_shared20.ERROR_CODES.BRIDGE_BUSY]: "The local bridge is busy.",
|
|
3883
|
+
[import_shared20.ERROR_CODES.EXTERNAL_AGENT_BUSY]: "The connected external Agent is busy.",
|
|
3884
|
+
[import_shared20.ERROR_CODES.ACTIVE_ADAPTER_CONFLICT]: "Another active Agent adapter is connected.",
|
|
3885
|
+
[import_shared20.ERROR_CODES.ACTIVE_ADAPTER_LEASE_INVALID]: "The active Agent adapter lease is invalid.",
|
|
3886
|
+
[import_shared20.ERROR_CODES.ACTIVE_DISPATCH_INVALID]: "The active Agent dispatch transition is invalid.",
|
|
3887
|
+
[import_shared20.ERROR_CODES.SESSION_NOT_FOUND]: "No active SpotPatch session was found.",
|
|
3888
|
+
[import_shared20.ERROR_CODES.SESSION_AMBIGUOUS]: "More than one SpotPatch session matches.",
|
|
3889
|
+
[import_shared20.ERROR_CODES.SESSION_CLOSED]: "The SpotPatch session has closed.",
|
|
3890
|
+
[import_shared20.ERROR_CODES.WORKTREE_DIRTY]: "Local changes require explicit inclusion consent.",
|
|
3891
|
+
[import_shared20.ERROR_CODES.WORKTREE_NOT_REPOSITORY]: "The project root is not a Git repository.",
|
|
3892
|
+
[import_shared20.ERROR_CODES.WORKTREE_OPERATION_IN_PROGRESS]: "A Git operation is currently in progress.",
|
|
3893
|
+
[import_shared20.ERROR_CODES.WORKTREE_CONFLICTED]: "The local workspace contains unresolved merge conflicts.",
|
|
3894
|
+
[import_shared20.ERROR_CODES.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: "The local workspace exceeds the safe isolation size limit.",
|
|
3895
|
+
[import_shared20.ERROR_CODES.WORKTREE_UNTRACKED_UNSUPPORTED]: "An untracked path cannot be isolated safely.",
|
|
3896
|
+
[import_shared20.ERROR_CODES.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: "The local workspace state cannot be isolated safely.",
|
|
3897
|
+
[import_shared20.ERROR_CODES.TOOL_DENIED]: "The Agent tool request was denied.",
|
|
3898
|
+
[import_shared20.ERROR_CODES.TOOL_INPUT_INVALID]: "The Agent tool input was invalid.",
|
|
3899
|
+
[import_shared20.ERROR_CODES.TOOL_ARGUMENTS_INVALID]: "The Agent tool arguments are invalid.",
|
|
3900
|
+
[import_shared20.ERROR_CODES.TOOL_CALL_ID_CONFLICT]: "A tool call ID conflicts within one Agent turn.",
|
|
3901
|
+
[import_shared20.ERROR_CODES.TOOL_PATH_DENIED]: "The Agent tool path was denied.",
|
|
3902
|
+
[import_shared20.ERROR_CODES.PATCH_REJECTED]: "The proposed patch was rejected.",
|
|
3903
|
+
[import_shared20.ERROR_CODES.VALIDATION_FAILED]: "The proposed change failed validation.",
|
|
3904
|
+
[import_shared20.ERROR_CODES.APPLY_CONFLICT]: "The change conflicts with the current worktree.",
|
|
3905
|
+
[import_shared20.ERROR_CODES.INTERNAL_ERROR]: "The request could not be completed."
|
|
3744
3906
|
});
|
|
3745
3907
|
function writeJson2(response, status, payload) {
|
|
3746
3908
|
response.statusCode = status;
|
|
@@ -3749,11 +3911,11 @@ function writeJson2(response, status, payload) {
|
|
|
3749
3911
|
response.end(JSON.stringify(payload));
|
|
3750
3912
|
}
|
|
3751
3913
|
function asSpotPatchError(error) {
|
|
3752
|
-
return error instanceof
|
|
3914
|
+
return error instanceof import_shared20.SpotPatchError ? error : new import_shared20.SpotPatchError(import_shared20.ERROR_CODES.INTERNAL_ERROR, void 0, { cause: error });
|
|
3753
3915
|
}
|
|
3754
3916
|
function writeError(response, error, logger) {
|
|
3755
3917
|
const normalized = asSpotPatchError(error);
|
|
3756
|
-
if (normalized.code ===
|
|
3918
|
+
if (normalized.code === import_shared20.ERROR_CODES.INTERNAL_ERROR) {
|
|
3757
3919
|
logger?.warn("[spotpatch:server] Internal request failure.");
|
|
3758
3920
|
}
|
|
3759
3921
|
writeJson2(response, STATUS_BY_ERROR[normalized.code], {
|
|
@@ -3772,11 +3934,11 @@ function requestPath(request) {
|
|
|
3772
3934
|
}
|
|
3773
3935
|
}
|
|
3774
3936
|
async function handleSourceContext(request, options) {
|
|
3775
|
-
const parsed =
|
|
3937
|
+
const parsed = import_shared20.sourceContextRequestSchema.safeParse(
|
|
3776
3938
|
await readJsonRequestBody(request)
|
|
3777
3939
|
);
|
|
3778
3940
|
if (!parsed.success) {
|
|
3779
|
-
throw new
|
|
3941
|
+
throw new import_shared20.SpotPatchError(import_shared20.ERROR_CODES.INVALID_REQUEST);
|
|
3780
3942
|
}
|
|
3781
3943
|
return readSourceContext({
|
|
3782
3944
|
request: parsed.data,
|
|
@@ -3787,9 +3949,9 @@ async function handleSourceContext(request, options) {
|
|
|
3787
3949
|
});
|
|
3788
3950
|
}
|
|
3789
3951
|
async function handleOpenEditor(request, options) {
|
|
3790
|
-
const parsed =
|
|
3952
|
+
const parsed = import_shared20.openEditorRequestSchema.safeParse(await readJsonRequestBody(request));
|
|
3791
3953
|
if (!parsed.success) {
|
|
3792
|
-
throw new
|
|
3954
|
+
throw new import_shared20.SpotPatchError(import_shared20.ERROR_CODES.INVALID_REQUEST);
|
|
3793
3955
|
}
|
|
3794
3956
|
const body = parsed.data;
|
|
3795
3957
|
const sourcePath = await resolveSourceFile({
|
|
@@ -3806,7 +3968,7 @@ async function handleOpenEditor(request, options) {
|
|
|
3806
3968
|
options.logger?.warn(
|
|
3807
3969
|
`[spotpatch:server] ${options.options.editor === "auto" ? "The detected editor" : options.options.editor} rejected an editor request.`
|
|
3808
3970
|
);
|
|
3809
|
-
throw new
|
|
3971
|
+
throw new import_shared20.SpotPatchError(import_shared20.ERROR_CODES.EDITOR_OPEN_FAILED, void 0, {
|
|
3810
3972
|
cause: error
|
|
3811
3973
|
});
|
|
3812
3974
|
}
|
|
@@ -3814,16 +3976,18 @@ async function handleOpenEditor(request, options) {
|
|
|
3814
3976
|
function createSpotPatchMiddleware(options) {
|
|
3815
3977
|
const bootstrap = options.bootstrap === void 0 ? void 0 : resolveRuntimeBootstrapOptions(options.bootstrap);
|
|
3816
3978
|
const dataFlowAnalyzer = createDataFlowAnalyzer(options);
|
|
3817
|
-
|
|
3979
|
+
const externalAgentController = options.externalAgentControl === void 0 ? void 0 : createExternalAgentBrowserController(options.externalAgentControl);
|
|
3980
|
+
const middleware = (request, response, next) => {
|
|
3818
3981
|
const path9 = requestPath(request);
|
|
3819
3982
|
const agentRoute = matchAgentRequestPath(path9);
|
|
3983
|
+
const externalAgentRoute = matchExternalAgentBrowserPath(path9);
|
|
3820
3984
|
const externalHandoffRoute = matchExternalHandoffBrowserPath(path9);
|
|
3821
|
-
if (path9 !==
|
|
3985
|
+
if (path9 !== import_shared20.SPOTPATCH_ENDPOINTS.sourceContext && path9 !== import_shared20.SPOTPATCH_ENDPOINTS.openEditor && path9 !== import_shared20.SPOTPATCH_ENDPOINTS.dataFlowComponentReport && path9 !== import_shared20.SPOTPATCH_ENDPOINTS.dataFlowPageReport && agentRoute === void 0 && externalAgentRoute === void 0 && externalHandoffRoute === void 0 && !path9.startsWith(`${import_shared20.SPOTPATCH_API_BASE}/`)) {
|
|
3822
3986
|
next();
|
|
3823
3987
|
return;
|
|
3824
3988
|
}
|
|
3825
3989
|
const handle = async () => {
|
|
3826
|
-
if (path9 ===
|
|
3990
|
+
if (path9 === import_shared20.SPOTPATCH_ENDPOINTS.bootstrap && bootstrap !== void 0) {
|
|
3827
3991
|
const data = await readRuntimeBootstrap(
|
|
3828
3992
|
request,
|
|
3829
3993
|
bootstrap
|
|
@@ -3835,25 +3999,25 @@ function createSpotPatchMiddleware(options) {
|
|
|
3835
3999
|
allowLan: options.options.allowLan,
|
|
3836
4000
|
sessionToken: options.session.token
|
|
3837
4001
|
});
|
|
3838
|
-
if (path9 ===
|
|
4002
|
+
if (path9 === import_shared20.SPOTPATCH_ENDPOINTS.sourceContext) {
|
|
3839
4003
|
if (request.method !== "POST") {
|
|
3840
|
-
throw new
|
|
4004
|
+
throw new import_shared20.SpotPatchError(import_shared20.ERROR_CODES.INVALID_REQUEST);
|
|
3841
4005
|
}
|
|
3842
4006
|
const data = await handleSourceContext(request, options);
|
|
3843
4007
|
writeJson2(response, 200, { ok: true, data });
|
|
3844
4008
|
return;
|
|
3845
4009
|
}
|
|
3846
|
-
if (path9 ===
|
|
4010
|
+
if (path9 === import_shared20.SPOTPATCH_ENDPOINTS.openEditor) {
|
|
3847
4011
|
if (request.method !== "POST") {
|
|
3848
|
-
throw new
|
|
4012
|
+
throw new import_shared20.SpotPatchError(import_shared20.ERROR_CODES.INVALID_REQUEST);
|
|
3849
4013
|
}
|
|
3850
4014
|
const data = await handleOpenEditor(request, options);
|
|
3851
4015
|
writeJson2(response, 200, { ok: true, data });
|
|
3852
4016
|
return;
|
|
3853
4017
|
}
|
|
3854
|
-
if (path9 ===
|
|
4018
|
+
if (path9 === import_shared20.SPOTPATCH_ENDPOINTS.dataFlowComponentReport) {
|
|
3855
4019
|
if (request.method !== "POST") {
|
|
3856
|
-
throw new
|
|
4020
|
+
throw new import_shared20.SpotPatchError(import_shared20.ERROR_CODES.INVALID_REQUEST);
|
|
3857
4021
|
}
|
|
3858
4022
|
const data = await handleComponentDataFlowReport(
|
|
3859
4023
|
request,
|
|
@@ -3863,14 +4027,28 @@ function createSpotPatchMiddleware(options) {
|
|
|
3863
4027
|
writeJson2(response, 200, { ok: true, data });
|
|
3864
4028
|
return;
|
|
3865
4029
|
}
|
|
3866
|
-
if (path9 ===
|
|
4030
|
+
if (path9 === import_shared20.SPOTPATCH_ENDPOINTS.dataFlowPageReport) {
|
|
3867
4031
|
if (request.method !== "POST") {
|
|
3868
|
-
throw new
|
|
4032
|
+
throw new import_shared20.SpotPatchError(import_shared20.ERROR_CODES.INVALID_REQUEST);
|
|
3869
4033
|
}
|
|
3870
4034
|
const data = await handlePageDataFlowReport(request, dataFlowAnalyzer, options);
|
|
3871
4035
|
writeJson2(response, 200, { ok: true, data });
|
|
3872
4036
|
return;
|
|
3873
4037
|
}
|
|
4038
|
+
if (externalAgentRoute !== void 0) {
|
|
4039
|
+
if (!options.options.externalAgent.enabled || externalAgentController === void 0) {
|
|
4040
|
+
throw new import_shared20.SpotPatchError(import_shared20.ERROR_CODES.EXTERNAL_HANDOFF_DISABLED);
|
|
4041
|
+
}
|
|
4042
|
+
await externalAgentController.handle(
|
|
4043
|
+
request,
|
|
4044
|
+
response,
|
|
4045
|
+
externalAgentRoute,
|
|
4046
|
+
(target, status, data) => {
|
|
4047
|
+
writeJson2(target, status, { ok: true, data });
|
|
4048
|
+
}
|
|
4049
|
+
);
|
|
4050
|
+
return;
|
|
4051
|
+
}
|
|
3874
4052
|
if (externalHandoffRoute !== void 0) {
|
|
3875
4053
|
await handleExternalHandoffBrowserRequest(
|
|
3876
4054
|
request,
|
|
@@ -3889,7 +4067,7 @@ function createSpotPatchMiddleware(options) {
|
|
|
3889
4067
|
return;
|
|
3890
4068
|
}
|
|
3891
4069
|
if (agentRoute === void 0) {
|
|
3892
|
-
throw new
|
|
4070
|
+
throw new import_shared20.SpotPatchError(import_shared20.ERROR_CODES.INVALID_REQUEST);
|
|
3893
4071
|
}
|
|
3894
4072
|
await handleAgentRequest(
|
|
3895
4073
|
request,
|
|
@@ -3905,6 +4083,11 @@ function createSpotPatchMiddleware(options) {
|
|
|
3905
4083
|
writeError(response, error, options.logger);
|
|
3906
4084
|
});
|
|
3907
4085
|
};
|
|
4086
|
+
return Object.assign(middleware, {
|
|
4087
|
+
dispose() {
|
|
4088
|
+
externalAgentController?.dispose();
|
|
4089
|
+
}
|
|
4090
|
+
});
|
|
3908
4091
|
}
|
|
3909
4092
|
|
|
3910
4093
|
// src/server/source-registration.ts
|
|
@@ -4237,8 +4420,10 @@ function parseSerializedSpotPatchOptions(value) {
|
|
|
4237
4420
|
readRuntimeBootstrap,
|
|
4238
4421
|
resolveCredentialEnvironment,
|
|
4239
4422
|
resolveEnvironmentAiConfiguration,
|
|
4423
|
+
resolveManagedExecutionValidation,
|
|
4240
4424
|
resolveOptions,
|
|
4241
4425
|
resolveProjectOptions,
|
|
4426
|
+
resolveProjectValidationChecks,
|
|
4242
4427
|
resolveRuntimeBootstrapOptions,
|
|
4243
4428
|
serializeResolvedSpotPatchOptions
|
|
4244
4429
|
});
|