@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.
- package/.env.example +8 -0
- package/LICENSE +21 -0
- package/README.md +39 -0
- package/dist/src/action/capture.d.ts +15 -0
- package/dist/src/action/capture.js +215 -0
- package/dist/src/action/capture.js.map +1 -0
- package/dist/src/action/mediate.d.ts +28 -0
- package/dist/src/action/mediate.js +109 -0
- package/dist/src/action/mediate.js.map +1 -0
- package/dist/src/action/mutate.d.ts +11 -0
- package/dist/src/action/mutate.js +189 -0
- package/dist/src/action/mutate.js.map +1 -0
- package/dist/src/action/objects.d.ts +5 -0
- package/dist/src/action/objects.js +50 -0
- package/dist/src/action/objects.js.map +1 -0
- package/dist/src/action/store.d.ts +24 -0
- package/dist/src/action/store.js +149 -0
- package/dist/src/action/store.js.map +1 -0
- package/dist/src/action/types.d.ts +84 -0
- package/dist/src/action/types.js +3 -0
- package/dist/src/action/types.js.map +1 -0
- package/dist/src/action/undo.d.ts +11 -0
- package/dist/src/action/undo.js +42 -0
- package/dist/src/action/undo.js.map +1 -0
- package/dist/src/adapters/filesystem.d.ts +35 -0
- package/dist/src/adapters/filesystem.js +499 -0
- package/dist/src/adapters/filesystem.js.map +1 -0
- package/dist/src/adapters/index.d.ts +7 -0
- package/dist/src/adapters/index.js +19 -0
- package/dist/src/adapters/index.js.map +1 -0
- package/dist/src/adapters/types.d.ts +54 -0
- package/dist/src/adapters/types.js +7 -0
- package/dist/src/adapters/types.js.map +1 -0
- package/dist/src/api/server.d.ts +25 -0
- package/dist/src/api/server.js +786 -0
- package/dist/src/api/server.js.map +1 -0
- package/dist/src/approval/store.d.ts +35 -0
- package/dist/src/approval/store.js +113 -0
- package/dist/src/approval/store.js.map +1 -0
- package/dist/src/checkpoint.d.ts +9 -0
- package/dist/src/checkpoint.js +140 -0
- package/dist/src/checkpoint.js.map +1 -0
- package/dist/src/cli/v5.d.ts +15 -0
- package/dist/src/cli/v5.js +110 -0
- package/dist/src/cli/v5.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +823 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/clients/types.d.ts +90 -0
- package/dist/src/clients/types.js +239 -0
- package/dist/src/clients/types.js.map +1 -0
- package/dist/src/commercial/auth/provider.d.ts +61 -0
- package/dist/src/commercial/auth/provider.js +227 -0
- package/dist/src/commercial/auth/provider.js.map +1 -0
- package/dist/src/commercial/entitlement/provider.d.ts +27 -0
- package/dist/src/commercial/entitlement/provider.js +192 -0
- package/dist/src/commercial/entitlement/provider.js.map +1 -0
- package/dist/src/commercial/entitlement/signing.d.ts +61 -0
- package/dist/src/commercial/entitlement/signing.js +124 -0
- package/dist/src/commercial/entitlement/signing.js.map +1 -0
- package/dist/src/commercial/env.d.ts +10 -0
- package/dist/src/commercial/env.js +40 -0
- package/dist/src/commercial/env.js.map +1 -0
- package/dist/src/commercial/product.d.ts +26 -0
- package/dist/src/commercial/product.js +39 -0
- package/dist/src/commercial/product.js.map +1 -0
- package/dist/src/commercial/store.d.ts +46 -0
- package/dist/src/commercial/store.js +104 -0
- package/dist/src/commercial/store.js.map +1 -0
- package/dist/src/commercial/stripe/checkout.d.ts +104 -0
- package/dist/src/commercial/stripe/checkout.js +469 -0
- package/dist/src/commercial/stripe/checkout.js.map +1 -0
- package/dist/src/core/compatibility.d.ts +15 -0
- package/dist/src/core/compatibility.js +226 -0
- package/dist/src/core/compatibility.js.map +1 -0
- package/dist/src/core/engine.d.ts +33 -0
- package/dist/src/core/engine.js +432 -0
- package/dist/src/core/engine.js.map +1 -0
- package/dist/src/core/lifecycle.d.ts +17 -0
- package/dist/src/core/lifecycle.js +56 -0
- package/dist/src/core/lifecycle.js.map +1 -0
- package/dist/src/core/types.d.ts +140 -0
- package/dist/src/core/types.js +37 -0
- package/dist/src/core/types.js.map +1 -0
- package/dist/src/destroy.d.ts +21 -0
- package/dist/src/destroy.js +219 -0
- package/dist/src/destroy.js.map +1 -0
- package/dist/src/diff.d.ts +13 -0
- package/dist/src/diff.js +251 -0
- package/dist/src/diff.js.map +1 -0
- package/dist/src/enforcement/detect.d.ts +13 -0
- package/dist/src/enforcement/detect.js +75 -0
- package/dist/src/enforcement/detect.js.map +1 -0
- package/dist/src/enforcement/hook-policy.d.ts +38 -0
- package/dist/src/enforcement/hook-policy.js +111 -0
- package/dist/src/enforcement/hook-policy.js.map +1 -0
- package/dist/src/enforcement/index.d.ts +8 -0
- package/dist/src/enforcement/index.js +8 -0
- package/dist/src/enforcement/index.js.map +1 -0
- package/dist/src/enforcement/perf.d.ts +6 -0
- package/dist/src/enforcement/perf.js +18 -0
- package/dist/src/enforcement/perf.js.map +1 -0
- package/dist/src/enforcement/policy.d.ts +12 -0
- package/dist/src/enforcement/policy.js +84 -0
- package/dist/src/enforcement/policy.js.map +1 -0
- package/dist/src/enforcement/readonly.d.ts +17 -0
- package/dist/src/enforcement/readonly.js +124 -0
- package/dist/src/enforcement/readonly.js.map +1 -0
- package/dist/src/enforcement/sandbox-clone.d.ts +6 -0
- package/dist/src/enforcement/sandbox-clone.js +11 -0
- package/dist/src/enforcement/sandbox-clone.js.map +1 -0
- package/dist/src/enforcement/setup.d.ts +33 -0
- package/dist/src/enforcement/setup.js +168 -0
- package/dist/src/enforcement/setup.js.map +1 -0
- package/dist/src/enforcement/trust.d.ts +10 -0
- package/dist/src/enforcement/trust.js +71 -0
- package/dist/src/enforcement/trust.js.map +1 -0
- package/dist/src/enforcement/types.d.ts +40 -0
- package/dist/src/enforcement/types.js +3 -0
- package/dist/src/enforcement/types.js.map +1 -0
- package/dist/src/entitlement/provider.d.ts +48 -0
- package/dist/src/entitlement/provider.js +95 -0
- package/dist/src/entitlement/provider.js.map +1 -0
- package/dist/src/hash.d.ts +9 -0
- package/dist/src/hash.js +23 -0
- package/dist/src/hash.js.map +1 -0
- package/dist/src/ledger/query.d.ts +19 -0
- package/dist/src/ledger/query.js +158 -0
- package/dist/src/ledger/query.js.map +1 -0
- package/dist/src/manifest.d.ts +32 -0
- package/dist/src/manifest.js +242 -0
- package/dist/src/manifest.js.map +1 -0
- package/dist/src/mcp/server.d.ts +7 -0
- package/dist/src/mcp/server.js +364 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/paths.d.ts +94 -0
- package/dist/src/paths.js +354 -0
- package/dist/src/paths.js.map +1 -0
- package/dist/src/permission/engine.d.ts +15 -0
- package/dist/src/permission/engine.js +147 -0
- package/dist/src/permission/engine.js.map +1 -0
- package/dist/src/permission/types.d.ts +42 -0
- package/dist/src/permission/types.js +3 -0
- package/dist/src/permission/types.js.map +1 -0
- package/dist/src/product/setup.d.ts +35 -0
- package/dist/src/product/setup.js +300 -0
- package/dist/src/product/setup.js.map +1 -0
- package/dist/src/protect/audit.d.ts +5 -0
- package/dist/src/protect/audit.js +26 -0
- package/dist/src/protect/audit.js.map +1 -0
- package/dist/src/protect/capture.d.ts +7 -0
- package/dist/src/protect/capture.js +250 -0
- package/dist/src/protect/capture.js.map +1 -0
- package/dist/src/protect/fingerprint.d.ts +4 -0
- package/dist/src/protect/fingerprint.js +49 -0
- package/dist/src/protect/fingerprint.js.map +1 -0
- package/dist/src/protect/hook-cli.d.ts +2 -0
- package/dist/src/protect/hook-cli.js +107 -0
- package/dist/src/protect/hook-cli.js.map +1 -0
- package/dist/src/protect/index.d.ts +12 -0
- package/dist/src/protect/index.js +12 -0
- package/dist/src/protect/index.js.map +1 -0
- package/dist/src/protect/install.d.ts +34 -0
- package/dist/src/protect/install.js +293 -0
- package/dist/src/protect/install.js.map +1 -0
- package/dist/src/protect/intercept.d.ts +11 -0
- package/dist/src/protect/intercept.js +390 -0
- package/dist/src/protect/intercept.js.map +1 -0
- package/dist/src/protect/protect.d.ts +17 -0
- package/dist/src/protect/protect.js +194 -0
- package/dist/src/protect/protect.js.map +1 -0
- package/dist/src/protect/root-paths.d.ts +13 -0
- package/dist/src/protect/root-paths.js +140 -0
- package/dist/src/protect/root-paths.js.map +1 -0
- package/dist/src/protect/shell-policy.d.ts +17 -0
- package/dist/src/protect/shell-policy.js +269 -0
- package/dist/src/protect/shell-policy.js.map +1 -0
- package/dist/src/protect/status.d.ts +11 -0
- package/dist/src/protect/status.js +165 -0
- package/dist/src/protect/status.js.map +1 -0
- package/dist/src/protect/store.d.ts +20 -0
- package/dist/src/protect/store.js +170 -0
- package/dist/src/protect/store.js.map +1 -0
- package/dist/src/protect/types.d.ts +116 -0
- package/dist/src/protect/types.js +3 -0
- package/dist/src/protect/types.js.map +1 -0
- package/dist/src/protect/undo-session.d.ts +14 -0
- package/dist/src/protect/undo-session.js +182 -0
- package/dist/src/protect/undo-session.js.map +1 -0
- package/dist/src/restore.d.ts +39 -0
- package/dist/src/restore.js +141 -0
- package/dist/src/restore.js.map +1 -0
- package/dist/src/sandbox.d.ts +23 -0
- package/dist/src/sandbox.js +198 -0
- package/dist/src/sandbox.js.map +1 -0
- package/dist/src/session/ambient.d.ts +10 -0
- package/dist/src/session/ambient.js +53 -0
- package/dist/src/session/ambient.js.map +1 -0
- package/dist/src/session/attribution.d.ts +27 -0
- package/dist/src/session/attribution.js +201 -0
- package/dist/src/session/attribution.js.map +1 -0
- package/dist/src/session/index.d.ts +25 -0
- package/dist/src/session/index.js +472 -0
- package/dist/src/session/index.js.map +1 -0
- package/dist/src/session/process.d.ts +34 -0
- package/dist/src/session/process.js +234 -0
- package/dist/src/session/process.js.map +1 -0
- package/dist/src/session/store.d.ts +14 -0
- package/dist/src/session/store.js +125 -0
- package/dist/src/session/store.js.map +1 -0
- package/dist/src/session/types.d.ts +111 -0
- package/dist/src/session/types.js +3 -0
- package/dist/src/session/types.js.map +1 -0
- package/dist/src/session/watcher.d.ts +32 -0
- package/dist/src/session/watcher.js +186 -0
- package/dist/src/session/watcher.js.map +1 -0
- package/dist/src/timing.d.ts +7 -0
- package/dist/src/timing.js +19 -0
- package/dist/src/timing.js.map +1 -0
- package/dist/src/types.d.ts +101 -0
- package/dist/src/types.js +3 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/verify.d.ts +8 -0
- package/dist/src/verify.js +126 -0
- package/dist/src/verify.js.map +1 -0
- package/docs/ADAPTERS.md +37 -0
- package/docs/APPROVALS.md +32 -0
- package/docs/ARCHITECTURE.md +57 -0
- package/docs/ATTRIBUTION.md +179 -0
- package/docs/COMMERCIAL.md +51 -0
- package/docs/CURSOR-ENFORCEMENT.md +73 -0
- package/docs/ENFORCEMENT.md +134 -0
- package/docs/FUTURE.md +91 -0
- package/docs/HUMAN-SETUP.md +98 -0
- package/docs/IDENTITY-AUTHORITY.md +35 -0
- package/docs/MCP.md +107 -0
- package/docs/PACKAGING.md +77 -0
- package/docs/PERMISSIONS.md +53 -0
- package/docs/PRODUCT-V5.md +63 -0
- package/docs/PRODUCT-V6.md +69 -0
- package/docs/PROTECTED-SESSIONS.md +155 -0
- package/docs/THREAT-MODEL.md +84 -0
- package/docs/TRANSACTIONS.md +57 -0
- package/package.json +85 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { assertApprovedSandbox, assertSafeRelativePath, getProjectRoot, isPathInside, } from "../paths.js";
|
|
2
|
+
import { matchAmbientPath } from "./ambient.js";
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { lstat } from "node:fs/promises";
|
|
6
|
+
import chokidar from "chokidar";
|
|
7
|
+
function toRel(root, abs) {
|
|
8
|
+
const rel = path.relative(root, abs).split(path.sep).join("/");
|
|
9
|
+
if (rel === "" || rel === ".") {
|
|
10
|
+
throw new Error("root event");
|
|
11
|
+
}
|
|
12
|
+
return assertSafeRelativePath(rel);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Live sandbox watcher.
|
|
16
|
+
* Uses chokidar (FSEvents on macOS) with debounce/coalesce.
|
|
17
|
+
* Events are signals — final manifest diff remains authoritative.
|
|
18
|
+
*/
|
|
19
|
+
export class SandboxWatcher {
|
|
20
|
+
sandboxRoot;
|
|
21
|
+
onEvent;
|
|
22
|
+
watcher = null;
|
|
23
|
+
pending = new Map();
|
|
24
|
+
closed = false;
|
|
25
|
+
debounceMs;
|
|
26
|
+
stats = {
|
|
27
|
+
rawSignals: 0,
|
|
28
|
+
emittedEvents: 0,
|
|
29
|
+
peakEventsPerSec: 0,
|
|
30
|
+
};
|
|
31
|
+
windowCount = 0;
|
|
32
|
+
windowTimer = null;
|
|
33
|
+
constructor(sandboxRoot, onEvent, options = {}) {
|
|
34
|
+
this.sandboxRoot = sandboxRoot;
|
|
35
|
+
this.onEvent = onEvent;
|
|
36
|
+
this.debounceMs = options.debounceMs ?? 75;
|
|
37
|
+
}
|
|
38
|
+
async start(projectRoot = getProjectRoot()) {
|
|
39
|
+
await assertApprovedSandbox(this.sandboxRoot, projectRoot);
|
|
40
|
+
this.windowTimer = setInterval(() => {
|
|
41
|
+
if (this.windowCount > this.stats.peakEventsPerSec) {
|
|
42
|
+
this.stats.peakEventsPerSec = this.windowCount;
|
|
43
|
+
}
|
|
44
|
+
this.windowCount = 0;
|
|
45
|
+
}, 1000);
|
|
46
|
+
this.windowTimer.unref?.();
|
|
47
|
+
this.watcher = chokidar.watch(this.sandboxRoot, {
|
|
48
|
+
ignoreInitial: true,
|
|
49
|
+
persistent: true,
|
|
50
|
+
awaitWriteFinish: {
|
|
51
|
+
stabilityThreshold: 50,
|
|
52
|
+
pollInterval: 25,
|
|
53
|
+
},
|
|
54
|
+
followSymlinks: false,
|
|
55
|
+
depth: 99,
|
|
56
|
+
});
|
|
57
|
+
this.watcher.on("all", (eventName, absPath) => {
|
|
58
|
+
void this.handleRaw(eventName, absPath);
|
|
59
|
+
});
|
|
60
|
+
this.watcher.on("error", (err) => {
|
|
61
|
+
// Surface as UNKNOWN modify on root marker — keep watching
|
|
62
|
+
console.error(`[watcher error] ${err instanceof Error ? err.message : String(err)}`);
|
|
63
|
+
});
|
|
64
|
+
// Ready wait
|
|
65
|
+
await new Promise((resolve) => {
|
|
66
|
+
this.watcher?.on("ready", () => resolve());
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async handleRaw(eventName, absPath) {
|
|
70
|
+
if (this.closed)
|
|
71
|
+
return;
|
|
72
|
+
if (!isPathInside(this.sandboxRoot, absPath))
|
|
73
|
+
return;
|
|
74
|
+
this.stats.rawSignals++;
|
|
75
|
+
this.windowCount++;
|
|
76
|
+
const key = absPath;
|
|
77
|
+
const existing = this.pending.get(key);
|
|
78
|
+
if (existing) {
|
|
79
|
+
existing.types.add(eventName);
|
|
80
|
+
existing.rawCount++;
|
|
81
|
+
clearTimeout(existing.timer);
|
|
82
|
+
existing.timer = setTimeout(() => {
|
|
83
|
+
void this.flush(key);
|
|
84
|
+
}, this.debounceMs);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const timer = setTimeout(() => {
|
|
88
|
+
void this.flush(key);
|
|
89
|
+
}, this.debounceMs);
|
|
90
|
+
this.pending.set(key, {
|
|
91
|
+
abs: absPath,
|
|
92
|
+
types: new Set([eventName]),
|
|
93
|
+
timer,
|
|
94
|
+
rawCount: 1,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async flush(key) {
|
|
98
|
+
const pending = this.pending.get(key);
|
|
99
|
+
if (!pending)
|
|
100
|
+
return;
|
|
101
|
+
this.pending.delete(key);
|
|
102
|
+
let rel;
|
|
103
|
+
try {
|
|
104
|
+
rel = toRel(this.sandboxRoot, pending.abs);
|
|
105
|
+
if (rel === ".")
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const type = await this.classify(pending.types, pending.abs);
|
|
112
|
+
const ambient = matchAmbientPath(rel);
|
|
113
|
+
const evidence = [
|
|
114
|
+
{
|
|
115
|
+
kind: "watcher_signal",
|
|
116
|
+
detail: `chokidar/FSEvents signals: ${[...pending.types].join(",")}`,
|
|
117
|
+
observedAt: new Date().toISOString(),
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
if (ambient.ambient) {
|
|
121
|
+
evidence.push({
|
|
122
|
+
kind: "ambient_filename_pattern",
|
|
123
|
+
detail: ambient.evidence,
|
|
124
|
+
observedAt: new Date().toISOString(),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const event = {
|
|
128
|
+
id: `ev-${Date.now().toString(36)}-${randomBytes(2).toString("hex")}`,
|
|
129
|
+
at: new Date().toISOString(),
|
|
130
|
+
type,
|
|
131
|
+
path: rel,
|
|
132
|
+
rawCount: pending.rawCount,
|
|
133
|
+
// Honest default: watcher does not know the actor
|
|
134
|
+
actor: ambient.ambient ? "SYSTEM" : "UNKNOWN",
|
|
135
|
+
confidence: ambient.ambient ? "MEDIUM" : "NONE",
|
|
136
|
+
evidence,
|
|
137
|
+
ambientSuspect: ambient.ambient,
|
|
138
|
+
};
|
|
139
|
+
this.stats.emittedEvents++;
|
|
140
|
+
this.onEvent(event);
|
|
141
|
+
}
|
|
142
|
+
async classify(types, abs) {
|
|
143
|
+
const has = (t) => types.has(t);
|
|
144
|
+
let isDir = false;
|
|
145
|
+
try {
|
|
146
|
+
const st = await lstat(abs);
|
|
147
|
+
isDir = st.isDirectory();
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// deleted
|
|
151
|
+
if (has("unlinkDir") || has("rmdir"))
|
|
152
|
+
return "DIRECTORY_DELETE";
|
|
153
|
+
if (has("unlink") || has("delete"))
|
|
154
|
+
return "DELETE";
|
|
155
|
+
return "DELETE";
|
|
156
|
+
}
|
|
157
|
+
if (has("addDir") || (has("add") && isDir))
|
|
158
|
+
return "DIRECTORY_CREATE";
|
|
159
|
+
if (has("unlinkDir"))
|
|
160
|
+
return "DIRECTORY_DELETE";
|
|
161
|
+
if (has("add") && !isDir)
|
|
162
|
+
return "CREATE";
|
|
163
|
+
if (has("change") || has("modify"))
|
|
164
|
+
return "MODIFY";
|
|
165
|
+
if (has("unlink"))
|
|
166
|
+
return "DELETE";
|
|
167
|
+
return "UNKNOWN";
|
|
168
|
+
}
|
|
169
|
+
async stop() {
|
|
170
|
+
this.closed = true;
|
|
171
|
+
for (const p of this.pending.values())
|
|
172
|
+
clearTimeout(p.timer);
|
|
173
|
+
// Flush remaining
|
|
174
|
+
const keys = [...this.pending.keys()];
|
|
175
|
+
for (const k of keys)
|
|
176
|
+
await this.flush(k);
|
|
177
|
+
if (this.windowTimer)
|
|
178
|
+
clearInterval(this.windowTimer);
|
|
179
|
+
if (this.watcher) {
|
|
180
|
+
await this.watcher.close();
|
|
181
|
+
this.watcher = null;
|
|
182
|
+
}
|
|
183
|
+
return { ...this.stats };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../../src/session/watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Simple timing helpers. */
|
|
2
|
+
export class Timer {
|
|
3
|
+
start;
|
|
4
|
+
constructor() {
|
|
5
|
+
this.start = process.hrtime.bigint();
|
|
6
|
+
}
|
|
7
|
+
elapsedMs() {
|
|
8
|
+
const ns = process.hrtime.bigint() - this.start;
|
|
9
|
+
return Number(ns) / 1e6;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function formatMs(ms) {
|
|
13
|
+
if (ms < 1)
|
|
14
|
+
return `${ms.toFixed(2)}ms`;
|
|
15
|
+
if (ms < 1000)
|
|
16
|
+
return `${ms.toFixed(1)}ms`;
|
|
17
|
+
return `${(ms / 1000).toFixed(2)}s`;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=timing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timing.js","sourceRoot":"","sources":["../../src/timing.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAE7B,MAAM,OAAO,KAAK;IACC,KAAK,CAAS;IAE/B;QACE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;IACvC,CAAC;IAED,SAAS;QACP,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAChD,OAAO,MAAM,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC;IAC1B,CAAC;CACF;AAED,MAAM,UAAU,QAAQ,CAAC,EAAU;IACjC,IAAI,EAAE,GAAG,CAAC;QAAE,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACxC,IAAI,EAAE,GAAG,IAAI;QAAE,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3C,OAAO,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/** Core domain types for REWIND V0. */
|
|
2
|
+
export type ChangeType = "CREATED" | "MODIFIED" | "DELETED" | "RENAMED" | "MOVED";
|
|
3
|
+
export type Reversibility = "REVERSIBLE" | "COMPENSABLE" | "IRREVERSIBLE";
|
|
4
|
+
export type EntryKind = "file" | "directory" | "symlink";
|
|
5
|
+
export interface ManifestEntry {
|
|
6
|
+
/** Relative path from sandbox root, POSIX-style, no leading slash. */
|
|
7
|
+
path: string;
|
|
8
|
+
kind: EntryKind;
|
|
9
|
+
/** SHA-256 of file contents; null for directories; symlink target hashed for symlinks. */
|
|
10
|
+
hash: string | null;
|
|
11
|
+
size: number;
|
|
12
|
+
/** Octal permission bits as number (e.g. 0o644). */
|
|
13
|
+
mode: number;
|
|
14
|
+
/** Symlink target if kind === "symlink". */
|
|
15
|
+
linkTarget: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface Manifest {
|
|
18
|
+
version: 1;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
rootLabel: string;
|
|
21
|
+
entries: ManifestEntry[];
|
|
22
|
+
/** SHA-256 of canonical serialized entries. */
|
|
23
|
+
fingerprint: string;
|
|
24
|
+
}
|
|
25
|
+
export interface CheckpointMeta {
|
|
26
|
+
id: string;
|
|
27
|
+
createdAt: string;
|
|
28
|
+
targetPath: string;
|
|
29
|
+
fingerprint: string;
|
|
30
|
+
fileCount: number;
|
|
31
|
+
directoryCount: number;
|
|
32
|
+
symlinkCount: number;
|
|
33
|
+
timingsMs: {
|
|
34
|
+
manifest: number;
|
|
35
|
+
contentStore: number;
|
|
36
|
+
total: number;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export interface Checkpoint {
|
|
40
|
+
meta: CheckpointMeta;
|
|
41
|
+
manifest: Manifest;
|
|
42
|
+
/** Absolute path to checkpoint directory under .rewind/checkpoints/<id> */
|
|
43
|
+
directory: string;
|
|
44
|
+
}
|
|
45
|
+
export interface Change {
|
|
46
|
+
type: ChangeType;
|
|
47
|
+
pathBefore: string | null;
|
|
48
|
+
pathAfter: string | null;
|
|
49
|
+
hashBefore: string | null;
|
|
50
|
+
hashAfter: string | null;
|
|
51
|
+
sizeBefore: number | null;
|
|
52
|
+
sizeAfter: number | null;
|
|
53
|
+
reversibility: Reversibility;
|
|
54
|
+
}
|
|
55
|
+
export type TransactionStatus = "open" | "recorded" | "rewound" | "verified" | "failed";
|
|
56
|
+
export interface Transaction {
|
|
57
|
+
id: string;
|
|
58
|
+
startedAt: string;
|
|
59
|
+
completedAt: string | null;
|
|
60
|
+
checkpointId: string;
|
|
61
|
+
targetPath: string;
|
|
62
|
+
changes: Change[];
|
|
63
|
+
status: TransactionStatus;
|
|
64
|
+
summary: TransactionSummary;
|
|
65
|
+
timingsMs: {
|
|
66
|
+
diff: number;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export interface TransactionSummary {
|
|
70
|
+
total: number;
|
|
71
|
+
modified: number;
|
|
72
|
+
created: number;
|
|
73
|
+
deleted: number;
|
|
74
|
+
renamed: number;
|
|
75
|
+
moved: number;
|
|
76
|
+
reversible: number;
|
|
77
|
+
compensable: number;
|
|
78
|
+
irreversible: number;
|
|
79
|
+
}
|
|
80
|
+
export interface VerificationResult {
|
|
81
|
+
ok: boolean;
|
|
82
|
+
filesChecked: number;
|
|
83
|
+
expectedFingerprint: string;
|
|
84
|
+
actualFingerprint: string;
|
|
85
|
+
mismatches: VerificationMismatch[];
|
|
86
|
+
timingsMs: {
|
|
87
|
+
manifest: number;
|
|
88
|
+
compare: number;
|
|
89
|
+
total: number;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export interface VerificationMismatch {
|
|
93
|
+
path: string;
|
|
94
|
+
reason: string;
|
|
95
|
+
expected?: string;
|
|
96
|
+
actual?: string;
|
|
97
|
+
}
|
|
98
|
+
export interface TimingRecord {
|
|
99
|
+
label: string;
|
|
100
|
+
ms: number;
|
|
101
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,uCAAuC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Checkpoint, VerificationResult } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Compare restored sandbox against a checkpoint manifest.
|
|
4
|
+
* Fail closed: any mismatch → ok: false.
|
|
5
|
+
*/
|
|
6
|
+
export declare function verifyAgainstCheckpoint(checkpoint: Checkpoint, projectRoot?: string): Promise<VerificationResult>;
|
|
7
|
+
export declare function verifyLatest(projectRoot?: string): Promise<VerificationResult>;
|
|
8
|
+
export declare function printVerification(result: VerificationResult): void;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { getProjectRoot, getSandboxPath, assertApprovedSandbox, } from "./paths.js";
|
|
2
|
+
import { loadCheckpoint } from "./checkpoint.js";
|
|
3
|
+
import { buildManifest, fingerprintManifestEntries, } from "./manifest.js";
|
|
4
|
+
import { Timer } from "./timing.js";
|
|
5
|
+
function entryKey(e) {
|
|
6
|
+
return [
|
|
7
|
+
e.path,
|
|
8
|
+
e.kind,
|
|
9
|
+
e.hash ?? "",
|
|
10
|
+
String(e.size),
|
|
11
|
+
String(e.mode),
|
|
12
|
+
e.linkTarget ?? "",
|
|
13
|
+
].join("|");
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Compare restored sandbox against a checkpoint manifest.
|
|
17
|
+
* Fail closed: any mismatch → ok: false.
|
|
18
|
+
*/
|
|
19
|
+
export async function verifyAgainstCheckpoint(checkpoint, projectRoot = getProjectRoot()) {
|
|
20
|
+
const totalTimer = new Timer();
|
|
21
|
+
const sandbox = getSandboxPath(projectRoot);
|
|
22
|
+
await assertApprovedSandbox(sandbox, projectRoot);
|
|
23
|
+
const manifestTimer = new Timer();
|
|
24
|
+
const actual = await buildManifest(sandbox, "sandbox-test-project");
|
|
25
|
+
const manifestMs = manifestTimer.elapsedMs();
|
|
26
|
+
const compareTimer = new Timer();
|
|
27
|
+
const mismatches = [];
|
|
28
|
+
const expectedMap = new Map(checkpoint.manifest.entries.map((e) => [e.path, e]));
|
|
29
|
+
const actualMap = new Map(actual.entries.map((e) => [e.path, e]));
|
|
30
|
+
for (const [p, exp] of expectedMap) {
|
|
31
|
+
const act = actualMap.get(p);
|
|
32
|
+
if (!act) {
|
|
33
|
+
mismatches.push({
|
|
34
|
+
path: p,
|
|
35
|
+
reason: "missing in restored tree",
|
|
36
|
+
expected: entryKey(exp),
|
|
37
|
+
});
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (entryKey(exp) !== entryKey(act)) {
|
|
41
|
+
const reasons = [];
|
|
42
|
+
if (exp.kind !== act.kind)
|
|
43
|
+
reasons.push(`kind ${exp.kind}→${act.kind}`);
|
|
44
|
+
if (exp.hash !== act.hash)
|
|
45
|
+
reasons.push("hash mismatch");
|
|
46
|
+
if (exp.size !== act.size)
|
|
47
|
+
reasons.push(`size ${exp.size}→${act.size}`);
|
|
48
|
+
if (exp.mode !== act.mode)
|
|
49
|
+
reasons.push(`mode ${exp.mode}→${act.mode}`);
|
|
50
|
+
if (exp.linkTarget !== act.linkTarget)
|
|
51
|
+
reasons.push("linkTarget mismatch");
|
|
52
|
+
mismatches.push({
|
|
53
|
+
path: p,
|
|
54
|
+
reason: reasons.join(", ") || "entry mismatch",
|
|
55
|
+
expected: entryKey(exp),
|
|
56
|
+
actual: entryKey(act),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
for (const [p, act] of actualMap) {
|
|
61
|
+
if (!expectedMap.has(p)) {
|
|
62
|
+
mismatches.push({
|
|
63
|
+
path: p,
|
|
64
|
+
reason: "unexpected path after restore",
|
|
65
|
+
actual: entryKey(act),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Fingerprint compare (authoritative whole-tree check)
|
|
70
|
+
const expectedFingerprint = checkpoint.manifest.fingerprint;
|
|
71
|
+
const actualFingerprint = fingerprintManifestEntries(actual.entries);
|
|
72
|
+
if (expectedFingerprint !== actualFingerprint && mismatches.length === 0) {
|
|
73
|
+
mismatches.push({
|
|
74
|
+
path: "(fingerprint)",
|
|
75
|
+
reason: "fingerprint mismatch without per-entry diffs (canonicalization bug?)",
|
|
76
|
+
expected: expectedFingerprint,
|
|
77
|
+
actual: actualFingerprint,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const compareMs = compareTimer.elapsedMs();
|
|
81
|
+
const filesChecked = checkpoint.manifest.entries.length;
|
|
82
|
+
return {
|
|
83
|
+
ok: mismatches.length === 0 && expectedFingerprint === actualFingerprint,
|
|
84
|
+
filesChecked,
|
|
85
|
+
expectedFingerprint,
|
|
86
|
+
actualFingerprint,
|
|
87
|
+
mismatches,
|
|
88
|
+
timingsMs: {
|
|
89
|
+
manifest: manifestMs,
|
|
90
|
+
compare: compareMs,
|
|
91
|
+
total: totalTimer.elapsedMs(),
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export async function verifyLatest(projectRoot = getProjectRoot()) {
|
|
96
|
+
const checkpoint = await loadCheckpoint("latest", projectRoot);
|
|
97
|
+
return verifyAgainstCheckpoint(checkpoint, projectRoot);
|
|
98
|
+
}
|
|
99
|
+
export function printVerification(result) {
|
|
100
|
+
console.log("REWIND VERIFICATION");
|
|
101
|
+
console.log("");
|
|
102
|
+
console.log(`Files checked: ${result.filesChecked}`);
|
|
103
|
+
console.log("");
|
|
104
|
+
console.log("Expected manifest hash:");
|
|
105
|
+
console.log(result.expectedFingerprint);
|
|
106
|
+
console.log("");
|
|
107
|
+
console.log("Actual manifest hash:");
|
|
108
|
+
console.log(result.actualFingerprint);
|
|
109
|
+
console.log("");
|
|
110
|
+
console.log("RESULT:");
|
|
111
|
+
console.log("");
|
|
112
|
+
if (result.ok) {
|
|
113
|
+
console.log("EXACT MATCH");
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
console.log("RESTORE FAILED");
|
|
117
|
+
console.log("");
|
|
118
|
+
for (const m of result.mismatches.slice(0, 50)) {
|
|
119
|
+
console.log(`- ${m.path}: ${m.reason}`);
|
|
120
|
+
}
|
|
121
|
+
if (result.mismatches.length > 50) {
|
|
122
|
+
console.log(`… and ${result.mismatches.length - 50} more`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/verify.ts"],"names":[],"mappings":"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"}
|
package/docs/ADAPTERS.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Adapters (V5)
|
|
2
|
+
|
|
3
|
+
## EXISTS
|
|
4
|
+
|
|
5
|
+
### Interface (`RewindAdapter`)
|
|
6
|
+
|
|
7
|
+
- `identifyTarget` / `validateOperation`
|
|
8
|
+
- `inspectCurrent` / `captureBefore`
|
|
9
|
+
- `execute` / `captureAfter`
|
|
10
|
+
- `compareExpectedCurrent` / `reverse`
|
|
11
|
+
- `describe`
|
|
12
|
+
|
|
13
|
+
### FilesystemAdapter
|
|
14
|
+
|
|
15
|
+
**Shipped.** Wraps proven V2 primitives:
|
|
16
|
+
|
|
17
|
+
- create / write / delete / move
|
|
18
|
+
- create_directory / delete_directory (subtree capture)
|
|
19
|
+
- CAS objects under `.rewind/objects`
|
|
20
|
+
- conflict-aware reverse via snapshot restore
|
|
21
|
+
|
|
22
|
+
MCP tools and `mediate*` functions call the transaction engine, which calls this adapter. There is one authoritative mediated FS path.
|
|
23
|
+
|
|
24
|
+
### Registry
|
|
25
|
+
|
|
26
|
+
`getAdapter("filesystem")` — only registered adapter in V5.
|
|
27
|
+
|
|
28
|
+
## PLANNED (not implemented)
|
|
29
|
+
|
|
30
|
+
| Adapter | Notes |
|
|
31
|
+
|---------|--------|
|
|
32
|
+
| shell | Command capture + cwd/env; often COMPENSATABLE/IRREVERSIBLE |
|
|
33
|
+
| database | Txn-aware reverse or compensating SQL |
|
|
34
|
+
| http/api | Idempotency keys + compensation |
|
|
35
|
+
| browser | DOM/navigation actions |
|
|
36
|
+
|
|
37
|
+
Do not treat these as available APIs.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Approvals (V5)
|
|
2
|
+
|
|
3
|
+
## EXISTS
|
|
4
|
+
|
|
5
|
+
When permission returns **ASK**:
|
|
6
|
+
|
|
7
|
+
1. Capture validated intent + before state
|
|
8
|
+
2. Fingerprint current target state
|
|
9
|
+
3. Persist `.rewind/approvals/appr_….json`
|
|
10
|
+
4. Transaction status = `AWAITING_APPROVAL`
|
|
11
|
+
5. **No mutation yet**
|
|
12
|
+
|
|
13
|
+
Human may:
|
|
14
|
+
|
|
15
|
+
- `rewind approvals`
|
|
16
|
+
- `rewind approve <appr_…>` — revalidate fingerprint + operation, then execute
|
|
17
|
+
- `rewind deny <appr_…>` — mark DENIED, never execute
|
|
18
|
+
|
|
19
|
+
### TOCTOU
|
|
20
|
+
|
|
21
|
+
If target state changes while waiting, approve **refuses** and does not apply the stale intent.
|
|
22
|
+
|
|
23
|
+
### UI / API
|
|
24
|
+
|
|
25
|
+
Local API: `GET /v1/approvals`, `POST /v1/approvals/:id/approve|deny`
|
|
26
|
+
Minimal control app shows Approve/Deny on real pending rows.
|
|
27
|
+
|
|
28
|
+
## PLANNED
|
|
29
|
+
|
|
30
|
+
- Rich desktop approval UX
|
|
31
|
+
- Push notifications
|
|
32
|
+
- Multi-approver / quorum (not designed yet)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Architecture (V5)
|
|
2
|
+
|
|
3
|
+
## EXISTS
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
MCP / CLI / Local API
|
|
7
|
+
│
|
|
8
|
+
▼
|
|
9
|
+
┌───────────────────┐
|
|
10
|
+
│ Transaction Engine │ prepare → permission → adapter → mutate
|
|
11
|
+
└─────────┬─────────┘
|
|
12
|
+
│
|
|
13
|
+
┌──────┴──────┐
|
|
14
|
+
▼ ▼
|
|
15
|
+
Permission Adapter Registry
|
|
16
|
+
Engine │
|
|
17
|
+
▼
|
|
18
|
+
FilesystemAdapter ← wraps action/capture|mutate|objects
|
|
19
|
+
│
|
|
20
|
+
▼
|
|
21
|
+
.rewind/actions (v2 JSON, append index)
|
|
22
|
+
.rewind/transactions (v5 overlays)
|
|
23
|
+
.rewind/objects (CAS)
|
|
24
|
+
.rewind/approvals
|
|
25
|
+
.rewind/policy/permissions.json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Layers
|
|
29
|
+
|
|
30
|
+
| Layer | Path | Role |
|
|
31
|
+
|-------|------|------|
|
|
32
|
+
| Core types | `src/core/types.ts` | Transaction, Actor, Authority, reversibility |
|
|
33
|
+
| Lifecycle | `src/core/lifecycle.ts` | Validated status transitions |
|
|
34
|
+
| Engine | `src/core/engine.ts` | Single mediated execution + undo + approval |
|
|
35
|
+
| Compatibility | `src/core/compatibility.ts` | ActionRecord v2 ↔ Transaction v5 |
|
|
36
|
+
| Adapters | `src/adapters/*` | RewindAdapter + FilesystemAdapter |
|
|
37
|
+
| Permission | `src/permission/*` | ALLOW / ASK / DENY policies |
|
|
38
|
+
| Approval | `src/approval/*` | Pending intents + TOCTOU fingerprint |
|
|
39
|
+
| Ledger query | `src/ledger/query.ts` | History / formatting |
|
|
40
|
+
| Entitlement | `src/entitlement/provider.ts` | Pluggable license boundary |
|
|
41
|
+
| Local API | `src/api/server.ts` | Loopback control API + minimal UI |
|
|
42
|
+
| MCP | `src/mcp/server.ts` | Tools → mediate* → engine |
|
|
43
|
+
| V0–V4 | `src/sandbox`, `session`, `enforcement`, `protect` | Unchanged proofs |
|
|
44
|
+
|
|
45
|
+
## PLANNED
|
|
46
|
+
|
|
47
|
+
- Additional adapters (shell, database, HTTP, browser)
|
|
48
|
+
- Stronger OS-level enforcement so bypass is hard
|
|
49
|
+
- Delegated authority with real principals
|
|
50
|
+
- Paid `EntitlementProvider` implementation (not in core)
|
|
51
|
+
|
|
52
|
+
## Design rules
|
|
53
|
+
|
|
54
|
+
- Do not duplicate FS mutation paths — adapter calls existing `action/mutate`.
|
|
55
|
+
- Do not claim COMPENSATABLE unless an adapter implements compensation.
|
|
56
|
+
- Do not fabricate human principals; authority may be UNKNOWN.
|
|
57
|
+
- Crash recovery marks PREPARED/EXECUTING/UNDOING as FAILED; never wipes AWAITING_APPROVAL.
|