@spotpatch/dev-server 0.1.2 → 0.3.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 +414 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -2
- package/dist/index.d.ts +32 -2
- package/dist/index.js +413 -54
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -32,19 +32,25 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
DEFAULT_EXCLUDE: () => DEFAULT_EXCLUDE,
|
|
34
34
|
DEFAULT_OPTIONS: () => DEFAULT_OPTIONS,
|
|
35
|
+
applyIntegrationPlan: () => applyIntegrationPlan,
|
|
35
36
|
createAgentJobManager: () => createAgentJobManager,
|
|
37
|
+
createIntegrationFileChange: () => createIntegrationFileChange,
|
|
36
38
|
createRuntimeAiConfig: () => createRuntimeAiConfig,
|
|
37
39
|
createSession: () => createSession,
|
|
38
40
|
createSourceRegistrationService: () => createSourceRegistrationService,
|
|
39
41
|
createSourceRegistry: () => createSourceRegistry,
|
|
40
42
|
createSpotPatchMiddleware: () => createSpotPatchMiddleware,
|
|
43
|
+
discoverProjectValidationCheck: () => discoverProjectValidationCheck,
|
|
44
|
+
integrationPathExists: () => integrationPathExists,
|
|
41
45
|
isLoopbackHostname: () => isLoopbackHostname,
|
|
42
46
|
parseSerializedSpotPatchOptions: () => parseSerializedSpotPatchOptions,
|
|
47
|
+
readIntegrationFile: () => readIntegrationFile,
|
|
43
48
|
readJsonRequestBody: () => readJsonRequestBody,
|
|
44
49
|
readRuntimeBootstrap: () => readRuntimeBootstrap,
|
|
45
50
|
resolveCredentialEnvironment: () => resolveCredentialEnvironment,
|
|
46
51
|
resolveEnvironmentAiConfiguration: () => resolveEnvironmentAiConfiguration,
|
|
47
52
|
resolveOptions: () => resolveOptions,
|
|
53
|
+
resolveProjectOptions: () => resolveProjectOptions,
|
|
48
54
|
resolveRuntimeBootstrapOptions: () => resolveRuntimeBootstrapOptions,
|
|
49
55
|
serializeResolvedSpotPatchOptions: () => serializeResolvedSpotPatchOptions
|
|
50
56
|
});
|
|
@@ -322,7 +328,8 @@ function createAgentJobManager(options) {
|
|
|
322
328
|
transition(job, "completed", "No source changes were proposed.");
|
|
323
329
|
return;
|
|
324
330
|
}
|
|
325
|
-
|
|
331
|
+
const shouldApplyDirectly = job.applyMode === "auto" && preparedChange.autoApplyEligible || job.applyMode === "trusted-auto" && job.trustedFastModeConsent;
|
|
332
|
+
if (shouldApplyDirectly) {
|
|
326
333
|
try {
|
|
327
334
|
await applyChange(job, preparedChange);
|
|
328
335
|
} catch {
|
|
@@ -397,6 +404,13 @@ function createAgentJobManager(options) {
|
|
|
397
404
|
if (closed) {
|
|
398
405
|
throw new import_shared.SpotPatchError(import_shared.ERROR_CODES.AI_DISABLED);
|
|
399
406
|
}
|
|
407
|
+
const configuredApplyMode = options.ai.execution.applyMode;
|
|
408
|
+
const requestedApplyMode = request.applyMode ?? (request.trustedFastModeConsent === true ? "trusted-auto" : configuredApplyMode);
|
|
409
|
+
const applyModeAllowed = configuredApplyMode === "trusted-auto" ? requestedApplyMode === "review" || requestedApplyMode === "trusted-auto" : requestedApplyMode === configuredApplyMode;
|
|
410
|
+
const trustedConsentMatches = requestedApplyMode === "trusted-auto" === (request.trustedFastModeConsent === true);
|
|
411
|
+
if (!applyModeAllowed || !trustedConsentMatches) {
|
|
412
|
+
throw new import_shared.SpotPatchError(import_shared.ERROR_CODES.INVALID_REQUEST);
|
|
413
|
+
}
|
|
400
414
|
if (hasActiveJob()) {
|
|
401
415
|
throw new import_shared.SpotPatchError(import_shared.ERROR_CODES.AGENT_BUSY);
|
|
402
416
|
}
|
|
@@ -415,6 +429,7 @@ function createAgentJobManager(options) {
|
|
|
415
429
|
const timestamp = dependencies.now();
|
|
416
430
|
const job = {
|
|
417
431
|
annotation: request.annotation,
|
|
432
|
+
applyMode: requestedApplyMode,
|
|
418
433
|
controller: new AbortController(),
|
|
419
434
|
createdAt: timestamp,
|
|
420
435
|
credential: selection.credential,
|
|
@@ -430,6 +445,7 @@ function createAgentJobManager(options) {
|
|
|
430
445
|
runPromise: void 0,
|
|
431
446
|
sequence: 0,
|
|
432
447
|
status: "queued",
|
|
448
|
+
trustedFastModeConsent: request.trustedFastModeConsent === true,
|
|
433
449
|
updatedAt: timestamp,
|
|
434
450
|
workingTreeMode: request.workingTreeMode
|
|
435
451
|
};
|
|
@@ -585,6 +601,171 @@ function resolveEnvironmentAiConfiguration(environment) {
|
|
|
585
601
|
});
|
|
586
602
|
}
|
|
587
603
|
|
|
604
|
+
// src/integration/file-plan.ts
|
|
605
|
+
var import_node_crypto2 = require("crypto");
|
|
606
|
+
var import_promises = require("fs/promises");
|
|
607
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
608
|
+
function isMissingPathError(error) {
|
|
609
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
610
|
+
}
|
|
611
|
+
function isPathWithin(root, target) {
|
|
612
|
+
const relative = import_node_path.default.relative(root, target);
|
|
613
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${import_node_path.default.sep}`) && !import_node_path.default.isAbsolute(relative);
|
|
614
|
+
}
|
|
615
|
+
function relativePathWithin(root, target) {
|
|
616
|
+
const relative = import_node_path.default.relative(root, target);
|
|
617
|
+
if (relative.length === 0 || !isPathWithin(root, target)) {
|
|
618
|
+
throw new Error("SpotPatch init refuses to modify a path outside the app root.");
|
|
619
|
+
}
|
|
620
|
+
return relative.split(import_node_path.default.sep).join("/");
|
|
621
|
+
}
|
|
622
|
+
async function integrationPathExists(absolutePath) {
|
|
623
|
+
try {
|
|
624
|
+
await (0, import_promises.access)(absolutePath);
|
|
625
|
+
return true;
|
|
626
|
+
} catch {
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
async function readIntegrationFile(absolutePath) {
|
|
631
|
+
const metadata = await (0, import_promises.lstat)(absolutePath);
|
|
632
|
+
if (!metadata.isFile() || metadata.isSymbolicLink()) {
|
|
633
|
+
throw new Error(
|
|
634
|
+
`SpotPatch refuses to modify the non-regular file ${import_node_path.default.basename(absolutePath)}.`
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
return (0, import_promises.readFile)(absolutePath, "utf8");
|
|
638
|
+
}
|
|
639
|
+
function createIntegrationFileChange(appRoot, absolutePath, nextContent, previousContent) {
|
|
640
|
+
if (previousContent === nextContent) {
|
|
641
|
+
return void 0;
|
|
642
|
+
}
|
|
643
|
+
const root = import_node_path.default.resolve(appRoot);
|
|
644
|
+
const target = import_node_path.default.resolve(absolutePath);
|
|
645
|
+
return Object.freeze({
|
|
646
|
+
absolutePath: target,
|
|
647
|
+
nextContent,
|
|
648
|
+
...previousContent === void 0 ? {} : { previousContent },
|
|
649
|
+
relativePath: relativePathWithin(root, target)
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
function temporaryPath(absolutePath, label) {
|
|
653
|
+
return import_node_path.default.join(
|
|
654
|
+
import_node_path.default.dirname(absolutePath),
|
|
655
|
+
`.${import_node_path.default.basename(absolutePath)}.spotpatch-${label}-${String(process.pid)}-${(0, import_node_crypto2.randomBytes)(8).toString("hex")}`
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
async function writeAtomic(absolutePath, content, mode) {
|
|
659
|
+
await (0, import_promises.mkdir)(import_node_path.default.dirname(absolutePath), { recursive: true });
|
|
660
|
+
const stagedPath = temporaryPath(absolutePath, "stage");
|
|
661
|
+
try {
|
|
662
|
+
await (0, import_promises.writeFile)(stagedPath, content, { encoding: "utf8", flag: "wx", mode });
|
|
663
|
+
await (0, import_promises.rename)(stagedPath, absolutePath);
|
|
664
|
+
} catch (error) {
|
|
665
|
+
await (0, import_promises.unlink)(stagedPath).catch(() => void 0);
|
|
666
|
+
throw error;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
async function rollbackChange(change) {
|
|
670
|
+
const currentContent = await readIntegrationFile(change.absolutePath);
|
|
671
|
+
if (currentContent !== change.nextContent) {
|
|
672
|
+
throw new Error(
|
|
673
|
+
`SpotPatch init cannot restore ${change.relativePath} because it changed during initialization.`
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
if (change.previousContent === void 0) {
|
|
677
|
+
await (0, import_promises.unlink)(change.absolutePath);
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
const mode = (await (0, import_promises.stat)(change.absolutePath)).mode & 511;
|
|
681
|
+
await writeAtomic(change.absolutePath, change.previousContent, mode);
|
|
682
|
+
}
|
|
683
|
+
async function assertSafeTarget(appRoot, realAppRoot, change) {
|
|
684
|
+
const target = import_node_path.default.resolve(change.absolutePath);
|
|
685
|
+
const relativePath = relativePathWithin(appRoot, target);
|
|
686
|
+
if (target !== change.absolutePath || relativePath !== change.relativePath || import_node_path.default.dirname(target) === target) {
|
|
687
|
+
throw new Error("SpotPatch init received an invalid integration file plan.");
|
|
688
|
+
}
|
|
689
|
+
let targetMetadata;
|
|
690
|
+
try {
|
|
691
|
+
targetMetadata = await (0, import_promises.lstat)(target);
|
|
692
|
+
} catch (error) {
|
|
693
|
+
if (!isMissingPathError(error)) {
|
|
694
|
+
throw error;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
if (targetMetadata?.isSymbolicLink()) {
|
|
698
|
+
throw new Error(
|
|
699
|
+
`SpotPatch refuses to modify the symbolic link ${change.relativePath}.`
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
const containmentAnchor = await (0, import_promises.realpath)(
|
|
703
|
+
targetMetadata === void 0 ? import_node_path.default.dirname(target) : target
|
|
704
|
+
);
|
|
705
|
+
if (!isPathWithin(realAppRoot, containmentAnchor)) {
|
|
706
|
+
throw new Error("SpotPatch init refuses to modify a path outside the app root.");
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
async function assertCurrentBaseline(change) {
|
|
710
|
+
if (change.previousContent === void 0) {
|
|
711
|
+
try {
|
|
712
|
+
await (0, import_promises.lstat)(change.absolutePath);
|
|
713
|
+
} catch (error) {
|
|
714
|
+
if (isMissingPathError(error)) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
throw error;
|
|
718
|
+
}
|
|
719
|
+
throw new Error(
|
|
720
|
+
`SpotPatch init cannot create ${change.relativePath} because it now exists.`
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
const currentContent = await readIntegrationFile(change.absolutePath);
|
|
724
|
+
if (currentContent !== change.previousContent) {
|
|
725
|
+
throw new Error(
|
|
726
|
+
`SpotPatch init cannot update ${change.relativePath} because it changed after the preview.`
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
async function applyIntegrationPlan(plan) {
|
|
731
|
+
if (plan.changes.length === 0) {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
const appRoot = import_node_path.default.resolve(plan.appRoot);
|
|
735
|
+
const realAppRoot = await (0, import_promises.realpath)(appRoot);
|
|
736
|
+
const targets = /* @__PURE__ */ new Set();
|
|
737
|
+
for (const change of plan.changes) {
|
|
738
|
+
if (targets.has(change.absolutePath)) {
|
|
739
|
+
throw new Error("SpotPatch init received duplicate integration file changes.");
|
|
740
|
+
}
|
|
741
|
+
targets.add(change.absolutePath);
|
|
742
|
+
await assertSafeTarget(appRoot, realAppRoot, change);
|
|
743
|
+
await assertCurrentBaseline(change);
|
|
744
|
+
}
|
|
745
|
+
const applied = [];
|
|
746
|
+
try {
|
|
747
|
+
for (const change of plan.changes) {
|
|
748
|
+
await assertCurrentBaseline(change);
|
|
749
|
+
const mode = change.previousContent === void 0 ? 384 : (await (0, import_promises.stat)(change.absolutePath)).mode & 511;
|
|
750
|
+
await writeAtomic(change.absolutePath, change.nextContent, mode);
|
|
751
|
+
applied.push(change);
|
|
752
|
+
}
|
|
753
|
+
} catch (error) {
|
|
754
|
+
const rollbackResults = await Promise.allSettled(
|
|
755
|
+
applied.reverse().map(rollbackChange)
|
|
756
|
+
);
|
|
757
|
+
if (rollbackResults.some((result) => result.status === "rejected")) {
|
|
758
|
+
throw new Error(
|
|
759
|
+
"SpotPatch init failed and could not completely restore the previous files.",
|
|
760
|
+
{ cause: error }
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
throw new Error("SpotPatch init failed; all written files were restored.", {
|
|
764
|
+
cause: error
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
588
769
|
// src/options.ts
|
|
589
770
|
var import_shared2 = require("@spotpatch/shared");
|
|
590
771
|
var import_zod = require("zod");
|
|
@@ -662,7 +843,7 @@ var aiOptionsSchema = import_zod.z.strictObject({
|
|
|
662
843
|
defaultProvider: import_zod.z.string(),
|
|
663
844
|
execution: import_zod.z.strictObject({
|
|
664
845
|
isolation: import_zod.z.literal("git-worktree").optional(),
|
|
665
|
-
applyMode: import_zod.z.enum(
|
|
846
|
+
applyMode: import_zod.z.enum(import_shared2.AGENT_APPLY_MODES).optional(),
|
|
666
847
|
checks: import_zod.z.record(import_zod.z.string(), agentCheckSchema).optional(),
|
|
667
848
|
limits: agentLimitsSchema
|
|
668
849
|
}).optional()
|
|
@@ -857,8 +1038,8 @@ function resolveAiOptions(options) {
|
|
|
857
1038
|
const limits = resolveLimits(validated.execution?.limits);
|
|
858
1039
|
const checks = resolveChecks(validated.execution?.checks, limits.checkTimeoutMs);
|
|
859
1040
|
const applyMode = validated.execution?.applyMode ?? "review";
|
|
860
|
-
if (applyMode
|
|
861
|
-
throw new RangeError(
|
|
1041
|
+
if (applyMode !== "review" && !Object.values(checks).some((check) => check.required)) {
|
|
1042
|
+
throw new RangeError(`SpotPatch AI ${applyMode} mode requires a required check.`);
|
|
862
1043
|
}
|
|
863
1044
|
const providers = resolveProviders(validated.providers);
|
|
864
1045
|
if (!(validated.defaultProvider in providers)) {
|
|
@@ -910,6 +1091,9 @@ function assertPositiveBudget(budget) {
|
|
|
910
1091
|
}
|
|
911
1092
|
}
|
|
912
1093
|
function resolveOptions(options = {}, environmentAi) {
|
|
1094
|
+
if (options.trustedFastMode !== void 0 && typeof options.trustedFastMode !== "boolean") {
|
|
1095
|
+
throw new RangeError("SpotPatch trustedFastMode must be a boolean.");
|
|
1096
|
+
}
|
|
913
1097
|
const budget = Object.freeze({
|
|
914
1098
|
...DEFAULT_OPTIONS.budget,
|
|
915
1099
|
...options.budget
|
|
@@ -949,17 +1133,179 @@ function resolveOptions(options = {}, environmentAi) {
|
|
|
949
1133
|
return Object.freeze(resolved);
|
|
950
1134
|
}
|
|
951
1135
|
|
|
1136
|
+
// src/project-validation.ts
|
|
1137
|
+
var import_node_child_process = require("child_process");
|
|
1138
|
+
var import_promises2 = require("fs/promises");
|
|
1139
|
+
var import_node_module = require("module");
|
|
1140
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
1141
|
+
var import_node_util = require("util");
|
|
1142
|
+
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
1143
|
+
var TYPESCRIPT_CHECK_ID = "spotpatch-typecheck";
|
|
1144
|
+
var TYPESCRIPT_CHECK_LABEL = "TypeScript";
|
|
1145
|
+
function isRecord(value) {
|
|
1146
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1147
|
+
}
|
|
1148
|
+
async function isRegularFile(absolutePath) {
|
|
1149
|
+
try {
|
|
1150
|
+
const metadata = await (0, import_promises2.lstat)(absolutePath);
|
|
1151
|
+
return metadata.isFile() && !metadata.isSymbolicLink();
|
|
1152
|
+
} catch {
|
|
1153
|
+
return false;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
async function readManifest(appRoot) {
|
|
1157
|
+
const manifestPath = import_node_path2.default.join(appRoot, "package.json");
|
|
1158
|
+
if (!await isRegularFile(manifestPath)) {
|
|
1159
|
+
return void 0;
|
|
1160
|
+
}
|
|
1161
|
+
try {
|
|
1162
|
+
const value = JSON.parse(await (0, import_promises2.readFile)(manifestPath, "utf8"));
|
|
1163
|
+
return isRecord(value) ? value : void 0;
|
|
1164
|
+
} catch {
|
|
1165
|
+
return void 0;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
function declaresTypeScript(manifest) {
|
|
1169
|
+
return [
|
|
1170
|
+
manifest.dependencies,
|
|
1171
|
+
manifest.devDependencies,
|
|
1172
|
+
manifest.peerDependencies
|
|
1173
|
+
].some(
|
|
1174
|
+
(dependencies) => isRecord(dependencies) && typeof dependencies.typescript === "string"
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
async function findGitRoot(appRoot) {
|
|
1178
|
+
try {
|
|
1179
|
+
const result = await execFileAsync("git", ["rev-parse", "--show-toplevel"], {
|
|
1180
|
+
cwd: appRoot,
|
|
1181
|
+
encoding: "utf8",
|
|
1182
|
+
timeout: 5e3,
|
|
1183
|
+
windowsHide: true
|
|
1184
|
+
});
|
|
1185
|
+
const root = await (0, import_promises2.realpath)(result.stdout.trim());
|
|
1186
|
+
const relative = import_node_path2.default.relative(root, appRoot);
|
|
1187
|
+
if (relative === "" || !relative.startsWith(`..${import_node_path2.default.sep}`) && relative !== ".." && !import_node_path2.default.isAbsolute(relative)) {
|
|
1188
|
+
return root;
|
|
1189
|
+
}
|
|
1190
|
+
} catch {
|
|
1191
|
+
return void 0;
|
|
1192
|
+
}
|
|
1193
|
+
return void 0;
|
|
1194
|
+
}
|
|
1195
|
+
async function resolveTypeScriptCli(appRoot) {
|
|
1196
|
+
const resolveFromApplication = (0, import_node_module.createRequire)(import_node_path2.default.join(appRoot, "package.json"));
|
|
1197
|
+
try {
|
|
1198
|
+
const packagePath = resolveFromApplication.resolve("typescript/package.json");
|
|
1199
|
+
const cliPath = import_node_path2.default.join(import_node_path2.default.dirname(packagePath), "bin", "tsc");
|
|
1200
|
+
await (0, import_promises2.access)(cliPath);
|
|
1201
|
+
return await (0, import_promises2.realpath)(cliPath);
|
|
1202
|
+
} catch {
|
|
1203
|
+
return void 0;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
function portableRelativePath(from, to) {
|
|
1207
|
+
return import_node_path2.default.relative(from, to).split(import_node_path2.default.sep).join("/");
|
|
1208
|
+
}
|
|
1209
|
+
async function discoverProjectValidationCheck(options) {
|
|
1210
|
+
const appRoot = await (0, import_promises2.realpath)(options.appRoot);
|
|
1211
|
+
const tsconfigPath = import_node_path2.default.join(appRoot, "tsconfig.json");
|
|
1212
|
+
const [manifest, projectRoot, hasTsconfig] = await Promise.all([
|
|
1213
|
+
readManifest(appRoot),
|
|
1214
|
+
findGitRoot(appRoot),
|
|
1215
|
+
isRegularFile(tsconfigPath)
|
|
1216
|
+
]);
|
|
1217
|
+
if (manifest === void 0 || projectRoot === void 0 || !hasTsconfig || !declaresTypeScript(manifest)) {
|
|
1218
|
+
return void 0;
|
|
1219
|
+
}
|
|
1220
|
+
const cliPath = await resolveTypeScriptCli(appRoot);
|
|
1221
|
+
if (cliPath === void 0) {
|
|
1222
|
+
return void 0;
|
|
1223
|
+
}
|
|
1224
|
+
const projectPath = portableRelativePath(projectRoot, tsconfigPath);
|
|
1225
|
+
if (projectPath.length === 0 || projectPath.startsWith("../")) {
|
|
1226
|
+
return void 0;
|
|
1227
|
+
}
|
|
1228
|
+
return Object.freeze({
|
|
1229
|
+
id: TYPESCRIPT_CHECK_ID,
|
|
1230
|
+
label: TYPESCRIPT_CHECK_LABEL,
|
|
1231
|
+
command: process.execPath,
|
|
1232
|
+
args: Object.freeze([
|
|
1233
|
+
cliPath,
|
|
1234
|
+
"--noEmit",
|
|
1235
|
+
"--pretty",
|
|
1236
|
+
"false",
|
|
1237
|
+
"--project",
|
|
1238
|
+
projectPath
|
|
1239
|
+
]),
|
|
1240
|
+
required: true,
|
|
1241
|
+
timeoutMs: options.timeoutMs
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
// src/project-options.ts
|
|
1246
|
+
function hasRequiredCheck(ai) {
|
|
1247
|
+
return Object.values(ai.execution.checks).some((check) => check.required);
|
|
1248
|
+
}
|
|
1249
|
+
function availableCheckId(checks, preferred) {
|
|
1250
|
+
if (checks[preferred] === void 0) {
|
|
1251
|
+
return preferred;
|
|
1252
|
+
}
|
|
1253
|
+
let suffix = 2;
|
|
1254
|
+
while (checks[`${preferred}-${String(suffix)}`] !== void 0) {
|
|
1255
|
+
suffix += 1;
|
|
1256
|
+
}
|
|
1257
|
+
return `${preferred}-${String(suffix)}`;
|
|
1258
|
+
}
|
|
1259
|
+
async function resolveProjectOptions(input) {
|
|
1260
|
+
const userOptions = input.options ?? {};
|
|
1261
|
+
const resolved = resolveOptions(userOptions, input.environmentAi);
|
|
1262
|
+
if (!userOptions.trustedFastMode || resolved.ai === false) {
|
|
1263
|
+
return resolved;
|
|
1264
|
+
}
|
|
1265
|
+
if (resolved.ai.execution.applyMode === "auto") {
|
|
1266
|
+
throw new RangeError(
|
|
1267
|
+
"SpotPatch trustedFastMode cannot be combined with applyMode auto."
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1270
|
+
let checks = resolved.ai.execution.checks;
|
|
1271
|
+
if (!hasRequiredCheck(resolved.ai)) {
|
|
1272
|
+
const discovered = await discoverProjectValidationCheck({
|
|
1273
|
+
appRoot: input.appRoot,
|
|
1274
|
+
timeoutMs: resolved.ai.execution.limits.checkTimeoutMs
|
|
1275
|
+
});
|
|
1276
|
+
if (discovered === void 0) {
|
|
1277
|
+
throw new RangeError(
|
|
1278
|
+
"SpotPatch trustedFastMode requires a configured required check or a local TypeScript project with tsconfig.json."
|
|
1279
|
+
);
|
|
1280
|
+
}
|
|
1281
|
+
const id = availableCheckId(checks, discovered.id);
|
|
1282
|
+
checks = Object.freeze({
|
|
1283
|
+
...checks,
|
|
1284
|
+
[id]: Object.freeze({ ...discovered, id })
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
const ai = Object.freeze({
|
|
1288
|
+
...resolved.ai,
|
|
1289
|
+
execution: Object.freeze({
|
|
1290
|
+
...resolved.ai.execution,
|
|
1291
|
+
applyMode: "trusted-auto",
|
|
1292
|
+
checks
|
|
1293
|
+
})
|
|
1294
|
+
});
|
|
1295
|
+
return Object.freeze({ ...resolved, ai });
|
|
1296
|
+
}
|
|
1297
|
+
|
|
952
1298
|
// src/registry/source-registry.ts
|
|
953
|
-
var
|
|
1299
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
954
1300
|
|
|
955
1301
|
// src/registry/source-id.ts
|
|
956
|
-
var
|
|
1302
|
+
var import_node_crypto3 = require("crypto");
|
|
957
1303
|
var SOURCE_ID_BYTES = 8;
|
|
958
|
-
var createRandomSourceId = () => (0,
|
|
1304
|
+
var createRandomSourceId = () => (0, import_node_crypto3.randomBytes)(SOURCE_ID_BYTES).toString("base64url");
|
|
959
1305
|
|
|
960
1306
|
// src/registry/source-registry.ts
|
|
961
1307
|
function normalizeAbsolutePath(absolutePath) {
|
|
962
|
-
return
|
|
1308
|
+
return import_node_path3.default.normalize(import_node_path3.default.resolve(absolutePath));
|
|
963
1309
|
}
|
|
964
1310
|
function createSourceRegistry(options = {}) {
|
|
965
1311
|
const createId = options.createId ?? createRandomSourceId;
|
|
@@ -997,13 +1343,13 @@ var import_shared10 = require("@spotpatch/shared");
|
|
|
997
1343
|
var import_shared7 = require("@spotpatch/shared");
|
|
998
1344
|
|
|
999
1345
|
// src/server/agent-request.ts
|
|
1000
|
-
var
|
|
1001
|
-
var
|
|
1346
|
+
var import_promises5 = require("fs/promises");
|
|
1347
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
1002
1348
|
var import_shared5 = require("@spotpatch/shared");
|
|
1003
1349
|
|
|
1004
1350
|
// src/server/source-context.ts
|
|
1005
|
-
var
|
|
1006
|
-
var
|
|
1351
|
+
var import_promises4 = require("fs/promises");
|
|
1352
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
1007
1353
|
var import_shared4 = require("@spotpatch/shared");
|
|
1008
1354
|
|
|
1009
1355
|
// src/server/extract-code-context.ts
|
|
@@ -1233,8 +1579,8 @@ function extractCodeContext(options) {
|
|
|
1233
1579
|
}
|
|
1234
1580
|
|
|
1235
1581
|
// src/server/source-file.ts
|
|
1236
|
-
var
|
|
1237
|
-
var
|
|
1582
|
+
var import_promises3 = require("fs/promises");
|
|
1583
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
1238
1584
|
var import_shared3 = require("@spotpatch/shared");
|
|
1239
1585
|
|
|
1240
1586
|
// src/server/constants.ts
|
|
@@ -1252,8 +1598,8 @@ async function assertInsideRoot(root, candidate) {
|
|
|
1252
1598
|
let realCandidate;
|
|
1253
1599
|
try {
|
|
1254
1600
|
[realRoot, realCandidate] = await Promise.all([
|
|
1255
|
-
(0,
|
|
1256
|
-
(0,
|
|
1601
|
+
(0, import_promises3.realpath)(root),
|
|
1602
|
+
(0, import_promises3.realpath)(candidate)
|
|
1257
1603
|
]);
|
|
1258
1604
|
} catch (error) {
|
|
1259
1605
|
if (isMissingFileError(error)) {
|
|
@@ -1263,8 +1609,8 @@ async function assertInsideRoot(root, candidate) {
|
|
|
1263
1609
|
}
|
|
1264
1610
|
throw error;
|
|
1265
1611
|
}
|
|
1266
|
-
const relative =
|
|
1267
|
-
const outside = relative.startsWith(`..${
|
|
1612
|
+
const relative = import_node_path4.default.relative(realRoot, realCandidate);
|
|
1613
|
+
const outside = relative.startsWith(`..${import_node_path4.default.sep}`) || relative === ".." || import_node_path4.default.isAbsolute(relative);
|
|
1268
1614
|
if (outside) {
|
|
1269
1615
|
throw new import_shared3.SpotPatchError(import_shared3.ERROR_CODES.SOURCE_OUTSIDE_ROOT);
|
|
1270
1616
|
}
|
|
@@ -1276,12 +1622,12 @@ async function resolveSourceFile(options) {
|
|
|
1276
1622
|
throw new import_shared3.SpotPatchError(import_shared3.ERROR_CODES.SOURCE_NOT_FOUND);
|
|
1277
1623
|
}
|
|
1278
1624
|
const sourcePath = await assertInsideRoot(options.root, registeredPath);
|
|
1279
|
-
if (!ALLOWED_EXTENSIONS.has(
|
|
1625
|
+
if (!ALLOWED_EXTENSIONS.has(import_node_path4.default.extname(sourcePath).toLowerCase())) {
|
|
1280
1626
|
throw new import_shared3.SpotPatchError(import_shared3.ERROR_CODES.SOURCE_NOT_FOUND);
|
|
1281
1627
|
}
|
|
1282
1628
|
let sourceStat;
|
|
1283
1629
|
try {
|
|
1284
|
-
sourceStat = await (0,
|
|
1630
|
+
sourceStat = await (0, import_promises3.stat)(sourcePath);
|
|
1285
1631
|
} catch (error) {
|
|
1286
1632
|
if (isMissingFileError(error)) {
|
|
1287
1633
|
throw new import_shared3.SpotPatchError(import_shared3.ERROR_CODES.SOURCE_NOT_FOUND, void 0, {
|
|
@@ -1301,7 +1647,7 @@ async function resolveSourceFile(options) {
|
|
|
1301
1647
|
|
|
1302
1648
|
// src/server/source-context.ts
|
|
1303
1649
|
function toDisplayPath(root, sourcePath) {
|
|
1304
|
-
return
|
|
1650
|
+
return import_node_path5.default.relative(root, sourcePath).split(import_node_path5.default.sep).join("/");
|
|
1305
1651
|
}
|
|
1306
1652
|
async function readSourceContext(options) {
|
|
1307
1653
|
const sourcePath = await resolveSourceFile({
|
|
@@ -1311,7 +1657,7 @@ async function readSourceContext(options) {
|
|
|
1311
1657
|
});
|
|
1312
1658
|
let source;
|
|
1313
1659
|
try {
|
|
1314
|
-
source = await (0,
|
|
1660
|
+
source = await (0, import_promises4.readFile)(sourcePath, "utf8");
|
|
1315
1661
|
} catch (error) {
|
|
1316
1662
|
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
|
1317
1663
|
throw new import_shared4.SpotPatchError(import_shared4.ERROR_CODES.SOURCE_NOT_FOUND, void 0, {
|
|
@@ -1324,11 +1670,11 @@ async function readSourceContext(options) {
|
|
|
1324
1670
|
if (options.request.line > lines.length) {
|
|
1325
1671
|
throw new import_shared4.SpotPatchError(import_shared4.ERROR_CODES.INVALID_REQUEST);
|
|
1326
1672
|
}
|
|
1327
|
-
const extension =
|
|
1673
|
+
const extension = import_node_path5.default.extname(sourcePath).toLowerCase();
|
|
1328
1674
|
return extractCodeContext({
|
|
1329
1675
|
source,
|
|
1330
1676
|
sourcePath,
|
|
1331
|
-
relativePath: toDisplayPath(await (0,
|
|
1677
|
+
relativePath: toDisplayPath(await (0, import_promises4.realpath)(options.root), sourcePath),
|
|
1332
1678
|
language: extension === ".tsx" ? "tsx" : "jsx",
|
|
1333
1679
|
line: options.request.line,
|
|
1334
1680
|
column: options.request.column,
|
|
@@ -1364,7 +1710,7 @@ async function authorizeSourceRef(source, registry, root) {
|
|
|
1364
1710
|
registry,
|
|
1365
1711
|
root
|
|
1366
1712
|
});
|
|
1367
|
-
const relativePath =
|
|
1713
|
+
const relativePath = import_node_path6.default.relative(await (0, import_promises5.realpath)(root), sourcePath).split(import_node_path6.default.sep).join("/");
|
|
1368
1714
|
if (source.relativePath !== void 0 && source.relativePath !== relativePath) {
|
|
1369
1715
|
throw new import_shared5.SpotPatchError(import_shared5.ERROR_CODES.INVALID_REQUEST);
|
|
1370
1716
|
}
|
|
@@ -1469,9 +1815,11 @@ async function authorizeAgentJobRequest(input) {
|
|
|
1469
1815
|
});
|
|
1470
1816
|
return Object.freeze({
|
|
1471
1817
|
annotation,
|
|
1818
|
+
...input.request.applyMode === void 0 ? {} : { applyMode: input.request.applyMode },
|
|
1472
1819
|
providerProfileId: input.request.providerProfileId,
|
|
1473
1820
|
modelProfileId: input.request.modelProfileId,
|
|
1474
1821
|
providerDataConsent: true,
|
|
1822
|
+
...input.request.trustedFastModeConsent === true ? { trustedFastModeConsent: true } : {},
|
|
1475
1823
|
workingTreeMode: input.request.workingTreeMode
|
|
1476
1824
|
});
|
|
1477
1825
|
}
|
|
@@ -1534,21 +1882,21 @@ var EVENT_STREAM_END_STATUSES = /* @__PURE__ */ new Set([
|
|
|
1534
1882
|
"reverted",
|
|
1535
1883
|
"failed"
|
|
1536
1884
|
]);
|
|
1537
|
-
function matchAgentRequestPath(
|
|
1538
|
-
if (
|
|
1885
|
+
function matchAgentRequestPath(path8) {
|
|
1886
|
+
if (path8 === import_shared7.SPOTPATCH_ENDPOINTS.agentCapability) {
|
|
1539
1887
|
return Object.freeze({ kind: "capability" });
|
|
1540
1888
|
}
|
|
1541
|
-
if (
|
|
1889
|
+
if (path8 === import_shared7.SPOTPATCH_ENDPOINTS.agentWorkspaceHealth) {
|
|
1542
1890
|
return Object.freeze({ kind: "workspace-health" });
|
|
1543
1891
|
}
|
|
1544
|
-
if (
|
|
1892
|
+
if (path8 === import_shared7.SPOTPATCH_ENDPOINTS.agentJobs) {
|
|
1545
1893
|
return Object.freeze({ kind: "create-job" });
|
|
1546
1894
|
}
|
|
1547
1895
|
const prefix = `${import_shared7.SPOTPATCH_ENDPOINTS.agentJobs}/`;
|
|
1548
|
-
if (!
|
|
1896
|
+
if (!path8.startsWith(prefix)) {
|
|
1549
1897
|
return void 0;
|
|
1550
1898
|
}
|
|
1551
|
-
const segments =
|
|
1899
|
+
const segments = path8.slice(prefix.length).split("/");
|
|
1552
1900
|
const jobId = segments[0];
|
|
1553
1901
|
const action = segments[1];
|
|
1554
1902
|
if (segments.length !== 2 || jobId === void 0 || !AGENT_JOB_ID_PATTERN.test(jobId) || action === void 0 || !AGENT_JOB_ACTIONS.has(action)) {
|
|
@@ -1718,7 +2066,7 @@ async function handleAgentRequest(request, response, options, route, writeSucces
|
|
|
1718
2066
|
}
|
|
1719
2067
|
|
|
1720
2068
|
// src/server/editor.ts
|
|
1721
|
-
var
|
|
2069
|
+
var import_node_child_process2 = require("child_process");
|
|
1722
2070
|
var import_launch_editor = __toESM(require("launch-editor"), 1);
|
|
1723
2071
|
var EDITOR_STARTUP_GRACE_MS = 300;
|
|
1724
2072
|
function normalizedEditorEnvironment(environment) {
|
|
@@ -1745,7 +2093,7 @@ function editorCommand(editor) {
|
|
|
1745
2093
|
var DEFAULT_DEPENDENCIES2 = Object.freeze({
|
|
1746
2094
|
environment: process.env,
|
|
1747
2095
|
fallbackLauncher: import_launch_editor.default,
|
|
1748
|
-
processSpawner: (command, arguments_, options) => (0,
|
|
2096
|
+
processSpawner: (command, arguments_, options) => (0, import_node_child_process2.spawn)(command, [...arguments_], options),
|
|
1749
2097
|
startupGraceMs: EDITOR_STARTUP_GRACE_MS
|
|
1750
2098
|
});
|
|
1751
2099
|
function createEditorLauncher(dependencies = DEFAULT_DEPENDENCIES2) {
|
|
@@ -1803,7 +2151,7 @@ function createEditorLauncher(dependencies = DEFAULT_DEPENDENCIES2) {
|
|
|
1803
2151
|
var launchConfiguredEditor = createEditorLauncher();
|
|
1804
2152
|
|
|
1805
2153
|
// src/server/request-security.ts
|
|
1806
|
-
var
|
|
2154
|
+
var import_node_crypto4 = require("crypto");
|
|
1807
2155
|
var import_node_net = require("net");
|
|
1808
2156
|
var import_shared8 = require("@spotpatch/shared");
|
|
1809
2157
|
function getSingleHeader(request, name) {
|
|
@@ -1816,7 +2164,7 @@ function tokensMatch(actual, expected) {
|
|
|
1816
2164
|
}
|
|
1817
2165
|
const actualBytes = Buffer.from(actual);
|
|
1818
2166
|
const expectedBytes = Buffer.from(expected);
|
|
1819
|
-
return actualBytes.byteLength === expectedBytes.byteLength && (0,
|
|
2167
|
+
return actualBytes.byteLength === expectedBytes.byteLength && (0, import_node_crypto4.timingSafeEqual)(actualBytes, expectedBytes);
|
|
1820
2168
|
}
|
|
1821
2169
|
function isLoopbackHostname(hostname) {
|
|
1822
2170
|
const normalized = hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
@@ -2071,14 +2419,14 @@ async function handleOpenEditor(request, options) {
|
|
|
2071
2419
|
function createSpotPatchMiddleware(options) {
|
|
2072
2420
|
const bootstrap = options.bootstrap === void 0 ? void 0 : resolveRuntimeBootstrapOptions(options.bootstrap);
|
|
2073
2421
|
return (request, response, next) => {
|
|
2074
|
-
const
|
|
2075
|
-
const agentRoute = matchAgentRequestPath(
|
|
2076
|
-
if (
|
|
2422
|
+
const path8 = requestPath(request);
|
|
2423
|
+
const agentRoute = matchAgentRequestPath(path8);
|
|
2424
|
+
if (path8 !== import_shared10.SPOTPATCH_ENDPOINTS.sourceContext && path8 !== import_shared10.SPOTPATCH_ENDPOINTS.openEditor && agentRoute === void 0 && !path8.startsWith(`${import_shared10.SPOTPATCH_API_BASE}/`)) {
|
|
2077
2425
|
next();
|
|
2078
2426
|
return;
|
|
2079
2427
|
}
|
|
2080
2428
|
const handle = async () => {
|
|
2081
|
-
if (
|
|
2429
|
+
if (path8 === import_shared10.SPOTPATCH_ENDPOINTS.bootstrap && bootstrap !== void 0) {
|
|
2082
2430
|
const data = await readRuntimeBootstrap(
|
|
2083
2431
|
request,
|
|
2084
2432
|
bootstrap
|
|
@@ -2090,7 +2438,7 @@ function createSpotPatchMiddleware(options) {
|
|
|
2090
2438
|
allowLan: options.options.allowLan,
|
|
2091
2439
|
sessionToken: options.session.token
|
|
2092
2440
|
});
|
|
2093
|
-
if (
|
|
2441
|
+
if (path8 === import_shared10.SPOTPATCH_ENDPOINTS.sourceContext) {
|
|
2094
2442
|
if (request.method !== "POST") {
|
|
2095
2443
|
throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.INVALID_REQUEST);
|
|
2096
2444
|
}
|
|
@@ -2098,7 +2446,7 @@ function createSpotPatchMiddleware(options) {
|
|
|
2098
2446
|
writeJson(response, 200, { ok: true, data });
|
|
2099
2447
|
return;
|
|
2100
2448
|
}
|
|
2101
|
-
if (
|
|
2449
|
+
if (path8 === import_shared10.SPOTPATCH_ENDPOINTS.openEditor) {
|
|
2102
2450
|
if (request.method !== "POST") {
|
|
2103
2451
|
throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.INVALID_REQUEST);
|
|
2104
2452
|
}
|
|
@@ -2126,9 +2474,9 @@ function createSpotPatchMiddleware(options) {
|
|
|
2126
2474
|
}
|
|
2127
2475
|
|
|
2128
2476
|
// src/server/source-registration.ts
|
|
2129
|
-
var
|
|
2130
|
-
var
|
|
2131
|
-
var
|
|
2477
|
+
var import_node_crypto5 = require("crypto");
|
|
2478
|
+
var import_promises6 = require("fs/promises");
|
|
2479
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
2132
2480
|
var import_compiler = require("@spotpatch/compiler");
|
|
2133
2481
|
var import_zod2 = require("zod");
|
|
2134
2482
|
var REGISTRATION_BODY_LIMIT_BYTES = 4096;
|
|
@@ -2149,14 +2497,14 @@ function identitiesMatch(actual, expected) {
|
|
|
2149
2497
|
}
|
|
2150
2498
|
const actualBytes = Buffer.from(actual);
|
|
2151
2499
|
const expectedBytes = Buffer.from(expected);
|
|
2152
|
-
return actualBytes.byteLength === expectedBytes.byteLength && (0,
|
|
2500
|
+
return actualBytes.byteLength === expectedBytes.byteLength && (0, import_node_crypto5.timingSafeEqual)(actualBytes, expectedBytes);
|
|
2153
2501
|
}
|
|
2154
2502
|
function isWithinRoot(root, candidate) {
|
|
2155
|
-
const relative =
|
|
2156
|
-
return relative === "" || !relative.startsWith(`..${
|
|
2503
|
+
const relative = import_node_path7.default.relative(root, candidate);
|
|
2504
|
+
return relative === "" || !relative.startsWith(`..${import_node_path7.default.sep}`) && relative !== ".." && !import_node_path7.default.isAbsolute(relative);
|
|
2157
2505
|
}
|
|
2158
2506
|
function hasForbiddenSegment(root, candidate) {
|
|
2159
|
-
return
|
|
2507
|
+
return import_node_path7.default.relative(root, candidate).split(import_node_path7.default.sep).some((segment) => FORBIDDEN_SOURCE_SEGMENTS.has(segment));
|
|
2160
2508
|
}
|
|
2161
2509
|
function writeJson2(response, statusCode, payload) {
|
|
2162
2510
|
const body = JSON.stringify(payload);
|
|
@@ -2178,15 +2526,15 @@ function requestComesFromLoopbackWorker(request) {
|
|
|
2178
2526
|
}
|
|
2179
2527
|
}
|
|
2180
2528
|
async function resolveAuthorizedSource(root, requestedPath, shouldTransform) {
|
|
2181
|
-
if (!
|
|
2529
|
+
if (!import_node_path7.default.isAbsolute(requestedPath) || requestedPath.includes("\0")) {
|
|
2182
2530
|
return void 0;
|
|
2183
2531
|
}
|
|
2184
2532
|
try {
|
|
2185
|
-
const sourceStat = await (0,
|
|
2533
|
+
const sourceStat = await (0, import_promises6.lstat)(requestedPath);
|
|
2186
2534
|
if (!sourceStat.isFile() || sourceStat.isSymbolicLink()) {
|
|
2187
2535
|
return void 0;
|
|
2188
2536
|
}
|
|
2189
|
-
const resolvedPath = await (0,
|
|
2537
|
+
const resolvedPath = await (0, import_promises6.realpath)(requestedPath);
|
|
2190
2538
|
if (!isWithinRoot(root, resolvedPath) || hasForbiddenSegment(root, resolvedPath) || !shouldTransform(resolvedPath)) {
|
|
2191
2539
|
return void 0;
|
|
2192
2540
|
}
|
|
@@ -2199,7 +2547,7 @@ async function createSourceRegistrationService(input) {
|
|
|
2199
2547
|
if (!REGISTRATION_IDENTITY_PATTERN.test(input.internalSecret) || !REGISTRATION_IDENTITY_PATTERN.test(input.registryEpoch)) {
|
|
2200
2548
|
throw new TypeError("The source registration identity is invalid.");
|
|
2201
2549
|
}
|
|
2202
|
-
const root = await (0,
|
|
2550
|
+
const root = await (0, import_promises6.realpath)(input.root);
|
|
2203
2551
|
const sourceFilter = (0, import_compiler.createSourceFilter)(root, input.options);
|
|
2204
2552
|
const handler = (request, response) => {
|
|
2205
2553
|
const handle = async () => {
|
|
@@ -2244,11 +2592,11 @@ async function createSourceRegistrationService(input) {
|
|
|
2244
2592
|
}
|
|
2245
2593
|
|
|
2246
2594
|
// src/session/session.ts
|
|
2247
|
-
var
|
|
2595
|
+
var import_node_crypto6 = require("crypto");
|
|
2248
2596
|
function createSession() {
|
|
2249
2597
|
return Object.freeze({
|
|
2250
|
-
id: (0,
|
|
2251
|
-
token: (0,
|
|
2598
|
+
id: (0, import_node_crypto6.randomBytes)(16).toString("base64url"),
|
|
2599
|
+
token: (0, import_node_crypto6.randomBytes)(16).toString("base64url")
|
|
2252
2600
|
});
|
|
2253
2601
|
}
|
|
2254
2602
|
|
|
@@ -2276,7 +2624,7 @@ var BUDGET_KEYS = Object.freeze([
|
|
|
2276
2624
|
"maxComponentDepth"
|
|
2277
2625
|
]);
|
|
2278
2626
|
var REGEXP_FLAGS_PATTERN = /^(?!.*(.).*\1)[dgimsuvy]*$/u;
|
|
2279
|
-
function
|
|
2627
|
+
function isRecord2(value) {
|
|
2280
2628
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2281
2629
|
}
|
|
2282
2630
|
function hasExactKeys(value, keys) {
|
|
@@ -2361,7 +2709,7 @@ function parseFilterList(value) {
|
|
|
2361
2709
|
}
|
|
2362
2710
|
return Object.freeze(
|
|
2363
2711
|
value.map((entry) => {
|
|
2364
|
-
if (!
|
|
2712
|
+
if (!isRecord2(entry)) {
|
|
2365
2713
|
throw new TypeError("The SpotPatch filter transport is invalid.");
|
|
2366
2714
|
}
|
|
2367
2715
|
if (entry.kind === "string" && hasExactKeys(entry, ["kind", "value"]) && typeof entry.value === "string" && entry.value.length > 0 && entry.value.length <= 1024 && !entry.value.includes("\0")) {
|
|
@@ -2379,7 +2727,7 @@ function parseFilterList(value) {
|
|
|
2379
2727
|
);
|
|
2380
2728
|
}
|
|
2381
2729
|
function parseBudget(value) {
|
|
2382
|
-
if (!
|
|
2730
|
+
if (!isRecord2(value) || !hasExactKeys(value, BUDGET_KEYS)) {
|
|
2383
2731
|
throw new TypeError("The SpotPatch budget transport is invalid.");
|
|
2384
2732
|
}
|
|
2385
2733
|
const budget = Object.fromEntries(
|
|
@@ -2388,10 +2736,10 @@ function parseBudget(value) {
|
|
|
2388
2736
|
return Object.freeze(budget);
|
|
2389
2737
|
}
|
|
2390
2738
|
function parseSerializedSpotPatchOptions(value) {
|
|
2391
|
-
if (!
|
|
2739
|
+
if (!isRecord2(value) || !hasExactKeys(value, OPTION_KEYS)) {
|
|
2392
2740
|
throw new TypeError("The SpotPatch options transport is invalid.");
|
|
2393
2741
|
}
|
|
2394
|
-
if (typeof value.enabled !== "boolean" || typeof value.redact !== "boolean" || typeof value.allowLan !== "boolean" || typeof value.debug !== "boolean" || typeof value.shortcut !== "string" || typeof value.maxTargets !== "number" || typeof value.editor !== "string" || typeof value.locale !== "string" || value.ai !== false && !
|
|
2742
|
+
if (typeof value.enabled !== "boolean" || typeof value.redact !== "boolean" || typeof value.allowLan !== "boolean" || typeof value.debug !== "boolean" || typeof value.shortcut !== "string" || typeof value.maxTargets !== "number" || typeof value.editor !== "string" || typeof value.locale !== "string" || value.ai !== false && !isRecord2(value.ai)) {
|
|
2395
2743
|
throw new TypeError("The SpotPatch options transport is invalid.");
|
|
2396
2744
|
}
|
|
2397
2745
|
try {
|
|
@@ -2419,19 +2767,25 @@ function parseSerializedSpotPatchOptions(value) {
|
|
|
2419
2767
|
0 && (module.exports = {
|
|
2420
2768
|
DEFAULT_EXCLUDE,
|
|
2421
2769
|
DEFAULT_OPTIONS,
|
|
2770
|
+
applyIntegrationPlan,
|
|
2422
2771
|
createAgentJobManager,
|
|
2772
|
+
createIntegrationFileChange,
|
|
2423
2773
|
createRuntimeAiConfig,
|
|
2424
2774
|
createSession,
|
|
2425
2775
|
createSourceRegistrationService,
|
|
2426
2776
|
createSourceRegistry,
|
|
2427
2777
|
createSpotPatchMiddleware,
|
|
2778
|
+
discoverProjectValidationCheck,
|
|
2779
|
+
integrationPathExists,
|
|
2428
2780
|
isLoopbackHostname,
|
|
2429
2781
|
parseSerializedSpotPatchOptions,
|
|
2782
|
+
readIntegrationFile,
|
|
2430
2783
|
readJsonRequestBody,
|
|
2431
2784
|
readRuntimeBootstrap,
|
|
2432
2785
|
resolveCredentialEnvironment,
|
|
2433
2786
|
resolveEnvironmentAiConfiguration,
|
|
2434
2787
|
resolveOptions,
|
|
2788
|
+
resolveProjectOptions,
|
|
2435
2789
|
resolveRuntimeBootstrapOptions,
|
|
2436
2790
|
serializeResolvedSpotPatchOptions
|
|
2437
2791
|
});
|