@oscharko-dev/keiko-tools 0.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.
Files changed (59) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/browser/cdp-client.d.ts +36 -0
  3. package/dist/browser/cdp-client.d.ts.map +1 -0
  4. package/dist/browser/cdp-client.js +218 -0
  5. package/dist/browser/errors.d.ts +27 -0
  6. package/dist/browser/errors.d.ts.map +1 -0
  7. package/dist/browser/errors.js +61 -0
  8. package/dist/browser/session.d.ts +46 -0
  9. package/dist/browser/session.d.ts.map +1 -0
  10. package/dist/browser/session.js +759 -0
  11. package/dist/browser/types.d.ts +49 -0
  12. package/dist/browser/types.d.ts.map +1 -0
  13. package/dist/browser/types.js +2 -0
  14. package/dist/browser/validators.d.ts +6 -0
  15. package/dist/browser/validators.d.ts.map +1 -0
  16. package/dist/browser/validators.js +97 -0
  17. package/dist/errors.d.ts +3 -0
  18. package/dist/errors.d.ts.map +1 -0
  19. package/dist/errors.js +4 -0
  20. package/dist/exec.d.ts +45 -0
  21. package/dist/exec.d.ts.map +1 -0
  22. package/dist/exec.js +372 -0
  23. package/dist/index.d.ts +20 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +33 -0
  26. package/dist/patch-content.d.ts +11 -0
  27. package/dist/patch-content.d.ts.map +1 -0
  28. package/dist/patch-content.js +130 -0
  29. package/dist/patch-normalize.d.ts +2 -0
  30. package/dist/patch-normalize.d.ts.map +1 -0
  31. package/dist/patch-normalize.js +85 -0
  32. package/dist/patch-parse.d.ts +9 -0
  33. package/dist/patch-parse.d.ts.map +1 -0
  34. package/dist/patch-parse.js +201 -0
  35. package/dist/patch.d.ts +22 -0
  36. package/dist/patch.d.ts.map +1 -0
  37. package/dist/patch.js +469 -0
  38. package/dist/registry.d.ts +35 -0
  39. package/dist/registry.d.ts.map +1 -0
  40. package/dist/registry.js +240 -0
  41. package/dist/sandbox.d.ts +9 -0
  42. package/dist/sandbox.d.ts.map +1 -0
  43. package/dist/sandbox.js +131 -0
  44. package/dist/schemas.d.ts +3 -0
  45. package/dist/schemas.d.ts.map +1 -0
  46. package/dist/schemas.js +51 -0
  47. package/dist/terminal-policy.d.ts +10 -0
  48. package/dist/terminal-policy.d.ts.map +1 -0
  49. package/dist/terminal-policy.js +306 -0
  50. package/dist/types.d.ts +5 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +18 -0
  53. package/dist/version.d.ts +2 -0
  54. package/dist/version.d.ts.map +1 -0
  55. package/dist/version.js +4 -0
  56. package/dist/writer.d.ts +8 -0
  57. package/dist/writer.d.ts.map +1 -0
  58. package/dist/writer.js +20 -0
  59. package/package.json +42 -0
package/dist/patch.js ADDED
@@ -0,0 +1,469 @@
1
+ // Patch workflow: validate (all checks, structured report), renderDryRun (preview, never
2
+ // writes), and applyPatch (fail-closed, atomic with multi-file rollback). Every target path is
3
+ // validated via resolveWithinWorkspace + isDenied; the validated absolute path is the ONLY value
4
+ // handed to the WorkspaceWriter, so a static analyser's path sanitizer sits on this boundary
5
+ // (ADR-0006 D4). node:fs is never imported here — reads go through WorkspaceFs, writes through
6
+ // WorkspaceWriter, both injected.
7
+ import { containedRealPathInfo, isDenied, PathDeniedError, PathEscapeError, resolveWithinWorkspace, } from "@oscharko-dev/keiko-workspace";
8
+ import { nodeWorkspaceFs } from "@oscharko-dev/keiko-workspace/internal/fs";
9
+ import { CommandCancelledError, PatchApplyDisabledError, PatchApplyError, PatchValidationError, } from "./errors.js";
10
+ import { computeFileContent } from "./patch-content.js";
11
+ import { normalizeUnifiedDiffHunks } from "./patch-normalize.js";
12
+ import { parseUnifiedDiff, PatchParseError } from "./patch-parse.js";
13
+ import { nodeWorkspaceWriter } from "./writer.js";
14
+ import { DEFAULT_PATCH_LIMITS, } from "./types.js";
15
+ function containsNul(value) {
16
+ return value.includes("\u0000");
17
+ }
18
+ function isBinaryDiff(diff) {
19
+ return (diff.includes("GIT binary patch") || /^Binary files .* differ$/m.test(diff) || containsNul(diff));
20
+ }
21
+ function hasEscapedDiffLineBreak(diff) {
22
+ return diff.includes("\\n+") || diff.includes("\\n-") || diff.includes("\\n ");
23
+ }
24
+ function enforcePath(workspace, fs, path) {
25
+ let resolved;
26
+ try {
27
+ resolved = resolveWithinWorkspace(workspace.root, path);
28
+ }
29
+ catch (error) {
30
+ if (error instanceof PathEscapeError) {
31
+ throw error;
32
+ }
33
+ throw error;
34
+ }
35
+ const rel = resolved.slice(workspace.root.length).replace(/^[/\\]/, "");
36
+ if (isDenied(rel === "" ? path : rel)) {
37
+ throw new PathDeniedError("path matches an always-on deny pattern", path);
38
+ }
39
+ const info = containedRealPathInfo(fs, workspace.root, resolved);
40
+ if (!realPathMatchesLexicalTarget(fs, resolved, rel, info.realRelative)) {
41
+ throw new PathDeniedError("path resolves through an in-workspace alias", path);
42
+ }
43
+ if (fs.exists(resolved) && (fs.stat(resolved).hardLinkCount ?? 1) > 1) {
44
+ throw new PathDeniedError("path resolves through a hard-linked alias", path);
45
+ }
46
+ if (isDenied(info.realRelative)) {
47
+ throw new PathDeniedError("path matches an always-on deny pattern", path);
48
+ }
49
+ return resolved;
50
+ }
51
+ function realPathMatchesLexicalTarget(fs, absolutePath, rel, realRelative) {
52
+ if (fs.exists(absolutePath)) {
53
+ return realRelative === rel;
54
+ }
55
+ return realRelative === "" || rel === realRelative || rel.startsWith(`${realRelative}/`);
56
+ }
57
+ function safePath(workspace, fs, path) {
58
+ try {
59
+ enforcePath(workspace, fs, path);
60
+ return undefined;
61
+ }
62
+ catch (error) {
63
+ if (error instanceof PathEscapeError) {
64
+ return { code: "path-unsafe", message: "path escapes the workspace", path };
65
+ }
66
+ if (error instanceof PathDeniedError) {
67
+ return { code: "path-denied", message: "path matches an always-on deny pattern", path };
68
+ }
69
+ throw error;
70
+ }
71
+ }
72
+ function collectPathReasons(workspace, fs, files) {
73
+ const reasons = [];
74
+ for (const file of files) {
75
+ const rejection = safePath(workspace, fs, file.path);
76
+ if (rejection !== undefined) {
77
+ reasons.push(rejection);
78
+ }
79
+ }
80
+ return reasons;
81
+ }
82
+ function readCurrent(workspace, fs, path) {
83
+ const absolute = resolveWithinWorkspace(workspace.root, path);
84
+ if (!fs.exists(absolute)) {
85
+ return undefined;
86
+ }
87
+ return fs.readFileUtf8(absolute);
88
+ }
89
+ function toLines(content) {
90
+ if (content.length === 0) {
91
+ return [];
92
+ }
93
+ const lines = content.split("\n");
94
+ if (lines.at(-1) === "") {
95
+ lines.pop();
96
+ }
97
+ return lines;
98
+ }
99
+ function hunkPreimageLines(file, hunkIndex) {
100
+ const hunk = file.hunks[hunkIndex];
101
+ if (hunk === undefined) {
102
+ return [];
103
+ }
104
+ return hunk.lines
105
+ .filter((line) => line.startsWith(" ") || line.startsWith("-"))
106
+ .map((line) => line.slice(1));
107
+ }
108
+ function startsWithSequence(lines, index, needle) {
109
+ return needle.every((line, offset) => lines[index + offset] === line);
110
+ }
111
+ function uniqueSequenceIndex(lines, needle) {
112
+ if (needle.length === 0 || needle.length > lines.length) {
113
+ return undefined;
114
+ }
115
+ let found;
116
+ for (let index = 0; index <= lines.length - needle.length; index += 1) {
117
+ if (!startsWithSequence(lines, index, needle)) {
118
+ continue;
119
+ }
120
+ if (found !== undefined) {
121
+ return undefined;
122
+ }
123
+ found = index;
124
+ }
125
+ return found;
126
+ }
127
+ function alignFileHunks(file, current) {
128
+ if (file.kind !== "modify") {
129
+ return file;
130
+ }
131
+ if (current === undefined) {
132
+ return isCreateOnlyModify(file) ? { ...file, kind: "create" } : file;
133
+ }
134
+ const currentLines = toLines(current);
135
+ const hunks = file.hunks.map((hunk, index) => {
136
+ const preimage = hunkPreimageLines(file, index);
137
+ if (hunk.oldStart > 0 && startsWithSequence(currentLines, hunk.oldStart - 1, preimage)) {
138
+ return hunk;
139
+ }
140
+ const anchor = uniqueSequenceIndex(currentLines, preimage);
141
+ if (anchor === undefined) {
142
+ return hunk;
143
+ }
144
+ const start = anchor + 1;
145
+ return { ...hunk, oldStart: start, newStart: start };
146
+ });
147
+ if (hunks.every((hunk, index) => hunk === file.hunks[index])) {
148
+ return file;
149
+ }
150
+ return { ...file, hunks };
151
+ }
152
+ function isCreateOnlyModify(file) {
153
+ return (file.hunks.length > 0 &&
154
+ file.hunks.every((hunk) => hunk.oldLines === 0 && hunk.lines.every((line) => line.startsWith("+"))));
155
+ }
156
+ function alignHunksToCurrentContent(workspace, fs, files) {
157
+ return files.map((file) => alignFileHunks(file, readCurrent(workspace, fs, file.path)));
158
+ }
159
+ function unanchoredModifyReasons(files) {
160
+ return files
161
+ .filter((file) => file.kind === "modify" && file.hunks.some((hunk) => hunk.oldStart <= 0))
162
+ .map((file) => ({
163
+ code: "malformed",
164
+ message: "modify hunk has no unique anchor",
165
+ path: file.path,
166
+ }));
167
+ }
168
+ function collectConflicts(workspace, fs, files, allowOverwrite) {
169
+ const conflicts = [];
170
+ for (const file of files) {
171
+ const current = readCurrent(workspace, fs, file.path);
172
+ const outcome = computeFileContent(file, current, allowOverwrite);
173
+ for (const conflict of outcome.conflicts) {
174
+ conflicts.push({ path: file.path, hunkIndex: conflict.hunkIndex, reason: conflict.reason });
175
+ }
176
+ }
177
+ return conflicts;
178
+ }
179
+ function sizeAndCountReasons(diff, files, totalChangedLines, limits, totalBytes) {
180
+ const reasons = [];
181
+ if (diff.trim().length > 0 && files.length === 0) {
182
+ reasons.push({ code: "malformed", message: "diff does not contain any file changes" });
183
+ }
184
+ if (totalBytes > limits.maxPatchBytes) {
185
+ reasons.push({
186
+ code: "size-limit",
187
+ message: `patch exceeds ${String(limits.maxPatchBytes)} bytes`,
188
+ });
189
+ }
190
+ if (isBinaryDiff(diff)) {
191
+ reasons.push({ code: "binary", message: "binary patches are not supported" });
192
+ }
193
+ if (hasEscapedDiffLineBreak(diff)) {
194
+ reasons.push({
195
+ code: "malformed",
196
+ message: "diff contains escaped newline markers; use real line breaks",
197
+ });
198
+ }
199
+ if (totalChangedLines > limits.maxChangedLines) {
200
+ reasons.push({
201
+ code: "line-limit",
202
+ message: `changed lines exceed ${String(limits.maxChangedLines)}`,
203
+ });
204
+ }
205
+ if (files.length > limits.maxFilesChanged) {
206
+ reasons.push({
207
+ code: "file-limit",
208
+ message: `files changed exceed ${String(limits.maxFilesChanged)}`,
209
+ });
210
+ }
211
+ return reasons;
212
+ }
213
+ function renderHeader(file) {
214
+ if (file.kind === "create") {
215
+ return ["--- /dev/null", `+++ b/${file.path}`];
216
+ }
217
+ if (file.kind === "delete") {
218
+ return [`--- a/${file.path}`, "+++ /dev/null"];
219
+ }
220
+ return [`--- a/${file.path}`, `+++ b/${file.path}`];
221
+ }
222
+ function renderParsedPatch(files) {
223
+ const lines = [];
224
+ for (const file of files) {
225
+ lines.push(...renderHeader(file));
226
+ for (const hunk of file.hunks) {
227
+ lines.push(`@@ -${String(hunk.oldStart)},${String(hunk.oldLines)} +${String(hunk.newStart)},${String(hunk.newLines)} @@`, ...hunk.lines);
228
+ }
229
+ }
230
+ return lines.join("\n");
231
+ }
232
+ function parseDiffForValidation(diff) {
233
+ try {
234
+ return { files: parseUnifiedDiff(diff).files, effectiveDiff: diff, normalized: false };
235
+ }
236
+ catch (error) {
237
+ if (!(error instanceof PatchParseError)) {
238
+ throw error;
239
+ }
240
+ const normalizedDiff = normalizeUnifiedDiffHunks(diff);
241
+ if (normalizedDiff === diff) {
242
+ throw error;
243
+ }
244
+ return {
245
+ files: parseUnifiedDiff(normalizedDiff).files,
246
+ effectiveDiff: normalizedDiff,
247
+ normalized: true,
248
+ };
249
+ }
250
+ }
251
+ function malformedValidation(diff, error) {
252
+ const message = error instanceof PatchParseError ? error.message : "unparseable diff";
253
+ return {
254
+ ok: false,
255
+ files: [],
256
+ totalChangedLines: 0,
257
+ totalBytes: Buffer.byteLength(diff, "utf8"),
258
+ reasons: [{ code: "malformed", message }],
259
+ conflicts: [],
260
+ };
261
+ }
262
+ function completeValidation(workspace, fs, limits, diff, parsed, allowOverwrite) {
263
+ const files = parsed.files;
264
+ const totalBytes = Buffer.byteLength(parsed.effectiveDiff, "utf8");
265
+ const totalChangedLines = files.reduce((sum, f) => sum + f.addedLines + f.removedLines, 0);
266
+ const pathAndSizeReasons = [
267
+ ...sizeAndCountReasons(parsed.effectiveDiff, files, totalChangedLines, limits, totalBytes),
268
+ ...collectPathReasons(workspace, fs, files),
269
+ ];
270
+ const alignedFiles = pathAndSizeReasons.length === 0 ? alignHunksToCurrentContent(workspace, fs, files) : files;
271
+ const aligned = alignedFiles.some((file, index) => file !== files[index]);
272
+ const effectiveDiff = parsed.normalized || aligned ? renderParsedPatch(alignedFiles) : diff;
273
+ const reasons = [...pathAndSizeReasons, ...unanchoredModifyReasons(alignedFiles)];
274
+ const conflicts = reasons.length === 0 ? collectConflicts(workspace, fs, alignedFiles, allowOverwrite) : [];
275
+ return {
276
+ ok: reasons.length === 0 && conflicts.length === 0,
277
+ files: alignedFiles,
278
+ totalChangedLines,
279
+ totalBytes: Buffer.byteLength(effectiveDiff, "utf8"),
280
+ ...(effectiveDiff === diff ? {} : { normalizedDiff: effectiveDiff }),
281
+ reasons,
282
+ conflicts,
283
+ };
284
+ }
285
+ export function validatePatch(workspace, diff, deps = {}) {
286
+ const fs = deps.fs ?? nodeWorkspaceFs;
287
+ const limits = deps.limits ?? DEFAULT_PATCH_LIMITS;
288
+ try {
289
+ return completeValidation(workspace, fs, limits, diff, parseDiffForValidation(diff), deps.allowOverwrite ?? false);
290
+ }
291
+ catch (error) {
292
+ return malformedValidation(diff, error);
293
+ }
294
+ }
295
+ function renderFileLine(file) {
296
+ return `${file.kind} ${file.path} (+${String(file.addedLines)} -${String(file.removedLines)})`;
297
+ }
298
+ // Human-readable preview returned by propose_patch. NEVER writes. Lists per-file +/- counts,
299
+ // any rejection reasons, and any conflicts so the reviewer sees exactly what apply would do.
300
+ export function renderDryRun(validation) {
301
+ const header = validation.ok
302
+ ? `PATCH OK — ${String(validation.files.length)} file(s), ${String(validation.totalChangedLines)} changed line(s)`
303
+ : "PATCH REJECTED";
304
+ const fileLines = validation.files.map(renderFileLine);
305
+ const reasonLines = validation.reasons.map((r) => `reject[${r.code}]: ${r.message}${r.path === undefined ? "" : ` (${r.path})`}`);
306
+ const conflictLines = validation.conflicts.map((c) => `conflict: ${c.path} hunk#${String(c.hunkIndex)}: ${c.reason}`);
307
+ return [header, ...fileLines, ...reasonLines, ...conflictLines].join("\n");
308
+ }
309
+ // ─── Inverse patch (guarded revert proposal — Issue #1204) ──────────────────────────────────────
310
+ // Produces the unified diff that exactly undoes `diff`, for a guarded revert proposal the user reviews
311
+ // and re-applies after a failed post-apply verification. The transformation is pure and lossless: a
312
+ // create becomes a delete (and vice-versa), and a modify swaps its old/new line ranges and its +/-
313
+ // markers. Applied to the post-apply content, the inverse restores the pre-apply state. Throws
314
+ // PatchParseError on an unparseable diff (the caller already validated/applied the forward diff).
315
+ function invertHunkLine(line) {
316
+ const marker = line.charAt(0);
317
+ if (marker === "+") {
318
+ return `-${line.slice(1)}`;
319
+ }
320
+ if (marker === "-") {
321
+ return `+${line.slice(1)}`;
322
+ }
323
+ return line;
324
+ }
325
+ function invertHunk(hunk) {
326
+ return {
327
+ oldStart: hunk.newStart,
328
+ oldLines: hunk.newLines,
329
+ newStart: hunk.oldStart,
330
+ newLines: hunk.oldLines,
331
+ lines: hunk.lines.map(invertHunkLine),
332
+ };
333
+ }
334
+ const INVERSE_KIND = {
335
+ create: "delete",
336
+ delete: "create",
337
+ modify: "modify",
338
+ };
339
+ function invertFileChange(file) {
340
+ return {
341
+ path: file.path,
342
+ kind: INVERSE_KIND[file.kind],
343
+ hunks: file.hunks.map(invertHunk),
344
+ addedLines: file.removedLines,
345
+ removedLines: file.addedLines,
346
+ };
347
+ }
348
+ export function invertPatch(diff) {
349
+ const parsed = parseUnifiedDiff(diff);
350
+ return renderParsedPatch(parsed.files.map(invertFileChange));
351
+ }
352
+ function hasCreateOverwrite(workspace, fs, files) {
353
+ return files.some((file) => file.kind === "create" && readCurrent(workspace, fs, file.path) !== undefined);
354
+ }
355
+ // Builds a guarded restore proposal only when the diff needed for restoration is already content-safe.
356
+ // For normal creates, modifies, and deletes, `invertPatch` contains only content that was already in the
357
+ // reviewed forward diff. For explicit create-over-existing overwrites, a faithful restore would need the
358
+ // undisclosed pre-existing file body; returning it would create a read/exfiltration path, and returning a
359
+ // delete would destroy the pre-existing file. In that case no restore diff is emitted.
360
+ export function buildRestorePatch(workspace, diff, deps = {}) {
361
+ const fs = deps.fs ?? nodeWorkspaceFs;
362
+ const allowOverwrite = deps.allowOverwrite ?? false;
363
+ const validation = validatePatch(workspace, diff, {
364
+ fs,
365
+ allowOverwrite,
366
+ ...(deps.limits ? { limits: deps.limits } : {}),
367
+ });
368
+ if (!validation.ok) {
369
+ throw new PatchValidationError("patch failed validation", validation.reasons, validation.conflicts);
370
+ }
371
+ if (allowOverwrite && hasCreateOverwrite(workspace, fs, validation.files)) {
372
+ return undefined;
373
+ }
374
+ return invertPatch(validation.normalizedDiff ?? diff);
375
+ }
376
+ function planWrites(workspace, fs, signal, files, allowOverwrite) {
377
+ const plans = [];
378
+ for (const file of files) {
379
+ if (signal.aborted) {
380
+ throw new CommandCancelledError("apply cancelled before write planning completed");
381
+ }
382
+ const absolute = enforcePath(workspace, fs, file.path);
383
+ const original = readCurrent(workspace, fs, file.path);
384
+ const outcome = computeFileContent(file, original, allowOverwrite);
385
+ plans.push({
386
+ path: file.path,
387
+ absolute,
388
+ kind: file.kind,
389
+ newContent: outcome.content,
390
+ original,
391
+ });
392
+ }
393
+ return plans;
394
+ }
395
+ function applyOne(writer, plan) {
396
+ if (plan.kind === "delete") {
397
+ writer.remove(plan.absolute);
398
+ return;
399
+ }
400
+ const dir = plan.absolute.replace(/[/\\][^/\\]*$/, "");
401
+ writer.mkdirp(dir);
402
+ writer.writeFileUtf8(plan.absolute, plan.newContent ?? "");
403
+ }
404
+ function rollback(writer, done) {
405
+ for (const plan of done) {
406
+ if (plan.original === undefined) {
407
+ writer.remove(plan.absolute);
408
+ }
409
+ else {
410
+ writer.writeFileUtf8(plan.absolute, plan.original);
411
+ }
412
+ }
413
+ }
414
+ function commit(writer, plans, signal) {
415
+ const done = [];
416
+ for (const plan of plans) {
417
+ if (isAbortRequested(signal)) {
418
+ rollback(writer, done);
419
+ throw new CommandCancelledError("apply cancelled during write phase");
420
+ }
421
+ try {
422
+ applyOne(writer, plan);
423
+ done.push(plan);
424
+ }
425
+ catch (error) {
426
+ rollback(writer, done);
427
+ const message = error instanceof Error ? error.message : "write failed";
428
+ throw new PatchApplyError(`apply failed, rolled back: ${message}`, plan.path);
429
+ }
430
+ if (isAbortRequested(signal)) {
431
+ rollback(writer, done);
432
+ throw new CommandCancelledError("apply cancelled during write phase");
433
+ }
434
+ }
435
+ }
436
+ function isAbortRequested(signal) {
437
+ return signal.aborted;
438
+ }
439
+ function summarize(plans) {
440
+ return {
441
+ changedFiles: plans.map((p) => p.path),
442
+ created: plans.filter((p) => p.kind === "create").map((p) => p.path),
443
+ deleted: plans.filter((p) => p.kind === "delete").map((p) => p.path),
444
+ };
445
+ }
446
+ // Applies a validated patch atomically. Fail-closed: no write unless applyEnabled === true.
447
+ // Order: gate → validate → abort check → plan (pure) → write with multi-file rollback.
448
+ export function applyPatch(workspace, diff, deps) {
449
+ if (!deps.applyEnabled) {
450
+ throw new PatchApplyDisabledError("apply is disabled (applyEnabled is false)");
451
+ }
452
+ const fs = deps.fs ?? nodeWorkspaceFs;
453
+ const writer = deps.writer ?? nodeWorkspaceWriter;
454
+ const allowOverwrite = deps.allowOverwrite ?? false;
455
+ const validation = validatePatch(workspace, diff, {
456
+ fs,
457
+ allowOverwrite,
458
+ ...(deps.limits ? { limits: deps.limits } : {}),
459
+ });
460
+ if (!validation.ok) {
461
+ throw new PatchValidationError("patch failed validation", validation.reasons, validation.conflicts);
462
+ }
463
+ if (deps.signal.aborted) {
464
+ throw new CommandCancelledError("apply cancelled before write phase");
465
+ }
466
+ const plans = planWrites(workspace, fs, deps.signal, validation.files, allowOverwrite);
467
+ commit(writer, plans, deps.signal);
468
+ return summarize(plans);
469
+ }
@@ -0,0 +1,35 @@
1
+ import type { ToolCallRequest, ToolCallResult, ToolDefinition, ToolPort } from "@oscharko-dev/keiko-contracts";
2
+ import { type WorkspaceFs, type WorkspaceInfo } from "@oscharko-dev/keiko-workspace";
3
+ import { type ExecutableResolver, type SpawnFn } from "./exec.js";
4
+ import { type WorkspaceWriter } from "./writer.js";
5
+ import { type ToolHostConfigInput } from "./types.js";
6
+ export declare class WorkspaceToolHost implements ToolPort {
7
+ private readonly workspace;
8
+ private readonly fs;
9
+ private readonly writer;
10
+ private readonly spawn;
11
+ private readonly resolveExecutable;
12
+ private readonly config;
13
+ private readonly processEnv;
14
+ private readonly now;
15
+ constructor(deps: {
16
+ readonly workspace: WorkspaceInfo;
17
+ readonly fs?: WorkspaceFs | undefined;
18
+ readonly writer?: WorkspaceWriter | undefined;
19
+ readonly spawn?: SpawnFn | undefined;
20
+ readonly resolveExecutable?: ExecutableResolver | undefined;
21
+ readonly config?: ToolHostConfigInput | undefined;
22
+ readonly processEnv?: NodeJS.ProcessEnv | undefined;
23
+ readonly now?: (() => number) | undefined;
24
+ });
25
+ listTools(): readonly ToolDefinition[];
26
+ execute(request: ToolCallRequest): Promise<ToolCallResult>;
27
+ private dispatch;
28
+ private readFile;
29
+ private listFiles;
30
+ private inspectScripts;
31
+ private runCommandTool;
32
+ private proposePatch;
33
+ private applyPatchTool;
34
+ }
35
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAEV,eAAe,EACf,cAAc,EACd,cAAc,EACd,QAAQ,EACT,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,aAAa,EACnB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAA2B,KAAK,kBAAkB,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAI3F,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,YAAY,CAAC;AA0EpB,qBAAa,iBAAkB,YAAW,QAAQ;IAChD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAc;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;IAChC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAiC;IACnE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAC/C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,IAAI,EAAE;QAChB,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;QAClC,QAAQ,CAAC,EAAE,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;QACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;QAC9C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QACrC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;QAC5D,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;QAClD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,CAAC;QACpD,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,CAAC;KAC3C;IAWD,SAAS,IAAI,SAAS,cAAc,EAAE;IAIhC,OAAO,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;IAehE,OAAO,CAAC,QAAQ;IAoBhB,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,SAAS;IAgBjB,OAAO,CAAC,cAAc;YAYR,cAAc;IA+C5B,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,cAAc;CAoBvB"}