@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/README.md
CHANGED
|
@@ -62,5 +62,12 @@ spotPatch({
|
|
|
62
62
|
The full provider map remains available for multiple providers, multiple models,
|
|
63
63
|
custom labels, checks, and limits.
|
|
64
64
|
|
|
65
|
+
AI runs directly when the Git workspace is clean. If staged, unstaged, or
|
|
66
|
+
bounded regular untracked files exist, the workbench reports their counts and
|
|
67
|
+
requires explicit consent before copying them into an isolated Agent baseline.
|
|
68
|
+
SpotPatch never stashes, resets, commits, or changes the source index, and Apply
|
|
69
|
+
or Revert touches only the Agent delta. Conflicts and unsupported workspace
|
|
70
|
+
states remain blocked with an actionable reason.
|
|
71
|
+
|
|
65
72
|
See the [repository README](https://github.com/huanglvjing/spotpatch#readme) for
|
|
66
73
|
the complete setup and security model.
|
package/dist/index.cjs
CHANGED
|
@@ -515,7 +515,7 @@ var import_node_path2 = __toESM(require("path"), 1);
|
|
|
515
515
|
// package.json
|
|
516
516
|
var package_default = {
|
|
517
517
|
name: "@spotpatch/vite",
|
|
518
|
-
version: "1.
|
|
518
|
+
version: "1.3.0",
|
|
519
519
|
description: "Vite development plugin for SpotPatch.",
|
|
520
520
|
license: "MIT",
|
|
521
521
|
repository: {
|
|
@@ -709,6 +709,7 @@ var DEFAULT_DEPENDENCIES = Object.freeze({
|
|
|
709
709
|
applyChange: import_agent.applyPreparedAgentChange,
|
|
710
710
|
createJobId: () => (0, import_node_crypto2.randomBytes)(16).toString("base64url"),
|
|
711
711
|
executeChange: import_agent.executeAgentChange,
|
|
712
|
+
inspectWorkspace: import_agent.inspectAgentWorkspace,
|
|
712
713
|
now: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
713
714
|
probeCapability: import_agent.probeProviderCapability,
|
|
714
715
|
resolveCredential: import_agent.resolveProviderCredential,
|
|
@@ -928,6 +929,7 @@ function createAgentJobManager(options) {
|
|
|
928
929
|
provider: job.provider,
|
|
929
930
|
root: options.root,
|
|
930
931
|
signal: job.controller.signal,
|
|
932
|
+
workingTreeMode: job.workingTreeMode,
|
|
931
933
|
...options.fetch === void 0 ? {} : { fetch: options.fetch }
|
|
932
934
|
});
|
|
933
935
|
job.preparedChange = preparedChange;
|
|
@@ -1064,7 +1066,8 @@ function createAgentJobManager(options) {
|
|
|
1064
1066
|
runPromise: void 0,
|
|
1065
1067
|
sequence: 0,
|
|
1066
1068
|
status: "queued",
|
|
1067
|
-
updatedAt: timestamp
|
|
1069
|
+
updatedAt: timestamp,
|
|
1070
|
+
workingTreeMode: request.workingTreeMode
|
|
1068
1071
|
};
|
|
1069
1072
|
jobs.set(id, job);
|
|
1070
1073
|
emitSnapshot(job);
|
|
@@ -1119,6 +1122,12 @@ function createAgentJobManager(options) {
|
|
|
1119
1122
|
return () => {
|
|
1120
1123
|
job.listeners.delete(listener);
|
|
1121
1124
|
};
|
|
1125
|
+
},
|
|
1126
|
+
workspaceHealth(signal) {
|
|
1127
|
+
if (closed) {
|
|
1128
|
+
throw new import_shared2.SpotPatchError(import_shared2.ERROR_CODES.AI_DISABLED);
|
|
1129
|
+
}
|
|
1130
|
+
return dependencies.inspectWorkspace(options.root, signal);
|
|
1122
1131
|
}
|
|
1123
1132
|
});
|
|
1124
1133
|
}
|
|
@@ -1603,7 +1612,8 @@ async function authorizeAgentJobRequest(input) {
|
|
|
1603
1612
|
annotation,
|
|
1604
1613
|
providerProfileId: input.request.providerProfileId,
|
|
1605
1614
|
modelProfileId: input.request.modelProfileId,
|
|
1606
|
-
providerDataConsent: true
|
|
1615
|
+
providerDataConsent: true,
|
|
1616
|
+
workingTreeMode: input.request.workingTreeMode
|
|
1607
1617
|
});
|
|
1608
1618
|
}
|
|
1609
1619
|
|
|
@@ -1669,6 +1679,9 @@ function matchAgentRequestPath(path11) {
|
|
|
1669
1679
|
if (path11 === import_shared7.SPOTPATCH_ENDPOINTS.agentCapability) {
|
|
1670
1680
|
return Object.freeze({ kind: "capability" });
|
|
1671
1681
|
}
|
|
1682
|
+
if (path11 === import_shared7.SPOTPATCH_ENDPOINTS.agentWorkspaceHealth) {
|
|
1683
|
+
return Object.freeze({ kind: "workspace-health" });
|
|
1684
|
+
}
|
|
1672
1685
|
if (path11 === import_shared7.SPOTPATCH_ENDPOINTS.agentJobs) {
|
|
1673
1686
|
return Object.freeze({ kind: "create-job" });
|
|
1674
1687
|
}
|
|
@@ -1785,6 +1798,28 @@ async function handleCreateJob(request, response, options, writeSuccess) {
|
|
|
1785
1798
|
const data = requireAgentManager(options).create(authorizedRequest);
|
|
1786
1799
|
writeSuccess(response, 202, data);
|
|
1787
1800
|
}
|
|
1801
|
+
async function handleWorkspaceHealth(request, response, options, writeSuccess) {
|
|
1802
|
+
if (request.method !== "POST") {
|
|
1803
|
+
throw new import_shared7.SpotPatchError(import_shared7.ERROR_CODES.INVALID_REQUEST);
|
|
1804
|
+
}
|
|
1805
|
+
const parsed = import_shared7.agentWorkspaceHealthRequestSchema.safeParse(
|
|
1806
|
+
await readJsonRequestBody(request)
|
|
1807
|
+
);
|
|
1808
|
+
if (!parsed.success) {
|
|
1809
|
+
throw new import_shared7.SpotPatchError(import_shared7.ERROR_CODES.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
|
+
}
|
|
1788
1823
|
async function handleJobAction(request, response, options, route, writeSuccess) {
|
|
1789
1824
|
const manager = requireAgentManager(options);
|
|
1790
1825
|
if (request.method !== "POST") {
|
|
@@ -1816,41 +1851,97 @@ async function handleAgentRequest(request, response, options, route, writeSucces
|
|
|
1816
1851
|
await handleCreateJob(request, response, options, writeSuccess);
|
|
1817
1852
|
return;
|
|
1818
1853
|
}
|
|
1854
|
+
if (route.kind === "workspace-health") {
|
|
1855
|
+
await handleWorkspaceHealth(request, response, options, writeSuccess);
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1819
1858
|
await handleJobAction(request, response, options, route, writeSuccess);
|
|
1820
1859
|
}
|
|
1821
1860
|
|
|
1822
1861
|
// src/server/editor.ts
|
|
1862
|
+
var import_node_child_process = require("child_process");
|
|
1823
1863
|
var import_launch_editor = __toESM(require("launch-editor"), 1);
|
|
1824
|
-
var EDITOR_STARTUP_GRACE_MS =
|
|
1825
|
-
function
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
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")) {
|
|
1830
1876
|
return "cursor";
|
|
1831
1877
|
}
|
|
1878
|
+
if (signature === "vscode" || signature.includes("visual studio code") || /(^|\/)(code|code-insiders)(\.exe)?($|\/)/u.test(signature)) {
|
|
1879
|
+
return "vscode";
|
|
1880
|
+
}
|
|
1832
1881
|
return void 0;
|
|
1833
1882
|
}
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
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
|
-
}
|
|
1883
|
+
function editorCommand(editor) {
|
|
1884
|
+
return editor === "cursor" ? "cursor" : "code";
|
|
1885
|
+
}
|
|
1886
|
+
var DEFAULT_DEPENDENCIES2 = Object.freeze({
|
|
1887
|
+
environment: process.env,
|
|
1888
|
+
fallbackLauncher: import_launch_editor.default,
|
|
1889
|
+
processSpawner: (command, arguments_, options) => (0, import_node_child_process.spawn)(command, [...arguments_], options),
|
|
1890
|
+
startupGraceMs: EDITOR_STARTUP_GRACE_MS
|
|
1853
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();
|
|
1854
1945
|
|
|
1855
1946
|
// src/server/request-security.ts
|
|
1856
1947
|
var import_node_crypto3 = require("crypto");
|
|
@@ -1944,7 +2035,15 @@ var STATUS_BY_ERROR = Object.freeze({
|
|
|
1944
2035
|
[import_shared9.ERROR_CODES.AGENT_LIMIT_EXCEEDED]: 413,
|
|
1945
2036
|
[import_shared9.ERROR_CODES.AGENT_CANCELLED]: 409,
|
|
1946
2037
|
[import_shared9.ERROR_CODES.WORKTREE_DIRTY]: 409,
|
|
2038
|
+
[import_shared9.ERROR_CODES.WORKTREE_NOT_REPOSITORY]: 409,
|
|
2039
|
+
[import_shared9.ERROR_CODES.WORKTREE_OPERATION_IN_PROGRESS]: 409,
|
|
2040
|
+
[import_shared9.ERROR_CODES.WORKTREE_CONFLICTED]: 409,
|
|
2041
|
+
[import_shared9.ERROR_CODES.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: 413,
|
|
2042
|
+
[import_shared9.ERROR_CODES.WORKTREE_UNTRACKED_UNSUPPORTED]: 409,
|
|
2043
|
+
[import_shared9.ERROR_CODES.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: 409,
|
|
1947
2044
|
[import_shared9.ERROR_CODES.TOOL_DENIED]: 403,
|
|
2045
|
+
[import_shared9.ERROR_CODES.TOOL_INPUT_INVALID]: 422,
|
|
2046
|
+
[import_shared9.ERROR_CODES.TOOL_PATH_DENIED]: 403,
|
|
1948
2047
|
[import_shared9.ERROR_CODES.PATCH_REJECTED]: 422,
|
|
1949
2048
|
[import_shared9.ERROR_CODES.VALIDATION_FAILED]: 422,
|
|
1950
2049
|
[import_shared9.ERROR_CODES.APPLY_CONFLICT]: 409,
|
|
@@ -1968,8 +2067,16 @@ var PUBLIC_MESSAGES = Object.freeze({
|
|
|
1968
2067
|
[import_shared9.ERROR_CODES.AGENT_BUSY]: "Another Agent job is already running.",
|
|
1969
2068
|
[import_shared9.ERROR_CODES.AGENT_LIMIT_EXCEEDED]: "The Agent job exceeded a safety limit.",
|
|
1970
2069
|
[import_shared9.ERROR_CODES.AGENT_CANCELLED]: "The Agent job was cancelled.",
|
|
1971
|
-
[import_shared9.ERROR_CODES.WORKTREE_DIRTY]: "
|
|
2070
|
+
[import_shared9.ERROR_CODES.WORKTREE_DIRTY]: "Local changes require explicit inclusion consent.",
|
|
2071
|
+
[import_shared9.ERROR_CODES.WORKTREE_NOT_REPOSITORY]: "The project root is not a Git repository.",
|
|
2072
|
+
[import_shared9.ERROR_CODES.WORKTREE_OPERATION_IN_PROGRESS]: "A Git operation is currently in progress.",
|
|
2073
|
+
[import_shared9.ERROR_CODES.WORKTREE_CONFLICTED]: "The local workspace contains unresolved merge conflicts.",
|
|
2074
|
+
[import_shared9.ERROR_CODES.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: "The local workspace exceeds the safe isolation size limit.",
|
|
2075
|
+
[import_shared9.ERROR_CODES.WORKTREE_UNTRACKED_UNSUPPORTED]: "An untracked path cannot be isolated safely.",
|
|
2076
|
+
[import_shared9.ERROR_CODES.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: "The local workspace state cannot be isolated safely.",
|
|
1972
2077
|
[import_shared9.ERROR_CODES.TOOL_DENIED]: "The Agent tool request was denied.",
|
|
2078
|
+
[import_shared9.ERROR_CODES.TOOL_INPUT_INVALID]: "The Agent tool input was invalid.",
|
|
2079
|
+
[import_shared9.ERROR_CODES.TOOL_PATH_DENIED]: "The Agent tool path was denied.",
|
|
1973
2080
|
[import_shared9.ERROR_CODES.PATCH_REJECTED]: "The proposed patch was rejected.",
|
|
1974
2081
|
[import_shared9.ERROR_CODES.VALIDATION_FAILED]: "The proposed change failed validation.",
|
|
1975
2082
|
[import_shared9.ERROR_CODES.APPLY_CONFLICT]: "The change conflicts with the current worktree.",
|
|
@@ -2033,7 +2140,8 @@ async function handleOpenEditor(request, options) {
|
|
|
2033
2140
|
const target = `${sourcePath}:${String(body.line)}:${String(body.column)}`;
|
|
2034
2141
|
const editorLauncher = options.editorLauncher ?? launchConfiguredEditor;
|
|
2035
2142
|
try {
|
|
2036
|
-
await editorLauncher(target, options.options.editor);
|
|
2143
|
+
const editor = await editorLauncher(target, options.options.editor);
|
|
2144
|
+
return Object.freeze({ editor });
|
|
2037
2145
|
} catch (error) {
|
|
2038
2146
|
options.logger?.warn(
|
|
2039
2147
|
`[spotpatch:server] ${options.options.editor === "auto" ? "The detected editor" : options.options.editor} rejected an editor request.`
|
|
@@ -2042,7 +2150,6 @@ async function handleOpenEditor(request, options) {
|
|
|
2042
2150
|
cause: error
|
|
2043
2151
|
});
|
|
2044
2152
|
}
|
|
2045
|
-
return Object.freeze({ editor: options.options.editor });
|
|
2046
2153
|
}
|
|
2047
2154
|
function createSpotPatchMiddleware(options) {
|
|
2048
2155
|
return (request, response, next) => {
|