@spotpatch/vite 1.0.0 → 1.2.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/README.md +35 -12
- package/dist/index.cjs +222 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -8
- package/dist/index.d.ts +19 -8
- package/dist/index.js +223 -43
- package/dist/index.js.map +1 -1
- package/dist/runtime-client.js +85 -32
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -22,22 +22,45 @@ import react from "@vitejs/plugin-react-swc";
|
|
|
22
22
|
import { defineConfig } from "vite";
|
|
23
23
|
|
|
24
24
|
export default defineConfig({
|
|
25
|
-
plugins: [
|
|
26
|
-
spotPatch({
|
|
27
|
-
editor: "vscode",
|
|
28
|
-
redact: true,
|
|
29
|
-
allowLan: false,
|
|
30
|
-
locale: "auto",
|
|
31
|
-
maxTargets: 8,
|
|
32
|
-
}),
|
|
33
|
-
react(),
|
|
34
|
-
],
|
|
25
|
+
plugins: [spotPatch(), react()],
|
|
35
26
|
});
|
|
36
27
|
```
|
|
37
28
|
|
|
38
29
|
SpotPatch injects no runtime, source markers, or local API endpoints into a
|
|
39
|
-
production build. AI
|
|
40
|
-
|
|
30
|
+
production build. AI stays disabled when no AI environment exists. To enable the
|
|
31
|
+
single-provider setup without changing `vite.config.ts`, add the three required
|
|
32
|
+
values to a Git-ignored `.env.local`:
|
|
33
|
+
|
|
34
|
+
```dotenv
|
|
35
|
+
SPOTPATCH_AI_BASE_URL=https://relay.example.com/v1
|
|
36
|
+
SPOTPATCH_AI_MODEL=provider-model-name
|
|
37
|
+
SPOTPATCH_AI_API_KEY=<your-key>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`SPOTPATCH_AI_PROTOCOL` optionally selects `chat-completions` (the default) or
|
|
41
|
+
`responses`. `SPOTPATCH_AI_AUTHENTICATION` optionally selects `bearer` (the
|
|
42
|
+
default) or `x-api-key`. Partial environment configuration fails fast without
|
|
43
|
+
printing credential values. API keys must never use a `VITE_` prefix.
|
|
44
|
+
|
|
45
|
+
Source actions auto-detect Cursor or VS Code and open the selected file at its
|
|
46
|
+
exact line and column. Set `editor: "cursor"` or `editor: "vscode"` only when an
|
|
47
|
+
explicit preference is required. The workbench also links to the
|
|
48
|
+
[SpotPatch GitHub repository](https://github.com/huanglvjing/spotpatch) for docs,
|
|
49
|
+
issues, and project updates.
|
|
50
|
+
|
|
51
|
+
Non-secret URL and model values can instead use the concise API:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
spotPatch({
|
|
55
|
+
ai: {
|
|
56
|
+
baseURL: "https://relay.example.com/v1",
|
|
57
|
+
model: "provider-model-name",
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The full provider map remains available for multiple providers, multiple models,
|
|
63
|
+
custom labels, checks, and limits.
|
|
41
64
|
|
|
42
65
|
See the [repository README](https://github.com/huanglvjing/spotpatch#readme) for
|
|
43
66
|
the complete setup and security model.
|
package/dist/index.cjs
CHANGED
|
@@ -37,6 +37,71 @@ __export(index_exports, {
|
|
|
37
37
|
});
|
|
38
38
|
module.exports = __toCommonJS(index_exports);
|
|
39
39
|
|
|
40
|
+
// src/plugin.ts
|
|
41
|
+
var import_node_path10 = __toESM(require("path"), 1);
|
|
42
|
+
var import_vite2 = require("vite");
|
|
43
|
+
|
|
44
|
+
// src/environment-ai.ts
|
|
45
|
+
var AI_ENVIRONMENT_NAMES = Object.freeze({
|
|
46
|
+
authentication: "SPOTPATCH_AI_AUTHENTICATION",
|
|
47
|
+
baseURL: "SPOTPATCH_AI_BASE_URL",
|
|
48
|
+
credential: "SPOTPATCH_AI_API_KEY",
|
|
49
|
+
model: "SPOTPATCH_AI_MODEL",
|
|
50
|
+
protocol: "SPOTPATCH_AI_PROTOCOL"
|
|
51
|
+
});
|
|
52
|
+
function normalizedValue(environment, name) {
|
|
53
|
+
const value = environment[name];
|
|
54
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
57
|
+
return value.trim();
|
|
58
|
+
}
|
|
59
|
+
function resolveEnvironmentAiConfiguration(environment) {
|
|
60
|
+
const baseURL = normalizedValue(environment, AI_ENVIRONMENT_NAMES.baseURL);
|
|
61
|
+
const model = normalizedValue(environment, AI_ENVIRONMENT_NAMES.model);
|
|
62
|
+
const credential = normalizedValue(environment, AI_ENVIRONMENT_NAMES.credential);
|
|
63
|
+
const protocol = normalizedValue(environment, AI_ENVIRONMENT_NAMES.protocol);
|
|
64
|
+
const authentication = normalizedValue(
|
|
65
|
+
environment,
|
|
66
|
+
AI_ENVIRONMENT_NAMES.authentication
|
|
67
|
+
);
|
|
68
|
+
const configuredValues = [baseURL, model, credential, protocol, authentication];
|
|
69
|
+
if (configuredValues.every((value) => value === void 0)) {
|
|
70
|
+
return Object.freeze({ ai: false });
|
|
71
|
+
}
|
|
72
|
+
const missing = [
|
|
73
|
+
[AI_ENVIRONMENT_NAMES.baseURL, baseURL],
|
|
74
|
+
[AI_ENVIRONMENT_NAMES.model, model],
|
|
75
|
+
[AI_ENVIRONMENT_NAMES.credential, credential]
|
|
76
|
+
].filter((entry) => entry[1] === void 0).map(([name]) => name);
|
|
77
|
+
if (missing.length > 0) {
|
|
78
|
+
throw new RangeError(
|
|
79
|
+
`SpotPatch AI environment configuration is incomplete; missing ${missing.join(", ")}.`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
if (baseURL === void 0 || model === void 0 || credential === void 0) {
|
|
83
|
+
throw new RangeError("SpotPatch AI environment configuration is incomplete.");
|
|
84
|
+
}
|
|
85
|
+
if (protocol !== void 0 && protocol !== "responses" && protocol !== "chat-completions") {
|
|
86
|
+
throw new RangeError(
|
|
87
|
+
"SpotPatch SPOTPATCH_AI_PROTOCOL must be responses or chat-completions."
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (authentication !== void 0 && authentication !== "bearer" && authentication !== "x-api-key") {
|
|
91
|
+
throw new RangeError(
|
|
92
|
+
"SpotPatch SPOTPATCH_AI_AUTHENTICATION must be bearer or x-api-key."
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return Object.freeze({
|
|
96
|
+
ai: Object.freeze({
|
|
97
|
+
baseURL,
|
|
98
|
+
model,
|
|
99
|
+
...protocol === void 0 ? {} : { protocol },
|
|
100
|
+
...authentication === void 0 ? {} : { authentication }
|
|
101
|
+
})
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
40
105
|
// src/options.ts
|
|
41
106
|
var import_shared = require("@spotpatch/shared");
|
|
42
107
|
var import_zod = require("zod");
|
|
@@ -61,7 +126,7 @@ var DEFAULT_OPTIONS = Object.freeze({
|
|
|
61
126
|
enabled: true,
|
|
62
127
|
include: DEFAULT_INCLUDE,
|
|
63
128
|
exclude: DEFAULT_EXCLUDE,
|
|
64
|
-
editor: "
|
|
129
|
+
editor: "auto",
|
|
65
130
|
redact: true,
|
|
66
131
|
budget: DEFAULT_BUDGET,
|
|
67
132
|
shortcut: "Mod+Shift+S",
|
|
@@ -101,6 +166,7 @@ var aiOptionsSchema = import_zod.z.strictObject({
|
|
|
101
166
|
type: import_zod.z.literal("openai-compatible"),
|
|
102
167
|
label: import_zod.z.string(),
|
|
103
168
|
protocol: import_zod.z.enum(["responses", "chat-completions"]),
|
|
169
|
+
authentication: import_zod.z.enum(["bearer", "x-api-key"]).optional(),
|
|
104
170
|
baseURL: import_zod.z.string(),
|
|
105
171
|
apiKeyEnv: import_zod.z.string(),
|
|
106
172
|
models: import_zod.z.record(
|
|
@@ -118,6 +184,16 @@ var aiOptionsSchema = import_zod.z.strictObject({
|
|
|
118
184
|
limits: agentLimitsSchema
|
|
119
185
|
}).optional()
|
|
120
186
|
});
|
|
187
|
+
var simpleAiOptionsSchema = import_zod.z.strictObject({
|
|
188
|
+
baseURL: import_zod.z.string(),
|
|
189
|
+
model: import_zod.z.string(),
|
|
190
|
+
apiKeyEnv: import_zod.z.string().optional(),
|
|
191
|
+
protocol: import_zod.z.enum(["responses", "chat-completions"]).optional(),
|
|
192
|
+
authentication: import_zod.z.enum(["bearer", "x-api-key"]).optional(),
|
|
193
|
+
providerLabel: import_zod.z.string().optional(),
|
|
194
|
+
modelLabel: import_zod.z.string().optional(),
|
|
195
|
+
execution: aiOptionsSchema.shape.execution
|
|
196
|
+
});
|
|
121
197
|
function assertIdentifier(value, label) {
|
|
122
198
|
if (!PROFILE_ID_PATTERN.test(value)) {
|
|
123
199
|
throw new RangeError(
|
|
@@ -217,6 +293,7 @@ function resolveProviders(providers) {
|
|
|
217
293
|
type: provider.type,
|
|
218
294
|
label: nonEmpty(provider.label, "provider label", 100),
|
|
219
295
|
protocol: provider.protocol,
|
|
296
|
+
authentication: provider.authentication ?? "bearer",
|
|
220
297
|
baseURL: normalizeProviderBaseURL(provider.baseURL),
|
|
221
298
|
apiKeyEnv: provider.apiKeyEnv,
|
|
222
299
|
models,
|
|
@@ -260,7 +337,36 @@ function resolveAiOptions(options) {
|
|
|
260
337
|
if (options === void 0 || options === false) {
|
|
261
338
|
return false;
|
|
262
339
|
}
|
|
263
|
-
const
|
|
340
|
+
const expanded = "providers" in options ? options : (() => {
|
|
341
|
+
const simple = simpleAiOptionsSchema.safeParse(options);
|
|
342
|
+
if (!simple.success) {
|
|
343
|
+
throw new RangeError("SpotPatch AI configuration is invalid.");
|
|
344
|
+
}
|
|
345
|
+
const providerId = "default";
|
|
346
|
+
const modelId = "default";
|
|
347
|
+
return {
|
|
348
|
+
providers: {
|
|
349
|
+
[providerId]: {
|
|
350
|
+
type: "openai-compatible",
|
|
351
|
+
label: simple.data.providerLabel ?? "AI provider",
|
|
352
|
+
protocol: simple.data.protocol ?? "chat-completions",
|
|
353
|
+
authentication: simple.data.authentication ?? "bearer",
|
|
354
|
+
baseURL: simple.data.baseURL,
|
|
355
|
+
apiKeyEnv: simple.data.apiKeyEnv ?? "SPOTPATCH_AI_API_KEY",
|
|
356
|
+
models: {
|
|
357
|
+
[modelId]: {
|
|
358
|
+
label: simple.data.modelLabel ?? "AI model",
|
|
359
|
+
model: simple.data.model
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
defaultModel: modelId
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
defaultProvider: providerId,
|
|
366
|
+
...simple.data.execution === void 0 ? {} : { execution: simple.data.execution }
|
|
367
|
+
};
|
|
368
|
+
})();
|
|
369
|
+
const parsed = aiOptionsSchema.safeParse(expanded);
|
|
264
370
|
if (!parsed.success) {
|
|
265
371
|
throw new RangeError("SpotPatch AI configuration is invalid.");
|
|
266
372
|
}
|
|
@@ -320,7 +426,7 @@ function assertPositiveBudget(budget) {
|
|
|
320
426
|
}
|
|
321
427
|
}
|
|
322
428
|
}
|
|
323
|
-
function resolveOptions(options = {}) {
|
|
429
|
+
function resolveOptions(options = {}, environmentAi) {
|
|
324
430
|
const budget = Object.freeze({
|
|
325
431
|
...DEFAULT_OPTIONS.budget,
|
|
326
432
|
...options.budget
|
|
@@ -328,9 +434,13 @@ function resolveOptions(options = {}) {
|
|
|
328
434
|
assertPositiveBudget(budget);
|
|
329
435
|
const maxTargets = options.maxTargets ?? DEFAULT_OPTIONS.maxTargets;
|
|
330
436
|
const locale = options.locale ?? DEFAULT_OPTIONS.locale;
|
|
437
|
+
const editor = options.editor ?? DEFAULT_OPTIONS.editor;
|
|
331
438
|
if (!import_shared.SPOTPATCH_LOCALE_PREFERENCES.includes(locale)) {
|
|
332
439
|
throw new RangeError("SpotPatch locale must be auto, en-US, or zh-CN.");
|
|
333
440
|
}
|
|
441
|
+
if (!import_shared.SPOTPATCH_EDITOR_PREFERENCES.includes(editor)) {
|
|
442
|
+
throw new RangeError("SpotPatch editor must be auto, vscode, or cursor.");
|
|
443
|
+
}
|
|
334
444
|
if (!Number.isSafeInteger(maxTargets) || maxTargets < 1 || maxTargets > import_shared.MAX_ANNOTATION_TARGETS) {
|
|
335
445
|
throw new RangeError(
|
|
336
446
|
`SpotPatch maxTargets must be an integer between 1 and ${String(import_shared.MAX_ANNOTATION_TARGETS)}.`
|
|
@@ -340,7 +450,7 @@ function resolveOptions(options = {}) {
|
|
|
340
450
|
enabled: options.enabled ?? DEFAULT_OPTIONS.enabled,
|
|
341
451
|
include: Object.freeze([...options.include ?? DEFAULT_OPTIONS.include]),
|
|
342
452
|
exclude: Object.freeze([...options.exclude ?? DEFAULT_OPTIONS.exclude]),
|
|
343
|
-
editor
|
|
453
|
+
editor,
|
|
344
454
|
redact: options.redact ?? DEFAULT_OPTIONS.redact,
|
|
345
455
|
budget,
|
|
346
456
|
shortcut: options.shortcut ?? DEFAULT_OPTIONS.shortcut,
|
|
@@ -348,7 +458,7 @@ function resolveOptions(options = {}) {
|
|
|
348
458
|
debug: options.debug ?? DEFAULT_OPTIONS.debug,
|
|
349
459
|
locale,
|
|
350
460
|
maxTargets,
|
|
351
|
-
ai: resolveAiOptions(options.ai)
|
|
461
|
+
ai: resolveAiOptions(options.ai ?? environmentAi)
|
|
352
462
|
};
|
|
353
463
|
if (resolved.shortcut.trim().length === 0) {
|
|
354
464
|
throw new RangeError("SpotPatch shortcut cannot be empty.");
|
|
@@ -405,7 +515,7 @@ var import_node_path2 = __toESM(require("path"), 1);
|
|
|
405
515
|
// package.json
|
|
406
516
|
var package_default = {
|
|
407
517
|
name: "@spotpatch/vite",
|
|
408
|
-
version: "1.
|
|
518
|
+
version: "1.2.0",
|
|
409
519
|
description: "Vite development plugin for SpotPatch.",
|
|
410
520
|
license: "MIT",
|
|
411
521
|
repository: {
|
|
@@ -497,15 +607,17 @@ function readConsumerViteVersion(root) {
|
|
|
497
607
|
return import_vite.version;
|
|
498
608
|
}
|
|
499
609
|
function createClientModule(input, clientBundle, viteVersion) {
|
|
610
|
+
const options = input.context.getOptions();
|
|
500
611
|
const runtimeConfig = {
|
|
501
|
-
ai: createRuntimeAiConfig(
|
|
502
|
-
budget:
|
|
503
|
-
debug:
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
612
|
+
ai: createRuntimeAiConfig(options.ai),
|
|
613
|
+
budget: options.budget,
|
|
614
|
+
debug: options.debug,
|
|
615
|
+
editor: options.editor,
|
|
616
|
+
locale: options.locale,
|
|
617
|
+
maxTargets: options.maxTargets,
|
|
618
|
+
redact: options.redact,
|
|
507
619
|
sessionToken: input.session.token,
|
|
508
|
-
shortcut:
|
|
620
|
+
shortcut: options.shortcut,
|
|
509
621
|
spotPatchVersion: package_default.version,
|
|
510
622
|
viteVersion
|
|
511
623
|
};
|
|
@@ -628,7 +740,7 @@ function snapshot(job) {
|
|
|
628
740
|
);
|
|
629
741
|
}
|
|
630
742
|
function capabilityCacheKey(provider, model) {
|
|
631
|
-
const configurationDigest = (0, import_node_crypto2.createHash)("sha256").update(provider.baseURL).update("\0").update(provider.protocol).digest("hex");
|
|
743
|
+
const configurationDigest = (0, import_node_crypto2.createHash)("sha256").update(provider.baseURL).update("\0").update(provider.protocol).update("\0").update(provider.authentication).digest("hex");
|
|
632
744
|
return `${provider.id}:${model.id}:${configurationDigest}`;
|
|
633
745
|
}
|
|
634
746
|
function freezeEvent(event) {
|
|
@@ -1553,18 +1665,18 @@ var EVENT_STREAM_END_STATUSES = /* @__PURE__ */ new Set([
|
|
|
1553
1665
|
"reverted",
|
|
1554
1666
|
"failed"
|
|
1555
1667
|
]);
|
|
1556
|
-
function matchAgentRequestPath(
|
|
1557
|
-
if (
|
|
1668
|
+
function matchAgentRequestPath(path11) {
|
|
1669
|
+
if (path11 === import_shared7.SPOTPATCH_ENDPOINTS.agentCapability) {
|
|
1558
1670
|
return Object.freeze({ kind: "capability" });
|
|
1559
1671
|
}
|
|
1560
|
-
if (
|
|
1672
|
+
if (path11 === import_shared7.SPOTPATCH_ENDPOINTS.agentJobs) {
|
|
1561
1673
|
return Object.freeze({ kind: "create-job" });
|
|
1562
1674
|
}
|
|
1563
1675
|
const prefix = `${import_shared7.SPOTPATCH_ENDPOINTS.agentJobs}/`;
|
|
1564
|
-
if (!
|
|
1676
|
+
if (!path11.startsWith(prefix)) {
|
|
1565
1677
|
return void 0;
|
|
1566
1678
|
}
|
|
1567
|
-
const segments =
|
|
1679
|
+
const segments = path11.slice(prefix.length).split("/");
|
|
1568
1680
|
const jobId = segments[0];
|
|
1569
1681
|
const action = segments[1];
|
|
1570
1682
|
if (segments.length !== 2 || jobId === void 0 || !AGENT_JOB_ID_PATTERN.test(jobId) || action === void 0 || !AGENT_JOB_ACTIONS.has(action)) {
|
|
@@ -1709,9 +1821,36 @@ async function handleAgentRequest(request, response, options, route, writeSucces
|
|
|
1709
1821
|
|
|
1710
1822
|
// src/server/editor.ts
|
|
1711
1823
|
var import_launch_editor = __toESM(require("launch-editor"), 1);
|
|
1712
|
-
var
|
|
1713
|
-
|
|
1714
|
-
|
|
1824
|
+
var EDITOR_STARTUP_GRACE_MS = 150;
|
|
1825
|
+
function editorCommand(editor) {
|
|
1826
|
+
if (editor === "vscode") {
|
|
1827
|
+
return "code";
|
|
1828
|
+
}
|
|
1829
|
+
if (editor === "cursor") {
|
|
1830
|
+
return "cursor";
|
|
1831
|
+
}
|
|
1832
|
+
return void 0;
|
|
1833
|
+
}
|
|
1834
|
+
var launchConfiguredEditor = (target, editor) => new Promise((resolve, reject) => {
|
|
1835
|
+
let settled = false;
|
|
1836
|
+
const startupTimer = setTimeout(() => {
|
|
1837
|
+
settled = true;
|
|
1838
|
+
resolve();
|
|
1839
|
+
}, EDITOR_STARTUP_GRACE_MS);
|
|
1840
|
+
const rejectStartup = () => {
|
|
1841
|
+
if (settled) {
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
settled = true;
|
|
1845
|
+
clearTimeout(startupTimer);
|
|
1846
|
+
reject(new Error("The configured editor could not be started."));
|
|
1847
|
+
};
|
|
1848
|
+
try {
|
|
1849
|
+
(0, import_launch_editor.default)(target, editorCommand(editor), rejectStartup);
|
|
1850
|
+
} catch {
|
|
1851
|
+
rejectStartup();
|
|
1852
|
+
}
|
|
1853
|
+
});
|
|
1715
1854
|
|
|
1716
1855
|
// src/server/request-security.ts
|
|
1717
1856
|
var import_node_crypto3 = require("crypto");
|
|
@@ -1892,23 +2031,24 @@ async function handleOpenEditor(request, options) {
|
|
|
1892
2031
|
root: options.root
|
|
1893
2032
|
});
|
|
1894
2033
|
const target = `${sourcePath}:${String(body.line)}:${String(body.column)}`;
|
|
1895
|
-
const editorLauncher = options.editorLauncher ??
|
|
2034
|
+
const editorLauncher = options.editorLauncher ?? launchConfiguredEditor;
|
|
1896
2035
|
try {
|
|
1897
|
-
editorLauncher(target,
|
|
1898
|
-
options.logger?.warn("[spotpatch:server] VS Code rejected an editor request.");
|
|
1899
|
-
});
|
|
2036
|
+
await editorLauncher(target, options.options.editor);
|
|
1900
2037
|
} catch (error) {
|
|
2038
|
+
options.logger?.warn(
|
|
2039
|
+
`[spotpatch:server] ${options.options.editor === "auto" ? "The detected editor" : options.options.editor} rejected an editor request.`
|
|
2040
|
+
);
|
|
1901
2041
|
throw new import_shared9.SpotPatchError(import_shared9.ERROR_CODES.EDITOR_OPEN_FAILED, void 0, {
|
|
1902
2042
|
cause: error
|
|
1903
2043
|
});
|
|
1904
2044
|
}
|
|
1905
|
-
return Object.freeze({});
|
|
2045
|
+
return Object.freeze({ editor: options.options.editor });
|
|
1906
2046
|
}
|
|
1907
2047
|
function createSpotPatchMiddleware(options) {
|
|
1908
2048
|
return (request, response, next) => {
|
|
1909
|
-
const
|
|
1910
|
-
const agentRoute = matchAgentRequestPath(
|
|
1911
|
-
if (
|
|
2049
|
+
const path11 = requestPath(request);
|
|
2050
|
+
const agentRoute = matchAgentRequestPath(path11);
|
|
2051
|
+
if (path11 !== import_shared9.SPOTPATCH_ENDPOINTS.sourceContext && path11 !== import_shared9.SPOTPATCH_ENDPOINTS.openEditor && agentRoute === void 0 && !path11.startsWith(`${import_shared9.SPOTPATCH_API_BASE}/`)) {
|
|
1912
2052
|
next();
|
|
1913
2053
|
return;
|
|
1914
2054
|
}
|
|
@@ -1917,7 +2057,7 @@ function createSpotPatchMiddleware(options) {
|
|
|
1917
2057
|
allowLan: options.options.allowLan,
|
|
1918
2058
|
sessionToken: options.session.token
|
|
1919
2059
|
});
|
|
1920
|
-
if (
|
|
2060
|
+
if (path11 === import_shared9.SPOTPATCH_ENDPOINTS.sourceContext) {
|
|
1921
2061
|
if (request.method !== "POST") {
|
|
1922
2062
|
throw new import_shared9.SpotPatchError(import_shared9.ERROR_CODES.INVALID_REQUEST);
|
|
1923
2063
|
}
|
|
@@ -1925,7 +2065,7 @@ function createSpotPatchMiddleware(options) {
|
|
|
1925
2065
|
writeJson(response, 200, { ok: true, data });
|
|
1926
2066
|
return;
|
|
1927
2067
|
}
|
|
1928
|
-
if (
|
|
2068
|
+
if (path11 === import_shared9.SPOTPATCH_ENDPOINTS.openEditor) {
|
|
1929
2069
|
if (request.method !== "POST") {
|
|
1930
2070
|
throw new import_shared9.SpotPatchError(import_shared9.ERROR_CODES.INVALID_REQUEST);
|
|
1931
2071
|
}
|
|
@@ -1973,11 +2113,16 @@ function createServerPlugin(input) {
|
|
|
1973
2113
|
throw new Error("SpotPatch server initialized before Vite config resolution.");
|
|
1974
2114
|
}
|
|
1975
2115
|
const root = import_node_path6.default.resolve(config.root);
|
|
1976
|
-
|
|
2116
|
+
const options = input.context.getOptions();
|
|
2117
|
+
agentManager = options.ai === false ? void 0 : createAgentJobManager({
|
|
2118
|
+
ai: options.ai,
|
|
2119
|
+
environment: input.context.getCredentialEnvironment(),
|
|
2120
|
+
root
|
|
2121
|
+
});
|
|
1977
2122
|
server.middlewares.use(
|
|
1978
2123
|
createSpotPatchMiddleware({
|
|
1979
2124
|
...agentManager === void 0 ? {} : { agentManager },
|
|
1980
|
-
options
|
|
2125
|
+
options,
|
|
1981
2126
|
registry: input.registry,
|
|
1982
2127
|
root,
|
|
1983
2128
|
session: input.session,
|
|
@@ -1988,7 +2133,7 @@ function createServerPlugin(input) {
|
|
|
1988
2133
|
void closeResources();
|
|
1989
2134
|
});
|
|
1990
2135
|
config.logger.info(
|
|
1991
|
-
`[spotpatch:vite] Ready. Toggle picker with ${
|
|
2136
|
+
`[spotpatch:vite] Ready. Toggle picker with ${options.shortcut}.`
|
|
1992
2137
|
);
|
|
1993
2138
|
},
|
|
1994
2139
|
async closeBundle() {
|
|
@@ -2180,7 +2325,7 @@ function getDisplayPath(root, id) {
|
|
|
2180
2325
|
}
|
|
2181
2326
|
function createTransformPlugin(input) {
|
|
2182
2327
|
let root = process.cwd();
|
|
2183
|
-
let filter = createTransformFilter(root, input.
|
|
2328
|
+
let filter = createTransformFilter(root, input.context.getOptions());
|
|
2184
2329
|
let logger;
|
|
2185
2330
|
const warnedFiles = /* @__PURE__ */ new Set();
|
|
2186
2331
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -2188,9 +2333,12 @@ function createTransformPlugin(input) {
|
|
|
2188
2333
|
name: "spotpatch:transform",
|
|
2189
2334
|
apply: "serve",
|
|
2190
2335
|
enforce: "pre",
|
|
2336
|
+
config(config, environment) {
|
|
2337
|
+
input.configure?.(config, environment);
|
|
2338
|
+
},
|
|
2191
2339
|
configResolved(config) {
|
|
2192
2340
|
root = import_node_path9.default.resolve(config.root);
|
|
2193
|
-
filter = createTransformFilter(root, input.
|
|
2341
|
+
filter = createTransformFilter(root, input.context.getOptions());
|
|
2194
2342
|
logger = config.logger;
|
|
2195
2343
|
},
|
|
2196
2344
|
transform(code, id) {
|
|
@@ -2203,6 +2351,7 @@ function createTransformPlugin(input) {
|
|
|
2203
2351
|
return cache.get(cacheKey) ?? null;
|
|
2204
2352
|
}
|
|
2205
2353
|
const startedAt = performance.now();
|
|
2354
|
+
const options = input.context.getOptions();
|
|
2206
2355
|
try {
|
|
2207
2356
|
const result = injectSourceMarkers({
|
|
2208
2357
|
code,
|
|
@@ -2220,7 +2369,7 @@ function createTransformPlugin(input) {
|
|
|
2220
2369
|
map: result.map.toString()
|
|
2221
2370
|
});
|
|
2222
2371
|
cache.set(cacheKey, output);
|
|
2223
|
-
if (
|
|
2372
|
+
if (options.debug) {
|
|
2224
2373
|
const elapsed = performance.now() - startedAt;
|
|
2225
2374
|
logger?.info(
|
|
2226
2375
|
`[spotpatch:transform] ${getDisplayPath(root, id)} ${elapsed.toFixed(2)}ms`
|
|
@@ -2230,7 +2379,7 @@ function createTransformPlugin(input) {
|
|
|
2230
2379
|
} catch (error) {
|
|
2231
2380
|
if (!warnedFiles.has(cleanId)) {
|
|
2232
2381
|
warnedFiles.add(cleanId);
|
|
2233
|
-
const detail =
|
|
2382
|
+
const detail = options.debug && error instanceof Error ? `: ${error.message}` : "";
|
|
2234
2383
|
logger?.warn(
|
|
2235
2384
|
`[spotpatch:transform] Failed to transform ${getDisplayPath(root, id)}; using original module${detail}`
|
|
2236
2385
|
);
|
|
@@ -2243,16 +2392,46 @@ function createTransformPlugin(input) {
|
|
|
2243
2392
|
|
|
2244
2393
|
// src/plugin.ts
|
|
2245
2394
|
function spotPatch(userOptions = {}) {
|
|
2246
|
-
|
|
2395
|
+
let options = resolveOptions(userOptions);
|
|
2396
|
+
let credentialEnvironment = Object.freeze({});
|
|
2247
2397
|
if (!options.enabled) {
|
|
2248
2398
|
return [];
|
|
2249
2399
|
}
|
|
2250
2400
|
const registry = createSourceRegistry();
|
|
2251
2401
|
const session = createSession();
|
|
2402
|
+
const context = Object.freeze({
|
|
2403
|
+
getCredentialEnvironment: () => credentialEnvironment,
|
|
2404
|
+
getOptions: () => options
|
|
2405
|
+
});
|
|
2406
|
+
const configure = (config, environment) => {
|
|
2407
|
+
const root = import_node_path10.default.resolve(process.cwd(), config.root ?? ".");
|
|
2408
|
+
const loadedEnvironment = config.envDir === false ? process.env : (0, import_vite2.loadEnv)(environment.mode, import_node_path10.default.resolve(root, config.envDir ?? "."), "");
|
|
2409
|
+
const environmentAi = userOptions.ai === void 0 ? resolveEnvironmentAiConfiguration(loadedEnvironment).ai : false;
|
|
2410
|
+
options = resolveOptions(userOptions, environmentAi);
|
|
2411
|
+
if (options.ai === false) {
|
|
2412
|
+
credentialEnvironment = Object.freeze({});
|
|
2413
|
+
return;
|
|
2414
|
+
}
|
|
2415
|
+
const names = new Set(
|
|
2416
|
+
Object.values(options.ai.providers).map((provider) => provider.apiKeyEnv)
|
|
2417
|
+
);
|
|
2418
|
+
const missing = [...names].filter((name) => {
|
|
2419
|
+
const value = loadedEnvironment[name];
|
|
2420
|
+
return value === void 0 || value.trim().length === 0;
|
|
2421
|
+
});
|
|
2422
|
+
if (missing.length > 0) {
|
|
2423
|
+
throw new RangeError(
|
|
2424
|
+
`SpotPatch AI credential environment is missing ${missing.join(", ")}.`
|
|
2425
|
+
);
|
|
2426
|
+
}
|
|
2427
|
+
credentialEnvironment = Object.freeze(
|
|
2428
|
+
Object.fromEntries([...names].map((name) => [name, loadedEnvironment[name]]))
|
|
2429
|
+
);
|
|
2430
|
+
};
|
|
2252
2431
|
return [
|
|
2253
|
-
createTransformPlugin({
|
|
2254
|
-
createRuntimeInjectionPlugin({
|
|
2255
|
-
createServerPlugin({
|
|
2432
|
+
createTransformPlugin({ configure, context, registry }),
|
|
2433
|
+
createRuntimeInjectionPlugin({ context, session }),
|
|
2434
|
+
createServerPlugin({ context, registry, session })
|
|
2256
2435
|
];
|
|
2257
2436
|
}
|
|
2258
2437
|
|