@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/dist/index.d.cts
CHANGED
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
|
-
import { ContextBudget, SpotPatchLocalePreference, ResolvedAiOptions, AiOptions } from '@spotpatch/shared';
|
|
3
|
-
export { AgentApplyMode, AgentCheckDefinition, AgentLimits, AiExecutionOptions, AiModelProfile, AiOptions, AiProviderProtocol, ContextBudget, DEFAULT_AGENT_LIMITS, OpenAICompatibleProviderOptions } from '@spotpatch/shared';
|
|
2
|
+
import { SpotPatchEditorPreference, ContextBudget, SpotPatchLocalePreference, ResolvedAiOptions, AiProviderProtocol, AiProviderAuthentication, AiExecutionOptions, AiOptions } from '@spotpatch/shared';
|
|
3
|
+
export { AgentApplyMode, AgentCheckDefinition, AgentLimits, AiExecutionOptions, AiModelProfile, AiOptions, AiProviderAuthentication, AiProviderProtocol, ContextBudget, DEFAULT_AGENT_LIMITS, OpenAICompatibleProviderOptions, SpotPatchEditorPreference } from '@spotpatch/shared';
|
|
4
4
|
|
|
5
5
|
type FilterEntry = string | RegExp;
|
|
6
|
+
interface SimpleAiOptions {
|
|
7
|
+
readonly baseURL: string;
|
|
8
|
+
readonly model: string;
|
|
9
|
+
readonly apiKeyEnv?: string;
|
|
10
|
+
readonly protocol?: AiProviderProtocol;
|
|
11
|
+
readonly authentication?: AiProviderAuthentication;
|
|
12
|
+
readonly providerLabel?: string;
|
|
13
|
+
readonly modelLabel?: string;
|
|
14
|
+
readonly execution?: AiExecutionOptions;
|
|
15
|
+
}
|
|
16
|
+
type SpotPatchAiOptions = false | SimpleAiOptions | AiOptions;
|
|
6
17
|
interface SpotPatchOptions {
|
|
7
18
|
readonly enabled?: boolean;
|
|
8
19
|
readonly include?: readonly FilterEntry[];
|
|
9
20
|
readonly exclude?: readonly FilterEntry[];
|
|
10
|
-
readonly editor?:
|
|
21
|
+
readonly editor?: SpotPatchEditorPreference;
|
|
11
22
|
readonly redact?: boolean;
|
|
12
23
|
readonly budget?: Partial<ContextBudget>;
|
|
13
24
|
readonly shortcut?: string;
|
|
@@ -15,13 +26,13 @@ interface SpotPatchOptions {
|
|
|
15
26
|
readonly debug?: boolean;
|
|
16
27
|
readonly locale?: SpotPatchLocalePreference;
|
|
17
28
|
readonly maxTargets?: number;
|
|
18
|
-
readonly ai?:
|
|
29
|
+
readonly ai?: SpotPatchAiOptions;
|
|
19
30
|
}
|
|
20
31
|
interface ResolvedSpotPatchOptions {
|
|
21
32
|
readonly enabled: boolean;
|
|
22
33
|
readonly include: readonly FilterEntry[];
|
|
23
34
|
readonly exclude: readonly FilterEntry[];
|
|
24
|
-
readonly editor:
|
|
35
|
+
readonly editor: SpotPatchEditorPreference;
|
|
25
36
|
readonly redact: boolean;
|
|
26
37
|
readonly budget: Readonly<ContextBudget>;
|
|
27
38
|
readonly shortcut: string;
|
|
@@ -35,7 +46,7 @@ declare const DEFAULT_OPTIONS: Readonly<{
|
|
|
35
46
|
enabled: true;
|
|
36
47
|
include: readonly RegExp[];
|
|
37
48
|
exclude: readonly RegExp[];
|
|
38
|
-
editor: "
|
|
49
|
+
editor: "auto";
|
|
39
50
|
redact: true;
|
|
40
51
|
budget: Readonly<{
|
|
41
52
|
totalCharacters: number;
|
|
@@ -52,8 +63,8 @@ declare const DEFAULT_OPTIONS: Readonly<{
|
|
|
52
63
|
maxTargets: number;
|
|
53
64
|
ai: false;
|
|
54
65
|
}>;
|
|
55
|
-
declare function resolveOptions(options?: SpotPatchOptions): ResolvedSpotPatchOptions;
|
|
66
|
+
declare function resolveOptions(options?: SpotPatchOptions, environmentAi?: false | SimpleAiOptions): ResolvedSpotPatchOptions;
|
|
56
67
|
|
|
57
68
|
declare function spotPatch(userOptions?: SpotPatchOptions): Plugin[];
|
|
58
69
|
|
|
59
|
-
export { DEFAULT_OPTIONS, type ResolvedSpotPatchOptions, type SpotPatchOptions, resolveOptions, spotPatch };
|
|
70
|
+
export { DEFAULT_OPTIONS, type ResolvedSpotPatchOptions, type SimpleAiOptions, type SpotPatchAiOptions, type SpotPatchOptions, resolveOptions, spotPatch };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
|
-
import { ContextBudget, SpotPatchLocalePreference, ResolvedAiOptions, AiOptions } from '@spotpatch/shared';
|
|
3
|
-
export { AgentApplyMode, AgentCheckDefinition, AgentLimits, AiExecutionOptions, AiModelProfile, AiOptions, AiProviderProtocol, ContextBudget, DEFAULT_AGENT_LIMITS, OpenAICompatibleProviderOptions } from '@spotpatch/shared';
|
|
2
|
+
import { SpotPatchEditorPreference, ContextBudget, SpotPatchLocalePreference, ResolvedAiOptions, AiProviderProtocol, AiProviderAuthentication, AiExecutionOptions, AiOptions } from '@spotpatch/shared';
|
|
3
|
+
export { AgentApplyMode, AgentCheckDefinition, AgentLimits, AiExecutionOptions, AiModelProfile, AiOptions, AiProviderAuthentication, AiProviderProtocol, ContextBudget, DEFAULT_AGENT_LIMITS, OpenAICompatibleProviderOptions, SpotPatchEditorPreference } from '@spotpatch/shared';
|
|
4
4
|
|
|
5
5
|
type FilterEntry = string | RegExp;
|
|
6
|
+
interface SimpleAiOptions {
|
|
7
|
+
readonly baseURL: string;
|
|
8
|
+
readonly model: string;
|
|
9
|
+
readonly apiKeyEnv?: string;
|
|
10
|
+
readonly protocol?: AiProviderProtocol;
|
|
11
|
+
readonly authentication?: AiProviderAuthentication;
|
|
12
|
+
readonly providerLabel?: string;
|
|
13
|
+
readonly modelLabel?: string;
|
|
14
|
+
readonly execution?: AiExecutionOptions;
|
|
15
|
+
}
|
|
16
|
+
type SpotPatchAiOptions = false | SimpleAiOptions | AiOptions;
|
|
6
17
|
interface SpotPatchOptions {
|
|
7
18
|
readonly enabled?: boolean;
|
|
8
19
|
readonly include?: readonly FilterEntry[];
|
|
9
20
|
readonly exclude?: readonly FilterEntry[];
|
|
10
|
-
readonly editor?:
|
|
21
|
+
readonly editor?: SpotPatchEditorPreference;
|
|
11
22
|
readonly redact?: boolean;
|
|
12
23
|
readonly budget?: Partial<ContextBudget>;
|
|
13
24
|
readonly shortcut?: string;
|
|
@@ -15,13 +26,13 @@ interface SpotPatchOptions {
|
|
|
15
26
|
readonly debug?: boolean;
|
|
16
27
|
readonly locale?: SpotPatchLocalePreference;
|
|
17
28
|
readonly maxTargets?: number;
|
|
18
|
-
readonly ai?:
|
|
29
|
+
readonly ai?: SpotPatchAiOptions;
|
|
19
30
|
}
|
|
20
31
|
interface ResolvedSpotPatchOptions {
|
|
21
32
|
readonly enabled: boolean;
|
|
22
33
|
readonly include: readonly FilterEntry[];
|
|
23
34
|
readonly exclude: readonly FilterEntry[];
|
|
24
|
-
readonly editor:
|
|
35
|
+
readonly editor: SpotPatchEditorPreference;
|
|
25
36
|
readonly redact: boolean;
|
|
26
37
|
readonly budget: Readonly<ContextBudget>;
|
|
27
38
|
readonly shortcut: string;
|
|
@@ -35,7 +46,7 @@ declare const DEFAULT_OPTIONS: Readonly<{
|
|
|
35
46
|
enabled: true;
|
|
36
47
|
include: readonly RegExp[];
|
|
37
48
|
exclude: readonly RegExp[];
|
|
38
|
-
editor: "
|
|
49
|
+
editor: "auto";
|
|
39
50
|
redact: true;
|
|
40
51
|
budget: Readonly<{
|
|
41
52
|
totalCharacters: number;
|
|
@@ -52,8 +63,8 @@ declare const DEFAULT_OPTIONS: Readonly<{
|
|
|
52
63
|
maxTargets: number;
|
|
53
64
|
ai: false;
|
|
54
65
|
}>;
|
|
55
|
-
declare function resolveOptions(options?: SpotPatchOptions): ResolvedSpotPatchOptions;
|
|
66
|
+
declare function resolveOptions(options?: SpotPatchOptions, environmentAi?: false | SimpleAiOptions): ResolvedSpotPatchOptions;
|
|
56
67
|
|
|
57
68
|
declare function spotPatch(userOptions?: SpotPatchOptions): Plugin[];
|
|
58
69
|
|
|
59
|
-
export { DEFAULT_OPTIONS, type ResolvedSpotPatchOptions, type SpotPatchOptions, resolveOptions, spotPatch };
|
|
70
|
+
export { DEFAULT_OPTIONS, type ResolvedSpotPatchOptions, type SimpleAiOptions, type SpotPatchAiOptions, type SpotPatchOptions, resolveOptions, spotPatch };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,73 @@
|
|
|
1
|
+
// src/plugin.ts
|
|
2
|
+
import path10 from "path";
|
|
3
|
+
import { loadEnv } from "vite";
|
|
4
|
+
|
|
5
|
+
// src/environment-ai.ts
|
|
6
|
+
var AI_ENVIRONMENT_NAMES = Object.freeze({
|
|
7
|
+
authentication: "SPOTPATCH_AI_AUTHENTICATION",
|
|
8
|
+
baseURL: "SPOTPATCH_AI_BASE_URL",
|
|
9
|
+
credential: "SPOTPATCH_AI_API_KEY",
|
|
10
|
+
model: "SPOTPATCH_AI_MODEL",
|
|
11
|
+
protocol: "SPOTPATCH_AI_PROTOCOL"
|
|
12
|
+
});
|
|
13
|
+
function normalizedValue(environment, name) {
|
|
14
|
+
const value = environment[name];
|
|
15
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
return value.trim();
|
|
19
|
+
}
|
|
20
|
+
function resolveEnvironmentAiConfiguration(environment) {
|
|
21
|
+
const baseURL = normalizedValue(environment, AI_ENVIRONMENT_NAMES.baseURL);
|
|
22
|
+
const model = normalizedValue(environment, AI_ENVIRONMENT_NAMES.model);
|
|
23
|
+
const credential = normalizedValue(environment, AI_ENVIRONMENT_NAMES.credential);
|
|
24
|
+
const protocol = normalizedValue(environment, AI_ENVIRONMENT_NAMES.protocol);
|
|
25
|
+
const authentication = normalizedValue(
|
|
26
|
+
environment,
|
|
27
|
+
AI_ENVIRONMENT_NAMES.authentication
|
|
28
|
+
);
|
|
29
|
+
const configuredValues = [baseURL, model, credential, protocol, authentication];
|
|
30
|
+
if (configuredValues.every((value) => value === void 0)) {
|
|
31
|
+
return Object.freeze({ ai: false });
|
|
32
|
+
}
|
|
33
|
+
const missing = [
|
|
34
|
+
[AI_ENVIRONMENT_NAMES.baseURL, baseURL],
|
|
35
|
+
[AI_ENVIRONMENT_NAMES.model, model],
|
|
36
|
+
[AI_ENVIRONMENT_NAMES.credential, credential]
|
|
37
|
+
].filter((entry) => entry[1] === void 0).map(([name]) => name);
|
|
38
|
+
if (missing.length > 0) {
|
|
39
|
+
throw new RangeError(
|
|
40
|
+
`SpotPatch AI environment configuration is incomplete; missing ${missing.join(", ")}.`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
if (baseURL === void 0 || model === void 0 || credential === void 0) {
|
|
44
|
+
throw new RangeError("SpotPatch AI environment configuration is incomplete.");
|
|
45
|
+
}
|
|
46
|
+
if (protocol !== void 0 && protocol !== "responses" && protocol !== "chat-completions") {
|
|
47
|
+
throw new RangeError(
|
|
48
|
+
"SpotPatch SPOTPATCH_AI_PROTOCOL must be responses or chat-completions."
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
if (authentication !== void 0 && authentication !== "bearer" && authentication !== "x-api-key") {
|
|
52
|
+
throw new RangeError(
|
|
53
|
+
"SpotPatch SPOTPATCH_AI_AUTHENTICATION must be bearer or x-api-key."
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return Object.freeze({
|
|
57
|
+
ai: Object.freeze({
|
|
58
|
+
baseURL,
|
|
59
|
+
model,
|
|
60
|
+
...protocol === void 0 ? {} : { protocol },
|
|
61
|
+
...authentication === void 0 ? {} : { authentication }
|
|
62
|
+
})
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
1
66
|
// src/options.ts
|
|
2
67
|
import {
|
|
3
68
|
DEFAULT_AGENT_LIMITS,
|
|
4
69
|
MAX_ANNOTATION_TARGETS,
|
|
70
|
+
SPOTPATCH_EDITOR_PREFERENCES,
|
|
5
71
|
SPOTPATCH_LOCALE_PREFERENCES
|
|
6
72
|
} from "@spotpatch/shared";
|
|
7
73
|
import { z } from "zod";
|
|
@@ -26,7 +92,7 @@ var DEFAULT_OPTIONS = Object.freeze({
|
|
|
26
92
|
enabled: true,
|
|
27
93
|
include: DEFAULT_INCLUDE,
|
|
28
94
|
exclude: DEFAULT_EXCLUDE,
|
|
29
|
-
editor: "
|
|
95
|
+
editor: "auto",
|
|
30
96
|
redact: true,
|
|
31
97
|
budget: DEFAULT_BUDGET,
|
|
32
98
|
shortcut: "Mod+Shift+S",
|
|
@@ -66,6 +132,7 @@ var aiOptionsSchema = z.strictObject({
|
|
|
66
132
|
type: z.literal("openai-compatible"),
|
|
67
133
|
label: z.string(),
|
|
68
134
|
protocol: z.enum(["responses", "chat-completions"]),
|
|
135
|
+
authentication: z.enum(["bearer", "x-api-key"]).optional(),
|
|
69
136
|
baseURL: z.string(),
|
|
70
137
|
apiKeyEnv: z.string(),
|
|
71
138
|
models: z.record(
|
|
@@ -83,6 +150,16 @@ var aiOptionsSchema = z.strictObject({
|
|
|
83
150
|
limits: agentLimitsSchema
|
|
84
151
|
}).optional()
|
|
85
152
|
});
|
|
153
|
+
var simpleAiOptionsSchema = z.strictObject({
|
|
154
|
+
baseURL: z.string(),
|
|
155
|
+
model: z.string(),
|
|
156
|
+
apiKeyEnv: z.string().optional(),
|
|
157
|
+
protocol: z.enum(["responses", "chat-completions"]).optional(),
|
|
158
|
+
authentication: z.enum(["bearer", "x-api-key"]).optional(),
|
|
159
|
+
providerLabel: z.string().optional(),
|
|
160
|
+
modelLabel: z.string().optional(),
|
|
161
|
+
execution: aiOptionsSchema.shape.execution
|
|
162
|
+
});
|
|
86
163
|
function assertIdentifier(value, label) {
|
|
87
164
|
if (!PROFILE_ID_PATTERN.test(value)) {
|
|
88
165
|
throw new RangeError(
|
|
@@ -182,6 +259,7 @@ function resolveProviders(providers) {
|
|
|
182
259
|
type: provider.type,
|
|
183
260
|
label: nonEmpty(provider.label, "provider label", 100),
|
|
184
261
|
protocol: provider.protocol,
|
|
262
|
+
authentication: provider.authentication ?? "bearer",
|
|
185
263
|
baseURL: normalizeProviderBaseURL(provider.baseURL),
|
|
186
264
|
apiKeyEnv: provider.apiKeyEnv,
|
|
187
265
|
models,
|
|
@@ -225,7 +303,36 @@ function resolveAiOptions(options) {
|
|
|
225
303
|
if (options === void 0 || options === false) {
|
|
226
304
|
return false;
|
|
227
305
|
}
|
|
228
|
-
const
|
|
306
|
+
const expanded = "providers" in options ? options : (() => {
|
|
307
|
+
const simple = simpleAiOptionsSchema.safeParse(options);
|
|
308
|
+
if (!simple.success) {
|
|
309
|
+
throw new RangeError("SpotPatch AI configuration is invalid.");
|
|
310
|
+
}
|
|
311
|
+
const providerId = "default";
|
|
312
|
+
const modelId = "default";
|
|
313
|
+
return {
|
|
314
|
+
providers: {
|
|
315
|
+
[providerId]: {
|
|
316
|
+
type: "openai-compatible",
|
|
317
|
+
label: simple.data.providerLabel ?? "AI provider",
|
|
318
|
+
protocol: simple.data.protocol ?? "chat-completions",
|
|
319
|
+
authentication: simple.data.authentication ?? "bearer",
|
|
320
|
+
baseURL: simple.data.baseURL,
|
|
321
|
+
apiKeyEnv: simple.data.apiKeyEnv ?? "SPOTPATCH_AI_API_KEY",
|
|
322
|
+
models: {
|
|
323
|
+
[modelId]: {
|
|
324
|
+
label: simple.data.modelLabel ?? "AI model",
|
|
325
|
+
model: simple.data.model
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
defaultModel: modelId
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
defaultProvider: providerId,
|
|
332
|
+
...simple.data.execution === void 0 ? {} : { execution: simple.data.execution }
|
|
333
|
+
};
|
|
334
|
+
})();
|
|
335
|
+
const parsed = aiOptionsSchema.safeParse(expanded);
|
|
229
336
|
if (!parsed.success) {
|
|
230
337
|
throw new RangeError("SpotPatch AI configuration is invalid.");
|
|
231
338
|
}
|
|
@@ -285,7 +392,7 @@ function assertPositiveBudget(budget) {
|
|
|
285
392
|
}
|
|
286
393
|
}
|
|
287
394
|
}
|
|
288
|
-
function resolveOptions(options = {}) {
|
|
395
|
+
function resolveOptions(options = {}, environmentAi) {
|
|
289
396
|
const budget = Object.freeze({
|
|
290
397
|
...DEFAULT_OPTIONS.budget,
|
|
291
398
|
...options.budget
|
|
@@ -293,9 +400,13 @@ function resolveOptions(options = {}) {
|
|
|
293
400
|
assertPositiveBudget(budget);
|
|
294
401
|
const maxTargets = options.maxTargets ?? DEFAULT_OPTIONS.maxTargets;
|
|
295
402
|
const locale = options.locale ?? DEFAULT_OPTIONS.locale;
|
|
403
|
+
const editor = options.editor ?? DEFAULT_OPTIONS.editor;
|
|
296
404
|
if (!SPOTPATCH_LOCALE_PREFERENCES.includes(locale)) {
|
|
297
405
|
throw new RangeError("SpotPatch locale must be auto, en-US, or zh-CN.");
|
|
298
406
|
}
|
|
407
|
+
if (!SPOTPATCH_EDITOR_PREFERENCES.includes(editor)) {
|
|
408
|
+
throw new RangeError("SpotPatch editor must be auto, vscode, or cursor.");
|
|
409
|
+
}
|
|
299
410
|
if (!Number.isSafeInteger(maxTargets) || maxTargets < 1 || maxTargets > MAX_ANNOTATION_TARGETS) {
|
|
300
411
|
throw new RangeError(
|
|
301
412
|
`SpotPatch maxTargets must be an integer between 1 and ${String(MAX_ANNOTATION_TARGETS)}.`
|
|
@@ -305,7 +416,7 @@ function resolveOptions(options = {}) {
|
|
|
305
416
|
enabled: options.enabled ?? DEFAULT_OPTIONS.enabled,
|
|
306
417
|
include: Object.freeze([...options.include ?? DEFAULT_OPTIONS.include]),
|
|
307
418
|
exclude: Object.freeze([...options.exclude ?? DEFAULT_OPTIONS.exclude]),
|
|
308
|
-
editor
|
|
419
|
+
editor,
|
|
309
420
|
redact: options.redact ?? DEFAULT_OPTIONS.redact,
|
|
310
421
|
budget,
|
|
311
422
|
shortcut: options.shortcut ?? DEFAULT_OPTIONS.shortcut,
|
|
@@ -313,7 +424,7 @@ function resolveOptions(options = {}) {
|
|
|
313
424
|
debug: options.debug ?? DEFAULT_OPTIONS.debug,
|
|
314
425
|
locale,
|
|
315
426
|
maxTargets,
|
|
316
|
-
ai: resolveAiOptions(options.ai)
|
|
427
|
+
ai: resolveAiOptions(options.ai ?? environmentAi)
|
|
317
428
|
};
|
|
318
429
|
if (resolved.shortcut.trim().length === 0) {
|
|
319
430
|
throw new RangeError("SpotPatch shortcut cannot be empty.");
|
|
@@ -370,7 +481,7 @@ import path2 from "path";
|
|
|
370
481
|
// package.json
|
|
371
482
|
var package_default = {
|
|
372
483
|
name: "@spotpatch/vite",
|
|
373
|
-
version: "1.
|
|
484
|
+
version: "1.2.0",
|
|
374
485
|
description: "Vite development plugin for SpotPatch.",
|
|
375
486
|
license: "MIT",
|
|
376
487
|
repository: {
|
|
@@ -462,15 +573,17 @@ function readConsumerViteVersion(root) {
|
|
|
462
573
|
return VITE_VERSION;
|
|
463
574
|
}
|
|
464
575
|
function createClientModule(input, clientBundle, viteVersion) {
|
|
576
|
+
const options = input.context.getOptions();
|
|
465
577
|
const runtimeConfig = {
|
|
466
|
-
ai: createRuntimeAiConfig(
|
|
467
|
-
budget:
|
|
468
|
-
debug:
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
578
|
+
ai: createRuntimeAiConfig(options.ai),
|
|
579
|
+
budget: options.budget,
|
|
580
|
+
debug: options.debug,
|
|
581
|
+
editor: options.editor,
|
|
582
|
+
locale: options.locale,
|
|
583
|
+
maxTargets: options.maxTargets,
|
|
584
|
+
redact: options.redact,
|
|
472
585
|
sessionToken: input.session.token,
|
|
473
|
-
shortcut:
|
|
586
|
+
shortcut: options.shortcut,
|
|
474
587
|
spotPatchVersion: package_default.version,
|
|
475
588
|
viteVersion
|
|
476
589
|
};
|
|
@@ -602,7 +715,7 @@ function snapshot(job) {
|
|
|
602
715
|
);
|
|
603
716
|
}
|
|
604
717
|
function capabilityCacheKey(provider, model) {
|
|
605
|
-
const configurationDigest = createHash("sha256").update(provider.baseURL).update("\0").update(provider.protocol).digest("hex");
|
|
718
|
+
const configurationDigest = createHash("sha256").update(provider.baseURL).update("\0").update(provider.protocol).update("\0").update(provider.authentication).digest("hex");
|
|
606
719
|
return `${provider.id}:${model.id}:${configurationDigest}`;
|
|
607
720
|
}
|
|
608
721
|
function freezeEvent(event) {
|
|
@@ -1550,18 +1663,18 @@ var EVENT_STREAM_END_STATUSES = /* @__PURE__ */ new Set([
|
|
|
1550
1663
|
"reverted",
|
|
1551
1664
|
"failed"
|
|
1552
1665
|
]);
|
|
1553
|
-
function matchAgentRequestPath(
|
|
1554
|
-
if (
|
|
1666
|
+
function matchAgentRequestPath(path11) {
|
|
1667
|
+
if (path11 === SPOTPATCH_ENDPOINTS.agentCapability) {
|
|
1555
1668
|
return Object.freeze({ kind: "capability" });
|
|
1556
1669
|
}
|
|
1557
|
-
if (
|
|
1670
|
+
if (path11 === SPOTPATCH_ENDPOINTS.agentJobs) {
|
|
1558
1671
|
return Object.freeze({ kind: "create-job" });
|
|
1559
1672
|
}
|
|
1560
1673
|
const prefix = `${SPOTPATCH_ENDPOINTS.agentJobs}/`;
|
|
1561
|
-
if (!
|
|
1674
|
+
if (!path11.startsWith(prefix)) {
|
|
1562
1675
|
return void 0;
|
|
1563
1676
|
}
|
|
1564
|
-
const segments =
|
|
1677
|
+
const segments = path11.slice(prefix.length).split("/");
|
|
1565
1678
|
const jobId = segments[0];
|
|
1566
1679
|
const action = segments[1];
|
|
1567
1680
|
if (segments.length !== 2 || jobId === void 0 || !AGENT_JOB_ID_PATTERN.test(jobId) || action === void 0 || !AGENT_JOB_ACTIONS.has(action)) {
|
|
@@ -1706,9 +1819,36 @@ async function handleAgentRequest(request, response, options, route, writeSucces
|
|
|
1706
1819
|
|
|
1707
1820
|
// src/server/editor.ts
|
|
1708
1821
|
import launchEditor from "launch-editor";
|
|
1709
|
-
var
|
|
1710
|
-
|
|
1711
|
-
|
|
1822
|
+
var EDITOR_STARTUP_GRACE_MS = 150;
|
|
1823
|
+
function editorCommand(editor) {
|
|
1824
|
+
if (editor === "vscode") {
|
|
1825
|
+
return "code";
|
|
1826
|
+
}
|
|
1827
|
+
if (editor === "cursor") {
|
|
1828
|
+
return "cursor";
|
|
1829
|
+
}
|
|
1830
|
+
return void 0;
|
|
1831
|
+
}
|
|
1832
|
+
var launchConfiguredEditor = (target, editor) => new Promise((resolve, reject) => {
|
|
1833
|
+
let settled = false;
|
|
1834
|
+
const startupTimer = setTimeout(() => {
|
|
1835
|
+
settled = true;
|
|
1836
|
+
resolve();
|
|
1837
|
+
}, EDITOR_STARTUP_GRACE_MS);
|
|
1838
|
+
const rejectStartup = () => {
|
|
1839
|
+
if (settled) {
|
|
1840
|
+
return;
|
|
1841
|
+
}
|
|
1842
|
+
settled = true;
|
|
1843
|
+
clearTimeout(startupTimer);
|
|
1844
|
+
reject(new Error("The configured editor could not be started."));
|
|
1845
|
+
};
|
|
1846
|
+
try {
|
|
1847
|
+
launchEditor(target, editorCommand(editor), rejectStartup);
|
|
1848
|
+
} catch {
|
|
1849
|
+
rejectStartup();
|
|
1850
|
+
}
|
|
1851
|
+
});
|
|
1712
1852
|
|
|
1713
1853
|
// src/server/request-security.ts
|
|
1714
1854
|
import { timingSafeEqual } from "crypto";
|
|
@@ -1889,23 +2029,24 @@ async function handleOpenEditor(request, options) {
|
|
|
1889
2029
|
root: options.root
|
|
1890
2030
|
});
|
|
1891
2031
|
const target = `${sourcePath}:${String(body.line)}:${String(body.column)}`;
|
|
1892
|
-
const editorLauncher = options.editorLauncher ??
|
|
2032
|
+
const editorLauncher = options.editorLauncher ?? launchConfiguredEditor;
|
|
1893
2033
|
try {
|
|
1894
|
-
editorLauncher(target,
|
|
1895
|
-
options.logger?.warn("[spotpatch:server] VS Code rejected an editor request.");
|
|
1896
|
-
});
|
|
2034
|
+
await editorLauncher(target, options.options.editor);
|
|
1897
2035
|
} catch (error) {
|
|
2036
|
+
options.logger?.warn(
|
|
2037
|
+
`[spotpatch:server] ${options.options.editor === "auto" ? "The detected editor" : options.options.editor} rejected an editor request.`
|
|
2038
|
+
);
|
|
1898
2039
|
throw new SpotPatchError8(ERROR_CODES8.EDITOR_OPEN_FAILED, void 0, {
|
|
1899
2040
|
cause: error
|
|
1900
2041
|
});
|
|
1901
2042
|
}
|
|
1902
|
-
return Object.freeze({});
|
|
2043
|
+
return Object.freeze({ editor: options.options.editor });
|
|
1903
2044
|
}
|
|
1904
2045
|
function createSpotPatchMiddleware(options) {
|
|
1905
2046
|
return (request, response, next) => {
|
|
1906
|
-
const
|
|
1907
|
-
const agentRoute = matchAgentRequestPath(
|
|
1908
|
-
if (
|
|
2047
|
+
const path11 = requestPath(request);
|
|
2048
|
+
const agentRoute = matchAgentRequestPath(path11);
|
|
2049
|
+
if (path11 !== SPOTPATCH_ENDPOINTS2.sourceContext && path11 !== SPOTPATCH_ENDPOINTS2.openEditor && agentRoute === void 0 && !path11.startsWith(`${SPOTPATCH_API_BASE}/`)) {
|
|
1909
2050
|
next();
|
|
1910
2051
|
return;
|
|
1911
2052
|
}
|
|
@@ -1914,7 +2055,7 @@ function createSpotPatchMiddleware(options) {
|
|
|
1914
2055
|
allowLan: options.options.allowLan,
|
|
1915
2056
|
sessionToken: options.session.token
|
|
1916
2057
|
});
|
|
1917
|
-
if (
|
|
2058
|
+
if (path11 === SPOTPATCH_ENDPOINTS2.sourceContext) {
|
|
1918
2059
|
if (request.method !== "POST") {
|
|
1919
2060
|
throw new SpotPatchError8(ERROR_CODES8.INVALID_REQUEST);
|
|
1920
2061
|
}
|
|
@@ -1922,7 +2063,7 @@ function createSpotPatchMiddleware(options) {
|
|
|
1922
2063
|
writeJson(response, 200, { ok: true, data });
|
|
1923
2064
|
return;
|
|
1924
2065
|
}
|
|
1925
|
-
if (
|
|
2066
|
+
if (path11 === SPOTPATCH_ENDPOINTS2.openEditor) {
|
|
1926
2067
|
if (request.method !== "POST") {
|
|
1927
2068
|
throw new SpotPatchError8(ERROR_CODES8.INVALID_REQUEST);
|
|
1928
2069
|
}
|
|
@@ -1970,11 +2111,16 @@ function createServerPlugin(input) {
|
|
|
1970
2111
|
throw new Error("SpotPatch server initialized before Vite config resolution.");
|
|
1971
2112
|
}
|
|
1972
2113
|
const root = path6.resolve(config.root);
|
|
1973
|
-
|
|
2114
|
+
const options = input.context.getOptions();
|
|
2115
|
+
agentManager = options.ai === false ? void 0 : createAgentJobManager({
|
|
2116
|
+
ai: options.ai,
|
|
2117
|
+
environment: input.context.getCredentialEnvironment(),
|
|
2118
|
+
root
|
|
2119
|
+
});
|
|
1974
2120
|
server.middlewares.use(
|
|
1975
2121
|
createSpotPatchMiddleware({
|
|
1976
2122
|
...agentManager === void 0 ? {} : { agentManager },
|
|
1977
|
-
options
|
|
2123
|
+
options,
|
|
1978
2124
|
registry: input.registry,
|
|
1979
2125
|
root,
|
|
1980
2126
|
session: input.session,
|
|
@@ -1985,7 +2131,7 @@ function createServerPlugin(input) {
|
|
|
1985
2131
|
void closeResources();
|
|
1986
2132
|
});
|
|
1987
2133
|
config.logger.info(
|
|
1988
|
-
`[spotpatch:vite] Ready. Toggle picker with ${
|
|
2134
|
+
`[spotpatch:vite] Ready. Toggle picker with ${options.shortcut}.`
|
|
1989
2135
|
);
|
|
1990
2136
|
},
|
|
1991
2137
|
async closeBundle() {
|
|
@@ -2180,7 +2326,7 @@ function getDisplayPath(root, id) {
|
|
|
2180
2326
|
}
|
|
2181
2327
|
function createTransformPlugin(input) {
|
|
2182
2328
|
let root = process.cwd();
|
|
2183
|
-
let filter = createTransformFilter(root, input.
|
|
2329
|
+
let filter = createTransformFilter(root, input.context.getOptions());
|
|
2184
2330
|
let logger;
|
|
2185
2331
|
const warnedFiles = /* @__PURE__ */ new Set();
|
|
2186
2332
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -2188,9 +2334,12 @@ function createTransformPlugin(input) {
|
|
|
2188
2334
|
name: "spotpatch:transform",
|
|
2189
2335
|
apply: "serve",
|
|
2190
2336
|
enforce: "pre",
|
|
2337
|
+
config(config, environment) {
|
|
2338
|
+
input.configure?.(config, environment);
|
|
2339
|
+
},
|
|
2191
2340
|
configResolved(config) {
|
|
2192
2341
|
root = path9.resolve(config.root);
|
|
2193
|
-
filter = createTransformFilter(root, input.
|
|
2342
|
+
filter = createTransformFilter(root, input.context.getOptions());
|
|
2194
2343
|
logger = config.logger;
|
|
2195
2344
|
},
|
|
2196
2345
|
transform(code, id) {
|
|
@@ -2203,6 +2352,7 @@ function createTransformPlugin(input) {
|
|
|
2203
2352
|
return cache.get(cacheKey) ?? null;
|
|
2204
2353
|
}
|
|
2205
2354
|
const startedAt = performance.now();
|
|
2355
|
+
const options = input.context.getOptions();
|
|
2206
2356
|
try {
|
|
2207
2357
|
const result = injectSourceMarkers({
|
|
2208
2358
|
code,
|
|
@@ -2220,7 +2370,7 @@ function createTransformPlugin(input) {
|
|
|
2220
2370
|
map: result.map.toString()
|
|
2221
2371
|
});
|
|
2222
2372
|
cache.set(cacheKey, output);
|
|
2223
|
-
if (
|
|
2373
|
+
if (options.debug) {
|
|
2224
2374
|
const elapsed = performance.now() - startedAt;
|
|
2225
2375
|
logger?.info(
|
|
2226
2376
|
`[spotpatch:transform] ${getDisplayPath(root, id)} ${elapsed.toFixed(2)}ms`
|
|
@@ -2230,7 +2380,7 @@ function createTransformPlugin(input) {
|
|
|
2230
2380
|
} catch (error) {
|
|
2231
2381
|
if (!warnedFiles.has(cleanId)) {
|
|
2232
2382
|
warnedFiles.add(cleanId);
|
|
2233
|
-
const detail =
|
|
2383
|
+
const detail = options.debug && error instanceof Error ? `: ${error.message}` : "";
|
|
2234
2384
|
logger?.warn(
|
|
2235
2385
|
`[spotpatch:transform] Failed to transform ${getDisplayPath(root, id)}; using original module${detail}`
|
|
2236
2386
|
);
|
|
@@ -2243,16 +2393,46 @@ function createTransformPlugin(input) {
|
|
|
2243
2393
|
|
|
2244
2394
|
// src/plugin.ts
|
|
2245
2395
|
function spotPatch(userOptions = {}) {
|
|
2246
|
-
|
|
2396
|
+
let options = resolveOptions(userOptions);
|
|
2397
|
+
let credentialEnvironment = Object.freeze({});
|
|
2247
2398
|
if (!options.enabled) {
|
|
2248
2399
|
return [];
|
|
2249
2400
|
}
|
|
2250
2401
|
const registry = createSourceRegistry();
|
|
2251
2402
|
const session = createSession();
|
|
2403
|
+
const context = Object.freeze({
|
|
2404
|
+
getCredentialEnvironment: () => credentialEnvironment,
|
|
2405
|
+
getOptions: () => options
|
|
2406
|
+
});
|
|
2407
|
+
const configure = (config, environment) => {
|
|
2408
|
+
const root = path10.resolve(process.cwd(), config.root ?? ".");
|
|
2409
|
+
const loadedEnvironment = config.envDir === false ? process.env : loadEnv(environment.mode, path10.resolve(root, config.envDir ?? "."), "");
|
|
2410
|
+
const environmentAi = userOptions.ai === void 0 ? resolveEnvironmentAiConfiguration(loadedEnvironment).ai : false;
|
|
2411
|
+
options = resolveOptions(userOptions, environmentAi);
|
|
2412
|
+
if (options.ai === false) {
|
|
2413
|
+
credentialEnvironment = Object.freeze({});
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
const names = new Set(
|
|
2417
|
+
Object.values(options.ai.providers).map((provider) => provider.apiKeyEnv)
|
|
2418
|
+
);
|
|
2419
|
+
const missing = [...names].filter((name) => {
|
|
2420
|
+
const value = loadedEnvironment[name];
|
|
2421
|
+
return value === void 0 || value.trim().length === 0;
|
|
2422
|
+
});
|
|
2423
|
+
if (missing.length > 0) {
|
|
2424
|
+
throw new RangeError(
|
|
2425
|
+
`SpotPatch AI credential environment is missing ${missing.join(", ")}.`
|
|
2426
|
+
);
|
|
2427
|
+
}
|
|
2428
|
+
credentialEnvironment = Object.freeze(
|
|
2429
|
+
Object.fromEntries([...names].map((name) => [name, loadedEnvironment[name]]))
|
|
2430
|
+
);
|
|
2431
|
+
};
|
|
2252
2432
|
return [
|
|
2253
|
-
createTransformPlugin({
|
|
2254
|
-
createRuntimeInjectionPlugin({
|
|
2255
|
-
createServerPlugin({
|
|
2433
|
+
createTransformPlugin({ configure, context, registry }),
|
|
2434
|
+
createRuntimeInjectionPlugin({ context, session }),
|
|
2435
|
+
createServerPlugin({ context, registry, session })
|
|
2256
2436
|
];
|
|
2257
2437
|
}
|
|
2258
2438
|
|