@spotpatch/vite 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/index.cjs +138 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +141 -32
- package/dist/index.js.map +1 -1
- package/dist/runtime-client.js +112 -34
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -481,7 +481,7 @@ import path2 from "path";
|
|
|
481
481
|
// package.json
|
|
482
482
|
var package_default = {
|
|
483
483
|
name: "@spotpatch/vite",
|
|
484
|
-
version: "1.
|
|
484
|
+
version: "1.3.0",
|
|
485
485
|
description: "Vite development plugin for SpotPatch.",
|
|
486
486
|
license: "MIT",
|
|
487
487
|
repository: {
|
|
@@ -646,6 +646,7 @@ import { createHash, randomBytes as randomBytes2 } from "crypto";
|
|
|
646
646
|
import {
|
|
647
647
|
applyPreparedAgentChange,
|
|
648
648
|
executeAgentChange,
|
|
649
|
+
inspectAgentWorkspace,
|
|
649
650
|
probeProviderCapability,
|
|
650
651
|
resolveProviderCredential,
|
|
651
652
|
revertPreparedAgentChange
|
|
@@ -684,6 +685,7 @@ var DEFAULT_DEPENDENCIES = Object.freeze({
|
|
|
684
685
|
applyChange: applyPreparedAgentChange,
|
|
685
686
|
createJobId: () => randomBytes2(16).toString("base64url"),
|
|
686
687
|
executeChange: executeAgentChange,
|
|
688
|
+
inspectWorkspace: inspectAgentWorkspace,
|
|
687
689
|
now: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
688
690
|
probeCapability: probeProviderCapability,
|
|
689
691
|
resolveCredential: resolveProviderCredential,
|
|
@@ -903,6 +905,7 @@ function createAgentJobManager(options) {
|
|
|
903
905
|
provider: job.provider,
|
|
904
906
|
root: options.root,
|
|
905
907
|
signal: job.controller.signal,
|
|
908
|
+
workingTreeMode: job.workingTreeMode,
|
|
906
909
|
...options.fetch === void 0 ? {} : { fetch: options.fetch }
|
|
907
910
|
});
|
|
908
911
|
job.preparedChange = preparedChange;
|
|
@@ -1039,7 +1042,8 @@ function createAgentJobManager(options) {
|
|
|
1039
1042
|
runPromise: void 0,
|
|
1040
1043
|
sequence: 0,
|
|
1041
1044
|
status: "queued",
|
|
1042
|
-
updatedAt: timestamp
|
|
1045
|
+
updatedAt: timestamp,
|
|
1046
|
+
workingTreeMode: request.workingTreeMode
|
|
1043
1047
|
};
|
|
1044
1048
|
jobs.set(id, job);
|
|
1045
1049
|
emitSnapshot(job);
|
|
@@ -1094,6 +1098,12 @@ function createAgentJobManager(options) {
|
|
|
1094
1098
|
return () => {
|
|
1095
1099
|
job.listeners.delete(listener);
|
|
1096
1100
|
};
|
|
1101
|
+
},
|
|
1102
|
+
workspaceHealth(signal) {
|
|
1103
|
+
if (closed) {
|
|
1104
|
+
throw new SpotPatchError(ERROR_CODES.AI_DISABLED);
|
|
1105
|
+
}
|
|
1106
|
+
return dependencies.inspectWorkspace(options.root, signal);
|
|
1097
1107
|
}
|
|
1098
1108
|
});
|
|
1099
1109
|
}
|
|
@@ -1115,7 +1125,8 @@ import {
|
|
|
1115
1125
|
SpotPatchError as SpotPatchError6,
|
|
1116
1126
|
agentCapabilityRequestSchema,
|
|
1117
1127
|
agentJobActionRequestSchema,
|
|
1118
|
-
agentJobCreateRequestSchema
|
|
1128
|
+
agentJobCreateRequestSchema,
|
|
1129
|
+
agentWorkspaceHealthRequestSchema
|
|
1119
1130
|
} from "@spotpatch/shared";
|
|
1120
1131
|
|
|
1121
1132
|
// src/server/agent-request.ts
|
|
@@ -1601,7 +1612,8 @@ async function authorizeAgentJobRequest(input) {
|
|
|
1601
1612
|
annotation,
|
|
1602
1613
|
providerProfileId: input.request.providerProfileId,
|
|
1603
1614
|
modelProfileId: input.request.modelProfileId,
|
|
1604
|
-
providerDataConsent: true
|
|
1615
|
+
providerDataConsent: true,
|
|
1616
|
+
workingTreeMode: input.request.workingTreeMode
|
|
1605
1617
|
});
|
|
1606
1618
|
}
|
|
1607
1619
|
|
|
@@ -1667,6 +1679,9 @@ function matchAgentRequestPath(path11) {
|
|
|
1667
1679
|
if (path11 === SPOTPATCH_ENDPOINTS.agentCapability) {
|
|
1668
1680
|
return Object.freeze({ kind: "capability" });
|
|
1669
1681
|
}
|
|
1682
|
+
if (path11 === SPOTPATCH_ENDPOINTS.agentWorkspaceHealth) {
|
|
1683
|
+
return Object.freeze({ kind: "workspace-health" });
|
|
1684
|
+
}
|
|
1670
1685
|
if (path11 === SPOTPATCH_ENDPOINTS.agentJobs) {
|
|
1671
1686
|
return Object.freeze({ kind: "create-job" });
|
|
1672
1687
|
}
|
|
@@ -1783,6 +1798,28 @@ async function handleCreateJob(request, response, options, writeSuccess) {
|
|
|
1783
1798
|
const data = requireAgentManager(options).create(authorizedRequest);
|
|
1784
1799
|
writeSuccess(response, 202, data);
|
|
1785
1800
|
}
|
|
1801
|
+
async function handleWorkspaceHealth(request, response, options, writeSuccess) {
|
|
1802
|
+
if (request.method !== "POST") {
|
|
1803
|
+
throw new SpotPatchError6(ERROR_CODES6.INVALID_REQUEST);
|
|
1804
|
+
}
|
|
1805
|
+
const parsed = agentWorkspaceHealthRequestSchema.safeParse(
|
|
1806
|
+
await readJsonRequestBody(request)
|
|
1807
|
+
);
|
|
1808
|
+
if (!parsed.success) {
|
|
1809
|
+
throw new SpotPatchError6(ERROR_CODES6.INVALID_REQUEST);
|
|
1810
|
+
}
|
|
1811
|
+
const controller = new AbortController();
|
|
1812
|
+
const abort = () => {
|
|
1813
|
+
controller.abort("agent-workspace-health-client-disconnected");
|
|
1814
|
+
};
|
|
1815
|
+
response.once("close", abort);
|
|
1816
|
+
try {
|
|
1817
|
+
const data = await requireAgentManager(options).workspaceHealth(controller.signal);
|
|
1818
|
+
writeSuccess(response, 200, data);
|
|
1819
|
+
} finally {
|
|
1820
|
+
response.removeListener("close", abort);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1786
1823
|
async function handleJobAction(request, response, options, route, writeSuccess) {
|
|
1787
1824
|
const manager = requireAgentManager(options);
|
|
1788
1825
|
if (request.method !== "POST") {
|
|
@@ -1814,41 +1851,97 @@ async function handleAgentRequest(request, response, options, route, writeSucces
|
|
|
1814
1851
|
await handleCreateJob(request, response, options, writeSuccess);
|
|
1815
1852
|
return;
|
|
1816
1853
|
}
|
|
1854
|
+
if (route.kind === "workspace-health") {
|
|
1855
|
+
await handleWorkspaceHealth(request, response, options, writeSuccess);
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1817
1858
|
await handleJobAction(request, response, options, route, writeSuccess);
|
|
1818
1859
|
}
|
|
1819
1860
|
|
|
1820
1861
|
// src/server/editor.ts
|
|
1862
|
+
import { spawn } from "child_process";
|
|
1821
1863
|
import launchEditor from "launch-editor";
|
|
1822
|
-
var EDITOR_STARTUP_GRACE_MS =
|
|
1823
|
-
function
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1864
|
+
var EDITOR_STARTUP_GRACE_MS = 300;
|
|
1865
|
+
function normalizedEditorEnvironment(environment) {
|
|
1866
|
+
return [
|
|
1867
|
+
environment.TERM_PROGRAM,
|
|
1868
|
+
environment.VSCODE_GIT_ASKPASS_NODE,
|
|
1869
|
+
environment.VSCODE_GIT_ASKPASS_MAIN,
|
|
1870
|
+
environment.GIT_ASKPASS
|
|
1871
|
+
].filter((value) => typeof value === "string").join("\n").replaceAll("\\", "/").toLowerCase();
|
|
1872
|
+
}
|
|
1873
|
+
function detectIntegratedEditor(environment) {
|
|
1874
|
+
const signature = normalizedEditorEnvironment(environment);
|
|
1875
|
+
if (signature === "cursor" || signature.includes("/cursor")) {
|
|
1828
1876
|
return "cursor";
|
|
1829
1877
|
}
|
|
1878
|
+
if (signature === "vscode" || signature.includes("visual studio code") || /(^|\/)(code|code-insiders)(\.exe)?($|\/)/u.test(signature)) {
|
|
1879
|
+
return "vscode";
|
|
1880
|
+
}
|
|
1830
1881
|
return void 0;
|
|
1831
1882
|
}
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
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
|
-
}
|
|
1883
|
+
function editorCommand(editor) {
|
|
1884
|
+
return editor === "cursor" ? "cursor" : "code";
|
|
1885
|
+
}
|
|
1886
|
+
var DEFAULT_DEPENDENCIES2 = Object.freeze({
|
|
1887
|
+
environment: process.env,
|
|
1888
|
+
fallbackLauncher: launchEditor,
|
|
1889
|
+
processSpawner: (command, arguments_, options) => spawn(command, [...arguments_], options),
|
|
1890
|
+
startupGraceMs: EDITOR_STARTUP_GRACE_MS
|
|
1851
1891
|
});
|
|
1892
|
+
function createEditorLauncher(dependencies = DEFAULT_DEPENDENCIES2) {
|
|
1893
|
+
return (target, configuredEditor) => {
|
|
1894
|
+
const integratedEditor = detectIntegratedEditor(dependencies.environment);
|
|
1895
|
+
const resolvedEditor = configuredEditor === "auto" ? integratedEditor : configuredEditor;
|
|
1896
|
+
return new Promise((resolve, reject) => {
|
|
1897
|
+
let settled = false;
|
|
1898
|
+
const settle = (error, editor = resolvedEditor ?? "auto") => {
|
|
1899
|
+
if (settled) {
|
|
1900
|
+
return;
|
|
1901
|
+
}
|
|
1902
|
+
settled = true;
|
|
1903
|
+
clearTimeout(startupTimer);
|
|
1904
|
+
if (error === void 0) {
|
|
1905
|
+
resolve(editor);
|
|
1906
|
+
} else {
|
|
1907
|
+
reject(error);
|
|
1908
|
+
}
|
|
1909
|
+
};
|
|
1910
|
+
const startupTimer = setTimeout(settle, dependencies.startupGraceMs);
|
|
1911
|
+
const rejectStartup = () => {
|
|
1912
|
+
settle(new Error("The configured editor could not be started."));
|
|
1913
|
+
};
|
|
1914
|
+
try {
|
|
1915
|
+
if (resolvedEditor === void 0) {
|
|
1916
|
+
dependencies.fallbackLauncher(target, void 0, rejectStartup);
|
|
1917
|
+
return;
|
|
1918
|
+
}
|
|
1919
|
+
const child = dependencies.processSpawner(
|
|
1920
|
+
editorCommand(resolvedEditor),
|
|
1921
|
+
["--goto", target],
|
|
1922
|
+
{
|
|
1923
|
+
env: dependencies.environment,
|
|
1924
|
+
stdio: "ignore",
|
|
1925
|
+
windowsHide: true
|
|
1926
|
+
}
|
|
1927
|
+
);
|
|
1928
|
+
child.once("error", rejectStartup);
|
|
1929
|
+
child.once("exit", (code, signal) => {
|
|
1930
|
+
if (code === 0) {
|
|
1931
|
+
settle(void 0, resolvedEditor);
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1934
|
+
if (code !== null || signal !== null) {
|
|
1935
|
+
rejectStartup();
|
|
1936
|
+
}
|
|
1937
|
+
});
|
|
1938
|
+
} catch {
|
|
1939
|
+
rejectStartup();
|
|
1940
|
+
}
|
|
1941
|
+
});
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
var launchConfiguredEditor = createEditorLauncher();
|
|
1852
1945
|
|
|
1853
1946
|
// src/server/request-security.ts
|
|
1854
1947
|
import { timingSafeEqual } from "crypto";
|
|
@@ -1942,7 +2035,15 @@ var STATUS_BY_ERROR = Object.freeze({
|
|
|
1942
2035
|
[ERROR_CODES8.AGENT_LIMIT_EXCEEDED]: 413,
|
|
1943
2036
|
[ERROR_CODES8.AGENT_CANCELLED]: 409,
|
|
1944
2037
|
[ERROR_CODES8.WORKTREE_DIRTY]: 409,
|
|
2038
|
+
[ERROR_CODES8.WORKTREE_NOT_REPOSITORY]: 409,
|
|
2039
|
+
[ERROR_CODES8.WORKTREE_OPERATION_IN_PROGRESS]: 409,
|
|
2040
|
+
[ERROR_CODES8.WORKTREE_CONFLICTED]: 409,
|
|
2041
|
+
[ERROR_CODES8.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: 413,
|
|
2042
|
+
[ERROR_CODES8.WORKTREE_UNTRACKED_UNSUPPORTED]: 409,
|
|
2043
|
+
[ERROR_CODES8.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: 409,
|
|
1945
2044
|
[ERROR_CODES8.TOOL_DENIED]: 403,
|
|
2045
|
+
[ERROR_CODES8.TOOL_INPUT_INVALID]: 422,
|
|
2046
|
+
[ERROR_CODES8.TOOL_PATH_DENIED]: 403,
|
|
1946
2047
|
[ERROR_CODES8.PATCH_REJECTED]: 422,
|
|
1947
2048
|
[ERROR_CODES8.VALIDATION_FAILED]: 422,
|
|
1948
2049
|
[ERROR_CODES8.APPLY_CONFLICT]: 409,
|
|
@@ -1966,8 +2067,16 @@ var PUBLIC_MESSAGES = Object.freeze({
|
|
|
1966
2067
|
[ERROR_CODES8.AGENT_BUSY]: "Another Agent job is already running.",
|
|
1967
2068
|
[ERROR_CODES8.AGENT_LIMIT_EXCEEDED]: "The Agent job exceeded a safety limit.",
|
|
1968
2069
|
[ERROR_CODES8.AGENT_CANCELLED]: "The Agent job was cancelled.",
|
|
1969
|
-
[ERROR_CODES8.WORKTREE_DIRTY]: "
|
|
2070
|
+
[ERROR_CODES8.WORKTREE_DIRTY]: "Local changes require explicit inclusion consent.",
|
|
2071
|
+
[ERROR_CODES8.WORKTREE_NOT_REPOSITORY]: "The project root is not a Git repository.",
|
|
2072
|
+
[ERROR_CODES8.WORKTREE_OPERATION_IN_PROGRESS]: "A Git operation is currently in progress.",
|
|
2073
|
+
[ERROR_CODES8.WORKTREE_CONFLICTED]: "The local workspace contains unresolved merge conflicts.",
|
|
2074
|
+
[ERROR_CODES8.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: "The local workspace exceeds the safe isolation size limit.",
|
|
2075
|
+
[ERROR_CODES8.WORKTREE_UNTRACKED_UNSUPPORTED]: "An untracked path cannot be isolated safely.",
|
|
2076
|
+
[ERROR_CODES8.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: "The local workspace state cannot be isolated safely.",
|
|
1970
2077
|
[ERROR_CODES8.TOOL_DENIED]: "The Agent tool request was denied.",
|
|
2078
|
+
[ERROR_CODES8.TOOL_INPUT_INVALID]: "The Agent tool input was invalid.",
|
|
2079
|
+
[ERROR_CODES8.TOOL_PATH_DENIED]: "The Agent tool path was denied.",
|
|
1971
2080
|
[ERROR_CODES8.PATCH_REJECTED]: "The proposed patch was rejected.",
|
|
1972
2081
|
[ERROR_CODES8.VALIDATION_FAILED]: "The proposed change failed validation.",
|
|
1973
2082
|
[ERROR_CODES8.APPLY_CONFLICT]: "The change conflicts with the current worktree.",
|
|
@@ -2031,7 +2140,8 @@ async function handleOpenEditor(request, options) {
|
|
|
2031
2140
|
const target = `${sourcePath}:${String(body.line)}:${String(body.column)}`;
|
|
2032
2141
|
const editorLauncher = options.editorLauncher ?? launchConfiguredEditor;
|
|
2033
2142
|
try {
|
|
2034
|
-
await editorLauncher(target, options.options.editor);
|
|
2143
|
+
const editor = await editorLauncher(target, options.options.editor);
|
|
2144
|
+
return Object.freeze({ editor });
|
|
2035
2145
|
} catch (error) {
|
|
2036
2146
|
options.logger?.warn(
|
|
2037
2147
|
`[spotpatch:server] ${options.options.editor === "auto" ? "The detected editor" : options.options.editor} rejected an editor request.`
|
|
@@ -2040,7 +2150,6 @@ async function handleOpenEditor(request, options) {
|
|
|
2040
2150
|
cause: error
|
|
2041
2151
|
});
|
|
2042
2152
|
}
|
|
2043
|
-
return Object.freeze({ editor: options.options.editor });
|
|
2044
2153
|
}
|
|
2045
2154
|
function createSpotPatchMiddleware(options) {
|
|
2046
2155
|
return (request, response, next) => {
|