@rewind-ai/cli 0.6.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 (244) hide show
  1. package/.env.example +8 -0
  2. package/LICENSE +21 -0
  3. package/README.md +39 -0
  4. package/dist/src/action/capture.d.ts +15 -0
  5. package/dist/src/action/capture.js +215 -0
  6. package/dist/src/action/capture.js.map +1 -0
  7. package/dist/src/action/mediate.d.ts +28 -0
  8. package/dist/src/action/mediate.js +109 -0
  9. package/dist/src/action/mediate.js.map +1 -0
  10. package/dist/src/action/mutate.d.ts +11 -0
  11. package/dist/src/action/mutate.js +189 -0
  12. package/dist/src/action/mutate.js.map +1 -0
  13. package/dist/src/action/objects.d.ts +5 -0
  14. package/dist/src/action/objects.js +50 -0
  15. package/dist/src/action/objects.js.map +1 -0
  16. package/dist/src/action/store.d.ts +24 -0
  17. package/dist/src/action/store.js +149 -0
  18. package/dist/src/action/store.js.map +1 -0
  19. package/dist/src/action/types.d.ts +84 -0
  20. package/dist/src/action/types.js +3 -0
  21. package/dist/src/action/types.js.map +1 -0
  22. package/dist/src/action/undo.d.ts +11 -0
  23. package/dist/src/action/undo.js +42 -0
  24. package/dist/src/action/undo.js.map +1 -0
  25. package/dist/src/adapters/filesystem.d.ts +35 -0
  26. package/dist/src/adapters/filesystem.js +499 -0
  27. package/dist/src/adapters/filesystem.js.map +1 -0
  28. package/dist/src/adapters/index.d.ts +7 -0
  29. package/dist/src/adapters/index.js +19 -0
  30. package/dist/src/adapters/index.js.map +1 -0
  31. package/dist/src/adapters/types.d.ts +54 -0
  32. package/dist/src/adapters/types.js +7 -0
  33. package/dist/src/adapters/types.js.map +1 -0
  34. package/dist/src/api/server.d.ts +25 -0
  35. package/dist/src/api/server.js +786 -0
  36. package/dist/src/api/server.js.map +1 -0
  37. package/dist/src/approval/store.d.ts +35 -0
  38. package/dist/src/approval/store.js +113 -0
  39. package/dist/src/approval/store.js.map +1 -0
  40. package/dist/src/checkpoint.d.ts +9 -0
  41. package/dist/src/checkpoint.js +140 -0
  42. package/dist/src/checkpoint.js.map +1 -0
  43. package/dist/src/cli/v5.d.ts +15 -0
  44. package/dist/src/cli/v5.js +110 -0
  45. package/dist/src/cli/v5.js.map +1 -0
  46. package/dist/src/cli.d.ts +2 -0
  47. package/dist/src/cli.js +823 -0
  48. package/dist/src/cli.js.map +1 -0
  49. package/dist/src/clients/types.d.ts +90 -0
  50. package/dist/src/clients/types.js +239 -0
  51. package/dist/src/clients/types.js.map +1 -0
  52. package/dist/src/commercial/auth/provider.d.ts +61 -0
  53. package/dist/src/commercial/auth/provider.js +227 -0
  54. package/dist/src/commercial/auth/provider.js.map +1 -0
  55. package/dist/src/commercial/entitlement/provider.d.ts +27 -0
  56. package/dist/src/commercial/entitlement/provider.js +192 -0
  57. package/dist/src/commercial/entitlement/provider.js.map +1 -0
  58. package/dist/src/commercial/entitlement/signing.d.ts +61 -0
  59. package/dist/src/commercial/entitlement/signing.js +124 -0
  60. package/dist/src/commercial/entitlement/signing.js.map +1 -0
  61. package/dist/src/commercial/env.d.ts +10 -0
  62. package/dist/src/commercial/env.js +40 -0
  63. package/dist/src/commercial/env.js.map +1 -0
  64. package/dist/src/commercial/product.d.ts +26 -0
  65. package/dist/src/commercial/product.js +39 -0
  66. package/dist/src/commercial/product.js.map +1 -0
  67. package/dist/src/commercial/store.d.ts +46 -0
  68. package/dist/src/commercial/store.js +104 -0
  69. package/dist/src/commercial/store.js.map +1 -0
  70. package/dist/src/commercial/stripe/checkout.d.ts +104 -0
  71. package/dist/src/commercial/stripe/checkout.js +469 -0
  72. package/dist/src/commercial/stripe/checkout.js.map +1 -0
  73. package/dist/src/core/compatibility.d.ts +15 -0
  74. package/dist/src/core/compatibility.js +226 -0
  75. package/dist/src/core/compatibility.js.map +1 -0
  76. package/dist/src/core/engine.d.ts +33 -0
  77. package/dist/src/core/engine.js +432 -0
  78. package/dist/src/core/engine.js.map +1 -0
  79. package/dist/src/core/lifecycle.d.ts +17 -0
  80. package/dist/src/core/lifecycle.js +56 -0
  81. package/dist/src/core/lifecycle.js.map +1 -0
  82. package/dist/src/core/types.d.ts +140 -0
  83. package/dist/src/core/types.js +37 -0
  84. package/dist/src/core/types.js.map +1 -0
  85. package/dist/src/destroy.d.ts +21 -0
  86. package/dist/src/destroy.js +219 -0
  87. package/dist/src/destroy.js.map +1 -0
  88. package/dist/src/diff.d.ts +13 -0
  89. package/dist/src/diff.js +251 -0
  90. package/dist/src/diff.js.map +1 -0
  91. package/dist/src/enforcement/detect.d.ts +13 -0
  92. package/dist/src/enforcement/detect.js +75 -0
  93. package/dist/src/enforcement/detect.js.map +1 -0
  94. package/dist/src/enforcement/hook-policy.d.ts +38 -0
  95. package/dist/src/enforcement/hook-policy.js +111 -0
  96. package/dist/src/enforcement/hook-policy.js.map +1 -0
  97. package/dist/src/enforcement/index.d.ts +8 -0
  98. package/dist/src/enforcement/index.js +8 -0
  99. package/dist/src/enforcement/index.js.map +1 -0
  100. package/dist/src/enforcement/perf.d.ts +6 -0
  101. package/dist/src/enforcement/perf.js +18 -0
  102. package/dist/src/enforcement/perf.js.map +1 -0
  103. package/dist/src/enforcement/policy.d.ts +12 -0
  104. package/dist/src/enforcement/policy.js +84 -0
  105. package/dist/src/enforcement/policy.js.map +1 -0
  106. package/dist/src/enforcement/readonly.d.ts +17 -0
  107. package/dist/src/enforcement/readonly.js +124 -0
  108. package/dist/src/enforcement/readonly.js.map +1 -0
  109. package/dist/src/enforcement/sandbox-clone.d.ts +6 -0
  110. package/dist/src/enforcement/sandbox-clone.js +11 -0
  111. package/dist/src/enforcement/sandbox-clone.js.map +1 -0
  112. package/dist/src/enforcement/setup.d.ts +33 -0
  113. package/dist/src/enforcement/setup.js +168 -0
  114. package/dist/src/enforcement/setup.js.map +1 -0
  115. package/dist/src/enforcement/trust.d.ts +10 -0
  116. package/dist/src/enforcement/trust.js +71 -0
  117. package/dist/src/enforcement/trust.js.map +1 -0
  118. package/dist/src/enforcement/types.d.ts +40 -0
  119. package/dist/src/enforcement/types.js +3 -0
  120. package/dist/src/enforcement/types.js.map +1 -0
  121. package/dist/src/entitlement/provider.d.ts +48 -0
  122. package/dist/src/entitlement/provider.js +95 -0
  123. package/dist/src/entitlement/provider.js.map +1 -0
  124. package/dist/src/hash.d.ts +9 -0
  125. package/dist/src/hash.js +23 -0
  126. package/dist/src/hash.js.map +1 -0
  127. package/dist/src/ledger/query.d.ts +19 -0
  128. package/dist/src/ledger/query.js +158 -0
  129. package/dist/src/ledger/query.js.map +1 -0
  130. package/dist/src/manifest.d.ts +32 -0
  131. package/dist/src/manifest.js +242 -0
  132. package/dist/src/manifest.js.map +1 -0
  133. package/dist/src/mcp/server.d.ts +7 -0
  134. package/dist/src/mcp/server.js +364 -0
  135. package/dist/src/mcp/server.js.map +1 -0
  136. package/dist/src/paths.d.ts +94 -0
  137. package/dist/src/paths.js +354 -0
  138. package/dist/src/paths.js.map +1 -0
  139. package/dist/src/permission/engine.d.ts +15 -0
  140. package/dist/src/permission/engine.js +147 -0
  141. package/dist/src/permission/engine.js.map +1 -0
  142. package/dist/src/permission/types.d.ts +42 -0
  143. package/dist/src/permission/types.js +3 -0
  144. package/dist/src/permission/types.js.map +1 -0
  145. package/dist/src/product/setup.d.ts +35 -0
  146. package/dist/src/product/setup.js +300 -0
  147. package/dist/src/product/setup.js.map +1 -0
  148. package/dist/src/protect/audit.d.ts +5 -0
  149. package/dist/src/protect/audit.js +26 -0
  150. package/dist/src/protect/audit.js.map +1 -0
  151. package/dist/src/protect/capture.d.ts +7 -0
  152. package/dist/src/protect/capture.js +250 -0
  153. package/dist/src/protect/capture.js.map +1 -0
  154. package/dist/src/protect/fingerprint.d.ts +4 -0
  155. package/dist/src/protect/fingerprint.js +49 -0
  156. package/dist/src/protect/fingerprint.js.map +1 -0
  157. package/dist/src/protect/hook-cli.d.ts +2 -0
  158. package/dist/src/protect/hook-cli.js +107 -0
  159. package/dist/src/protect/hook-cli.js.map +1 -0
  160. package/dist/src/protect/index.d.ts +12 -0
  161. package/dist/src/protect/index.js +12 -0
  162. package/dist/src/protect/index.js.map +1 -0
  163. package/dist/src/protect/install.d.ts +34 -0
  164. package/dist/src/protect/install.js +293 -0
  165. package/dist/src/protect/install.js.map +1 -0
  166. package/dist/src/protect/intercept.d.ts +11 -0
  167. package/dist/src/protect/intercept.js +390 -0
  168. package/dist/src/protect/intercept.js.map +1 -0
  169. package/dist/src/protect/protect.d.ts +17 -0
  170. package/dist/src/protect/protect.js +194 -0
  171. package/dist/src/protect/protect.js.map +1 -0
  172. package/dist/src/protect/root-paths.d.ts +13 -0
  173. package/dist/src/protect/root-paths.js +140 -0
  174. package/dist/src/protect/root-paths.js.map +1 -0
  175. package/dist/src/protect/shell-policy.d.ts +17 -0
  176. package/dist/src/protect/shell-policy.js +269 -0
  177. package/dist/src/protect/shell-policy.js.map +1 -0
  178. package/dist/src/protect/status.d.ts +11 -0
  179. package/dist/src/protect/status.js +165 -0
  180. package/dist/src/protect/status.js.map +1 -0
  181. package/dist/src/protect/store.d.ts +20 -0
  182. package/dist/src/protect/store.js +170 -0
  183. package/dist/src/protect/store.js.map +1 -0
  184. package/dist/src/protect/types.d.ts +116 -0
  185. package/dist/src/protect/types.js +3 -0
  186. package/dist/src/protect/types.js.map +1 -0
  187. package/dist/src/protect/undo-session.d.ts +14 -0
  188. package/dist/src/protect/undo-session.js +182 -0
  189. package/dist/src/protect/undo-session.js.map +1 -0
  190. package/dist/src/restore.d.ts +39 -0
  191. package/dist/src/restore.js +141 -0
  192. package/dist/src/restore.js.map +1 -0
  193. package/dist/src/sandbox.d.ts +23 -0
  194. package/dist/src/sandbox.js +198 -0
  195. package/dist/src/sandbox.js.map +1 -0
  196. package/dist/src/session/ambient.d.ts +10 -0
  197. package/dist/src/session/ambient.js +53 -0
  198. package/dist/src/session/ambient.js.map +1 -0
  199. package/dist/src/session/attribution.d.ts +27 -0
  200. package/dist/src/session/attribution.js +201 -0
  201. package/dist/src/session/attribution.js.map +1 -0
  202. package/dist/src/session/index.d.ts +25 -0
  203. package/dist/src/session/index.js +472 -0
  204. package/dist/src/session/index.js.map +1 -0
  205. package/dist/src/session/process.d.ts +34 -0
  206. package/dist/src/session/process.js +234 -0
  207. package/dist/src/session/process.js.map +1 -0
  208. package/dist/src/session/store.d.ts +14 -0
  209. package/dist/src/session/store.js +125 -0
  210. package/dist/src/session/store.js.map +1 -0
  211. package/dist/src/session/types.d.ts +111 -0
  212. package/dist/src/session/types.js +3 -0
  213. package/dist/src/session/types.js.map +1 -0
  214. package/dist/src/session/watcher.d.ts +32 -0
  215. package/dist/src/session/watcher.js +186 -0
  216. package/dist/src/session/watcher.js.map +1 -0
  217. package/dist/src/timing.d.ts +7 -0
  218. package/dist/src/timing.js +19 -0
  219. package/dist/src/timing.js.map +1 -0
  220. package/dist/src/types.d.ts +101 -0
  221. package/dist/src/types.js +3 -0
  222. package/dist/src/types.js.map +1 -0
  223. package/dist/src/verify.d.ts +8 -0
  224. package/dist/src/verify.js +126 -0
  225. package/dist/src/verify.js.map +1 -0
  226. package/docs/ADAPTERS.md +37 -0
  227. package/docs/APPROVALS.md +32 -0
  228. package/docs/ARCHITECTURE.md +57 -0
  229. package/docs/ATTRIBUTION.md +179 -0
  230. package/docs/COMMERCIAL.md +51 -0
  231. package/docs/CURSOR-ENFORCEMENT.md +73 -0
  232. package/docs/ENFORCEMENT.md +134 -0
  233. package/docs/FUTURE.md +91 -0
  234. package/docs/HUMAN-SETUP.md +98 -0
  235. package/docs/IDENTITY-AUTHORITY.md +35 -0
  236. package/docs/MCP.md +107 -0
  237. package/docs/PACKAGING.md +77 -0
  238. package/docs/PERMISSIONS.md +53 -0
  239. package/docs/PRODUCT-V5.md +63 -0
  240. package/docs/PRODUCT-V6.md +69 -0
  241. package/docs/PROTECTED-SESSIONS.md +155 -0
  242. package/docs/THREAT-MODEL.md +84 -0
  243. package/docs/TRANSACTIONS.md +57 -0
  244. package/package.json +85 -0
package/.env.example ADDED
@@ -0,0 +1,8 @@
1
+ # REWIND local Stripe TEST MODE (copy to .env — never commit .env)
2
+ # Use Stripe Dashboard TEST mode keys only (sk_test_… / pk_test_…).
3
+ # Webhook secret: from `stripe listen` output (whsec_…) for local proof.
4
+
5
+ REWIND_STRIPE_SECRET_KEY=
6
+ REWIND_STRIPE_PRICE_ID=
7
+ REWIND_STRIPE_WEBHOOK_SECRET=
8
+ REWIND_STRIPE_PUBLISHABLE_KEY=
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 REWIND
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # REWIND
2
+
3
+ **Undo for AI agents.** — REWIND Personal (£2.99/month)
4
+
5
+ Local control and transaction layer for AI agent actions.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g @rewind-ai/cli
11
+ cd your-project
12
+ rewind setup
13
+ rewind doctor
14
+ rewind open
15
+ ```
16
+
17
+ The npm package is `@rewind-ai/cli`. The CLI command remains `rewind`.
18
+
19
+ ## Develop from source
20
+
21
+ ```bash
22
+ npm install && npm run build
23
+ npm pack && npm install -g ./rewind-ai-cli-*.tgz
24
+ # or: npx tsx src/cli.ts setup
25
+ ```
26
+
27
+ Mediated agent actions are recorded as reversible transactions.
28
+ Native editor writes outside REWIND are **not** claimed as protected.
29
+
30
+ ## Docs
31
+
32
+ - [docs/PRODUCT-V6.md](docs/PRODUCT-V6.md) — product surface
33
+ - [docs/HUMAN-SETUP.md](docs/HUMAN-SETUP.md) — Stripe / Auth / keys checklist
34
+ - [docs/COMMERCIAL.md](docs/COMMERCIAL.md) — privacy + offline + undo invariant
35
+ - [docs/PACKAGING.md](docs/PACKAGING.md) — pack / publish / paths
36
+ - [docs/THREAT-MODEL.md](docs/THREAT-MODEL.md)
37
+ - [docs/PRODUCT-V5.md](docs/PRODUCT-V5.md) — core architecture
38
+
39
+ Marketing site under `website/` is separate.
@@ -0,0 +1,15 @@
1
+ import { type SandboxName } from "../paths.js";
2
+ import type { EntrySnapshot } from "./types.js";
3
+ /**
4
+ * Resolve a user/MCP-supplied path to a sandbox-relative POSIX path.
5
+ * Rejects escapes, absolute outsides, .rewind, empty, etc.
6
+ */
7
+ export declare function resolveSandboxRelative(input: string, projectRoot: string, sandboxName?: SandboxName): string;
8
+ export declare function sandboxAbs(rel: string, projectRoot: string, sandboxName?: SandboxName): string;
9
+ /** Capture snapshot of a single path; optionally store file contents. */
10
+ export declare function captureEntry(rel: string, projectRoot: string, options?: {
11
+ storeContent?: boolean;
12
+ captureSubtree?: boolean;
13
+ sandboxName?: SandboxName;
14
+ }): Promise<EntrySnapshot>;
15
+ export declare function snapshotIdentityHash(snap: EntrySnapshot): string | null;
@@ -0,0 +1,215 @@
1
+ import path from "node:path";
2
+ import { lstat, readFile, readlink, readdir } from "node:fs/promises";
3
+ import { assertNoSymlinkEscape, assertSafeRelativePath, getSandboxPath, isPathInside, PathSafetyError, SANDBOX_DIR_NAME, } from "../paths.js";
4
+ import { sha256 } from "../hash.js";
5
+ import { storeObject } from "./objects.js";
6
+ import { toRelativePosix } from "../manifest.js";
7
+ function modeOf(m) {
8
+ return m & 0o7777;
9
+ }
10
+ function sandboxOf(projectRoot, sandboxName = SANDBOX_DIR_NAME) {
11
+ return getSandboxPath(projectRoot, sandboxName);
12
+ }
13
+ /**
14
+ * Resolve a user/MCP-supplied path to a sandbox-relative POSIX path.
15
+ * Rejects escapes, absolute outsides, .rewind, empty, etc.
16
+ */
17
+ export function resolveSandboxRelative(input, projectRoot, sandboxName = SANDBOX_DIR_NAME) {
18
+ if (!input || input.includes("\0")) {
19
+ throw new PathSafetyError("Invalid path");
20
+ }
21
+ // Strip leading ./
22
+ let cleaned = input.replace(/\\/g, "/");
23
+ if (cleaned.startsWith("/")) {
24
+ // Absolute: must resolve inside sandbox
25
+ const sandbox = sandboxOf(projectRoot, sandboxName);
26
+ const abs = path.resolve(cleaned);
27
+ if (!isPathInside(sandbox, abs) || abs === path.resolve(sandbox)) {
28
+ // allow only paths inside sandbox, not the root itself for file ops? root itself ok for list
29
+ if (abs !== path.resolve(sandbox)) {
30
+ throw new PathSafetyError(`Absolute path outside sandbox: ${input}`);
31
+ }
32
+ return "";
33
+ }
34
+ cleaned = toRelativePosix(sandbox, abs);
35
+ }
36
+ // Block .rewind and parent escapes via relative form
37
+ const safe = cleaned === "" || cleaned === "."
38
+ ? ""
39
+ : assertSafeRelativePath(cleaned);
40
+ if (safe === ".rewind" ||
41
+ safe.startsWith(".rewind/") ||
42
+ safe.split("/").includes(".rewind")) {
43
+ throw new PathSafetyError("Refusing to access .rewind internals via MCP");
44
+ }
45
+ if (safe === ".." || safe.startsWith("../")) {
46
+ throw new PathSafetyError(`Path traversal rejected: ${input}`);
47
+ }
48
+ const sandbox = sandboxOf(projectRoot, sandboxName);
49
+ const abs = safe === "" ? sandbox : path.resolve(sandbox, safe);
50
+ if (!isPathInside(sandbox, abs)) {
51
+ throw new PathSafetyError(`Path escapes sandbox: ${input}`);
52
+ }
53
+ return safe;
54
+ }
55
+ export function sandboxAbs(rel, projectRoot, sandboxName = SANDBOX_DIR_NAME) {
56
+ const sandbox = sandboxOf(projectRoot, sandboxName);
57
+ if (rel === "" || rel === ".")
58
+ return sandbox;
59
+ const safe = assertSafeRelativePath(rel);
60
+ const abs = path.resolve(sandbox, safe);
61
+ if (!isPathInside(sandbox, abs)) {
62
+ throw new PathSafetyError(`Path escapes sandbox: ${rel}`);
63
+ }
64
+ return abs;
65
+ }
66
+ /** Capture snapshot of a single path; optionally store file contents. */
67
+ export async function captureEntry(rel, projectRoot, options = {}) {
68
+ const sandboxName = options.sandboxName ?? SANDBOX_DIR_NAME;
69
+ const abs = sandboxAbs(rel, projectRoot, sandboxName);
70
+ const sandbox = sandboxOf(projectRoot, sandboxName);
71
+ await assertNoSymlinkEscape(abs, sandbox).catch(() => undefined);
72
+ let st;
73
+ try {
74
+ st = await lstat(abs);
75
+ }
76
+ catch {
77
+ return {
78
+ exists: false,
79
+ kind: "absent",
80
+ hash: null,
81
+ size: 0,
82
+ mode: null,
83
+ linkTarget: null,
84
+ objectId: null,
85
+ };
86
+ }
87
+ if (st.isSymbolicLink()) {
88
+ const linkTarget = await readlink(abs);
89
+ const hash = sha256(linkTarget);
90
+ return {
91
+ exists: true,
92
+ kind: "symlink",
93
+ hash,
94
+ size: Buffer.byteLength(linkTarget),
95
+ mode: modeOf(st.mode),
96
+ linkTarget,
97
+ objectId: null,
98
+ };
99
+ }
100
+ if (st.isDirectory()) {
101
+ const kids = await readdir(abs);
102
+ kids.sort((a, b) => a.localeCompare(b));
103
+ const listingHash = sha256(kids.join("\n"));
104
+ const snap = {
105
+ exists: true,
106
+ kind: "directory",
107
+ hash: listingHash,
108
+ size: kids.length,
109
+ mode: modeOf(st.mode),
110
+ linkTarget: null,
111
+ objectId: null,
112
+ };
113
+ if (options.captureSubtree) {
114
+ snap.subtree = await captureSubtree(rel, projectRoot, sandboxName);
115
+ snap.hash = snapshotIdentityHash({ ...snap, subtree: snap.subtree });
116
+ }
117
+ return snap;
118
+ }
119
+ if (st.isFile()) {
120
+ const data = await readFile(abs);
121
+ const hash = sha256(data);
122
+ let objectId = null;
123
+ if (options.storeContent !== false) {
124
+ objectId = await storeObject(data, projectRoot);
125
+ }
126
+ return {
127
+ exists: true,
128
+ kind: "file",
129
+ hash,
130
+ size: data.length,
131
+ mode: modeOf(st.mode),
132
+ linkTarget: null,
133
+ objectId,
134
+ };
135
+ }
136
+ throw new PathSafetyError(`Unsupported entry type at ${rel}`);
137
+ }
138
+ async function captureSubtree(rootRel, projectRoot, sandboxName = SANDBOX_DIR_NAME) {
139
+ const sandbox = sandboxOf(projectRoot, sandboxName);
140
+ const rootAbs = sandboxAbs(rootRel, projectRoot, sandboxName);
141
+ const out = [];
142
+ async function walk(rel) {
143
+ const abs = sandboxAbs(rel, projectRoot, sandboxName);
144
+ await assertNoSymlinkEscape(abs, sandbox);
145
+ const st = await lstat(abs);
146
+ if (st.isSymbolicLink()) {
147
+ const linkTarget = await readlink(abs);
148
+ out.push({
149
+ path: rel,
150
+ kind: "symlink",
151
+ hash: sha256(linkTarget),
152
+ size: Buffer.byteLength(linkTarget),
153
+ mode: modeOf(st.mode),
154
+ linkTarget,
155
+ objectId: null,
156
+ });
157
+ return;
158
+ }
159
+ if (st.isDirectory()) {
160
+ out.push({
161
+ path: rel,
162
+ kind: "directory",
163
+ hash: null,
164
+ size: 0,
165
+ mode: modeOf(st.mode),
166
+ linkTarget: null,
167
+ objectId: null,
168
+ });
169
+ const kids = await readdir(abs);
170
+ kids.sort((a, b) => a.localeCompare(b));
171
+ for (const name of kids) {
172
+ const childRel = rel === "" || rel === "." ? name : `${rel}/${name}`;
173
+ await walk(childRel);
174
+ }
175
+ return;
176
+ }
177
+ if (st.isFile()) {
178
+ const data = await readFile(abs);
179
+ const objectId = await storeObject(data, projectRoot);
180
+ out.push({
181
+ path: rel,
182
+ kind: "file",
183
+ hash: sha256(data),
184
+ size: data.length,
185
+ mode: modeOf(st.mode),
186
+ linkTarget: null,
187
+ objectId,
188
+ });
189
+ }
190
+ }
191
+ // Include root itself
192
+ await walk(rootRel === "" ? toRelativePosix(sandbox, rootAbs) : rootRel);
193
+ // Sort deepest last for restore-friendly order (dirs first by path)
194
+ out.sort((a, b) => a.path.localeCompare(b.path));
195
+ return out;
196
+ }
197
+ export function snapshotIdentityHash(snap) {
198
+ if (!snap.exists)
199
+ return "ABSENT";
200
+ if (snap.kind === "directory") {
201
+ if (snap.subtree) {
202
+ return sha256(JSON.stringify(snap.subtree.map((e) => ({
203
+ path: e.path,
204
+ kind: e.kind,
205
+ hash: e.hash,
206
+ mode: e.mode,
207
+ linkTarget: e.linkTarget,
208
+ }))));
209
+ }
210
+ // Immediate children listing hash is stored in snap.hash by captureEntry
211
+ return `DIR:${snap.mode ?? 0}:${snap.hash ?? ""}:${snap.size}`;
212
+ }
213
+ return snap.hash;
214
+ }
215
+ //# sourceMappingURL=capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.js","sourceRoot":"","sources":["../../../src/action/capture.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Filesystem mediation — thin wrappers over the V5 transaction engine.
3
+ * Preserves the V2 ActionRecord return shape for existing callers/tests.
4
+ */
5
+ import { type SandboxName } from "../paths.js";
6
+ import type { ActionRecord, MediateContext } from "./types.js";
7
+ export interface MediateWriteInput {
8
+ path: string;
9
+ content: string;
10
+ createOnly?: boolean;
11
+ }
12
+ export interface MediateDeleteInput {
13
+ path: string;
14
+ }
15
+ export interface MediateMoveInput {
16
+ from: string;
17
+ to: string;
18
+ }
19
+ export interface MediateDirInput {
20
+ path: string;
21
+ }
22
+ export declare function mediateWriteFile(ctx: MediateContext, input: MediateWriteInput): Promise<ActionRecord>;
23
+ export declare function mediateDeleteFile(ctx: MediateContext, input: MediateDeleteInput): Promise<ActionRecord>;
24
+ export declare function mediateMoveFile(ctx: MediateContext, input: MediateMoveInput): Promise<ActionRecord>;
25
+ export declare function mediateCreateDirectory(ctx: MediateContext, input: MediateDirInput): Promise<ActionRecord>;
26
+ export declare function mediateDeleteDirectory(ctx: MediateContext, input: MediateDirInput): Promise<ActionRecord>;
27
+ export declare function formatActionResponse(action: ActionRecord): string;
28
+ export declare function defaultMediateContext(projectRoot?: string, client?: ActionRecord["client"], sandboxName?: SandboxName): MediateContext;
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Filesystem mediation — thin wrappers over the V5 transaction engine.
3
+ * Preserves the V2 ActionRecord return shape for existing callers/tests.
4
+ */
5
+ import { getProjectRoot, SANDBOX_DIR_NAME, } from "../paths.js";
6
+ import { snapshotIdentityHash } from "./capture.js";
7
+ import { actorFromMcpClient, } from "../core/types.js";
8
+ import { executeTransaction, transactionToLegacyAction, } from "../core/engine.js";
9
+ function sandboxNameOf(ctx) {
10
+ return ctx.sandboxName ?? SANDBOX_DIR_NAME;
11
+ }
12
+ function baseIntent(ctx, operation, input, resourceHint) {
13
+ const intent = {
14
+ adapter: "filesystem",
15
+ operation,
16
+ target: {
17
+ type: "file",
18
+ resource: resourceHint,
19
+ displayName: resourceHint,
20
+ secondary: null,
21
+ },
22
+ input,
23
+ actor: actorFromMcpClient(ctx.client),
24
+ source: "MCP",
25
+ sandbox: sandboxNameOf(ctx),
26
+ metadata: { projectRoot: ctx.projectRoot },
27
+ };
28
+ if (ctx.sessionId !== "pending") {
29
+ intent.sessionId = ctx.sessionId;
30
+ }
31
+ return intent;
32
+ }
33
+ export async function mediateWriteFile(ctx, input) {
34
+ const operation = input.createOnly ? "create" : "write";
35
+ const txn = await executeTransaction(baseIntent(ctx, operation, input, input.path), ctx.projectRoot);
36
+ if (txn.status === "AWAITING_APPROVAL") {
37
+ return transactionToLegacyAction(txn);
38
+ }
39
+ return transactionToLegacyAction(txn);
40
+ }
41
+ export async function mediateDeleteFile(ctx, input) {
42
+ const txn = await executeTransaction(baseIntent(ctx, "delete", input, input.path), ctx.projectRoot);
43
+ return transactionToLegacyAction(txn);
44
+ }
45
+ export async function mediateMoveFile(ctx, input) {
46
+ const intent = baseIntent(ctx, "move", input, input.from);
47
+ intent.target = {
48
+ type: "file",
49
+ resource: input.from,
50
+ displayName: `${input.from} → ${input.to}`,
51
+ secondary: input.to,
52
+ };
53
+ const txn = await executeTransaction(intent, ctx.projectRoot);
54
+ return transactionToLegacyAction(txn);
55
+ }
56
+ export async function mediateCreateDirectory(ctx, input) {
57
+ const intent = baseIntent(ctx, "create_directory", input, input.path);
58
+ intent.target = {
59
+ type: "directory",
60
+ resource: input.path,
61
+ displayName: input.path,
62
+ secondary: null,
63
+ };
64
+ const txn = await executeTransaction(intent, ctx.projectRoot);
65
+ return transactionToLegacyAction(txn);
66
+ }
67
+ export async function mediateDeleteDirectory(ctx, input) {
68
+ const intent = baseIntent(ctx, "delete_directory", input, input.path);
69
+ intent.target = {
70
+ type: "directory",
71
+ resource: input.path,
72
+ displayName: input.path,
73
+ secondary: null,
74
+ };
75
+ const txn = await executeTransaction(intent, ctx.projectRoot);
76
+ return transactionToLegacyAction(txn);
77
+ }
78
+ export function formatActionResponse(action) {
79
+ const before = snapshotIdentityHash(action.before);
80
+ const after = snapshotIdentityHash(action.after);
81
+ return [
82
+ "Action completed.",
83
+ "",
84
+ `Action ID: ${action.id}`,
85
+ `Tool: ${action.tool}`,
86
+ `Sandbox: ${action.sandbox ?? SANDBOX_DIR_NAME}`,
87
+ `Target: ${action.target}${action.targetSecondary ? ` → ${action.targetSecondary}` : ""}`,
88
+ `Before: ${before}`,
89
+ `After: ${after}`,
90
+ `Reversible: ${action.reversibility === "REVERSIBLE" ? "YES" : action.reversibility}`,
91
+ `Trust: AUTHORITATIVE`,
92
+ `Source: MCP`,
93
+ `Client: ${action.client.name}@${action.client.version}`,
94
+ `Session: ${action.sessionId}`,
95
+ `Sequence: ${action.sequence}`,
96
+ ].join("\n");
97
+ }
98
+ export function defaultMediateContext(projectRoot = getProjectRoot(), client = {
99
+ name: "UNKNOWN_MCP_CLIENT",
100
+ version: "unknown",
101
+ }, sandboxName = SANDBOX_DIR_NAME) {
102
+ return {
103
+ projectRoot,
104
+ sessionId: "pending",
105
+ client,
106
+ sandboxName,
107
+ };
108
+ }
109
+ //# sourceMappingURL=mediate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mediate.js","sourceRoot":"","sources":["../../../src/action/mediate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { type SandboxName } from "../paths.js";
2
+ import type { EntrySnapshot } from "./types.js";
3
+ export declare function writeFileMediated(rel: string, content: string | Buffer, projectRoot: string, mode?: number, sandboxName?: SandboxName): Promise<void>;
4
+ export declare function createFileMediated(rel: string, content: string | Buffer, projectRoot: string, mode?: number, sandboxName?: SandboxName): Promise<void>;
5
+ export declare function deleteFileMediated(rel: string, projectRoot: string, sandboxName?: SandboxName): Promise<void>;
6
+ export declare function moveFileMediated(fromRel: string, toRel: string, projectRoot: string, sandboxName?: SandboxName): Promise<void>;
7
+ export declare function createDirectoryMediated(rel: string, projectRoot: string, mode?: number, sandboxName?: SandboxName): Promise<void>;
8
+ export declare function deleteDirectoryMediated(rel: string, projectRoot: string, sandboxName?: SandboxName): Promise<void>;
9
+ /** Restore an entry snapshot onto a relative path (inverse apply). */
10
+ export declare function restoreSnapshot(rel: string, snap: EntrySnapshot, projectRoot: string, sandboxName?: SandboxName): Promise<void>;
11
+ export declare function listDirectoryMediated(rel: string, projectRoot: string, sandboxName?: SandboxName): Promise<string[]>;
@@ -0,0 +1,189 @@
1
+ import path from "node:path";
2
+ import { mkdir, writeFile, rm, rename, chmod, symlink, lstat, readdir, } from "node:fs/promises";
3
+ import { assertApprovedSandbox, getSandboxPath, isPathInside, PathSafetyError, SANDBOX_DIR_NAME, ENFORCED_SANDBOX_DIR_NAME, } from "../paths.js";
4
+ import { sandboxAbs } from "./capture.js";
5
+ import { readObject } from "./objects.js";
6
+ import { withEnforcedWriteAccess, } from "../enforcement/readonly.js";
7
+ import { isEnforcedLocked, loadEnforcementState, } from "../enforcement/setup.js";
8
+ async function ensureParent(abs, sandbox) {
9
+ const parent = path.dirname(abs);
10
+ if (!isPathInside(sandbox, parent) && parent !== sandbox) {
11
+ throw new PathSafetyError(`Parent escapes sandbox: ${parent}`);
12
+ }
13
+ await mkdir(parent, { recursive: true });
14
+ }
15
+ async function maybeBroker(projectRoot, sandboxName, fn) {
16
+ if (sandboxName !== ENFORCED_SANDBOX_DIR_NAME) {
17
+ return fn();
18
+ }
19
+ const state = await loadEnforcementState(projectRoot);
20
+ if (state?.enabled && state.locked) {
21
+ return withEnforcedWriteAccess(projectRoot, fn);
22
+ }
23
+ // Even if state missing, check lock bit via isEnforcedLocked
24
+ if (await isEnforcedLocked(projectRoot)) {
25
+ return withEnforcedWriteAccess(projectRoot, fn);
26
+ }
27
+ return fn();
28
+ }
29
+ export async function writeFileMediated(rel, content, projectRoot, mode = 0o644, sandboxName = SANDBOX_DIR_NAME) {
30
+ const sandbox = getSandboxPath(projectRoot, sandboxName);
31
+ await assertApprovedSandbox(sandbox, projectRoot, sandboxName);
32
+ await maybeBroker(projectRoot, sandboxName, async () => {
33
+ const abs = sandboxAbs(rel, projectRoot, sandboxName);
34
+ await ensureParent(abs, sandbox);
35
+ const data = typeof content === "string" ? Buffer.from(content, "utf8") : content;
36
+ const tmp = `${abs}.rewind-mcp-tmp-${process.pid}`;
37
+ await writeFile(tmp, data, { mode });
38
+ await rename(tmp, abs);
39
+ await chmod(abs, mode);
40
+ });
41
+ }
42
+ export async function createFileMediated(rel, content, projectRoot, mode = 0o644, sandboxName = SANDBOX_DIR_NAME) {
43
+ const abs = sandboxAbs(rel, projectRoot, sandboxName);
44
+ try {
45
+ await lstat(abs);
46
+ throw new PathSafetyError(`File already exists: ${rel}`);
47
+ }
48
+ catch (err) {
49
+ if (err instanceof PathSafetyError)
50
+ throw err;
51
+ const code = err && typeof err === "object" && "code" in err
52
+ ? String(err.code)
53
+ : "";
54
+ if (code !== "ENOENT")
55
+ throw err;
56
+ }
57
+ await writeFileMediated(rel, content, projectRoot, mode, sandboxName);
58
+ }
59
+ export async function deleteFileMediated(rel, projectRoot, sandboxName = SANDBOX_DIR_NAME) {
60
+ const sandbox = getSandboxPath(projectRoot, sandboxName);
61
+ await assertApprovedSandbox(sandbox, projectRoot, sandboxName);
62
+ await maybeBroker(projectRoot, sandboxName, async () => {
63
+ const abs = sandboxAbs(rel, projectRoot, sandboxName);
64
+ const st = await lstat(abs);
65
+ if (!st.isFile() && !st.isSymbolicLink()) {
66
+ throw new PathSafetyError(`Not a file: ${rel}`);
67
+ }
68
+ await rm(abs, { force: true });
69
+ });
70
+ }
71
+ export async function moveFileMediated(fromRel, toRel, projectRoot, sandboxName = SANDBOX_DIR_NAME) {
72
+ const sandbox = getSandboxPath(projectRoot, sandboxName);
73
+ await assertApprovedSandbox(sandbox, projectRoot, sandboxName);
74
+ await maybeBroker(projectRoot, sandboxName, async () => {
75
+ const fromAbs = sandboxAbs(fromRel, projectRoot, sandboxName);
76
+ const toAbs = sandboxAbs(toRel, projectRoot, sandboxName);
77
+ await ensureParent(toAbs, sandbox);
78
+ await rename(fromAbs, toAbs);
79
+ });
80
+ }
81
+ export async function createDirectoryMediated(rel, projectRoot, mode = 0o755, sandboxName = SANDBOX_DIR_NAME) {
82
+ const sandbox = getSandboxPath(projectRoot, sandboxName);
83
+ await assertApprovedSandbox(sandbox, projectRoot, sandboxName);
84
+ await maybeBroker(projectRoot, sandboxName, async () => {
85
+ const abs = sandboxAbs(rel, projectRoot, sandboxName);
86
+ await mkdir(abs, { recursive: true, mode });
87
+ await chmod(abs, mode);
88
+ });
89
+ }
90
+ export async function deleteDirectoryMediated(rel, projectRoot, sandboxName = SANDBOX_DIR_NAME) {
91
+ const sandbox = getSandboxPath(projectRoot, sandboxName);
92
+ await assertApprovedSandbox(sandbox, projectRoot, sandboxName);
93
+ await maybeBroker(projectRoot, sandboxName, async () => {
94
+ const abs = sandboxAbs(rel, projectRoot, sandboxName);
95
+ if (abs === path.resolve(sandbox)) {
96
+ throw new PathSafetyError("Refusing to delete sandbox root");
97
+ }
98
+ const st = await lstat(abs);
99
+ if (!st.isDirectory()) {
100
+ throw new PathSafetyError(`Not a directory: ${rel}`);
101
+ }
102
+ await rm(abs, { recursive: true, force: true });
103
+ });
104
+ }
105
+ /** Restore an entry snapshot onto a relative path (inverse apply). */
106
+ export async function restoreSnapshot(rel, snap, projectRoot, sandboxName = SANDBOX_DIR_NAME) {
107
+ const sandbox = getSandboxPath(projectRoot, sandboxName);
108
+ await assertApprovedSandbox(sandbox, projectRoot, sandboxName);
109
+ await maybeBroker(projectRoot, sandboxName, async () => {
110
+ const abs = sandboxAbs(rel, projectRoot, sandboxName);
111
+ if (!snap.exists) {
112
+ await rm(abs, { recursive: true, force: true });
113
+ return;
114
+ }
115
+ if (snap.kind === "directory" && snap.subtree) {
116
+ await rm(abs, { recursive: true, force: true }).catch(() => undefined);
117
+ await restoreSubtree(snap.subtree, projectRoot, sandboxName);
118
+ return;
119
+ }
120
+ if (snap.kind === "directory") {
121
+ await mkdir(abs, { recursive: true });
122
+ if (snap.mode != null)
123
+ await chmod(abs, snap.mode);
124
+ return;
125
+ }
126
+ if (snap.kind === "symlink") {
127
+ if (!snap.linkTarget)
128
+ throw new Error(`Missing linkTarget for ${rel}`);
129
+ await ensureParent(abs, sandbox);
130
+ await rm(abs, { force: true }).catch(() => undefined);
131
+ await symlink(snap.linkTarget, abs);
132
+ return;
133
+ }
134
+ if (snap.kind === "file") {
135
+ if (!snap.objectId) {
136
+ throw new Error(`Missing objectId for file restore: ${rel}`);
137
+ }
138
+ const data = await readObject(snap.objectId, projectRoot);
139
+ // Nested writeFileMediated would double-broker — call inner directly
140
+ const fileAbs = sandboxAbs(rel, projectRoot, sandboxName);
141
+ await ensureParent(fileAbs, sandbox);
142
+ const tmp = `${fileAbs}.rewind-mcp-tmp-${process.pid}`;
143
+ await writeFile(tmp, data, { mode: snap.mode ?? 0o644 });
144
+ await rename(tmp, fileAbs);
145
+ await chmod(fileAbs, snap.mode ?? 0o644);
146
+ return;
147
+ }
148
+ throw new Error(`Cannot restore kind ${snap.kind} at ${rel}`);
149
+ });
150
+ }
151
+ async function restoreSubtree(entries, projectRoot, sandboxName) {
152
+ const sandbox = getSandboxPath(projectRoot, sandboxName);
153
+ for (const e of entries.filter((x) => x.kind === "directory")) {
154
+ const abs = sandboxAbs(e.path, projectRoot, sandboxName);
155
+ await mkdir(abs, { recursive: true });
156
+ await chmod(abs, e.mode);
157
+ }
158
+ for (const e of entries.filter((x) => x.kind === "file")) {
159
+ if (!e.objectId)
160
+ throw new Error(`Missing object for ${e.path}`);
161
+ const data = await readObject(e.objectId, projectRoot);
162
+ const abs = sandboxAbs(e.path, projectRoot, sandboxName);
163
+ await ensureParent(abs, sandbox);
164
+ const tmp = `${abs}.rewind-mcp-tmp-${process.pid}`;
165
+ await writeFile(tmp, data, { mode: e.mode });
166
+ await rename(tmp, abs);
167
+ await chmod(abs, e.mode);
168
+ }
169
+ for (const e of entries.filter((x) => x.kind === "symlink")) {
170
+ if (!e.linkTarget)
171
+ throw new Error(`Missing link for ${e.path}`);
172
+ const abs = sandboxAbs(e.path, projectRoot, sandboxName);
173
+ await mkdir(path.dirname(abs), { recursive: true });
174
+ await rm(abs, { force: true }).catch(() => undefined);
175
+ const resolved = path.resolve(path.dirname(abs), e.linkTarget);
176
+ if (!isPathInside(sandbox, resolved)) {
177
+ throw new PathSafetyError(`Symlink escape on restore: ${e.path}`);
178
+ }
179
+ await symlink(e.linkTarget, abs);
180
+ }
181
+ }
182
+ export async function listDirectoryMediated(rel, projectRoot, sandboxName = SANDBOX_DIR_NAME) {
183
+ const target = rel === "" || rel === "."
184
+ ? getSandboxPath(projectRoot, sandboxName)
185
+ : sandboxAbs(rel, projectRoot, sandboxName);
186
+ const names = await readdir(target);
187
+ return names.sort((a, b) => a.localeCompare(b));
188
+ }
189
+ //# sourceMappingURL=mutate.js.map