@spotpatch/dev-server 0.2.0 → 0.4.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 +416 -61
- 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 +414 -55
- 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
|
});
|
|
@@ -246,7 +252,11 @@ function createAgentJobManager(options) {
|
|
|
246
252
|
}
|
|
247
253
|
};
|
|
248
254
|
const applyChange = async (job, preparedChange) => {
|
|
249
|
-
transition(
|
|
255
|
+
transition(
|
|
256
|
+
job,
|
|
257
|
+
"applying",
|
|
258
|
+
job.applyMode === "trusted-auto" ? "Applying trusted change directly to the project." : "Applying validated changes to the project."
|
|
259
|
+
);
|
|
250
260
|
try {
|
|
251
261
|
await dependencies.applyChange(preparedChange);
|
|
252
262
|
transition(job, "applied", "Changes were applied to local project files.");
|
|
@@ -285,11 +295,15 @@ function createAgentJobManager(options) {
|
|
|
285
295
|
);
|
|
286
296
|
}
|
|
287
297
|
};
|
|
298
|
+
const execution = Object.freeze({
|
|
299
|
+
...options.ai.execution,
|
|
300
|
+
applyMode: job.applyMode
|
|
301
|
+
});
|
|
288
302
|
const preparedChange = await dependencies.executeChange({
|
|
289
303
|
annotation: job.annotation,
|
|
290
304
|
callbacks,
|
|
291
305
|
credential: job.credential,
|
|
292
|
-
execution
|
|
306
|
+
execution,
|
|
293
307
|
jobId: job.id,
|
|
294
308
|
model: job.model,
|
|
295
309
|
provider: job.provider,
|
|
@@ -322,7 +336,7 @@ function createAgentJobManager(options) {
|
|
|
322
336
|
transition(job, "completed", "No source changes were proposed.");
|
|
323
337
|
return;
|
|
324
338
|
}
|
|
325
|
-
const shouldApplyDirectly =
|
|
339
|
+
const shouldApplyDirectly = job.applyMode === "auto" && preparedChange.autoApplyEligible || job.applyMode === "trusted-auto" && job.trustedFastModeConsent;
|
|
326
340
|
if (shouldApplyDirectly) {
|
|
327
341
|
try {
|
|
328
342
|
await applyChange(job, preparedChange);
|
|
@@ -398,8 +412,11 @@ function createAgentJobManager(options) {
|
|
|
398
412
|
if (closed) {
|
|
399
413
|
throw new import_shared.SpotPatchError(import_shared.ERROR_CODES.AI_DISABLED);
|
|
400
414
|
}
|
|
401
|
-
const
|
|
402
|
-
|
|
415
|
+
const configuredApplyMode = options.ai.execution.applyMode;
|
|
416
|
+
const requestedApplyMode = request.applyMode ?? (request.trustedFastModeConsent === true ? "trusted-auto" : configuredApplyMode);
|
|
417
|
+
const applyModeAllowed = configuredApplyMode === "trusted-auto" ? requestedApplyMode === "review" || requestedApplyMode === "trusted-auto" : requestedApplyMode === configuredApplyMode;
|
|
418
|
+
const trustedConsentMatches = requestedApplyMode === "trusted-auto" === (request.trustedFastModeConsent === true);
|
|
419
|
+
if (!applyModeAllowed || !trustedConsentMatches) {
|
|
403
420
|
throw new import_shared.SpotPatchError(import_shared.ERROR_CODES.INVALID_REQUEST);
|
|
404
421
|
}
|
|
405
422
|
if (hasActiveJob()) {
|
|
@@ -420,6 +437,7 @@ function createAgentJobManager(options) {
|
|
|
420
437
|
const timestamp = dependencies.now();
|
|
421
438
|
const job = {
|
|
422
439
|
annotation: request.annotation,
|
|
440
|
+
applyMode: requestedApplyMode,
|
|
423
441
|
controller: new AbortController(),
|
|
424
442
|
createdAt: timestamp,
|
|
425
443
|
credential: selection.credential,
|
|
@@ -591,6 +609,171 @@ function resolveEnvironmentAiConfiguration(environment) {
|
|
|
591
609
|
});
|
|
592
610
|
}
|
|
593
611
|
|
|
612
|
+
// src/integration/file-plan.ts
|
|
613
|
+
var import_node_crypto2 = require("crypto");
|
|
614
|
+
var import_promises = require("fs/promises");
|
|
615
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
616
|
+
function isMissingPathError(error) {
|
|
617
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
618
|
+
}
|
|
619
|
+
function isPathWithin(root, target) {
|
|
620
|
+
const relative = import_node_path.default.relative(root, target);
|
|
621
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${import_node_path.default.sep}`) && !import_node_path.default.isAbsolute(relative);
|
|
622
|
+
}
|
|
623
|
+
function relativePathWithin(root, target) {
|
|
624
|
+
const relative = import_node_path.default.relative(root, target);
|
|
625
|
+
if (relative.length === 0 || !isPathWithin(root, target)) {
|
|
626
|
+
throw new Error("SpotPatch init refuses to modify a path outside the app root.");
|
|
627
|
+
}
|
|
628
|
+
return relative.split(import_node_path.default.sep).join("/");
|
|
629
|
+
}
|
|
630
|
+
async function integrationPathExists(absolutePath) {
|
|
631
|
+
try {
|
|
632
|
+
await (0, import_promises.access)(absolutePath);
|
|
633
|
+
return true;
|
|
634
|
+
} catch {
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
async function readIntegrationFile(absolutePath) {
|
|
639
|
+
const metadata = await (0, import_promises.lstat)(absolutePath);
|
|
640
|
+
if (!metadata.isFile() || metadata.isSymbolicLink()) {
|
|
641
|
+
throw new Error(
|
|
642
|
+
`SpotPatch refuses to modify the non-regular file ${import_node_path.default.basename(absolutePath)}.`
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
return (0, import_promises.readFile)(absolutePath, "utf8");
|
|
646
|
+
}
|
|
647
|
+
function createIntegrationFileChange(appRoot, absolutePath, nextContent, previousContent) {
|
|
648
|
+
if (previousContent === nextContent) {
|
|
649
|
+
return void 0;
|
|
650
|
+
}
|
|
651
|
+
const root = import_node_path.default.resolve(appRoot);
|
|
652
|
+
const target = import_node_path.default.resolve(absolutePath);
|
|
653
|
+
return Object.freeze({
|
|
654
|
+
absolutePath: target,
|
|
655
|
+
nextContent,
|
|
656
|
+
...previousContent === void 0 ? {} : { previousContent },
|
|
657
|
+
relativePath: relativePathWithin(root, target)
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
function temporaryPath(absolutePath, label) {
|
|
661
|
+
return import_node_path.default.join(
|
|
662
|
+
import_node_path.default.dirname(absolutePath),
|
|
663
|
+
`.${import_node_path.default.basename(absolutePath)}.spotpatch-${label}-${String(process.pid)}-${(0, import_node_crypto2.randomBytes)(8).toString("hex")}`
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
async function writeAtomic(absolutePath, content, mode) {
|
|
667
|
+
await (0, import_promises.mkdir)(import_node_path.default.dirname(absolutePath), { recursive: true });
|
|
668
|
+
const stagedPath = temporaryPath(absolutePath, "stage");
|
|
669
|
+
try {
|
|
670
|
+
await (0, import_promises.writeFile)(stagedPath, content, { encoding: "utf8", flag: "wx", mode });
|
|
671
|
+
await (0, import_promises.rename)(stagedPath, absolutePath);
|
|
672
|
+
} catch (error) {
|
|
673
|
+
await (0, import_promises.unlink)(stagedPath).catch(() => void 0);
|
|
674
|
+
throw error;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
async function rollbackChange(change) {
|
|
678
|
+
const currentContent = await readIntegrationFile(change.absolutePath);
|
|
679
|
+
if (currentContent !== change.nextContent) {
|
|
680
|
+
throw new Error(
|
|
681
|
+
`SpotPatch init cannot restore ${change.relativePath} because it changed during initialization.`
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
if (change.previousContent === void 0) {
|
|
685
|
+
await (0, import_promises.unlink)(change.absolutePath);
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
const mode = (await (0, import_promises.stat)(change.absolutePath)).mode & 511;
|
|
689
|
+
await writeAtomic(change.absolutePath, change.previousContent, mode);
|
|
690
|
+
}
|
|
691
|
+
async function assertSafeTarget(appRoot, realAppRoot, change) {
|
|
692
|
+
const target = import_node_path.default.resolve(change.absolutePath);
|
|
693
|
+
const relativePath = relativePathWithin(appRoot, target);
|
|
694
|
+
if (target !== change.absolutePath || relativePath !== change.relativePath || import_node_path.default.dirname(target) === target) {
|
|
695
|
+
throw new Error("SpotPatch init received an invalid integration file plan.");
|
|
696
|
+
}
|
|
697
|
+
let targetMetadata;
|
|
698
|
+
try {
|
|
699
|
+
targetMetadata = await (0, import_promises.lstat)(target);
|
|
700
|
+
} catch (error) {
|
|
701
|
+
if (!isMissingPathError(error)) {
|
|
702
|
+
throw error;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
if (targetMetadata?.isSymbolicLink()) {
|
|
706
|
+
throw new Error(
|
|
707
|
+
`SpotPatch refuses to modify the symbolic link ${change.relativePath}.`
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
const containmentAnchor = await (0, import_promises.realpath)(
|
|
711
|
+
targetMetadata === void 0 ? import_node_path.default.dirname(target) : target
|
|
712
|
+
);
|
|
713
|
+
if (!isPathWithin(realAppRoot, containmentAnchor)) {
|
|
714
|
+
throw new Error("SpotPatch init refuses to modify a path outside the app root.");
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
async function assertCurrentBaseline(change) {
|
|
718
|
+
if (change.previousContent === void 0) {
|
|
719
|
+
try {
|
|
720
|
+
await (0, import_promises.lstat)(change.absolutePath);
|
|
721
|
+
} catch (error) {
|
|
722
|
+
if (isMissingPathError(error)) {
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
throw error;
|
|
726
|
+
}
|
|
727
|
+
throw new Error(
|
|
728
|
+
`SpotPatch init cannot create ${change.relativePath} because it now exists.`
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
const currentContent = await readIntegrationFile(change.absolutePath);
|
|
732
|
+
if (currentContent !== change.previousContent) {
|
|
733
|
+
throw new Error(
|
|
734
|
+
`SpotPatch init cannot update ${change.relativePath} because it changed after the preview.`
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
async function applyIntegrationPlan(plan) {
|
|
739
|
+
if (plan.changes.length === 0) {
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
const appRoot = import_node_path.default.resolve(plan.appRoot);
|
|
743
|
+
const realAppRoot = await (0, import_promises.realpath)(appRoot);
|
|
744
|
+
const targets = /* @__PURE__ */ new Set();
|
|
745
|
+
for (const change of plan.changes) {
|
|
746
|
+
if (targets.has(change.absolutePath)) {
|
|
747
|
+
throw new Error("SpotPatch init received duplicate integration file changes.");
|
|
748
|
+
}
|
|
749
|
+
targets.add(change.absolutePath);
|
|
750
|
+
await assertSafeTarget(appRoot, realAppRoot, change);
|
|
751
|
+
await assertCurrentBaseline(change);
|
|
752
|
+
}
|
|
753
|
+
const applied = [];
|
|
754
|
+
try {
|
|
755
|
+
for (const change of plan.changes) {
|
|
756
|
+
await assertCurrentBaseline(change);
|
|
757
|
+
const mode = change.previousContent === void 0 ? 384 : (await (0, import_promises.stat)(change.absolutePath)).mode & 511;
|
|
758
|
+
await writeAtomic(change.absolutePath, change.nextContent, mode);
|
|
759
|
+
applied.push(change);
|
|
760
|
+
}
|
|
761
|
+
} catch (error) {
|
|
762
|
+
const rollbackResults = await Promise.allSettled(
|
|
763
|
+
applied.reverse().map(rollbackChange)
|
|
764
|
+
);
|
|
765
|
+
if (rollbackResults.some((result) => result.status === "rejected")) {
|
|
766
|
+
throw new Error(
|
|
767
|
+
"SpotPatch init failed and could not completely restore the previous files.",
|
|
768
|
+
{ cause: error }
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
throw new Error("SpotPatch init failed; all written files were restored.", {
|
|
772
|
+
cause: error
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
594
777
|
// src/options.ts
|
|
595
778
|
var import_shared2 = require("@spotpatch/shared");
|
|
596
779
|
var import_zod = require("zod");
|
|
@@ -916,6 +1099,9 @@ function assertPositiveBudget(budget) {
|
|
|
916
1099
|
}
|
|
917
1100
|
}
|
|
918
1101
|
function resolveOptions(options = {}, environmentAi) {
|
|
1102
|
+
if (options.trustedFastMode !== void 0 && typeof options.trustedFastMode !== "boolean") {
|
|
1103
|
+
throw new RangeError("SpotPatch trustedFastMode must be a boolean.");
|
|
1104
|
+
}
|
|
919
1105
|
const budget = Object.freeze({
|
|
920
1106
|
...DEFAULT_OPTIONS.budget,
|
|
921
1107
|
...options.budget
|
|
@@ -955,17 +1141,179 @@ function resolveOptions(options = {}, environmentAi) {
|
|
|
955
1141
|
return Object.freeze(resolved);
|
|
956
1142
|
}
|
|
957
1143
|
|
|
1144
|
+
// src/project-validation.ts
|
|
1145
|
+
var import_node_child_process = require("child_process");
|
|
1146
|
+
var import_promises2 = require("fs/promises");
|
|
1147
|
+
var import_node_module = require("module");
|
|
1148
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
1149
|
+
var import_node_util = require("util");
|
|
1150
|
+
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
1151
|
+
var TYPESCRIPT_CHECK_ID = "spotpatch-typecheck";
|
|
1152
|
+
var TYPESCRIPT_CHECK_LABEL = "TypeScript";
|
|
1153
|
+
function isRecord(value) {
|
|
1154
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1155
|
+
}
|
|
1156
|
+
async function isRegularFile(absolutePath) {
|
|
1157
|
+
try {
|
|
1158
|
+
const metadata = await (0, import_promises2.lstat)(absolutePath);
|
|
1159
|
+
return metadata.isFile() && !metadata.isSymbolicLink();
|
|
1160
|
+
} catch {
|
|
1161
|
+
return false;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
async function readManifest(appRoot) {
|
|
1165
|
+
const manifestPath = import_node_path2.default.join(appRoot, "package.json");
|
|
1166
|
+
if (!await isRegularFile(manifestPath)) {
|
|
1167
|
+
return void 0;
|
|
1168
|
+
}
|
|
1169
|
+
try {
|
|
1170
|
+
const value = JSON.parse(await (0, import_promises2.readFile)(manifestPath, "utf8"));
|
|
1171
|
+
return isRecord(value) ? value : void 0;
|
|
1172
|
+
} catch {
|
|
1173
|
+
return void 0;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
function declaresTypeScript(manifest) {
|
|
1177
|
+
return [
|
|
1178
|
+
manifest.dependencies,
|
|
1179
|
+
manifest.devDependencies,
|
|
1180
|
+
manifest.peerDependencies
|
|
1181
|
+
].some(
|
|
1182
|
+
(dependencies) => isRecord(dependencies) && typeof dependencies.typescript === "string"
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
1185
|
+
async function findGitRoot(appRoot) {
|
|
1186
|
+
try {
|
|
1187
|
+
const result = await execFileAsync("git", ["rev-parse", "--show-toplevel"], {
|
|
1188
|
+
cwd: appRoot,
|
|
1189
|
+
encoding: "utf8",
|
|
1190
|
+
timeout: 5e3,
|
|
1191
|
+
windowsHide: true
|
|
1192
|
+
});
|
|
1193
|
+
const root = await (0, import_promises2.realpath)(result.stdout.trim());
|
|
1194
|
+
const relative = import_node_path2.default.relative(root, appRoot);
|
|
1195
|
+
if (relative === "" || !relative.startsWith(`..${import_node_path2.default.sep}`) && relative !== ".." && !import_node_path2.default.isAbsolute(relative)) {
|
|
1196
|
+
return root;
|
|
1197
|
+
}
|
|
1198
|
+
} catch {
|
|
1199
|
+
return void 0;
|
|
1200
|
+
}
|
|
1201
|
+
return void 0;
|
|
1202
|
+
}
|
|
1203
|
+
async function resolveTypeScriptCli(appRoot) {
|
|
1204
|
+
const resolveFromApplication = (0, import_node_module.createRequire)(import_node_path2.default.join(appRoot, "package.json"));
|
|
1205
|
+
try {
|
|
1206
|
+
const packagePath = resolveFromApplication.resolve("typescript/package.json");
|
|
1207
|
+
const cliPath = import_node_path2.default.join(import_node_path2.default.dirname(packagePath), "bin", "tsc");
|
|
1208
|
+
await (0, import_promises2.access)(cliPath);
|
|
1209
|
+
return await (0, import_promises2.realpath)(cliPath);
|
|
1210
|
+
} catch {
|
|
1211
|
+
return void 0;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
function portableRelativePath(from, to) {
|
|
1215
|
+
return import_node_path2.default.relative(from, to).split(import_node_path2.default.sep).join("/");
|
|
1216
|
+
}
|
|
1217
|
+
async function discoverProjectValidationCheck(options) {
|
|
1218
|
+
const appRoot = await (0, import_promises2.realpath)(options.appRoot);
|
|
1219
|
+
const tsconfigPath = import_node_path2.default.join(appRoot, "tsconfig.json");
|
|
1220
|
+
const [manifest, projectRoot, hasTsconfig] = await Promise.all([
|
|
1221
|
+
readManifest(appRoot),
|
|
1222
|
+
findGitRoot(appRoot),
|
|
1223
|
+
isRegularFile(tsconfigPath)
|
|
1224
|
+
]);
|
|
1225
|
+
if (manifest === void 0 || projectRoot === void 0 || !hasTsconfig || !declaresTypeScript(manifest)) {
|
|
1226
|
+
return void 0;
|
|
1227
|
+
}
|
|
1228
|
+
const cliPath = await resolveTypeScriptCli(appRoot);
|
|
1229
|
+
if (cliPath === void 0) {
|
|
1230
|
+
return void 0;
|
|
1231
|
+
}
|
|
1232
|
+
const projectPath = portableRelativePath(projectRoot, tsconfigPath);
|
|
1233
|
+
if (projectPath.length === 0 || projectPath.startsWith("../")) {
|
|
1234
|
+
return void 0;
|
|
1235
|
+
}
|
|
1236
|
+
return Object.freeze({
|
|
1237
|
+
id: TYPESCRIPT_CHECK_ID,
|
|
1238
|
+
label: TYPESCRIPT_CHECK_LABEL,
|
|
1239
|
+
command: process.execPath,
|
|
1240
|
+
args: Object.freeze([
|
|
1241
|
+
cliPath,
|
|
1242
|
+
"--noEmit",
|
|
1243
|
+
"--pretty",
|
|
1244
|
+
"false",
|
|
1245
|
+
"--project",
|
|
1246
|
+
projectPath
|
|
1247
|
+
]),
|
|
1248
|
+
required: true,
|
|
1249
|
+
timeoutMs: options.timeoutMs
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// src/project-options.ts
|
|
1254
|
+
function hasRequiredCheck(ai) {
|
|
1255
|
+
return Object.values(ai.execution.checks).some((check) => check.required);
|
|
1256
|
+
}
|
|
1257
|
+
function availableCheckId(checks, preferred) {
|
|
1258
|
+
if (checks[preferred] === void 0) {
|
|
1259
|
+
return preferred;
|
|
1260
|
+
}
|
|
1261
|
+
let suffix = 2;
|
|
1262
|
+
while (checks[`${preferred}-${String(suffix)}`] !== void 0) {
|
|
1263
|
+
suffix += 1;
|
|
1264
|
+
}
|
|
1265
|
+
return `${preferred}-${String(suffix)}`;
|
|
1266
|
+
}
|
|
1267
|
+
async function resolveProjectOptions(input) {
|
|
1268
|
+
const userOptions = input.options ?? {};
|
|
1269
|
+
const resolved = resolveOptions(userOptions, input.environmentAi);
|
|
1270
|
+
if (!userOptions.trustedFastMode || resolved.ai === false) {
|
|
1271
|
+
return resolved;
|
|
1272
|
+
}
|
|
1273
|
+
if (resolved.ai.execution.applyMode === "auto") {
|
|
1274
|
+
throw new RangeError(
|
|
1275
|
+
"SpotPatch trustedFastMode cannot be combined with applyMode auto."
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1278
|
+
let checks = resolved.ai.execution.checks;
|
|
1279
|
+
if (!hasRequiredCheck(resolved.ai)) {
|
|
1280
|
+
const discovered = await discoverProjectValidationCheck({
|
|
1281
|
+
appRoot: input.appRoot,
|
|
1282
|
+
timeoutMs: resolved.ai.execution.limits.checkTimeoutMs
|
|
1283
|
+
});
|
|
1284
|
+
if (discovered === void 0) {
|
|
1285
|
+
throw new RangeError(
|
|
1286
|
+
"SpotPatch trustedFastMode requires a configured required check or a local TypeScript project with tsconfig.json."
|
|
1287
|
+
);
|
|
1288
|
+
}
|
|
1289
|
+
const id = availableCheckId(checks, discovered.id);
|
|
1290
|
+
checks = Object.freeze({
|
|
1291
|
+
...checks,
|
|
1292
|
+
[id]: Object.freeze({ ...discovered, id })
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
const ai = Object.freeze({
|
|
1296
|
+
...resolved.ai,
|
|
1297
|
+
execution: Object.freeze({
|
|
1298
|
+
...resolved.ai.execution,
|
|
1299
|
+
applyMode: "trusted-auto",
|
|
1300
|
+
checks
|
|
1301
|
+
})
|
|
1302
|
+
});
|
|
1303
|
+
return Object.freeze({ ...resolved, ai });
|
|
1304
|
+
}
|
|
1305
|
+
|
|
958
1306
|
// src/registry/source-registry.ts
|
|
959
|
-
var
|
|
1307
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
960
1308
|
|
|
961
1309
|
// src/registry/source-id.ts
|
|
962
|
-
var
|
|
1310
|
+
var import_node_crypto3 = require("crypto");
|
|
963
1311
|
var SOURCE_ID_BYTES = 8;
|
|
964
|
-
var createRandomSourceId = () => (0,
|
|
1312
|
+
var createRandomSourceId = () => (0, import_node_crypto3.randomBytes)(SOURCE_ID_BYTES).toString("base64url");
|
|
965
1313
|
|
|
966
1314
|
// src/registry/source-registry.ts
|
|
967
1315
|
function normalizeAbsolutePath(absolutePath) {
|
|
968
|
-
return
|
|
1316
|
+
return import_node_path3.default.normalize(import_node_path3.default.resolve(absolutePath));
|
|
969
1317
|
}
|
|
970
1318
|
function createSourceRegistry(options = {}) {
|
|
971
1319
|
const createId = options.createId ?? createRandomSourceId;
|
|
@@ -1003,13 +1351,13 @@ var import_shared10 = require("@spotpatch/shared");
|
|
|
1003
1351
|
var import_shared7 = require("@spotpatch/shared");
|
|
1004
1352
|
|
|
1005
1353
|
// src/server/agent-request.ts
|
|
1006
|
-
var
|
|
1007
|
-
var
|
|
1354
|
+
var import_promises5 = require("fs/promises");
|
|
1355
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
1008
1356
|
var import_shared5 = require("@spotpatch/shared");
|
|
1009
1357
|
|
|
1010
1358
|
// src/server/source-context.ts
|
|
1011
|
-
var
|
|
1012
|
-
var
|
|
1359
|
+
var import_promises4 = require("fs/promises");
|
|
1360
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
1013
1361
|
var import_shared4 = require("@spotpatch/shared");
|
|
1014
1362
|
|
|
1015
1363
|
// src/server/extract-code-context.ts
|
|
@@ -1239,8 +1587,8 @@ function extractCodeContext(options) {
|
|
|
1239
1587
|
}
|
|
1240
1588
|
|
|
1241
1589
|
// src/server/source-file.ts
|
|
1242
|
-
var
|
|
1243
|
-
var
|
|
1590
|
+
var import_promises3 = require("fs/promises");
|
|
1591
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
1244
1592
|
var import_shared3 = require("@spotpatch/shared");
|
|
1245
1593
|
|
|
1246
1594
|
// src/server/constants.ts
|
|
@@ -1258,8 +1606,8 @@ async function assertInsideRoot(root, candidate) {
|
|
|
1258
1606
|
let realCandidate;
|
|
1259
1607
|
try {
|
|
1260
1608
|
[realRoot, realCandidate] = await Promise.all([
|
|
1261
|
-
(0,
|
|
1262
|
-
(0,
|
|
1609
|
+
(0, import_promises3.realpath)(root),
|
|
1610
|
+
(0, import_promises3.realpath)(candidate)
|
|
1263
1611
|
]);
|
|
1264
1612
|
} catch (error) {
|
|
1265
1613
|
if (isMissingFileError(error)) {
|
|
@@ -1269,8 +1617,8 @@ async function assertInsideRoot(root, candidate) {
|
|
|
1269
1617
|
}
|
|
1270
1618
|
throw error;
|
|
1271
1619
|
}
|
|
1272
|
-
const relative =
|
|
1273
|
-
const outside = relative.startsWith(`..${
|
|
1620
|
+
const relative = import_node_path4.default.relative(realRoot, realCandidate);
|
|
1621
|
+
const outside = relative.startsWith(`..${import_node_path4.default.sep}`) || relative === ".." || import_node_path4.default.isAbsolute(relative);
|
|
1274
1622
|
if (outside) {
|
|
1275
1623
|
throw new import_shared3.SpotPatchError(import_shared3.ERROR_CODES.SOURCE_OUTSIDE_ROOT);
|
|
1276
1624
|
}
|
|
@@ -1282,12 +1630,12 @@ async function resolveSourceFile(options) {
|
|
|
1282
1630
|
throw new import_shared3.SpotPatchError(import_shared3.ERROR_CODES.SOURCE_NOT_FOUND);
|
|
1283
1631
|
}
|
|
1284
1632
|
const sourcePath = await assertInsideRoot(options.root, registeredPath);
|
|
1285
|
-
if (!ALLOWED_EXTENSIONS.has(
|
|
1633
|
+
if (!ALLOWED_EXTENSIONS.has(import_node_path4.default.extname(sourcePath).toLowerCase())) {
|
|
1286
1634
|
throw new import_shared3.SpotPatchError(import_shared3.ERROR_CODES.SOURCE_NOT_FOUND);
|
|
1287
1635
|
}
|
|
1288
1636
|
let sourceStat;
|
|
1289
1637
|
try {
|
|
1290
|
-
sourceStat = await (0,
|
|
1638
|
+
sourceStat = await (0, import_promises3.stat)(sourcePath);
|
|
1291
1639
|
} catch (error) {
|
|
1292
1640
|
if (isMissingFileError(error)) {
|
|
1293
1641
|
throw new import_shared3.SpotPatchError(import_shared3.ERROR_CODES.SOURCE_NOT_FOUND, void 0, {
|
|
@@ -1307,7 +1655,7 @@ async function resolveSourceFile(options) {
|
|
|
1307
1655
|
|
|
1308
1656
|
// src/server/source-context.ts
|
|
1309
1657
|
function toDisplayPath(root, sourcePath) {
|
|
1310
|
-
return
|
|
1658
|
+
return import_node_path5.default.relative(root, sourcePath).split(import_node_path5.default.sep).join("/");
|
|
1311
1659
|
}
|
|
1312
1660
|
async function readSourceContext(options) {
|
|
1313
1661
|
const sourcePath = await resolveSourceFile({
|
|
@@ -1317,7 +1665,7 @@ async function readSourceContext(options) {
|
|
|
1317
1665
|
});
|
|
1318
1666
|
let source;
|
|
1319
1667
|
try {
|
|
1320
|
-
source = await (0,
|
|
1668
|
+
source = await (0, import_promises4.readFile)(sourcePath, "utf8");
|
|
1321
1669
|
} catch (error) {
|
|
1322
1670
|
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
|
1323
1671
|
throw new import_shared4.SpotPatchError(import_shared4.ERROR_CODES.SOURCE_NOT_FOUND, void 0, {
|
|
@@ -1330,11 +1678,11 @@ async function readSourceContext(options) {
|
|
|
1330
1678
|
if (options.request.line > lines.length) {
|
|
1331
1679
|
throw new import_shared4.SpotPatchError(import_shared4.ERROR_CODES.INVALID_REQUEST);
|
|
1332
1680
|
}
|
|
1333
|
-
const extension =
|
|
1681
|
+
const extension = import_node_path5.default.extname(sourcePath).toLowerCase();
|
|
1334
1682
|
return extractCodeContext({
|
|
1335
1683
|
source,
|
|
1336
1684
|
sourcePath,
|
|
1337
|
-
relativePath: toDisplayPath(await (0,
|
|
1685
|
+
relativePath: toDisplayPath(await (0, import_promises4.realpath)(options.root), sourcePath),
|
|
1338
1686
|
language: extension === ".tsx" ? "tsx" : "jsx",
|
|
1339
1687
|
line: options.request.line,
|
|
1340
1688
|
column: options.request.column,
|
|
@@ -1370,7 +1718,7 @@ async function authorizeSourceRef(source, registry, root) {
|
|
|
1370
1718
|
registry,
|
|
1371
1719
|
root
|
|
1372
1720
|
});
|
|
1373
|
-
const relativePath =
|
|
1721
|
+
const relativePath = import_node_path6.default.relative(await (0, import_promises5.realpath)(root), sourcePath).split(import_node_path6.default.sep).join("/");
|
|
1374
1722
|
if (source.relativePath !== void 0 && source.relativePath !== relativePath) {
|
|
1375
1723
|
throw new import_shared5.SpotPatchError(import_shared5.ERROR_CODES.INVALID_REQUEST);
|
|
1376
1724
|
}
|
|
@@ -1475,6 +1823,7 @@ async function authorizeAgentJobRequest(input) {
|
|
|
1475
1823
|
});
|
|
1476
1824
|
return Object.freeze({
|
|
1477
1825
|
annotation,
|
|
1826
|
+
...input.request.applyMode === void 0 ? {} : { applyMode: input.request.applyMode },
|
|
1478
1827
|
providerProfileId: input.request.providerProfileId,
|
|
1479
1828
|
modelProfileId: input.request.modelProfileId,
|
|
1480
1829
|
providerDataConsent: true,
|
|
@@ -1541,21 +1890,21 @@ var EVENT_STREAM_END_STATUSES = /* @__PURE__ */ new Set([
|
|
|
1541
1890
|
"reverted",
|
|
1542
1891
|
"failed"
|
|
1543
1892
|
]);
|
|
1544
|
-
function matchAgentRequestPath(
|
|
1545
|
-
if (
|
|
1893
|
+
function matchAgentRequestPath(path8) {
|
|
1894
|
+
if (path8 === import_shared7.SPOTPATCH_ENDPOINTS.agentCapability) {
|
|
1546
1895
|
return Object.freeze({ kind: "capability" });
|
|
1547
1896
|
}
|
|
1548
|
-
if (
|
|
1897
|
+
if (path8 === import_shared7.SPOTPATCH_ENDPOINTS.agentWorkspaceHealth) {
|
|
1549
1898
|
return Object.freeze({ kind: "workspace-health" });
|
|
1550
1899
|
}
|
|
1551
|
-
if (
|
|
1900
|
+
if (path8 === import_shared7.SPOTPATCH_ENDPOINTS.agentJobs) {
|
|
1552
1901
|
return Object.freeze({ kind: "create-job" });
|
|
1553
1902
|
}
|
|
1554
1903
|
const prefix = `${import_shared7.SPOTPATCH_ENDPOINTS.agentJobs}/`;
|
|
1555
|
-
if (!
|
|
1904
|
+
if (!path8.startsWith(prefix)) {
|
|
1556
1905
|
return void 0;
|
|
1557
1906
|
}
|
|
1558
|
-
const segments =
|
|
1907
|
+
const segments = path8.slice(prefix.length).split("/");
|
|
1559
1908
|
const jobId = segments[0];
|
|
1560
1909
|
const action = segments[1];
|
|
1561
1910
|
if (segments.length !== 2 || jobId === void 0 || !AGENT_JOB_ID_PATTERN.test(jobId) || action === void 0 || !AGENT_JOB_ACTIONS.has(action)) {
|
|
@@ -1725,7 +2074,7 @@ async function handleAgentRequest(request, response, options, route, writeSucces
|
|
|
1725
2074
|
}
|
|
1726
2075
|
|
|
1727
2076
|
// src/server/editor.ts
|
|
1728
|
-
var
|
|
2077
|
+
var import_node_child_process2 = require("child_process");
|
|
1729
2078
|
var import_launch_editor = __toESM(require("launch-editor"), 1);
|
|
1730
2079
|
var EDITOR_STARTUP_GRACE_MS = 300;
|
|
1731
2080
|
function normalizedEditorEnvironment(environment) {
|
|
@@ -1752,7 +2101,7 @@ function editorCommand(editor) {
|
|
|
1752
2101
|
var DEFAULT_DEPENDENCIES2 = Object.freeze({
|
|
1753
2102
|
environment: process.env,
|
|
1754
2103
|
fallbackLauncher: import_launch_editor.default,
|
|
1755
|
-
processSpawner: (command, arguments_, options) => (0,
|
|
2104
|
+
processSpawner: (command, arguments_, options) => (0, import_node_child_process2.spawn)(command, [...arguments_], options),
|
|
1756
2105
|
startupGraceMs: EDITOR_STARTUP_GRACE_MS
|
|
1757
2106
|
});
|
|
1758
2107
|
function createEditorLauncher(dependencies = DEFAULT_DEPENDENCIES2) {
|
|
@@ -1810,7 +2159,7 @@ function createEditorLauncher(dependencies = DEFAULT_DEPENDENCIES2) {
|
|
|
1810
2159
|
var launchConfiguredEditor = createEditorLauncher();
|
|
1811
2160
|
|
|
1812
2161
|
// src/server/request-security.ts
|
|
1813
|
-
var
|
|
2162
|
+
var import_node_crypto4 = require("crypto");
|
|
1814
2163
|
var import_node_net = require("net");
|
|
1815
2164
|
var import_shared8 = require("@spotpatch/shared");
|
|
1816
2165
|
function getSingleHeader(request, name) {
|
|
@@ -1823,7 +2172,7 @@ function tokensMatch(actual, expected) {
|
|
|
1823
2172
|
}
|
|
1824
2173
|
const actualBytes = Buffer.from(actual);
|
|
1825
2174
|
const expectedBytes = Buffer.from(expected);
|
|
1826
|
-
return actualBytes.byteLength === expectedBytes.byteLength && (0,
|
|
2175
|
+
return actualBytes.byteLength === expectedBytes.byteLength && (0, import_node_crypto4.timingSafeEqual)(actualBytes, expectedBytes);
|
|
1827
2176
|
}
|
|
1828
2177
|
function isLoopbackHostname(hostname) {
|
|
1829
2178
|
const normalized = hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
@@ -2078,14 +2427,14 @@ async function handleOpenEditor(request, options) {
|
|
|
2078
2427
|
function createSpotPatchMiddleware(options) {
|
|
2079
2428
|
const bootstrap = options.bootstrap === void 0 ? void 0 : resolveRuntimeBootstrapOptions(options.bootstrap);
|
|
2080
2429
|
return (request, response, next) => {
|
|
2081
|
-
const
|
|
2082
|
-
const agentRoute = matchAgentRequestPath(
|
|
2083
|
-
if (
|
|
2430
|
+
const path8 = requestPath(request);
|
|
2431
|
+
const agentRoute = matchAgentRequestPath(path8);
|
|
2432
|
+
if (path8 !== import_shared10.SPOTPATCH_ENDPOINTS.sourceContext && path8 !== import_shared10.SPOTPATCH_ENDPOINTS.openEditor && agentRoute === void 0 && !path8.startsWith(`${import_shared10.SPOTPATCH_API_BASE}/`)) {
|
|
2084
2433
|
next();
|
|
2085
2434
|
return;
|
|
2086
2435
|
}
|
|
2087
2436
|
const handle = async () => {
|
|
2088
|
-
if (
|
|
2437
|
+
if (path8 === import_shared10.SPOTPATCH_ENDPOINTS.bootstrap && bootstrap !== void 0) {
|
|
2089
2438
|
const data = await readRuntimeBootstrap(
|
|
2090
2439
|
request,
|
|
2091
2440
|
bootstrap
|
|
@@ -2097,7 +2446,7 @@ function createSpotPatchMiddleware(options) {
|
|
|
2097
2446
|
allowLan: options.options.allowLan,
|
|
2098
2447
|
sessionToken: options.session.token
|
|
2099
2448
|
});
|
|
2100
|
-
if (
|
|
2449
|
+
if (path8 === import_shared10.SPOTPATCH_ENDPOINTS.sourceContext) {
|
|
2101
2450
|
if (request.method !== "POST") {
|
|
2102
2451
|
throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.INVALID_REQUEST);
|
|
2103
2452
|
}
|
|
@@ -2105,7 +2454,7 @@ function createSpotPatchMiddleware(options) {
|
|
|
2105
2454
|
writeJson(response, 200, { ok: true, data });
|
|
2106
2455
|
return;
|
|
2107
2456
|
}
|
|
2108
|
-
if (
|
|
2457
|
+
if (path8 === import_shared10.SPOTPATCH_ENDPOINTS.openEditor) {
|
|
2109
2458
|
if (request.method !== "POST") {
|
|
2110
2459
|
throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.INVALID_REQUEST);
|
|
2111
2460
|
}
|
|
@@ -2133,9 +2482,9 @@ function createSpotPatchMiddleware(options) {
|
|
|
2133
2482
|
}
|
|
2134
2483
|
|
|
2135
2484
|
// src/server/source-registration.ts
|
|
2136
|
-
var
|
|
2137
|
-
var
|
|
2138
|
-
var
|
|
2485
|
+
var import_node_crypto5 = require("crypto");
|
|
2486
|
+
var import_promises6 = require("fs/promises");
|
|
2487
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
2139
2488
|
var import_compiler = require("@spotpatch/compiler");
|
|
2140
2489
|
var import_zod2 = require("zod");
|
|
2141
2490
|
var REGISTRATION_BODY_LIMIT_BYTES = 4096;
|
|
@@ -2156,14 +2505,14 @@ function identitiesMatch(actual, expected) {
|
|
|
2156
2505
|
}
|
|
2157
2506
|
const actualBytes = Buffer.from(actual);
|
|
2158
2507
|
const expectedBytes = Buffer.from(expected);
|
|
2159
|
-
return actualBytes.byteLength === expectedBytes.byteLength && (0,
|
|
2508
|
+
return actualBytes.byteLength === expectedBytes.byteLength && (0, import_node_crypto5.timingSafeEqual)(actualBytes, expectedBytes);
|
|
2160
2509
|
}
|
|
2161
2510
|
function isWithinRoot(root, candidate) {
|
|
2162
|
-
const relative =
|
|
2163
|
-
return relative === "" || !relative.startsWith(`..${
|
|
2511
|
+
const relative = import_node_path7.default.relative(root, candidate);
|
|
2512
|
+
return relative === "" || !relative.startsWith(`..${import_node_path7.default.sep}`) && relative !== ".." && !import_node_path7.default.isAbsolute(relative);
|
|
2164
2513
|
}
|
|
2165
2514
|
function hasForbiddenSegment(root, candidate) {
|
|
2166
|
-
return
|
|
2515
|
+
return import_node_path7.default.relative(root, candidate).split(import_node_path7.default.sep).some((segment) => FORBIDDEN_SOURCE_SEGMENTS.has(segment));
|
|
2167
2516
|
}
|
|
2168
2517
|
function writeJson2(response, statusCode, payload) {
|
|
2169
2518
|
const body = JSON.stringify(payload);
|
|
@@ -2185,15 +2534,15 @@ function requestComesFromLoopbackWorker(request) {
|
|
|
2185
2534
|
}
|
|
2186
2535
|
}
|
|
2187
2536
|
async function resolveAuthorizedSource(root, requestedPath, shouldTransform) {
|
|
2188
|
-
if (!
|
|
2537
|
+
if (!import_node_path7.default.isAbsolute(requestedPath) || requestedPath.includes("\0")) {
|
|
2189
2538
|
return void 0;
|
|
2190
2539
|
}
|
|
2191
2540
|
try {
|
|
2192
|
-
const sourceStat = await (0,
|
|
2541
|
+
const sourceStat = await (0, import_promises6.lstat)(requestedPath);
|
|
2193
2542
|
if (!sourceStat.isFile() || sourceStat.isSymbolicLink()) {
|
|
2194
2543
|
return void 0;
|
|
2195
2544
|
}
|
|
2196
|
-
const resolvedPath = await (0,
|
|
2545
|
+
const resolvedPath = await (0, import_promises6.realpath)(requestedPath);
|
|
2197
2546
|
if (!isWithinRoot(root, resolvedPath) || hasForbiddenSegment(root, resolvedPath) || !shouldTransform(resolvedPath)) {
|
|
2198
2547
|
return void 0;
|
|
2199
2548
|
}
|
|
@@ -2206,7 +2555,7 @@ async function createSourceRegistrationService(input) {
|
|
|
2206
2555
|
if (!REGISTRATION_IDENTITY_PATTERN.test(input.internalSecret) || !REGISTRATION_IDENTITY_PATTERN.test(input.registryEpoch)) {
|
|
2207
2556
|
throw new TypeError("The source registration identity is invalid.");
|
|
2208
2557
|
}
|
|
2209
|
-
const root = await (0,
|
|
2558
|
+
const root = await (0, import_promises6.realpath)(input.root);
|
|
2210
2559
|
const sourceFilter = (0, import_compiler.createSourceFilter)(root, input.options);
|
|
2211
2560
|
const handler = (request, response) => {
|
|
2212
2561
|
const handle = async () => {
|
|
@@ -2251,11 +2600,11 @@ async function createSourceRegistrationService(input) {
|
|
|
2251
2600
|
}
|
|
2252
2601
|
|
|
2253
2602
|
// src/session/session.ts
|
|
2254
|
-
var
|
|
2603
|
+
var import_node_crypto6 = require("crypto");
|
|
2255
2604
|
function createSession() {
|
|
2256
2605
|
return Object.freeze({
|
|
2257
|
-
id: (0,
|
|
2258
|
-
token: (0,
|
|
2606
|
+
id: (0, import_node_crypto6.randomBytes)(16).toString("base64url"),
|
|
2607
|
+
token: (0, import_node_crypto6.randomBytes)(16).toString("base64url")
|
|
2259
2608
|
});
|
|
2260
2609
|
}
|
|
2261
2610
|
|
|
@@ -2283,7 +2632,7 @@ var BUDGET_KEYS = Object.freeze([
|
|
|
2283
2632
|
"maxComponentDepth"
|
|
2284
2633
|
]);
|
|
2285
2634
|
var REGEXP_FLAGS_PATTERN = /^(?!.*(.).*\1)[dgimsuvy]*$/u;
|
|
2286
|
-
function
|
|
2635
|
+
function isRecord2(value) {
|
|
2287
2636
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2288
2637
|
}
|
|
2289
2638
|
function hasExactKeys(value, keys) {
|
|
@@ -2368,7 +2717,7 @@ function parseFilterList(value) {
|
|
|
2368
2717
|
}
|
|
2369
2718
|
return Object.freeze(
|
|
2370
2719
|
value.map((entry) => {
|
|
2371
|
-
if (!
|
|
2720
|
+
if (!isRecord2(entry)) {
|
|
2372
2721
|
throw new TypeError("The SpotPatch filter transport is invalid.");
|
|
2373
2722
|
}
|
|
2374
2723
|
if (entry.kind === "string" && hasExactKeys(entry, ["kind", "value"]) && typeof entry.value === "string" && entry.value.length > 0 && entry.value.length <= 1024 && !entry.value.includes("\0")) {
|
|
@@ -2386,7 +2735,7 @@ function parseFilterList(value) {
|
|
|
2386
2735
|
);
|
|
2387
2736
|
}
|
|
2388
2737
|
function parseBudget(value) {
|
|
2389
|
-
if (!
|
|
2738
|
+
if (!isRecord2(value) || !hasExactKeys(value, BUDGET_KEYS)) {
|
|
2390
2739
|
throw new TypeError("The SpotPatch budget transport is invalid.");
|
|
2391
2740
|
}
|
|
2392
2741
|
const budget = Object.fromEntries(
|
|
@@ -2395,10 +2744,10 @@ function parseBudget(value) {
|
|
|
2395
2744
|
return Object.freeze(budget);
|
|
2396
2745
|
}
|
|
2397
2746
|
function parseSerializedSpotPatchOptions(value) {
|
|
2398
|
-
if (!
|
|
2747
|
+
if (!isRecord2(value) || !hasExactKeys(value, OPTION_KEYS)) {
|
|
2399
2748
|
throw new TypeError("The SpotPatch options transport is invalid.");
|
|
2400
2749
|
}
|
|
2401
|
-
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 && !
|
|
2750
|
+
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)) {
|
|
2402
2751
|
throw new TypeError("The SpotPatch options transport is invalid.");
|
|
2403
2752
|
}
|
|
2404
2753
|
try {
|
|
@@ -2426,19 +2775,25 @@ function parseSerializedSpotPatchOptions(value) {
|
|
|
2426
2775
|
0 && (module.exports = {
|
|
2427
2776
|
DEFAULT_EXCLUDE,
|
|
2428
2777
|
DEFAULT_OPTIONS,
|
|
2778
|
+
applyIntegrationPlan,
|
|
2429
2779
|
createAgentJobManager,
|
|
2780
|
+
createIntegrationFileChange,
|
|
2430
2781
|
createRuntimeAiConfig,
|
|
2431
2782
|
createSession,
|
|
2432
2783
|
createSourceRegistrationService,
|
|
2433
2784
|
createSourceRegistry,
|
|
2434
2785
|
createSpotPatchMiddleware,
|
|
2786
|
+
discoverProjectValidationCheck,
|
|
2787
|
+
integrationPathExists,
|
|
2435
2788
|
isLoopbackHostname,
|
|
2436
2789
|
parseSerializedSpotPatchOptions,
|
|
2790
|
+
readIntegrationFile,
|
|
2437
2791
|
readJsonRequestBody,
|
|
2438
2792
|
readRuntimeBootstrap,
|
|
2439
2793
|
resolveCredentialEnvironment,
|
|
2440
2794
|
resolveEnvironmentAiConfiguration,
|
|
2441
2795
|
resolveOptions,
|
|
2796
|
+
resolveProjectOptions,
|
|
2442
2797
|
resolveRuntimeBootstrapOptions,
|
|
2443
2798
|
serializeResolvedSpotPatchOptions
|
|
2444
2799
|
});
|