@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,170 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { mkdir, writeFile, readFile, readdir, rename, } from "node:fs/promises";
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
4
|
+
import { getProtectDir, getProtectSessionsDir, getProtectActionsDir, } from "./root-paths.js";
|
|
5
|
+
import { PathSafetyError } from "../paths.js";
|
|
6
|
+
async function writeJsonAtomic(file, data) {
|
|
7
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
8
|
+
const tmp = `${file}.tmp-${process.pid}`;
|
|
9
|
+
await writeFile(tmp, JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
10
|
+
await rename(tmp, file);
|
|
11
|
+
}
|
|
12
|
+
export function newProtectedSessionId() {
|
|
13
|
+
return `rw_${randomBytes(8).toString("hex")}`;
|
|
14
|
+
}
|
|
15
|
+
export function newProtectActionId() {
|
|
16
|
+
return `act_${randomBytes(8).toString("hex")}`;
|
|
17
|
+
}
|
|
18
|
+
export function protectStatePath(projectRoot) {
|
|
19
|
+
return path.join(getProtectDir(projectRoot), "state.json");
|
|
20
|
+
}
|
|
21
|
+
export function protectConfigPath(projectRoot) {
|
|
22
|
+
return path.join(getProtectDir(projectRoot), "config.json");
|
|
23
|
+
}
|
|
24
|
+
export async function loadProtectState(projectRoot) {
|
|
25
|
+
try {
|
|
26
|
+
const raw = await readFile(protectStatePath(projectRoot), "utf8");
|
|
27
|
+
return JSON.parse(raw);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export async function saveProtectState(state, projectRoot) {
|
|
34
|
+
await writeJsonAtomic(protectStatePath(projectRoot), state);
|
|
35
|
+
}
|
|
36
|
+
export async function loadProtectConfig(projectRoot) {
|
|
37
|
+
try {
|
|
38
|
+
const raw = await readFile(protectConfigPath(projectRoot), "utf8");
|
|
39
|
+
return JSON.parse(raw);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export async function saveProtectConfig(config, projectRoot) {
|
|
46
|
+
await writeJsonAtomic(protectConfigPath(projectRoot), config);
|
|
47
|
+
}
|
|
48
|
+
export function sessionPath(id, projectRoot) {
|
|
49
|
+
if (!/^rw_[A-Za-z0-9]+$/.test(id)) {
|
|
50
|
+
throw new PathSafetyError(`Invalid session id: ${id}`);
|
|
51
|
+
}
|
|
52
|
+
return path.join(getProtectSessionsDir(projectRoot), `${id}.json`);
|
|
53
|
+
}
|
|
54
|
+
export async function saveSession(session, projectRoot) {
|
|
55
|
+
await mkdir(getProtectSessionsDir(projectRoot), { recursive: true });
|
|
56
|
+
await writeJsonAtomic(sessionPath(session.id, projectRoot), session);
|
|
57
|
+
await writeJsonAtomic(path.join(getProtectSessionsDir(projectRoot), "active.json"), { id: session.id });
|
|
58
|
+
await writeJsonAtomic(path.join(getProtectSessionsDir(projectRoot), "latest.json"), { id: session.id });
|
|
59
|
+
}
|
|
60
|
+
export async function loadSession(id, projectRoot) {
|
|
61
|
+
try {
|
|
62
|
+
const raw = await readFile(sessionPath(id, projectRoot), "utf8");
|
|
63
|
+
return JSON.parse(raw);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
throw new PathSafetyError(`Session not found: ${id}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export async function loadActiveSession(projectRoot) {
|
|
70
|
+
try {
|
|
71
|
+
const raw = await readFile(path.join(getProtectSessionsDir(projectRoot), "active.json"), "utf8");
|
|
72
|
+
const { id } = JSON.parse(raw);
|
|
73
|
+
return loadSession(id, projectRoot);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export async function listSessions(projectRoot) {
|
|
80
|
+
const dir = getProtectSessionsDir(projectRoot);
|
|
81
|
+
let names;
|
|
82
|
+
try {
|
|
83
|
+
names = await readdir(dir);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
const out = [];
|
|
89
|
+
for (const n of names) {
|
|
90
|
+
if (!n.startsWith("rw_") || !n.endsWith(".json"))
|
|
91
|
+
continue;
|
|
92
|
+
try {
|
|
93
|
+
out.push(await loadSession(n.replace(/\.json$/, ""), projectRoot));
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
/* skip */
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
out.sort((a, b) => a.startedAt.localeCompare(b.startedAt));
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
export function protectActionPath(id, projectRoot) {
|
|
103
|
+
if (!/^act_[A-Za-z0-9]+$/.test(id)) {
|
|
104
|
+
throw new PathSafetyError(`Invalid action id: ${id}`);
|
|
105
|
+
}
|
|
106
|
+
return path.join(getProtectActionsDir(projectRoot), `${id}.json`);
|
|
107
|
+
}
|
|
108
|
+
export async function saveProtectAction(action, projectRoot) {
|
|
109
|
+
await mkdir(getProtectActionsDir(projectRoot), { recursive: true });
|
|
110
|
+
await writeJsonAtomic(protectActionPath(action.id, projectRoot), action);
|
|
111
|
+
const { appendFile } = await import("node:fs/promises");
|
|
112
|
+
await appendFile(path.join(getProtectActionsDir(projectRoot), "index.jsonl"), JSON.stringify({
|
|
113
|
+
id: action.id,
|
|
114
|
+
protectedSessionId: action.protectedSessionId,
|
|
115
|
+
sequence: action.sequence,
|
|
116
|
+
tool: action.tool,
|
|
117
|
+
target: action.target,
|
|
118
|
+
status: action.status,
|
|
119
|
+
at: action.completedAt ?? action.requestedAt,
|
|
120
|
+
}) + "\n", "utf8").catch(() => undefined);
|
|
121
|
+
await writeJsonAtomic(path.join(getProtectActionsDir(projectRoot), "latest.json"), { id: action.id });
|
|
122
|
+
}
|
|
123
|
+
export async function loadProtectAction(id, projectRoot) {
|
|
124
|
+
try {
|
|
125
|
+
const raw = await readFile(protectActionPath(id, projectRoot), "utf8");
|
|
126
|
+
return JSON.parse(raw);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
throw new PathSafetyError(`Action not found: ${id}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
export async function listProtectActions(projectRoot) {
|
|
133
|
+
const dir = getProtectActionsDir(projectRoot);
|
|
134
|
+
let names;
|
|
135
|
+
try {
|
|
136
|
+
names = await readdir(dir);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
const out = [];
|
|
142
|
+
for (const n of names) {
|
|
143
|
+
if (!n.startsWith("act_") || !n.endsWith(".json"))
|
|
144
|
+
continue;
|
|
145
|
+
try {
|
|
146
|
+
out.push(await loadProtectAction(n.replace(/\.json$/, ""), projectRoot));
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
/* skip corrupt */
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
out.sort((a, b) => a.sequence - b.sequence || a.requestedAt.localeCompare(b.requestedAt));
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
export async function listIncompleteProtectActions(projectRoot) {
|
|
156
|
+
const all = await listProtectActions(projectRoot);
|
|
157
|
+
return all.filter((a) => a.status === "PREPARED" || a.status === "EXECUTING");
|
|
158
|
+
}
|
|
159
|
+
export async function markIncompleteProtectFailed(projectRoot) {
|
|
160
|
+
const incomplete = await listIncompleteProtectActions(projectRoot);
|
|
161
|
+
for (const a of incomplete) {
|
|
162
|
+
a.status = "FAILED";
|
|
163
|
+
a.error =
|
|
164
|
+
a.error ?? "Incomplete action recovered (crash / missing post-hook)";
|
|
165
|
+
a.completedAt = new Date().toISOString();
|
|
166
|
+
await writeJsonAtomic(protectActionPath(a.id, projectRoot), a);
|
|
167
|
+
}
|
|
168
|
+
return incomplete.length;
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/protect/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/** V4 protected Cursor session types. */
|
|
2
|
+
import type { EntrySnapshot } from "../action/types.js";
|
|
3
|
+
export type ProtectedSessionStatus = "ACTIVE" | "COMPLETED" | "PARTIALLY_UNDONE" | "UNDONE" | "CONFLICTED" | "ABORTED";
|
|
4
|
+
export type TrustState = "AUTHORITATIVE" | "UNTRUSTED";
|
|
5
|
+
export interface CursorBinding {
|
|
6
|
+
conversationId?: string;
|
|
7
|
+
sessionId?: string;
|
|
8
|
+
generationId?: string;
|
|
9
|
+
cursorVersion?: string;
|
|
10
|
+
model?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ProtectedSession {
|
|
13
|
+
id: string;
|
|
14
|
+
version: 4;
|
|
15
|
+
projectRoot: string;
|
|
16
|
+
startedAt: string;
|
|
17
|
+
endedAt: string | null;
|
|
18
|
+
status: ProtectedSessionStatus;
|
|
19
|
+
client: "Cursor";
|
|
20
|
+
cursor: CursorBinding;
|
|
21
|
+
actionIds: string[];
|
|
22
|
+
policyEventIds: string[];
|
|
23
|
+
blockedShellCount: number;
|
|
24
|
+
baselineFingerprint: string;
|
|
25
|
+
finalFingerprint: string | null;
|
|
26
|
+
trust: TrustState;
|
|
27
|
+
architecture: "MODEL_A_INTERCEPT";
|
|
28
|
+
}
|
|
29
|
+
export interface ProtectState {
|
|
30
|
+
version: 4;
|
|
31
|
+
protected: boolean;
|
|
32
|
+
projectRoot: string;
|
|
33
|
+
activeSessionId: string | null;
|
|
34
|
+
rewindPackageRoot: string;
|
|
35
|
+
hooksInstalled: boolean;
|
|
36
|
+
failClosed: boolean;
|
|
37
|
+
installedAt: string;
|
|
38
|
+
updatedAt: string;
|
|
39
|
+
/** Marker entries we added to hooks.json / mcp.json (for safe unprotect). */
|
|
40
|
+
managedHookEvents: string[];
|
|
41
|
+
managedMcpServerName: string;
|
|
42
|
+
}
|
|
43
|
+
export interface ProtectConfig {
|
|
44
|
+
version: 4;
|
|
45
|
+
projectRoot: string;
|
|
46
|
+
rewindPackageRoot: string;
|
|
47
|
+
hookModule: string;
|
|
48
|
+
architecture: "MODEL_A_INTERCEPT";
|
|
49
|
+
}
|
|
50
|
+
export type AuditEventType = "PROTECTED_SESSION_STARTED" | "PROTECTED_SESSION_ENDED" | "CURSOR_SESSION_STARTED" | "TOOL_REQUESTED" | "ACTION_PREPARED" | "ACTION_COMPLETED" | "ACTION_FAILED" | "SHELL_BLOCKED" | "SHELL_ALLOWED" | "POLICY_VIOLATION" | "UNKNOWN_MUTATION" | "ACTION_UNDONE" | "UNDO_CONFLICT" | "SESSION_UNDO_STARTED" | "SESSION_UNDO_FINISHED" | "CRASH_RECOVERY" | "HOOK_DENY" | "PROTECT_ENABLED" | "PROTECT_DISABLED";
|
|
51
|
+
export interface AuditEvent {
|
|
52
|
+
type: AuditEventType;
|
|
53
|
+
time: string;
|
|
54
|
+
sessionId?: string;
|
|
55
|
+
actionId?: string;
|
|
56
|
+
tool?: string;
|
|
57
|
+
path?: string;
|
|
58
|
+
detail?: string;
|
|
59
|
+
[key: string]: unknown;
|
|
60
|
+
}
|
|
61
|
+
export type CursorNativeTool = "Write" | "Delete" | "StrReplace" | "EditNotebook" | "DeleteNotebookCell";
|
|
62
|
+
export type ProtectActionTool = "cursor_Write" | "cursor_Delete" | "cursor_StrReplace" | "cursor_EditNotebook";
|
|
63
|
+
export interface ProtectActionRecord {
|
|
64
|
+
id: string;
|
|
65
|
+
version: 4;
|
|
66
|
+
protectedSessionId: string;
|
|
67
|
+
sequence: number;
|
|
68
|
+
source: "CURSOR_HOOK";
|
|
69
|
+
tool: ProtectActionTool;
|
|
70
|
+
/** Provenance: Cursor Agent requested this tool during protected session. */
|
|
71
|
+
provenance: "cursor_agent_tool_request";
|
|
72
|
+
requestedAt: string;
|
|
73
|
+
completedAt: string | null;
|
|
74
|
+
target: string;
|
|
75
|
+
targets: string[];
|
|
76
|
+
targetSecondary: string | null;
|
|
77
|
+
before: EntrySnapshot;
|
|
78
|
+
after: EntrySnapshot;
|
|
79
|
+
beforeSecondary: EntrySnapshot | null;
|
|
80
|
+
afterSecondary: EntrySnapshot | null;
|
|
81
|
+
reversibility: "REVERSIBLE" | "IRREVERSIBLE";
|
|
82
|
+
status: "PREPARED" | "EXECUTING" | "COMPLETED" | "FAILED" | "UNDONE" | "CONFLICTED";
|
|
83
|
+
client: {
|
|
84
|
+
name: "Cursor";
|
|
85
|
+
version: string;
|
|
86
|
+
};
|
|
87
|
+
cursor: CursorBinding;
|
|
88
|
+
toolCallId: string | null;
|
|
89
|
+
error: string | null;
|
|
90
|
+
undoneAt: string | null;
|
|
91
|
+
undoVerification: {
|
|
92
|
+
expectedAfterHash: string | null;
|
|
93
|
+
actualBeforeUndoHash: string | null;
|
|
94
|
+
restoredHash: string | null;
|
|
95
|
+
ok: boolean;
|
|
96
|
+
} | null;
|
|
97
|
+
conflict: {
|
|
98
|
+
expectedHash: string | null;
|
|
99
|
+
actualHash: string | null;
|
|
100
|
+
message: string;
|
|
101
|
+
} | null;
|
|
102
|
+
}
|
|
103
|
+
export interface SessionUndoResult {
|
|
104
|
+
sessionId: string;
|
|
105
|
+
undone: number;
|
|
106
|
+
conflicts: string[];
|
|
107
|
+
failed: number;
|
|
108
|
+
status: ProtectedSessionStatus;
|
|
109
|
+
message: string;
|
|
110
|
+
}
|
|
111
|
+
export interface HookDecision {
|
|
112
|
+
permission: "allow" | "deny" | "ask";
|
|
113
|
+
user_message?: string;
|
|
114
|
+
agent_message?: string;
|
|
115
|
+
additional_context?: string;
|
|
116
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/protect/types.ts"],"names":[],"mappings":"AAAA,yCAAyC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ProtectActionRecord, SessionUndoResult } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Undo a single COMPLETED protect action iff current == after.
|
|
4
|
+
*/
|
|
5
|
+
export declare function undoProtectAction(actionId: string, projectRoot: string): Promise<{
|
|
6
|
+
ok: boolean;
|
|
7
|
+
conflict: boolean;
|
|
8
|
+
action: ProtectActionRecord;
|
|
9
|
+
message: string;
|
|
10
|
+
}>;
|
|
11
|
+
/**
|
|
12
|
+
* Session undo: newest → oldest. Preserve conflicts; continue independent actions.
|
|
13
|
+
*/
|
|
14
|
+
export declare function undoProtectedSession(sessionId: string, projectRoot: string): Promise<SessionUndoResult>;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { captureProtectedEntry, restoreProtectedSnapshot, snapshotIdentityHash, } from "./capture.js";
|
|
2
|
+
import { appendAuditEvent } from "./audit.js";
|
|
3
|
+
import { loadProtectAction, loadSession, listProtectActions, saveProtectAction, saveSession, } from "./store.js";
|
|
4
|
+
import { fingerprintProject } from "./fingerprint.js";
|
|
5
|
+
async function currentMatchesAfter(action, projectRoot) {
|
|
6
|
+
const now = await captureProtectedEntry(action.target, projectRoot, {
|
|
7
|
+
storeContent: true,
|
|
8
|
+
});
|
|
9
|
+
const expected = snapshotIdentityHash(action.after);
|
|
10
|
+
const actual = snapshotIdentityHash(now);
|
|
11
|
+
return { ok: expected === actual, expected, actual };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Undo a single COMPLETED protect action iff current == after.
|
|
15
|
+
*/
|
|
16
|
+
export async function undoProtectAction(actionId, projectRoot) {
|
|
17
|
+
const action = await loadProtectAction(actionId, projectRoot);
|
|
18
|
+
if (action.status === "UNDONE") {
|
|
19
|
+
return {
|
|
20
|
+
ok: false,
|
|
21
|
+
conflict: false,
|
|
22
|
+
action,
|
|
23
|
+
message: `Action ${actionId} already UNDONE`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (action.status !== "COMPLETED") {
|
|
27
|
+
return {
|
|
28
|
+
ok: false,
|
|
29
|
+
conflict: false,
|
|
30
|
+
action,
|
|
31
|
+
message: `Action ${actionId} status is ${action.status}`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const match = await currentMatchesAfter(action, projectRoot);
|
|
35
|
+
if (!match.ok) {
|
|
36
|
+
action.status = "CONFLICTED";
|
|
37
|
+
action.conflict = {
|
|
38
|
+
expectedHash: match.expected,
|
|
39
|
+
actualHash: match.actual,
|
|
40
|
+
message: `UNDO CONFLICT\n\nAction: ${action.id}\nTarget: ${action.target}\n\n` +
|
|
41
|
+
`Expected after-hash: ${match.expected}\nActual: ${match.actual}\n\n` +
|
|
42
|
+
`Human/newer work was not overwritten.`,
|
|
43
|
+
};
|
|
44
|
+
await saveProtectAction(action, projectRoot);
|
|
45
|
+
await appendAuditEvent({
|
|
46
|
+
type: "UNDO_CONFLICT",
|
|
47
|
+
sessionId: action.protectedSessionId,
|
|
48
|
+
actionId: action.id,
|
|
49
|
+
path: action.target,
|
|
50
|
+
}, projectRoot);
|
|
51
|
+
return {
|
|
52
|
+
ok: false,
|
|
53
|
+
conflict: true,
|
|
54
|
+
action,
|
|
55
|
+
message: action.conflict.message,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
await restoreProtectedSnapshot(action.target, action.before, projectRoot);
|
|
59
|
+
const restored = await captureProtectedEntry(action.target, projectRoot, {
|
|
60
|
+
storeContent: true,
|
|
61
|
+
});
|
|
62
|
+
let ok;
|
|
63
|
+
let restoredHash;
|
|
64
|
+
if (!action.before.exists) {
|
|
65
|
+
ok = !restored.exists;
|
|
66
|
+
restoredHash = restored.exists ? snapshotIdentityHash(restored) : "ABSENT";
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
restoredHash = snapshotIdentityHash(restored);
|
|
70
|
+
ok = restoredHash === snapshotIdentityHash(action.before);
|
|
71
|
+
}
|
|
72
|
+
action.status = ok ? "UNDONE" : "FAILED";
|
|
73
|
+
action.undoneAt = new Date().toISOString();
|
|
74
|
+
action.undoVerification = {
|
|
75
|
+
expectedAfterHash: match.expected,
|
|
76
|
+
actualBeforeUndoHash: match.actual,
|
|
77
|
+
restoredHash,
|
|
78
|
+
ok,
|
|
79
|
+
};
|
|
80
|
+
if (!ok)
|
|
81
|
+
action.error = "Undo verification failed";
|
|
82
|
+
await saveProtectAction(action, projectRoot);
|
|
83
|
+
await appendAuditEvent({
|
|
84
|
+
type: ok ? "ACTION_UNDONE" : "ACTION_FAILED",
|
|
85
|
+
sessionId: action.protectedSessionId,
|
|
86
|
+
actionId: action.id,
|
|
87
|
+
path: action.target,
|
|
88
|
+
}, projectRoot);
|
|
89
|
+
return {
|
|
90
|
+
ok,
|
|
91
|
+
conflict: false,
|
|
92
|
+
action,
|
|
93
|
+
message: ok
|
|
94
|
+
? `Undone ${action.id}: ${action.target}`
|
|
95
|
+
: `Undo verification failed for ${action.id}`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Session undo: newest → oldest. Preserve conflicts; continue independent actions.
|
|
100
|
+
*/
|
|
101
|
+
export async function undoProtectedSession(sessionId, projectRoot) {
|
|
102
|
+
const session = await loadSession(sessionId, projectRoot);
|
|
103
|
+
await appendAuditEvent({
|
|
104
|
+
type: "SESSION_UNDO_STARTED",
|
|
105
|
+
sessionId,
|
|
106
|
+
}, projectRoot);
|
|
107
|
+
const actions = (await listProtectActions(projectRoot))
|
|
108
|
+
.filter((a) => a.protectedSessionId === sessionId && a.status === "COMPLETED")
|
|
109
|
+
.sort((a, b) => b.sequence - a.sequence);
|
|
110
|
+
let undone = 0;
|
|
111
|
+
let failed = 0;
|
|
112
|
+
const conflicts = [];
|
|
113
|
+
for (const a of actions) {
|
|
114
|
+
const result = await undoProtectAction(a.id, projectRoot);
|
|
115
|
+
if (result.conflict) {
|
|
116
|
+
conflicts.push(a.target);
|
|
117
|
+
}
|
|
118
|
+
else if (result.ok) {
|
|
119
|
+
undone += 1;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
failed += 1;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
let status;
|
|
126
|
+
if (conflicts.length > 0 && undone > 0) {
|
|
127
|
+
status = "PARTIALLY_UNDONE";
|
|
128
|
+
}
|
|
129
|
+
else if (conflicts.length > 0 && undone === 0) {
|
|
130
|
+
status = "CONFLICTED";
|
|
131
|
+
}
|
|
132
|
+
else if (failed > 0 && undone > 0) {
|
|
133
|
+
status = "PARTIALLY_UNDONE";
|
|
134
|
+
}
|
|
135
|
+
else if (undone > 0 && conflicts.length === 0 && failed === 0) {
|
|
136
|
+
status = "UNDONE";
|
|
137
|
+
}
|
|
138
|
+
else if (actions.length === 0) {
|
|
139
|
+
status = "UNDONE";
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
status = "CONFLICTED";
|
|
143
|
+
}
|
|
144
|
+
session.status = status;
|
|
145
|
+
session.endedAt = session.endedAt ?? new Date().toISOString();
|
|
146
|
+
session.finalFingerprint = await fingerprintProject(projectRoot);
|
|
147
|
+
await saveSession(session, projectRoot);
|
|
148
|
+
const message = [
|
|
149
|
+
"REWIND SESSION UNDO",
|
|
150
|
+
"",
|
|
151
|
+
`Session: ${sessionId}`,
|
|
152
|
+
"",
|
|
153
|
+
`✓ ${undone} actions undone`,
|
|
154
|
+
conflicts.length
|
|
155
|
+
? `! ${conflicts.length} conflicts preserved`
|
|
156
|
+
: "✓ no conflicts",
|
|
157
|
+
failed ? `✗ ${failed} failed` : null,
|
|
158
|
+
"",
|
|
159
|
+
conflicts.length ? "Conflicts:" : null,
|
|
160
|
+
...conflicts.map((c) => ` ${c}`),
|
|
161
|
+
conflicts.length ? "" : null,
|
|
162
|
+
conflicts.length
|
|
163
|
+
? "Human/newer work was not overwritten."
|
|
164
|
+
: null,
|
|
165
|
+
]
|
|
166
|
+
.filter((l) => l !== null)
|
|
167
|
+
.join("\n");
|
|
168
|
+
await appendAuditEvent({
|
|
169
|
+
type: "SESSION_UNDO_FINISHED",
|
|
170
|
+
sessionId,
|
|
171
|
+
detail: `undone=${undone} conflicts=${conflicts.length} failed=${failed}`,
|
|
172
|
+
}, projectRoot);
|
|
173
|
+
return {
|
|
174
|
+
sessionId,
|
|
175
|
+
undone,
|
|
176
|
+
conflicts,
|
|
177
|
+
failed,
|
|
178
|
+
status,
|
|
179
|
+
message,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=undo-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"undo-session.js","sourceRoot":"","sources":["../../../src/protect/undo-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { buildManifest } from "./manifest.js";
|
|
2
|
+
import type { Checkpoint, VerificationResult } from "./types.js";
|
|
3
|
+
export interface RestoreResult {
|
|
4
|
+
checkpoint: Checkpoint;
|
|
5
|
+
verification: VerificationResult;
|
|
6
|
+
timingsMs: {
|
|
7
|
+
clear: number;
|
|
8
|
+
materialize: number;
|
|
9
|
+
verify: number;
|
|
10
|
+
total: number;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Restore sandbox to checkpoint state.
|
|
15
|
+
*
|
|
16
|
+
* Strategy (correctness-first):
|
|
17
|
+
* 1. Validate target is approved sandbox.
|
|
18
|
+
* 2. Load checkpoint.
|
|
19
|
+
* 3. Remove all current sandbox contents.
|
|
20
|
+
* 4. Materialize files/dirs/symlinks from checkpoint object store.
|
|
21
|
+
* 5. Verify byte-for-byte against checkpoint manifest.
|
|
22
|
+
*
|
|
23
|
+
* If verification fails, throw — never report success.
|
|
24
|
+
*/
|
|
25
|
+
export declare function restoreSandbox(checkpointId?: string | "latest", projectRoot?: string, options?: {
|
|
26
|
+
verify?: boolean;
|
|
27
|
+
}): Promise<RestoreResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Delete everything inside the sandbox without deleting the sandbox root.
|
|
30
|
+
* Refuses if any entry resolves outside the sandbox.
|
|
31
|
+
*/
|
|
32
|
+
export declare function clearSandboxContents(sandbox: string, projectRoot?: string): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Incremental restore alternative used for analysis — not the primary path.
|
|
35
|
+
* Kept for potential future use / tests of selective apply.
|
|
36
|
+
*/
|
|
37
|
+
export declare function listExtraneousPaths(sandbox: string, checkpointPaths: Set<string>): Promise<string[]>;
|
|
38
|
+
export declare function sandboxExists(projectRoot?: string): Promise<boolean>;
|
|
39
|
+
export { buildManifest };
|