harnery 0.45.0 → 0.45.2

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 (235) hide show
  1. package/dist/commander.js +0 -1
  2. package/dist/commands/files.d.ts +2 -2
  3. package/dist/commands/files.d.ts.map +1 -1
  4. package/dist/commands/files.js +4 -7
  5. package/dist/commands/servers.d.ts.map +1 -1
  6. package/dist/commands/servers.js +17 -11
  7. package/dist/commands/tunnel.d.ts +0 -15
  8. package/dist/commands/tunnel.d.ts.map +1 -1
  9. package/dist/commands/tunnel.js +153 -453
  10. package/dist/commands/workflow.d.ts.map +1 -1
  11. package/dist/commands/workflow.js +6 -45
  12. package/dist/core/agents/coord-client.d.ts.map +1 -1
  13. package/dist/core/agents/coord-client.js +0 -18
  14. package/dist/core/agents/live-authority-v3.d.ts +0 -1
  15. package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
  16. package/dist/core/agents/live-authority-v3.js +0 -11
  17. package/dist/core/agents/live-lifecycle-v3.d.ts +2 -4
  18. package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
  19. package/dist/core/agents/live-lifecycle-v3.js +7 -28
  20. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  21. package/dist/core/agents/state/heartbeat-writer.js +14 -25
  22. package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
  23. package/dist/core/agents/state/live-coordination-writer.js +0 -5
  24. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  25. package/dist/core/agents/state/pidmap.js +2 -7
  26. package/dist/core/agents/state/proc-start.d.ts +20 -31
  27. package/dist/core/agents/state/proc-start.d.ts.map +1 -1
  28. package/dist/core/agents/state/proc-start.js +37 -70
  29. package/dist/core/agents/state/service-process.d.ts +61 -0
  30. package/dist/core/agents/state/service-process.d.ts.map +1 -0
  31. package/dist/core/agents/state/service-process.js +143 -0
  32. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  33. package/dist/core/agents/state/stale-sweep.js +70 -180
  34. package/dist/core/artifacts/activity.d.ts +4 -1
  35. package/dist/core/artifacts/activity.d.ts.map +1 -1
  36. package/dist/core/artifacts/activity.js +8 -2
  37. package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
  38. package/dist/core/artifacts/delivery-card.js +1 -2
  39. package/dist/core/artifacts/index.d.ts.map +1 -1
  40. package/dist/core/artifacts/index.js +38 -12
  41. package/dist/core/context/index.d.ts +6 -4
  42. package/dist/core/context/index.d.ts.map +1 -1
  43. package/dist/core/context/index.js +194 -256
  44. package/dist/core/context/observation.d.ts +9 -9
  45. package/dist/core/context/observation.d.ts.map +1 -1
  46. package/dist/core/context/observation.js +37 -6
  47. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
  48. package/dist/core/events/v3/authority-outbox.js +0 -29
  49. package/dist/core/events/v3/producers/coordination-recorder.d.ts +0 -1
  50. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
  51. package/dist/core/events/v3/producers/coordination-recorder.js +0 -7
  52. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  53. package/dist/core/events/v3/producers/recorder.js +64 -77
  54. package/dist/core/events/v3/runtime-build.d.ts +14 -0
  55. package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
  56. package/dist/core/events/v3/runtime-build.js +44 -0
  57. package/dist/core/governor/index.d.ts +2 -1
  58. package/dist/core/governor/index.d.ts.map +1 -1
  59. package/dist/core/governor/index.js +2 -1
  60. package/dist/core/governor/service-read.d.ts +3 -10
  61. package/dist/core/governor/service-read.d.ts.map +1 -1
  62. package/dist/core/governor/service-read.js +10 -94
  63. package/dist/core/governor/service.d.ts +3 -7
  64. package/dist/core/governor/service.d.ts.map +1 -1
  65. package/dist/core/governor/service.js +17 -182
  66. package/dist/core/hooks/cli.js +78 -48
  67. package/dist/core/hooks/resolve/owner.js +2 -2
  68. package/dist/core/resources/sampler.js +2 -2
  69. package/dist/core/resources/service-status.d.ts +2 -14
  70. package/dist/core/resources/service-status.d.ts.map +1 -1
  71. package/dist/core/resources/service-status.js +3 -23
  72. package/dist/core/resources/service.d.ts +3 -7
  73. package/dist/core/resources/service.d.ts.map +1 -1
  74. package/dist/core/resources/service.js +12 -101
  75. package/dist/core/resources/status.d.ts.map +1 -1
  76. package/dist/core/resources/status.js +3 -2
  77. package/dist/core/semantic/service.d.ts.map +1 -1
  78. package/dist/core/semantic/service.js +9 -2
  79. package/dist/core/servers/index.d.ts +11 -10
  80. package/dist/core/servers/index.d.ts.map +1 -1
  81. package/dist/core/servers/index.js +82 -192
  82. package/dist/core/servers/tunnels.d.ts +3 -5
  83. package/dist/core/servers/tunnels.d.ts.map +1 -1
  84. package/dist/core/servers/tunnels.js +2 -45
  85. package/dist/core/storage/durable-history.js +56 -22
  86. package/dist/core/storage/segments.d.ts.map +1 -1
  87. package/dist/core/storage/segments.js +73 -17
  88. package/dist/core/supervisor/service.d.ts +4 -7
  89. package/dist/core/supervisor/service.d.ts.map +1 -1
  90. package/dist/core/supervisor/service.js +23 -122
  91. package/dist/core/supervisor/services.js +2 -2
  92. package/dist/core/supervisor/status.d.ts +3 -17
  93. package/dist/core/supervisor/status.d.ts.map +1 -1
  94. package/dist/core/supervisor/status.js +7 -70
  95. package/dist/core/work/state.js +7 -7
  96. package/dist/core/workflow/engine.d.ts.map +1 -1
  97. package/dist/core/workflow/engine.js +23 -66
  98. package/dist/core/workflow/index.d.ts +3 -4
  99. package/dist/core/workflow/index.d.ts.map +1 -1
  100. package/dist/core/workflow/index.js +2 -2
  101. package/dist/core/workflow/live-session-v3.d.ts +0 -6
  102. package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
  103. package/dist/core/workflow/live-session-v3.js +5 -126
  104. package/dist/core/workflow/proof.d.ts +4 -4
  105. package/dist/core/workflow/proof.d.ts.map +1 -1
  106. package/dist/core/workflow/proof.js +34 -86
  107. package/dist/core/workflow/resume-lock.d.ts +17 -0
  108. package/dist/core/workflow/resume-lock.d.ts.map +1 -0
  109. package/dist/core/workflow/resume-lock.js +158 -0
  110. package/dist/core/workflow/run-state.d.ts +0 -21
  111. package/dist/core/workflow/run-state.d.ts.map +1 -1
  112. package/dist/core/workflow/run-state.js +3 -174
  113. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  114. package/dist/core/workflow/spawn-claude.js +0 -19
  115. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  116. package/dist/core/workflow/spawn-codex.js +0 -13
  117. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  118. package/dist/core/workflow/spawn-cursor.js +0 -19
  119. package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
  120. package/dist/core/workflow/spawn-opencode.js +0 -14
  121. package/dist/core/workflow/types.d.ts +11 -54
  122. package/dist/core/workflow/types.d.ts.map +1 -1
  123. package/dist/core/workflow/workspaces/leases.d.ts +4 -0
  124. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  125. package/dist/core/workflow/workspaces/leases.js +1 -1
  126. package/dist/core/workflow/workspaces/local-git.js +91 -210
  127. package/dist/core/workflow/workspaces/ownership.d.ts +0 -7
  128. package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -1
  129. package/dist/core/workflow/workspaces/ownership.js +3 -130
  130. package/dist/core/workflow/workspaces/reclaim.d.ts +4 -7
  131. package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
  132. package/dist/core/workflow/workspaces/reclaim.js +0 -4
  133. package/dist/core/workflow/workspaces/recovery.d.ts +1 -1
  134. package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
  135. package/dist/core/workflow/workspaces/recovery.js +1 -1
  136. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  137. package/dist/core/workflow/workspaces/state.js +1 -4
  138. package/dist/lib/identities/assume.d.ts.map +1 -1
  139. package/dist/lib/identities/assume.js +0 -4
  140. package/dist/lib/tunnel/state.d.ts +6 -13
  141. package/dist/lib/tunnel/state.d.ts.map +1 -1
  142. package/dist/lib/tunnel/state.js +12 -58
  143. package/package.json +1 -6
  144. package/src/commander.ts +0 -10
  145. package/src/commands/files.ts +5 -13
  146. package/src/commands/servers.ts +23 -12
  147. package/src/commands/tunnel.ts +166 -533
  148. package/src/commands/workflow.ts +8 -58
  149. package/src/core/agents/coord-client.ts +0 -20
  150. package/src/core/agents/live-authority-v3.ts +0 -17
  151. package/src/core/agents/live-lifecycle-v3.ts +14 -52
  152. package/src/core/agents/state/heartbeat-writer.ts +12 -29
  153. package/src/core/agents/state/live-coordination-writer.ts +0 -29
  154. package/src/core/agents/state/pidmap.ts +2 -15
  155. package/src/core/agents/state/proc-start.ts +41 -69
  156. package/src/core/agents/state/service-process.ts +217 -0
  157. package/src/core/agents/state/stale-sweep.ts +82 -247
  158. package/src/core/artifacts/activity.ts +9 -2
  159. package/src/core/artifacts/delivery-card.ts +1 -7
  160. package/src/core/artifacts/index.ts +45 -12
  161. package/src/core/context/index.ts +207 -272
  162. package/src/core/context/observation.ts +40 -9
  163. package/src/core/events/v3/authority-outbox.ts +0 -56
  164. package/src/core/events/v3/producers/coordination-recorder.ts +0 -15
  165. package/src/core/events/v3/producers/recorder.ts +68 -94
  166. package/src/core/events/v3/runtime-build.ts +62 -0
  167. package/src/core/governor/index.ts +1 -2
  168. package/src/core/governor/service-read.ts +15 -114
  169. package/src/core/governor/service.ts +36 -220
  170. package/src/core/hooks/cli.ts +85 -47
  171. package/src/core/hooks/resolve/owner.ts +2 -2
  172. package/src/core/resources/sampler.ts +3 -2
  173. package/src/core/resources/service-status.ts +6 -35
  174. package/src/core/resources/service.ts +23 -133
  175. package/src/core/resources/status.ts +3 -2
  176. package/src/core/semantic/service.ts +8 -2
  177. package/src/core/servers/index.ts +86 -228
  178. package/src/core/servers/tunnels.ts +3 -55
  179. package/src/core/storage/durable-history.ts +52 -20
  180. package/src/core/storage/segments.ts +79 -17
  181. package/src/core/supervisor/service.ts +33 -155
  182. package/src/core/supervisor/services.ts +2 -2
  183. package/src/core/supervisor/status.ts +10 -85
  184. package/src/core/work/state.ts +7 -7
  185. package/src/core/workflow/engine.ts +22 -74
  186. package/src/core/workflow/index.ts +2 -13
  187. package/src/core/workflow/live-session-v3.ts +5 -146
  188. package/src/core/workflow/proof.ts +40 -104
  189. package/src/core/workflow/resume-lock.ts +179 -0
  190. package/src/core/workflow/run-state.ts +3 -207
  191. package/src/core/workflow/spawn-claude.ts +0 -18
  192. package/src/core/workflow/spawn-codex.ts +0 -13
  193. package/src/core/workflow/spawn-cursor.ts +0 -18
  194. package/src/core/workflow/spawn-opencode.ts +0 -14
  195. package/src/core/workflow/types.ts +11 -41
  196. package/src/core/workflow/workspaces/leases.ts +1 -1
  197. package/src/core/workflow/workspaces/local-git.ts +120 -239
  198. package/src/core/workflow/workspaces/ownership.ts +3 -158
  199. package/src/core/workflow/workspaces/reclaim.ts +3 -9
  200. package/src/core/workflow/workspaces/recovery.ts +1 -1
  201. package/src/core/workflow/workspaces/state.ts +1 -5
  202. package/src/lib/identities/assume.ts +0 -4
  203. package/src/lib/tunnel/state.ts +15 -70
  204. package/dist/commands/turn-rollback.d.ts +0 -5
  205. package/dist/commands/turn-rollback.d.ts.map +0 -1
  206. package/dist/commands/turn-rollback.js +0 -76
  207. package/dist/core/agents/state/process-handle.d.ts +0 -26
  208. package/dist/core/agents/state/process-handle.d.ts.map +0 -1
  209. package/dist/core/agents/state/process-handle.js +0 -121
  210. package/dist/core/agents/state/unchanged-file.d.ts +0 -44
  211. package/dist/core/agents/state/unchanged-file.d.ts.map +0 -1
  212. package/dist/core/agents/state/unchanged-file.js +0 -390
  213. package/dist/core/events/v3/generation-custody.d.ts +0 -5
  214. package/dist/core/events/v3/generation-custody.d.ts.map +0 -1
  215. package/dist/core/events/v3/generation-custody.js +0 -50
  216. package/dist/core/turn-rollback/content.d.ts +0 -53
  217. package/dist/core/turn-rollback/content.d.ts.map +0 -1
  218. package/dist/core/turn-rollback/content.js +0 -396
  219. package/dist/core/turn-rollback/index.d.ts +0 -72
  220. package/dist/core/turn-rollback/index.d.ts.map +0 -1
  221. package/dist/core/turn-rollback/index.js +0 -443
  222. package/dist/core/workflow/failure-evidence.d.ts +0 -16
  223. package/dist/core/workflow/failure-evidence.d.ts.map +0 -1
  224. package/dist/core/workflow/failure-evidence.js +0 -114
  225. package/dist/core/workflow/recovery-audit.d.ts +0 -30
  226. package/dist/core/workflow/recovery-audit.d.ts.map +0 -1
  227. package/dist/core/workflow/recovery-audit.js +0 -116
  228. package/src/commands/turn-rollback.ts +0 -111
  229. package/src/core/agents/state/process-handle.ts +0 -165
  230. package/src/core/agents/state/unchanged-file.ts +0 -456
  231. package/src/core/events/v3/generation-custody.ts +0 -59
  232. package/src/core/turn-rollback/content.ts +0 -499
  233. package/src/core/turn-rollback/index.ts +0 -580
  234. package/src/core/workflow/failure-evidence.ts +0 -141
  235. package/src/core/workflow/recovery-audit.ts +0 -181
@@ -1,396 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import { randomUUID } from "node:crypto";
3
- import { closeSync, constants, existsSync, fchmodSync, fstatSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
4
- import { dirname, join, relative, resolve } from "node:path";
5
- import { stateDirMode, stateFileMode } from "../storage/modes.js";
6
- import { fsyncParentDirectory, stableDigest } from "../workflow/durable-record.js";
7
- import { assertSafeRelativeSegment, containsPath, descriptorPath, filesystemIdentity, sameFilesystemIdentity, } from "../workflow/workspaces/paths.js";
8
- export const SNAPSHOT_LIMIT = 8 * 1024 * 1024;
9
- export function git(root, args, options = {}) {
10
- const env = { ...process.env };
11
- for (const key of Object.keys(env))
12
- if (key.startsWith("GIT_"))
13
- delete env[key];
14
- Object.assign(env, {
15
- GIT_OPTIONAL_LOCKS: "0",
16
- GIT_CONFIG_NOSYSTEM: "1",
17
- GIT_CONFIG_GLOBAL: "/dev/null",
18
- GIT_LITERAL_PATHSPECS: "1",
19
- });
20
- if (args[0] === "check-ignore")
21
- delete env.GIT_LITERAL_PATHSPECS;
22
- if (options.index)
23
- env.GIT_INDEX_FILE = options.index;
24
- return execFileSync("git", [options.bare ? `--git-dir=${root}` : "-C", ...(options.bare ? [] : [root]), ...args], {
25
- env,
26
- input: options.input,
27
- maxBuffer: SNAPSHOT_LIMIT + 1024 * 1024,
28
- timeout: 10_000,
29
- stdio: ["pipe", "pipe", "pipe"],
30
- });
31
- }
32
- export function textGit(root, args, options) {
33
- return git(root, args, options).toString("utf8").trim();
34
- }
35
- export function safePaths(binding, paths) {
36
- if (!Array.isArray(paths) || paths.length === 0 || paths.length > 100)
37
- throw new Error("path count limit is 1 to 100");
38
- const normalized = paths
39
- .map((path) => {
40
- if (typeof path !== "string" || path.length > 2000)
41
- throw new Error("invalid content path");
42
- for (const part of path.split("/")) {
43
- assertSafeRelativeSegment(part, "content path");
44
- if ([".git", ".harnery"].includes(part.toLowerCase()))
45
- throw new Error("administrative content path is forbidden");
46
- }
47
- if (relative(binding.workspace_root, resolve(binding.workspace_root, path)).replaceAll("\\", "/") !== path)
48
- throw new Error("content path escapes workspace");
49
- return path;
50
- })
51
- .sort();
52
- if (new Set(normalized).size !== normalized.length)
53
- throw new Error("duplicate content paths");
54
- for (const path of normalized) {
55
- const opened = openParent(binding, path);
56
- opened.close();
57
- }
58
- return normalized;
59
- }
60
- export function openParent(binding, path) {
61
- const fds = [];
62
- try {
63
- const root = binding.workspace_root;
64
- let fd = openSync(root, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
65
- fds.push(fd);
66
- if (!sameFilesystemIdentity(filesystemIdentity(descriptorPath(fd)), binding.workspace_root_identity))
67
- throw new Error("workspace root identity changed");
68
- for (const segment of path.split("/").slice(0, -1)) {
69
- fd = openSync(join(descriptorPath(fd), segment), constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
70
- fds.push(fd);
71
- if (existsSync(join(descriptorPath(fd), ".git")))
72
- throw new Error("nested repository boundary is forbidden");
73
- }
74
- const parent = descriptorPath(fd);
75
- const real = realpathSync(parent);
76
- if (!containsPath(root, real))
77
- throw new Error("content parent escaped workspace");
78
- for (const reserved of [
79
- binding.repository?.gitdir.realpath,
80
- binding.repository?.common_dir.realpath,
81
- ]) {
82
- if (reserved && containsPath(reserved, real))
83
- throw new Error("Git administrative content is forbidden");
84
- }
85
- return {
86
- path: join(parent, path.split("/").at(-1)),
87
- close: () => {
88
- for (const handle of fds.reverse())
89
- closeSync(handle);
90
- },
91
- };
92
- }
93
- catch (error) {
94
- for (const fd of fds.reverse())
95
- closeSync(fd);
96
- throw error;
97
- }
98
- }
99
- function bytesAt(binding, path) {
100
- const parent = openParent(binding, path);
101
- try {
102
- let stat;
103
- try {
104
- stat = lstatSync(parent.path);
105
- }
106
- catch (error) {
107
- if (error.code === "ENOENT")
108
- return null;
109
- throw error;
110
- }
111
- if (!stat.isFile() || stat.nlink !== 1)
112
- throw new Error("content must be an unlinked regular file");
113
- if (stat.size > SNAPSHOT_LIMIT)
114
- throw new Error("snapshot byte limit exceeded");
115
- const fd = openSync(parent.path, constants.O_RDONLY | constants.O_NOFOLLOW);
116
- try {
117
- const current = fstatSync(fd);
118
- if (!current.isFile() ||
119
- current.nlink !== 1 ||
120
- current.ino !== stat.ino ||
121
- current.dev !== stat.dev)
122
- throw new Error("content identity changed");
123
- const bytes = readFileSync(fd);
124
- const after = fstatSync(fd);
125
- if (after.size !== current.size ||
126
- after.mtimeMs !== current.mtimeMs ||
127
- after.ctimeMs !== current.ctimeMs)
128
- throw new Error("content changed during capture");
129
- return { bytes, mode: current.mode & 0o111 ? "100755" : "100644" };
130
- }
131
- finally {
132
- closeSync(fd);
133
- }
134
- }
135
- finally {
136
- parent.close();
137
- }
138
- }
139
- function indexEntries(binding, paths) {
140
- const root = binding.workspace_root;
141
- if (textGit(root, ["rev-parse", "--shared-index-path"]))
142
- throw new Error("split index is unsupported");
143
- try {
144
- if (textGit(root, ["config", "--bool", "index.sparse"]) === "true")
145
- throw new Error("sparse index is unsupported");
146
- }
147
- catch (error) {
148
- if (!(error && typeof error === "object" && "status" in error && error.status === 1))
149
- throw error;
150
- }
151
- const flags = git(root, ["ls-files", "-v", "-z", "--", ...paths])
152
- .toString("utf8")
153
- .split("\0")
154
- .filter(Boolean);
155
- if (flags.some((flag) => !flag.startsWith("H ")))
156
- throw new Error("selected index flags are unsupported");
157
- const entries = new Map();
158
- const debugFlags = git(root, ["ls-files", "--debug", "-z", "--", ...paths]).toString("utf8");
159
- for (const match of debugFlags.matchAll(/flags: ([a-f0-9]+)\n/g)) {
160
- if ((Number.parseInt(match[1], 16) & 0x20000000) !== 0)
161
- throw new Error("intent-to-add index entries are unsupported");
162
- }
163
- for (const line of git(root, ["ls-files", "--stage", "-z", "--", ...paths])
164
- .toString("utf8")
165
- .split("\0")
166
- .filter(Boolean)) {
167
- const match = /^(100644|100755) ([a-f0-9]{40,64}) 0\t(.+)$/.exec(line);
168
- if (!match || /^0+$/.test(match[2]) || !paths.includes(match[3]))
169
- throw new Error("unsupported index entry");
170
- entries.set(match[3], { mode: match[1], source: match[2] });
171
- }
172
- return entries;
173
- }
174
- export function observe(binding, store, paths) {
175
- const staged = indexEntries(binding, paths);
176
- const blobs = new Map();
177
- let total = 0;
178
- const blob = (bytes, mode) => {
179
- total += bytes.length;
180
- if (total > SNAPSHOT_LIMIT)
181
- throw new Error("snapshot byte limit exceeded");
182
- const oid = textGit(store, ["hash-object", "--stdin"], { bare: true, input: bytes });
183
- blobs.set(oid, bytes);
184
- return { oid, mode };
185
- };
186
- const entries = paths.map((path) => {
187
- const index = staged.get(path);
188
- const worktree = bytesAt(binding, path);
189
- // check-ignore without --no-index only reports untracked ignored paths.
190
- if (!index) {
191
- try {
192
- git(binding.workspace_root, ["check-ignore", "-q", "--", path]);
193
- throw new Error("ignored content path is forbidden");
194
- }
195
- catch (error) {
196
- if (!(error && typeof error === "object" && "status" in error && error.status === 1))
197
- throw error;
198
- }
199
- }
200
- return {
201
- path,
202
- index: index
203
- ? blob(git(binding.workspace_root, ["cat-file", "blob", index.source]), index.mode)
204
- : null,
205
- worktree: worktree ? blob(worktree.bytes, worktree.mode) : null,
206
- untracked: !index && worktree !== null,
207
- };
208
- });
209
- return { entries, blobs, total, head: textGit(binding.workspace_root, ["rev-parse", "HEAD"]) };
210
- }
211
- function tree(store, entries, side) {
212
- const temporary = join(dirname(store), `tree-${randomUUID()}.index`);
213
- try {
214
- git(store, ["read-tree", "--empty"], { bare: true, index: temporary });
215
- for (const entry of entries) {
216
- const blob = entry[side];
217
- if (blob)
218
- git(store, ["update-index", "--add", "--cacheinfo", `${blob.mode},${blob.oid},${entry.path}`], { bare: true, index: temporary });
219
- }
220
- return textGit(store, ["write-tree"], { bare: true, index: temporary });
221
- }
222
- finally {
223
- if (existsSync(temporary))
224
- unlinkSync(temporary);
225
- }
226
- }
227
- export function persistSnapshot(store, observed, ref) {
228
- for (const bytes of observed.blobs.values())
229
- git(store, ["hash-object", "-w", "--stdin"], { bare: true, input: bytes });
230
- const basis = {
231
- head: observed.head,
232
- entries: observed.entries,
233
- total_bytes: observed.total,
234
- index_tree: tree(store, observed.entries, "index"),
235
- worktree_tree: tree(store, observed.entries, "worktree"),
236
- };
237
- for (const side of ["index", "worktree"])
238
- git(store, ["update-ref", `refs/turns/${ref}/${side}`, basis[`${side}_tree`]], { bare: true });
239
- return { ...basis, sha256: stableDigest(basis) };
240
- }
241
- export function verifySnapshot(store, snapshot) {
242
- const { sha256, ...basis } = snapshot;
243
- if (sha256 !== stableDigest(basis) ||
244
- !Number.isSafeInteger(snapshot.total_bytes) ||
245
- snapshot.total_bytes < 0 ||
246
- snapshot.total_bytes > SNAPSHOT_LIMIT)
247
- throw new Error("snapshot digest or byte limit changed");
248
- let total = 0;
249
- for (const side of ["index", "worktree"]) {
250
- const actual = git(store, ["ls-tree", "-r", "-z", snapshot[`${side}_tree`]], { bare: true })
251
- .toString("utf8")
252
- .split("\0")
253
- .filter(Boolean)
254
- .sort();
255
- const expected = snapshot.entries
256
- .filter((entry) => entry[side])
257
- .map((entry) => `${entry[side].mode} blob ${entry[side].oid}\t${entry.path}`)
258
- .sort();
259
- if (stableDigest(actual) !== stableDigest(expected))
260
- throw new Error("snapshot tree changed");
261
- for (const entry of snapshot.entries) {
262
- const blob = entry[side];
263
- if (blob) {
264
- const bytes = git(store, ["cat-file", "blob", blob.oid], { bare: true });
265
- if (textGit(store, ["hash-object", "--stdin"], { bare: true, input: bytes }) !== blob.oid)
266
- throw new Error("snapshot blob digest changed");
267
- total += bytes.length;
268
- }
269
- }
270
- }
271
- if (total !== snapshot.total_bytes)
272
- throw new Error("snapshot byte count changed");
273
- }
274
- export function currentMatches(binding, store, snapshot) {
275
- const current = observe(binding, store, snapshot.entries.map((entry) => entry.path));
276
- return (current.head === snapshot.head &&
277
- stableDigest(current.entries) === stableDigest(snapshot.entries));
278
- }
279
- export function lockIndex(binding, state) {
280
- const path = join(binding.repository.gitdir.realpath, "index");
281
- const lock = `${path}.lock`;
282
- const fd = openSync(lock, "wx", stateFileMode());
283
- const identity = fstatSync(fd, { bigint: true });
284
- const privatePath = join(state, `live-${randomUUID()}.index`);
285
- let published = false;
286
- const exact = () => {
287
- const current = lstatSync(lock, { bigint: true });
288
- if (!current.isFile() || current.dev !== identity.dev || current.ino !== identity.ino)
289
- throw new Error("Git index lock changed");
290
- };
291
- try {
292
- writeFileSync(privatePath, readFileSync(path), { flag: "wx", mode: stateFileMode() });
293
- }
294
- catch (error) {
295
- closeSync(fd);
296
- exact();
297
- unlinkSync(lock);
298
- throw error;
299
- }
300
- return {
301
- path,
302
- privatePath,
303
- publish() {
304
- exact();
305
- writeFileSync(fd, readFileSync(privatePath));
306
- fsyncSync(fd);
307
- exact();
308
- renameSync(lock, path);
309
- published = true;
310
- const dir = openSync(dirname(path), "r");
311
- try {
312
- fsyncSync(dir);
313
- }
314
- finally {
315
- closeSync(dir);
316
- }
317
- },
318
- close() {
319
- closeSync(fd);
320
- if (!published) {
321
- exact();
322
- unlinkSync(lock);
323
- }
324
- if (existsSync(privatePath))
325
- unlinkSync(privatePath);
326
- },
327
- };
328
- }
329
- export function stageBefore(binding, store, before, index) {
330
- for (const entry of before.entries) {
331
- if (entry.index) {
332
- const bytes = git(store, ["cat-file", "blob", entry.index.oid], { bare: true });
333
- const sourceOid = textGit(binding.workspace_root, ["hash-object", "-w", "--stdin"], {
334
- input: bytes,
335
- });
336
- git(binding.workspace_root, ["update-index", "--add", "--cacheinfo", `${entry.index.mode},${sourceOid},${entry.path}`], { index: index.privatePath });
337
- }
338
- else
339
- git(binding.workspace_root, ["update-index", "--force-remove", "--", entry.path], {
340
- index: index.privatePath,
341
- });
342
- }
343
- }
344
- export function restoreFile(binding, store, entry) {
345
- const parent = openParent(binding, entry.path);
346
- const temporary = `${parent.path}.restore-${randomUUID()}`;
347
- try {
348
- if (!entry.worktree) {
349
- if (existsSync(parent.path))
350
- unlinkSync(parent.path);
351
- }
352
- else {
353
- const fd = openSync(temporary, "wx", entry.worktree.mode === "100755" ? 0o755 : 0o644);
354
- try {
355
- fchmodSync(fd, entry.worktree.mode === "100755" ? 0o755 : 0o644);
356
- writeFileSync(fd, git(store, ["cat-file", "blob", entry.worktree.oid], { bare: true }));
357
- fsyncSync(fd);
358
- }
359
- finally {
360
- closeSync(fd);
361
- }
362
- renameSync(temporary, parent.path);
363
- }
364
- const fd = openSync(dirname(parent.path), "r");
365
- try {
366
- fsyncSync(fd);
367
- }
368
- finally {
369
- closeSync(fd);
370
- }
371
- }
372
- finally {
373
- if (existsSync(temporary))
374
- unlinkSync(temporary);
375
- parent.close();
376
- }
377
- }
378
- export function ensureDirectory(path) {
379
- if (!existsSync(path)) {
380
- ensureDirectory(dirname(path));
381
- mkdirSync(path, { mode: stateDirMode() });
382
- const fd = openSync(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
383
- try {
384
- fchmodSync(fd, stateDirMode());
385
- fsyncSync(fd);
386
- }
387
- finally {
388
- closeSync(fd);
389
- }
390
- fsyncParentDirectory(path);
391
- }
392
- if (!lstatSync(path).isDirectory() ||
393
- lstatSync(path).isSymbolicLink() ||
394
- realpathSync(path) !== resolve(path))
395
- throw new Error("rollback state path is unsafe");
396
- }
@@ -1,72 +0,0 @@
1
- import type { WorkspaceBinding } from "../workflow/workspaces/types.js";
2
- import { type ContentEntry, type ContentSnapshot } from "./content.js";
3
- export type { ContentEntry, ContentSnapshot } from "./content.js";
4
- export type TurnIdentity = {
5
- turn_id: string;
6
- generation: number;
7
- };
8
- export interface TurnCapture {
9
- schema_version: 1;
10
- capture_id: string;
11
- binding_sha256: string;
12
- binding_generation: number;
13
- turn: TurnIdentity;
14
- paths: string[];
15
- before: ContentSnapshot;
16
- sha256: string;
17
- end?: ContentSnapshot;
18
- }
19
- export interface TurnRollbackPlan {
20
- schema_version: 1;
21
- capture_id: string;
22
- binding_sha256: string;
23
- turn: TurnIdentity;
24
- before_sha256: string;
25
- end_sha256: string;
26
- paths: Array<{
27
- path: string;
28
- before: ContentEntry;
29
- end: ContentEntry;
30
- }>;
31
- plan_sha256: string;
32
- }
33
- export interface TurnRollbackResult {
34
- schema_version: 1;
35
- status: "preview" | "applied" | "already_applied" | "unknown";
36
- capture_id: string;
37
- plan_sha256: string;
38
- reason?: string;
39
- }
40
- export interface TurnRollbackStatus {
41
- status: "missing" | "open" | "sealed" | "applied" | "unknown";
42
- capture_id: string;
43
- }
44
- type CaptureInput = {
45
- binding: WorkspaceBinding;
46
- capture_id: string;
47
- };
48
- /** Capture the exact named files before a turn, without staging anything. */
49
- export declare function beginTurnRollback(coordRoot: string, input: {
50
- binding: WorkspaceBinding;
51
- turn: TurnIdentity;
52
- paths: readonly string[];
53
- onSnapshotStep?: (step: "content_persisted", captureId: string) => void;
54
- }): Promise<TurnCapture>;
55
- /** Seal the end boundary of the latest captured turn. */
56
- export declare function sealTurnRollback(coordRoot: string, input: CaptureInput & {
57
- onSnapshotStep?: (step: "content_persisted", captureId: string) => void;
58
- }): Promise<TurnCapture>;
59
- /** Produce a dry-run exact-path proposal bound to both captured boundaries. */
60
- export declare function prepareTurnRollback(coordRoot: string, input: CaptureInput): Promise<TurnRollbackPlan>;
61
- export declare function applyTurnRollback(coordRoot: string, input: {
62
- binding: WorkspaceBinding;
63
- plan: TurnRollbackPlan;
64
- yes?: boolean;
65
- actor?: string;
66
- reason?: string;
67
- /** Observation/fault boundary; does not replace any authority or content guard. */
68
- onStep?: (step: "started" | "worktree_published" | "index_published") => void;
69
- }): Promise<TurnRollbackResult>;
70
- /** Read recovery evidence without attempting to continue an interrupted restore. */
71
- export declare function inspectTurnRollback(coordRoot: string, input: CaptureInput): TurnRollbackStatus;
72
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/turn-rollback/index.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,eAAe,EAWrB,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAClE,MAAM,MAAM,YAAY,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AACnE,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE,CAAC,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,eAAe,CAAC;CACvB;AACD,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,CAAC,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,YAAY,CAAC;QAAC,GAAG,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;IACxE,WAAW,EAAE,MAAM,CAAC;CACrB;AACD,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,CAAC,CAAC;IAClB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,CAAC;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AACD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IAC9D,UAAU,EAAE,MAAM,CAAC;CACpB;AACD,KAAK,YAAY,GAAG;IAAE,OAAO,EAAE,gBAAgB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAyKtE,6EAA6E;AAC7E,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;IACL,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACzE,GACA,OAAO,CAAC,WAAW,CAAC,CA+DtB;AAED,yDAAyD;AACzD,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,YAAY,GAAG;IAAE,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GAChG,OAAO,CAAC,WAAW,CAAC,CA0CtB;AAoBD,+EAA+E;AAC/E,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,gBAAgB,CAAC,CAe3B;AAED,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;IACL,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,EAAE,gBAAgB,CAAC;IACvB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,oBAAoB,GAAG,iBAAiB,KAAK,IAAI,CAAC;CAC/E,GACA,OAAO,CAAC,kBAAkB,CAAC,CA8G7B;AAED,oFAAoF;AACpF,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,kBAAkB,CA0D9F"}