@hheei/pi-apply-patch 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +8 -2
- package/THIRD_PARTY_NOTICES.md +3 -0
- package/dist/apply-patch/executor.d.ts +15 -0
- package/dist/apply-patch/executor.js +407 -0
- package/dist/apply-patch/fs.d.ts +37 -0
- package/dist/apply-patch/fs.js +186 -0
- package/dist/apply-patch/glyph.d.ts +8 -0
- package/dist/apply-patch/glyph.js +23 -0
- package/dist/apply-patch/index.d.ts +6 -0
- package/dist/apply-patch/index.js +5 -0
- package/dist/apply-patch/jsdiff-matching.d.ts +13 -0
- package/dist/apply-patch/jsdiff-matching.js +613 -0
- package/dist/apply-patch/jsdiff-worker.d.ts +1 -0
- package/dist/apply-patch/jsdiff-worker.js +18 -0
- package/dist/apply-patch/jsdiff.d.ts +9 -0
- package/dist/apply-patch/jsdiff.js +219 -0
- package/dist/apply-patch/outcome.d.ts +84 -0
- package/dist/apply-patch/outcome.js +1 -0
- package/dist/apply-patch/parser.d.ts +85 -0
- package/dist/apply-patch/parser.js +486 -0
- package/dist/apply-patch/paths.d.ts +5 -0
- package/dist/apply-patch/paths.js +23 -0
- package/dist/apply-patch/policy.d.ts +11 -0
- package/dist/apply-patch/policy.js +44 -0
- package/dist/apply-patch/presentation.d.ts +7 -0
- package/dist/apply-patch/presentation.js +127 -0
- package/dist/apply-patch/settings.d.ts +5 -0
- package/dist/apply-patch/settings.js +33 -0
- package/dist/apply-patch/tool.d.ts +18 -0
- package/dist/apply-patch/tool.js +178 -0
- package/dist/apply-patch/view.d.ts +34 -0
- package/dist/apply-patch/view.js +256 -0
- package/dist/apply-patch-tool.d.ts +1 -0
- package/dist/apply-patch-tool.js +1 -0
- package/dist/extension.d.ts +2 -0
- package/dist/extension.js +22 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +57 -5
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { link, lstat, mkdir, readdir, readFile, realpath, rename, stat, unlink, writeFile, } from "node:fs/promises";
|
|
3
|
+
import { dirname, isAbsolute, win32 } from "node:path";
|
|
4
|
+
import { resolvePatchPath } from "./paths.js";
|
|
5
|
+
export const APPLY_PATCH_MAX_FILE_SIZE = 32 * 1024 * 1024;
|
|
6
|
+
export const SFTP_SMALL_BYTES = 1_048_576;
|
|
7
|
+
export const SFTP_SMALL_TIMEOUT_MS = 30_000;
|
|
8
|
+
export const SFTP_LARGE_TIMEOUT_MS = 60_000;
|
|
9
|
+
const TEMP_RE = /^\..+\.agentpatch-[0-9a-f-]{36}\.tmp$/iu;
|
|
10
|
+
export class FsTransportError extends Error {
|
|
11
|
+
phase;
|
|
12
|
+
timedOut;
|
|
13
|
+
constructor(phase, timedOut, message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "FsTransportError";
|
|
16
|
+
this.phase = phase;
|
|
17
|
+
this.timedOut = timedOut;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function sftpTimeoutMs(bytes) {
|
|
21
|
+
return bytes <= SFTP_SMALL_BYTES ? SFTP_SMALL_TIMEOUT_MS : SFTP_LARGE_TIMEOUT_MS;
|
|
22
|
+
}
|
|
23
|
+
export function agentPatchTempName(base) {
|
|
24
|
+
return `.${base}.agentpatch-${randomUUID()}.tmp`;
|
|
25
|
+
}
|
|
26
|
+
export function isAgentPatchTemp(name) {
|
|
27
|
+
return TEMP_RE.test(name);
|
|
28
|
+
}
|
|
29
|
+
export function fileTooLarge(size, path) {
|
|
30
|
+
return new Error(`file_too_large (${size} > ${APPLY_PATCH_MAX_FILE_SIZE}) at ${path}; use another tool suitable for large-file edits.`);
|
|
31
|
+
}
|
|
32
|
+
/** Installs fully prepared bytes through one sibling temporary file. */
|
|
33
|
+
export async function publishPreparedFile(fs, path, data, mode, replaceExisting, signal, beforePublish) {
|
|
34
|
+
const slash = path.lastIndexOf("/");
|
|
35
|
+
const directory = slash < 0 ? "." : path.slice(0, slash);
|
|
36
|
+
if (directory !== ".")
|
|
37
|
+
await fs.mkdirp(directory, signal);
|
|
38
|
+
const base = slash < 0 ? path : path.slice(slash + 1);
|
|
39
|
+
const temp = `${directory === "." ? "" : `${directory}/`}${agentPatchTempName(base)}`;
|
|
40
|
+
try {
|
|
41
|
+
await fs.writeAtomic(temp, data, mode, signal);
|
|
42
|
+
await beforePublish?.();
|
|
43
|
+
if (replaceExisting)
|
|
44
|
+
await fs.replace(temp, path, signal);
|
|
45
|
+
else
|
|
46
|
+
await fs.renameNew(temp, path, signal);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (!(error instanceof FsTransportError) || error.phase === "write")
|
|
50
|
+
await fs.unlink(temp).catch(() => undefined);
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function isMissing(error) {
|
|
55
|
+
return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
|
|
56
|
+
}
|
|
57
|
+
function localMeta(info, kindOverride) {
|
|
58
|
+
const kind = kindOverride ??
|
|
59
|
+
(info.isSymbolicLink()
|
|
60
|
+
? "symlink"
|
|
61
|
+
: info.isFile()
|
|
62
|
+
? "file"
|
|
63
|
+
: info.isDirectory()
|
|
64
|
+
? "directory"
|
|
65
|
+
: "other");
|
|
66
|
+
return {
|
|
67
|
+
kind,
|
|
68
|
+
size: info.size,
|
|
69
|
+
mode: info.mode & 0o7777,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export function createLocalPatchFs(workspaceRoot) {
|
|
73
|
+
const abs = (path) => isAbsolute(path) || win32.isAbsolute(path) ? path : resolvePatchPath(workspaceRoot, path);
|
|
74
|
+
return {
|
|
75
|
+
scope: workspaceRoot,
|
|
76
|
+
async lstat(path, signal) {
|
|
77
|
+
signal?.throwIfAborted();
|
|
78
|
+
try {
|
|
79
|
+
return localMeta(await lstat(abs(path)));
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (isMissing(error))
|
|
83
|
+
return { kind: "missing", size: 0 };
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
async statFollow(path, signal) {
|
|
88
|
+
signal?.throwIfAborted();
|
|
89
|
+
try {
|
|
90
|
+
const info = await stat(abs(path));
|
|
91
|
+
return localMeta(info, info.isFile() ? "file" : info.isDirectory() ? "directory" : "other");
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (isMissing(error))
|
|
95
|
+
return { kind: "missing", size: 0 };
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
async followLeaf(path, signal) {
|
|
100
|
+
signal?.throwIfAborted();
|
|
101
|
+
const current = abs(path);
|
|
102
|
+
try {
|
|
103
|
+
const info = await lstat(current);
|
|
104
|
+
if (!info.isSymbolicLink())
|
|
105
|
+
return path;
|
|
106
|
+
return await realpath(current);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
if (isMissing(error))
|
|
110
|
+
return path;
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
async readFollow(path, signal) {
|
|
115
|
+
return await readFile(abs(path), signal === undefined ? undefined : { signal });
|
|
116
|
+
},
|
|
117
|
+
async mkdirp(path, signal) {
|
|
118
|
+
signal?.throwIfAborted();
|
|
119
|
+
await mkdir(abs(path), { recursive: true });
|
|
120
|
+
},
|
|
121
|
+
async writeAtomic(path, data, mode, signal) {
|
|
122
|
+
signal?.throwIfAborted();
|
|
123
|
+
const target = abs(path);
|
|
124
|
+
await mkdir(dirname(target), { recursive: true });
|
|
125
|
+
await writeFile(target, data, {
|
|
126
|
+
...(signal === undefined ? {} : { signal }),
|
|
127
|
+
...(mode === undefined ? {} : { mode }),
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
async renameNew(from, to, signal) {
|
|
131
|
+
signal?.throwIfAborted();
|
|
132
|
+
await link(abs(from), abs(to));
|
|
133
|
+
await unlink(abs(from)).catch(() => undefined);
|
|
134
|
+
},
|
|
135
|
+
async replace(from, to, signal) {
|
|
136
|
+
if (signal?.aborted)
|
|
137
|
+
throw new FsTransportError("replace", false, "cancelled before replace");
|
|
138
|
+
try {
|
|
139
|
+
await rename(abs(from), abs(to));
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
if (signal?.aborted)
|
|
143
|
+
throw new FsTransportError("replace", false, "cancelled during replace");
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
async unlink(path, signal) {
|
|
148
|
+
if (signal?.aborted)
|
|
149
|
+
throw new FsTransportError("remove", false, "cancelled before remove");
|
|
150
|
+
try {
|
|
151
|
+
await unlink(abs(path));
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (signal?.aborted)
|
|
155
|
+
throw new FsTransportError("remove", false, "cancelled during remove");
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
async list(path) {
|
|
160
|
+
try {
|
|
161
|
+
return await readdir(path === "" || path === "." ? workspaceRoot : abs(path));
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
if (isMissing(error))
|
|
165
|
+
return [];
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
export async function gcAgentPatchTemps(fs, directories, signal) {
|
|
172
|
+
const seen = new Set();
|
|
173
|
+
for (const directory of directories) {
|
|
174
|
+
const key = directory === "" ? "." : directory;
|
|
175
|
+
if (seen.has(key))
|
|
176
|
+
continue;
|
|
177
|
+
seen.add(key);
|
|
178
|
+
const names = await fs.list(key, signal);
|
|
179
|
+
for (const name of names) {
|
|
180
|
+
if (!isAgentPatchTemp(name))
|
|
181
|
+
continue;
|
|
182
|
+
const path = key === "." ? name : `${key}/${name}`;
|
|
183
|
+
await fs.unlink(path, signal).catch(() => undefined);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ApplyPatchOperationStatus } from "./outcome.js";
|
|
2
|
+
/**
|
|
3
|
+
* How one mutation outcome is drawn, in the Nerd Font vocabulary the framed tool status and the
|
|
4
|
+
* todo rows already use. `apply_patch` operation rows and the remote `edit`/`write` failure lines
|
|
5
|
+
* share it, so one outcome never draws two different symbols across the tools.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MUTATION_GLYPH: Record<ApplyPatchOperationStatus, string>;
|
|
8
|
+
export declare const MUTATION_TONE: Record<ApplyPatchOperationStatus, "success" | "warning" | "dim" | "error">;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How one mutation outcome is drawn, in the Nerd Font vocabulary the framed tool status and the
|
|
3
|
+
* todo rows already use. `apply_patch` operation rows and the remote `edit`/`write` failure lines
|
|
4
|
+
* share it, so one outcome never draws two different symbols across the tools.
|
|
5
|
+
*/
|
|
6
|
+
export const MUTATION_GLYPH = {
|
|
7
|
+
pending: "",
|
|
8
|
+
applied: "",
|
|
9
|
+
partial: "",
|
|
10
|
+
fuzzy: "",
|
|
11
|
+
unconfirmed: "",
|
|
12
|
+
not_applied: "",
|
|
13
|
+
rejected: "",
|
|
14
|
+
};
|
|
15
|
+
export const MUTATION_TONE = {
|
|
16
|
+
pending: "dim",
|
|
17
|
+
applied: "success",
|
|
18
|
+
partial: "warning",
|
|
19
|
+
fuzzy: "warning",
|
|
20
|
+
unconfirmed: "warning",
|
|
21
|
+
not_applied: "dim",
|
|
22
|
+
rejected: "error",
|
|
23
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { type ApplyPatchInWorkspaceOptions, applyPatchInWorkspace, } from "./executor.js";
|
|
2
|
+
export { APPLY_PATCH_MAX_FILE_SIZE, createLocalPatchFs, FsTransportError, fileTooLarge, gcAgentPatchTemps, type PatchFs, publishPreparedFile, } from "./fs.js";
|
|
3
|
+
export { MUTATION_GLYPH, MUTATION_TONE } from "./glyph.js";
|
|
4
|
+
export type { ApplyPatchAppliedOperation, ApplyPatchHunkSnapshot, ApplyPatchInWorkspaceResult, ApplyPatchOperationProgress, ApplyPatchProgress, ApplyPatchRejection, PatchHunkOutcome, PreparedPatchUpdate, } from "./outcome.js";
|
|
5
|
+
export { compileV4aUpdateToUnifiedDiff, createV4aPreviewCursor, findV4aPatchConflicts, MAX_V4A_HUNK_LINES, MAX_V4A_HUNKS_PER_UPDATE, MAX_V4A_OPERATIONS, MAX_V4A_PATCH_BYTES, MAX_V4A_PATH_BYTES, MAX_V4A_PATH_SEGMENT_BYTES, operationTouchedPaths, parseV4aPatch, parseV4aPatchProgressively, previewV4aPatchFileCount, previewV4aPatchPrefix, type V4aAddedLine, type V4aAddOperation, type V4aContextLine, type V4aDeleteOperation, type V4aPatch, type V4aPatchConflict, type V4aPatchOperation, type V4aPreviewCursor, type V4aPreviewOperation, type V4aRemovedLine, type V4aUpdateHunk, type V4aUpdateLine, type V4aUpdateOperation, } from "./parser.js";
|
|
6
|
+
export { type ApplyPatchPolicy, DEFAULT_APPLY_PATCH_POLICY, type LoadApplyPatchPolicyOptions, loadApplyPatchPolicy, } from "./policy.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { applyPatchInWorkspace, } from "./executor.js";
|
|
2
|
+
export { APPLY_PATCH_MAX_FILE_SIZE, createLocalPatchFs, FsTransportError, fileTooLarge, gcAgentPatchTemps, publishPreparedFile, } from "./fs.js";
|
|
3
|
+
export { MUTATION_GLYPH, MUTATION_TONE } from "./glyph.js";
|
|
4
|
+
export { compileV4aUpdateToUnifiedDiff, createV4aPreviewCursor, findV4aPatchConflicts, MAX_V4A_HUNK_LINES, MAX_V4A_HUNKS_PER_UPDATE, MAX_V4A_OPERATIONS, MAX_V4A_PATCH_BYTES, MAX_V4A_PATH_BYTES, MAX_V4A_PATH_SEGMENT_BYTES, operationTouchedPaths, parseV4aPatch, parseV4aPatchProgressively, previewV4aPatchFileCount, previewV4aPatchPrefix, } from "./parser.js";
|
|
5
|
+
export { DEFAULT_APPLY_PATCH_POLICY, loadApplyPatchPolicy, } from "./policy.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PreparedPatchUpdate } from "./outcome.js";
|
|
2
|
+
import type { V4aUpdateHunk } from "./parser.js";
|
|
3
|
+
export interface JsDiffHunkInput {
|
|
4
|
+
readonly unifiedDiff: string;
|
|
5
|
+
readonly hunk: V4aUpdateHunk;
|
|
6
|
+
}
|
|
7
|
+
export interface JsDiffUpdateInput {
|
|
8
|
+
readonly before: Uint8Array;
|
|
9
|
+
readonly hunks: readonly JsDiffHunkInput[];
|
|
10
|
+
readonly fuzzFactor: number;
|
|
11
|
+
readonly snapshotPath: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function prepareJsDiffUpdate(data: JsDiffUpdateInput): PreparedPatchUpdate;
|