@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
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,823 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { loadRewindEnv } from "./commercial/env.js";
|
|
3
|
+
loadRewindEnv();
|
|
4
|
+
import { createSandbox } from "./sandbox.js";
|
|
5
|
+
import { createCheckpoint, loadCheckpoint } from "./checkpoint.js";
|
|
6
|
+
import { destroySandbox } from "./destroy.js";
|
|
7
|
+
import { recordDiffTransaction, printTransactionSummary } from "./diff.js";
|
|
8
|
+
import { restoreSandbox } from "./restore.js";
|
|
9
|
+
import { verifyLatest, printVerification } from "./verify.js";
|
|
10
|
+
import { buildManifest } from "./manifest.js";
|
|
11
|
+
import { getProjectRoot, getSandboxPath, PathSafetyError, } from "./paths.js";
|
|
12
|
+
import { formatMs } from "./timing.js";
|
|
13
|
+
import { startSession, stopSession, sessionStatus, rewindSession, showSessionReport, printProcessProbe, demoV1, } from "./session/index.js";
|
|
14
|
+
import { runMcpServer, printActionCli, printMcpSessionReport, } from "./mcp/server.js";
|
|
15
|
+
import { undoAction, formatUndoConflict } from "./action/undo.js";
|
|
16
|
+
import { markIncompleteFailed } from "./action/store.js";
|
|
17
|
+
import { enableEnforcement, disableEnforcement, loadEnforcementState, runUnderWriteDenySeatbelt, writeSeatbeltProfile, } from "./enforcement/setup.js";
|
|
18
|
+
import { probeDirectWriteBlocked, unlockEnforcedWritable, } from "./enforcement/readonly.js";
|
|
19
|
+
import { listPolicyEvents } from "./enforcement/policy.js";
|
|
20
|
+
import { listTrustRecords } from "./enforcement/trust.js";
|
|
21
|
+
import { measureMs, formatPerfTable } from "./enforcement/perf.js";
|
|
22
|
+
import { defaultMediateContext, mediateWriteFile, } from "./action/mediate.js";
|
|
23
|
+
import { ENFORCED_SANDBOX_DIR_NAME, getEnforcedSandboxPath } from "./paths.js";
|
|
24
|
+
import { readFile } from "node:fs/promises";
|
|
25
|
+
import path from "node:path";
|
|
26
|
+
import { protectProject, unprotectProject, resolveProtectTarget, formatProtectStatus, formatSessionsList, formatActionsList, undoProtectAction, undoProtectedSession, loadProtectState, } from "./protect/index.js";
|
|
27
|
+
import { cliStatus, cliTransactions, cliTransaction, cliUndo, cliApprovals, cliApprove, cliDeny, cliPolicy, cliPolicyInit, } from "./cli/v5.js";
|
|
28
|
+
import { startLocalApiServer } from "./api/server.js";
|
|
29
|
+
import { PRODUCT_VERSION, runSetup, runDoctor, runUninstall, versionText, } from "./product/setup.js";
|
|
30
|
+
import { getAuthProvider, loadSession } from "./commercial/auth/provider.js";
|
|
31
|
+
import { createCheckoutSession, confirmSimulatedCheckout, LOCAL_API_DEFAULT_PORT, } from "./commercial/stripe/checkout.js";
|
|
32
|
+
import { issueEntitlementForCurrentUser, SignedEntitlementProvider, } from "./commercial/entitlement/provider.js";
|
|
33
|
+
import { setEntitlementProvider } from "./entitlement/provider.js";
|
|
34
|
+
import { execFile } from "node:child_process";
|
|
35
|
+
function printUsage() {
|
|
36
|
+
console.log(`REWIND ${PRODUCT_VERSION} — Undo for AI agents (REWIND Personal)
|
|
37
|
+
|
|
38
|
+
Setup: setup | doctor | open | uninstall | version | update
|
|
39
|
+
Account: login | logout | subscribe | whoami
|
|
40
|
+
Product: status | transactions | transaction <id> | undo <id>
|
|
41
|
+
approvals | approve <id> | deny <id> | policy
|
|
42
|
+
Service: api | mcp
|
|
43
|
+
Legacy: protect | enforce | session | sandbox:create | …
|
|
44
|
+
|
|
45
|
+
Honesty: REWIND CONNECTED means mediated actions are recorded.
|
|
46
|
+
Native agent writes outside REWIND are not authoritative transactions.
|
|
47
|
+
`);
|
|
48
|
+
}
|
|
49
|
+
async function cmdSandboxCreate() {
|
|
50
|
+
const result = await createSandbox();
|
|
51
|
+
console.log("REWIND");
|
|
52
|
+
console.log("");
|
|
53
|
+
console.log("Disposable sandbox created.");
|
|
54
|
+
console.log(`Root: ${result.root}`);
|
|
55
|
+
console.log(`Files: ${result.fileCount}`);
|
|
56
|
+
console.log(`Directories: ${result.directoryCount}`);
|
|
57
|
+
console.log(`Symlinks: ${result.symlinkCount}`);
|
|
58
|
+
console.log(`Total entries: ${result.totalEntries}`);
|
|
59
|
+
}
|
|
60
|
+
async function cmdCheckpoint() {
|
|
61
|
+
const cp = await createCheckpoint();
|
|
62
|
+
console.log("REWIND");
|
|
63
|
+
console.log("");
|
|
64
|
+
console.log("Checkpoint created.");
|
|
65
|
+
console.log(`ID: ${cp.meta.id}`);
|
|
66
|
+
console.log(`Fingerprint: ${cp.meta.fingerprint}`);
|
|
67
|
+
console.log(`Files: ${cp.meta.fileCount}`);
|
|
68
|
+
console.log(`Directories: ${cp.meta.directoryCount}`);
|
|
69
|
+
console.log(`Symlinks: ${cp.meta.symlinkCount}`);
|
|
70
|
+
console.log(`Timings: manifest ${formatMs(cp.meta.timingsMs.manifest)}, store ${formatMs(cp.meta.timingsMs.contentStore)}, total ${formatMs(cp.meta.timingsMs.total)}`);
|
|
71
|
+
}
|
|
72
|
+
async function cmdDestroy() {
|
|
73
|
+
const result = await destroySandbox();
|
|
74
|
+
console.log("");
|
|
75
|
+
console.log("Chaos complete.");
|
|
76
|
+
console.log(`Modified: ${result.modified}`);
|
|
77
|
+
console.log(`Deleted: ${result.deleted}`);
|
|
78
|
+
console.log(`Created: ${result.created}`);
|
|
79
|
+
console.log(`Renamed: ${result.renamed}`);
|
|
80
|
+
console.log(`Moved: ${result.moved}`);
|
|
81
|
+
console.log(`Chmod: ${result.permissionChanges}`);
|
|
82
|
+
console.log(`Actions: ${result.totalActions}`);
|
|
83
|
+
console.log(`Time: ${formatMs(result.timingsMs)}`);
|
|
84
|
+
}
|
|
85
|
+
async function cmdDiff() {
|
|
86
|
+
const projectRoot = getProjectRoot();
|
|
87
|
+
const sandbox = getSandboxPath(projectRoot);
|
|
88
|
+
const cp = await loadCheckpoint("latest", projectRoot);
|
|
89
|
+
const current = await buildManifest(sandbox, "sandbox-test-project");
|
|
90
|
+
const tx = await recordDiffTransaction(cp.manifest, current, cp.meta.id, sandbox, projectRoot);
|
|
91
|
+
printTransactionSummary(tx);
|
|
92
|
+
console.log("");
|
|
93
|
+
console.log(`Transaction ID: ${tx.id}`);
|
|
94
|
+
console.log(`Diff time: ${formatMs(tx.timingsMs.diff)}`);
|
|
95
|
+
console.log("");
|
|
96
|
+
console.log("Sample changes:");
|
|
97
|
+
for (const c of tx.changes.slice(0, 25)) {
|
|
98
|
+
const from = c.pathBefore ?? "—";
|
|
99
|
+
const to = c.pathAfter ?? "—";
|
|
100
|
+
if (c.type === "RENAMED" || c.type === "MOVED") {
|
|
101
|
+
console.log(` ${c.type.padEnd(8)} ${from} → ${to}`);
|
|
102
|
+
}
|
|
103
|
+
else if (c.type === "DELETED") {
|
|
104
|
+
console.log(` ${c.type.padEnd(8)} ${from}`);
|
|
105
|
+
}
|
|
106
|
+
else if (c.type === "CREATED") {
|
|
107
|
+
console.log(` ${c.type.padEnd(8)} ${to}`);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
console.log(` ${c.type.padEnd(8)} ${from}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (tx.changes.length > 25) {
|
|
114
|
+
console.log(` … ${tx.changes.length - 25} more`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async function cmdRewind() {
|
|
118
|
+
console.log("REWINDING…");
|
|
119
|
+
console.log("");
|
|
120
|
+
console.log("Restoring files…");
|
|
121
|
+
console.log("Removing created files…");
|
|
122
|
+
console.log("Recreating deleted files…");
|
|
123
|
+
console.log("Restoring modified files…");
|
|
124
|
+
console.log("");
|
|
125
|
+
const result = await restoreSandbox("latest");
|
|
126
|
+
console.log(`Restored from checkpoint: ${result.checkpoint.meta.id}`);
|
|
127
|
+
console.log(`Timings: clear ${formatMs(result.timingsMs.clear)}, materialize ${formatMs(result.timingsMs.materialize)}, verify ${formatMs(result.timingsMs.verify)}, total ${formatMs(result.timingsMs.total)}`);
|
|
128
|
+
console.log("");
|
|
129
|
+
printVerification(result.verification);
|
|
130
|
+
}
|
|
131
|
+
async function cmdVerify() {
|
|
132
|
+
const result = await verifyLatest();
|
|
133
|
+
printVerification(result);
|
|
134
|
+
if (!result.ok)
|
|
135
|
+
process.exitCode = 1;
|
|
136
|
+
}
|
|
137
|
+
async function cmdDemo() {
|
|
138
|
+
const line = "⸻";
|
|
139
|
+
console.log(line);
|
|
140
|
+
console.log("");
|
|
141
|
+
console.log("REWIND V0");
|
|
142
|
+
console.log("");
|
|
143
|
+
console.log("Creating disposable project…");
|
|
144
|
+
const sandbox = await createSandbox();
|
|
145
|
+
console.log("");
|
|
146
|
+
console.log(`${sandbox.fileCount} files created.`);
|
|
147
|
+
console.log("");
|
|
148
|
+
console.log("Creating checkpoint…");
|
|
149
|
+
const cp = await createCheckpoint();
|
|
150
|
+
console.log("");
|
|
151
|
+
console.log("Checkpoint:");
|
|
152
|
+
console.log(cp.meta.id);
|
|
153
|
+
console.log("");
|
|
154
|
+
console.log("Project fingerprint:");
|
|
155
|
+
console.log(cp.meta.fingerprint);
|
|
156
|
+
console.log("");
|
|
157
|
+
console.log(`(checkpoint ${formatMs(cp.meta.timingsMs.total)} — manifest ${formatMs(cp.meta.timingsMs.manifest)}, store ${formatMs(cp.meta.timingsMs.contentStore)})`);
|
|
158
|
+
console.log("");
|
|
159
|
+
console.log("DESTROYING PROJECT…");
|
|
160
|
+
console.log("");
|
|
161
|
+
const destroyed = await destroySandbox();
|
|
162
|
+
const afterManifest = await buildManifest(getSandboxPath(), "sandbox-test-project");
|
|
163
|
+
const tx = await recordDiffTransaction(cp.manifest, afterManifest, cp.meta.id, getSandboxPath());
|
|
164
|
+
console.log("");
|
|
165
|
+
console.log(`${tx.summary.total} filesystem changes performed.`);
|
|
166
|
+
console.log("");
|
|
167
|
+
console.log("Current fingerprint:");
|
|
168
|
+
console.log(afterManifest.fingerprint);
|
|
169
|
+
console.log("");
|
|
170
|
+
console.log("Project no longer matches checkpoint.");
|
|
171
|
+
console.log(`(destroy ${formatMs(destroyed.timingsMs)}, diff ${formatMs(tx.timingsMs.diff)})`);
|
|
172
|
+
console.log("");
|
|
173
|
+
console.log("REWINDING…");
|
|
174
|
+
console.log("");
|
|
175
|
+
console.log("Restoring files…");
|
|
176
|
+
console.log("Removing created files…");
|
|
177
|
+
console.log("Recreating deleted files…");
|
|
178
|
+
console.log("Restoring modified files…");
|
|
179
|
+
console.log("");
|
|
180
|
+
const restored = await restoreSandbox("latest");
|
|
181
|
+
console.log(`(rewind ${formatMs(restored.timingsMs.total)})`);
|
|
182
|
+
console.log("");
|
|
183
|
+
console.log("VERIFYING…");
|
|
184
|
+
console.log("");
|
|
185
|
+
console.log("Original:");
|
|
186
|
+
console.log(cp.meta.fingerprint);
|
|
187
|
+
console.log("");
|
|
188
|
+
console.log("Restored:");
|
|
189
|
+
console.log(restored.verification.actualFingerprint);
|
|
190
|
+
console.log("");
|
|
191
|
+
if (restored.verification.ok) {
|
|
192
|
+
console.log("✓ EXACT MATCH");
|
|
193
|
+
console.log("");
|
|
194
|
+
console.log("REWIND SUCCESSFUL");
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
console.log("✗ RESTORE FAILED");
|
|
198
|
+
printVerification(restored.verification);
|
|
199
|
+
process.exitCode = 1;
|
|
200
|
+
}
|
|
201
|
+
console.log("");
|
|
202
|
+
console.log(line);
|
|
203
|
+
}
|
|
204
|
+
async function cmdCycles(count) {
|
|
205
|
+
console.log(`REWIND CYCLE TEST — ${count} iterations`);
|
|
206
|
+
console.log("");
|
|
207
|
+
await createSandbox();
|
|
208
|
+
const cp = await createCheckpoint();
|
|
209
|
+
const original = cp.meta.fingerprint;
|
|
210
|
+
console.log(`Baseline fingerprint: ${original}`);
|
|
211
|
+
console.log("");
|
|
212
|
+
for (let i = 1; i <= count; i++) {
|
|
213
|
+
await destroySandbox();
|
|
214
|
+
const result = await restoreSandbox(cp.meta.id);
|
|
215
|
+
const fp = result.verification.actualFingerprint;
|
|
216
|
+
const ok = result.verification.ok && fp === original;
|
|
217
|
+
console.log(`Cycle ${String(i).padStart(2, "0")}/${count}: ${ok ? "PASS" : "FAIL"} fp=${fp.slice(0, 12)}… (${formatMs(result.timingsMs.total)})`);
|
|
218
|
+
if (!ok) {
|
|
219
|
+
printVerification(result.verification);
|
|
220
|
+
process.exitCode = 1;
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
console.log("");
|
|
225
|
+
console.log(`All ${count} cycles passed. Fingerprint stable: ${original}`);
|
|
226
|
+
}
|
|
227
|
+
async function dispatchSession(args) {
|
|
228
|
+
const sub = args[0];
|
|
229
|
+
switch (sub) {
|
|
230
|
+
case "start":
|
|
231
|
+
await startSession({
|
|
232
|
+
recreateSandbox: args.includes("--recreate"),
|
|
233
|
+
});
|
|
234
|
+
break;
|
|
235
|
+
case "stop":
|
|
236
|
+
await stopSession();
|
|
237
|
+
break;
|
|
238
|
+
case "status":
|
|
239
|
+
await sessionStatus();
|
|
240
|
+
break;
|
|
241
|
+
case "report":
|
|
242
|
+
await showSessionReport("latest");
|
|
243
|
+
break;
|
|
244
|
+
case "rewind":
|
|
245
|
+
await rewindSession("latest");
|
|
246
|
+
break;
|
|
247
|
+
case "probe":
|
|
248
|
+
await printProcessProbe();
|
|
249
|
+
break;
|
|
250
|
+
default:
|
|
251
|
+
console.error(`Unknown session subcommand: ${sub ?? "(none)"}`);
|
|
252
|
+
printUsage();
|
|
253
|
+
process.exitCode = 1;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async function dispatchEnforce(args) {
|
|
257
|
+
const sub = args[0];
|
|
258
|
+
const projectRoot = getProjectRoot();
|
|
259
|
+
switch (sub) {
|
|
260
|
+
case "enable": {
|
|
261
|
+
const mechanismArg = args.find((a) => a.startsWith("--mechanism="));
|
|
262
|
+
const mechanism = (mechanismArg?.split("=")[1] ??
|
|
263
|
+
"combined");
|
|
264
|
+
const state = await enableEnforcement(projectRoot, { mechanism });
|
|
265
|
+
console.log("REWIND V3 ENFORCEMENT ENABLED");
|
|
266
|
+
console.log(`Sandbox: ${state.sandboxName}`);
|
|
267
|
+
console.log(`Mechanism: ${state.mechanism}`);
|
|
268
|
+
console.log(`FS locked: ${state.locked ? "YES" : "NO"}`);
|
|
269
|
+
console.log(`Seatbelt profile: ${state.seatbeltProfilePath ?? "—"}`);
|
|
270
|
+
console.log(`Hooks: .cursor/hooks.json (Cursor tool-loop)`);
|
|
271
|
+
console.log("");
|
|
272
|
+
console.log("Human editor writes are NOT blocked by Cursor hooks — only agent tools.");
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
case "disable": {
|
|
276
|
+
await disableEnforcement(projectRoot, {
|
|
277
|
+
removeTree: args.includes("--remove"),
|
|
278
|
+
});
|
|
279
|
+
console.log("REWIND V3 ENFORCEMENT DISABLED");
|
|
280
|
+
if (args.includes("--remove")) {
|
|
281
|
+
console.log("sandbox-enforced-project removed.");
|
|
282
|
+
}
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
case "status": {
|
|
286
|
+
const state = await loadEnforcementState(projectRoot);
|
|
287
|
+
console.log("REWIND V3 ENFORCEMENT STATUS");
|
|
288
|
+
console.log(JSON.stringify(state, null, 2));
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
case "probe": {
|
|
292
|
+
const state = await loadEnforcementState(projectRoot);
|
|
293
|
+
if (!state?.enabled) {
|
|
294
|
+
console.log("Enforcement not enabled. Run: rewind enforce enable");
|
|
295
|
+
process.exitCode = 1;
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const direct = await probeDirectWriteBlocked(projectRoot);
|
|
299
|
+
console.log("TEST A — direct write (fs-readonly)");
|
|
300
|
+
console.log(direct.blocked
|
|
301
|
+
? `BLOCKED ✓ (${direct.error})`
|
|
302
|
+
: "NOT BLOCKED ✗ — V3 fs-readonly failed");
|
|
303
|
+
let profile = state.seatbeltProfilePath;
|
|
304
|
+
if (!profile)
|
|
305
|
+
profile = await writeSeatbeltProfile(projectRoot);
|
|
306
|
+
const target = path.join(getEnforcedSandboxPath(projectRoot), "shell-bypass.txt");
|
|
307
|
+
const seatbelt = await runUnderWriteDenySeatbelt("/bin/bash", ["-c", `echo bypass > '${target}'`], profile);
|
|
308
|
+
console.log("");
|
|
309
|
+
console.log("TEST B — shell under sandbox-exec wrapper");
|
|
310
|
+
console.log(seatbelt.code !== 0
|
|
311
|
+
? `BLOCKED ✓ exit=${seatbelt.code} ${seatbelt.stderr.trim() || seatbelt.stdout.trim()}`
|
|
312
|
+
: "NOT BLOCKED ✗ — seatbelt wrapper failed");
|
|
313
|
+
// Unrestricted shell (same UID) may still succeed if tree unlocked —
|
|
314
|
+
// with fs-readonly lock it should fail without chmod.
|
|
315
|
+
console.log("");
|
|
316
|
+
console.log("NOTE: Unrestricted same-UID shell without seatbelt:");
|
|
317
|
+
try {
|
|
318
|
+
const { writeFile } = await import("node:fs/promises");
|
|
319
|
+
await writeFile(target, "unrestricted");
|
|
320
|
+
console.log("SUCCEEDED — tree writable to owner (lock may be off, or chmod bypass).");
|
|
321
|
+
await unlockEnforcedWritable(projectRoot).catch(() => undefined);
|
|
322
|
+
}
|
|
323
|
+
catch (err) {
|
|
324
|
+
console.log(`BLOCKED ✓ (${err instanceof Error ? err.message : String(err)})`);
|
|
325
|
+
}
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
case "perf": {
|
|
329
|
+
await enableEnforcement(projectRoot, { mechanism: "combined" });
|
|
330
|
+
const samples = [];
|
|
331
|
+
const readPath = path.join(getEnforcedSandboxPath(projectRoot), "src/index.ts");
|
|
332
|
+
{
|
|
333
|
+
const { sample } = await measureMs("read_enforced_file", async () => readFile(readPath));
|
|
334
|
+
samples.push(sample);
|
|
335
|
+
}
|
|
336
|
+
{
|
|
337
|
+
const { sample } = await measureMs("mediated_write", async () => {
|
|
338
|
+
const ctx = defaultMediateContext(projectRoot, { name: "perf", version: "0" }, ENFORCED_SANDBOX_DIR_NAME);
|
|
339
|
+
return mediateWriteFile(ctx, {
|
|
340
|
+
path: "perf-mediated.txt",
|
|
341
|
+
content: `perf ${Date.now()}`,
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
samples.push(sample);
|
|
345
|
+
}
|
|
346
|
+
{
|
|
347
|
+
const { sample } = await measureMs("probe_blocked_write", async () => probeDirectWriteBlocked(projectRoot));
|
|
348
|
+
samples.push(sample);
|
|
349
|
+
}
|
|
350
|
+
console.log(formatPerfTable(samples));
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
default:
|
|
354
|
+
console.error(`Unknown enforce subcommand: ${sub ?? "(none)"}`);
|
|
355
|
+
printUsage();
|
|
356
|
+
process.exitCode = 1;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
async function main() {
|
|
360
|
+
const args = process.argv.slice(2);
|
|
361
|
+
const cmd = args[0];
|
|
362
|
+
try {
|
|
363
|
+
switch (cmd) {
|
|
364
|
+
case "sandbox:create":
|
|
365
|
+
await cmdSandboxCreate();
|
|
366
|
+
break;
|
|
367
|
+
case "checkpoint":
|
|
368
|
+
await cmdCheckpoint();
|
|
369
|
+
break;
|
|
370
|
+
case "destroy":
|
|
371
|
+
await cmdDestroy();
|
|
372
|
+
break;
|
|
373
|
+
case "diff":
|
|
374
|
+
await cmdDiff();
|
|
375
|
+
break;
|
|
376
|
+
case "rewind":
|
|
377
|
+
await cmdRewind();
|
|
378
|
+
break;
|
|
379
|
+
case "verify":
|
|
380
|
+
await cmdVerify();
|
|
381
|
+
break;
|
|
382
|
+
case "demo":
|
|
383
|
+
await cmdDemo();
|
|
384
|
+
break;
|
|
385
|
+
case "demo:v1":
|
|
386
|
+
await demoV1();
|
|
387
|
+
break;
|
|
388
|
+
case "session":
|
|
389
|
+
await dispatchSession(args.slice(1));
|
|
390
|
+
break;
|
|
391
|
+
case "session:start":
|
|
392
|
+
await startSession({
|
|
393
|
+
recreateSandbox: args.includes("--recreate"),
|
|
394
|
+
});
|
|
395
|
+
break;
|
|
396
|
+
case "session:stop":
|
|
397
|
+
await stopSession();
|
|
398
|
+
break;
|
|
399
|
+
case "session:status":
|
|
400
|
+
await sessionStatus();
|
|
401
|
+
break;
|
|
402
|
+
case "session:report":
|
|
403
|
+
await showSessionReport("latest");
|
|
404
|
+
break;
|
|
405
|
+
case "session:rewind":
|
|
406
|
+
await rewindSession("latest");
|
|
407
|
+
break;
|
|
408
|
+
case "session:probe":
|
|
409
|
+
await printProcessProbe();
|
|
410
|
+
break;
|
|
411
|
+
case "mcp":
|
|
412
|
+
await runMcpServer();
|
|
413
|
+
break;
|
|
414
|
+
case "setup": {
|
|
415
|
+
console.log(await runSetup(getProjectRoot()));
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
case "doctor": {
|
|
419
|
+
const { text, checks } = await runDoctor(getProjectRoot());
|
|
420
|
+
console.log(text);
|
|
421
|
+
if (checks.some((c) => c.status === "FAIL"))
|
|
422
|
+
process.exitCode = 1;
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
case "version":
|
|
426
|
+
case "--version":
|
|
427
|
+
case "-V": {
|
|
428
|
+
console.log(versionText());
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
case "update": {
|
|
432
|
+
console.log("REWIND UPDATE");
|
|
433
|
+
console.log("");
|
|
434
|
+
console.log(`Current version: ${PRODUCT_VERSION}`);
|
|
435
|
+
console.log("");
|
|
436
|
+
console.log("Packaging channels (when published):");
|
|
437
|
+
console.log(" npm: npm update -g @rewind-ai/cli");
|
|
438
|
+
console.log(" Homebrew: brew upgrade rewind # after tap exists");
|
|
439
|
+
console.log("");
|
|
440
|
+
console.log("Updates never delete .rewind transaction history or CAS objects.");
|
|
441
|
+
console.log("See docs/PACKAGING.md for release status.");
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
case "uninstall": {
|
|
445
|
+
const deleteData = args.includes("--delete-recovery-data");
|
|
446
|
+
if (deleteData && !args.includes("--yes")) {
|
|
447
|
+
console.error("Refusing to delete recovery data without --yes.\n" +
|
|
448
|
+
"Usage: rewind uninstall --delete-recovery-data --yes");
|
|
449
|
+
process.exitCode = 2;
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
console.log(await runUninstall({
|
|
453
|
+
deleteRecoveryData: deleteData,
|
|
454
|
+
removeMcp: !args.includes("--keep-mcp"),
|
|
455
|
+
projectRoot: getProjectRoot(),
|
|
456
|
+
}));
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
case "login": {
|
|
460
|
+
const emailIdx = args.indexOf("--email");
|
|
461
|
+
const email = emailIdx >= 0 ? args[emailIdx + 1] : undefined;
|
|
462
|
+
const provider = getAuthProvider();
|
|
463
|
+
const session = await provider.login(email ? { email, openBrowser: false } : { openBrowser: true });
|
|
464
|
+
setEntitlementProvider(new SignedEntitlementProvider());
|
|
465
|
+
await issueEntitlementForCurrentUser().catch(() => undefined);
|
|
466
|
+
console.log("REWIND");
|
|
467
|
+
console.log("");
|
|
468
|
+
console.log(`✓ Signed in as ${session.email}`);
|
|
469
|
+
console.log(` Installation ${session.installationId}`);
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
case "logout": {
|
|
473
|
+
await getAuthProvider().logout();
|
|
474
|
+
console.log("Signed out.");
|
|
475
|
+
break;
|
|
476
|
+
}
|
|
477
|
+
case "whoami": {
|
|
478
|
+
const session = await loadSession();
|
|
479
|
+
if (!session) {
|
|
480
|
+
console.log("Not signed in. Run: rewind login");
|
|
481
|
+
process.exitCode = 1;
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
console.log(`${session.email} (${session.provider})`);
|
|
485
|
+
console.log(`user=${session.userId} install=${session.installationId}`);
|
|
486
|
+
}
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
case "subscribe": {
|
|
490
|
+
const session = await loadSession();
|
|
491
|
+
if (!session) {
|
|
492
|
+
console.error("Sign in first: rewind login");
|
|
493
|
+
process.exitCode = 1;
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
const checkout = await createCheckoutSession({
|
|
497
|
+
userId: session.userId,
|
|
498
|
+
email: session.email,
|
|
499
|
+
successUrl: `http://127.0.0.1:${LOCAL_API_DEFAULT_PORT}/subscribed`,
|
|
500
|
+
cancelUrl: `http://127.0.0.1:${LOCAL_API_DEFAULT_PORT}/canceled`,
|
|
501
|
+
});
|
|
502
|
+
console.log("REWIND PERSONAL — £2.99/month");
|
|
503
|
+
console.log("");
|
|
504
|
+
if (checkout.mode === "test-simulated") {
|
|
505
|
+
console.log("SIMULATED DEVELOPMENT MODE");
|
|
506
|
+
console.log("");
|
|
507
|
+
console.log(checkout.message);
|
|
508
|
+
await confirmSimulatedCheckout(session.userId, checkout.sessionId);
|
|
509
|
+
setEntitlementProvider(new SignedEntitlementProvider());
|
|
510
|
+
await issueEntitlementForCurrentUser();
|
|
511
|
+
console.log("");
|
|
512
|
+
console.log("✓ Simulated subscription activated (not Stripe).");
|
|
513
|
+
console.log(" Entitlement refreshed.");
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
console.log("STRIPE TEST MODE");
|
|
517
|
+
console.log("");
|
|
518
|
+
console.log(checkout.message);
|
|
519
|
+
console.log("");
|
|
520
|
+
console.log("Personal is NOT activated locally yet — wait for the signed Stripe webhook.");
|
|
521
|
+
console.log(`Webhook: POST http://127.0.0.1:${LOCAL_API_DEFAULT_PORT}/v1/commercial/stripe/webhook`);
|
|
522
|
+
console.log("Keep `rewind api` (or `rewind open`) running and forward with Stripe CLI.");
|
|
523
|
+
if (checkout.url) {
|
|
524
|
+
console.log("");
|
|
525
|
+
console.log(`Checkout URL: ${checkout.url}`);
|
|
526
|
+
execFile(process.platform === "darwin" ? "open" : "xdg-open", [checkout.url], () => undefined);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
case "open": {
|
|
532
|
+
const handle = await startLocalApiServer({
|
|
533
|
+
projectRoot: getProjectRoot(),
|
|
534
|
+
port: 7429,
|
|
535
|
+
listen: false,
|
|
536
|
+
});
|
|
537
|
+
console.log("");
|
|
538
|
+
console.log("Opening control app…");
|
|
539
|
+
execFile(process.platform === "darwin" ? "open" : "xdg-open", [handle.url], () => undefined);
|
|
540
|
+
// Keep process alive for the UI
|
|
541
|
+
await new Promise(() => {
|
|
542
|
+
/* until killed */
|
|
543
|
+
});
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
case "protect": {
|
|
547
|
+
const { summary } = await protectProject(args[1]);
|
|
548
|
+
console.log(summary);
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
case "unprotect": {
|
|
552
|
+
const msg = await unprotectProject(args[1]);
|
|
553
|
+
console.log(msg);
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
case "status": {
|
|
557
|
+
// V5 product status; append protect summary when a protected project is given
|
|
558
|
+
console.log(await cliStatus(getProjectRoot()));
|
|
559
|
+
if (args[1]) {
|
|
560
|
+
const root = resolveProtectTarget(args[1]);
|
|
561
|
+
console.log("");
|
|
562
|
+
console.log(await formatProtectStatus(root));
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
const root = resolveProtectTarget(undefined);
|
|
566
|
+
const state = await loadProtectState(root);
|
|
567
|
+
if (state?.protected) {
|
|
568
|
+
console.log("");
|
|
569
|
+
console.log(await formatProtectStatus(root));
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
case "transactions": {
|
|
575
|
+
const limitIdx = args.indexOf("--limit");
|
|
576
|
+
const limit = limitIdx >= 0 && args[limitIdx + 1]
|
|
577
|
+
? Number(args[limitIdx + 1])
|
|
578
|
+
: undefined;
|
|
579
|
+
console.log(await cliTransactions(getProjectRoot(), limit));
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
582
|
+
case "transaction": {
|
|
583
|
+
const id = args[1];
|
|
584
|
+
if (!id)
|
|
585
|
+
throw new Error("Usage: transaction <id>");
|
|
586
|
+
console.log(await cliTransaction(id, getProjectRoot()));
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
case "approvals": {
|
|
590
|
+
console.log(await cliApprovals(getProjectRoot()));
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
case "approve": {
|
|
594
|
+
const id = args[1];
|
|
595
|
+
if (!id)
|
|
596
|
+
throw new Error("Usage: approve <approvalId>");
|
|
597
|
+
console.log(await cliApprove(id, getProjectRoot()));
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
600
|
+
case "deny": {
|
|
601
|
+
const id = args[1];
|
|
602
|
+
if (!id)
|
|
603
|
+
throw new Error("Usage: deny <approvalId>");
|
|
604
|
+
console.log(await cliDeny(id, getProjectRoot()));
|
|
605
|
+
break;
|
|
606
|
+
}
|
|
607
|
+
case "api": {
|
|
608
|
+
const portArg = args.indexOf("--port");
|
|
609
|
+
const port = portArg >= 0 && args[portArg + 1]
|
|
610
|
+
? Number(args[portArg + 1])
|
|
611
|
+
: 7429;
|
|
612
|
+
await startLocalApiServer({
|
|
613
|
+
projectRoot: getProjectRoot(),
|
|
614
|
+
port: Number.isFinite(port) ? port : 7429,
|
|
615
|
+
});
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
case "sessions": {
|
|
619
|
+
const root = resolveProtectTarget(args[1]);
|
|
620
|
+
console.log(await formatSessionsList(root));
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
case "actions": {
|
|
624
|
+
// V4 protect actions when project is protected; else V2 MCP actions
|
|
625
|
+
const root = resolveProtectTarget(args[1]?.startsWith("rw_") || args[1]?.startsWith("act_") ? undefined : args[1]);
|
|
626
|
+
const state = await loadProtectState(root);
|
|
627
|
+
if (state?.protected || args.includes("--protect")) {
|
|
628
|
+
const sessionFilter = args.find((a) => a.startsWith("rw_"));
|
|
629
|
+
console.log(await formatActionsList(root, sessionFilter));
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
await printActionCli(args[1]);
|
|
633
|
+
}
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
636
|
+
case "undo": {
|
|
637
|
+
const id = args[1];
|
|
638
|
+
if (!id)
|
|
639
|
+
throw new Error("Usage: undo <id>");
|
|
640
|
+
// Protect-session actions (non act_/txn_) use V4 undo when protected
|
|
641
|
+
const root = resolveProtectTarget(args[2]);
|
|
642
|
+
const state = await loadProtectState(root);
|
|
643
|
+
if (state?.protected &&
|
|
644
|
+
!id.startsWith("act_") &&
|
|
645
|
+
!id.startsWith("txn_")) {
|
|
646
|
+
try {
|
|
647
|
+
const result = await undoProtectAction(id, root);
|
|
648
|
+
if (result.conflict) {
|
|
649
|
+
console.log(result.message);
|
|
650
|
+
process.exitCode = 3;
|
|
651
|
+
}
|
|
652
|
+
else if (!result.ok) {
|
|
653
|
+
console.log(result.message);
|
|
654
|
+
process.exitCode = 1;
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
console.log(result.message);
|
|
658
|
+
console.log(`Status: ${result.action.status}`);
|
|
659
|
+
}
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
catch {
|
|
663
|
+
/* fall through */
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
const v5 = await cliUndo(id, getProjectRoot());
|
|
667
|
+
console.log(v5.text);
|
|
668
|
+
process.exitCode = v5.exitCode;
|
|
669
|
+
break;
|
|
670
|
+
}
|
|
671
|
+
case "undo-session": {
|
|
672
|
+
const id = args[1];
|
|
673
|
+
if (!id)
|
|
674
|
+
throw new Error("Usage: undo-session <sessionId>");
|
|
675
|
+
const root = resolveProtectTarget(args[2]);
|
|
676
|
+
const result = await undoProtectedSession(id, root);
|
|
677
|
+
console.log(result.message);
|
|
678
|
+
if (result.conflicts.length > 0)
|
|
679
|
+
process.exitCode = 3;
|
|
680
|
+
else if (result.failed > 0)
|
|
681
|
+
process.exitCode = 1;
|
|
682
|
+
break;
|
|
683
|
+
}
|
|
684
|
+
case "mcp:actions":
|
|
685
|
+
await printActionCli(args[1]);
|
|
686
|
+
break;
|
|
687
|
+
case "mcp:undo": {
|
|
688
|
+
const id = args[1];
|
|
689
|
+
if (!id)
|
|
690
|
+
throw new Error("Usage: mcp:undo <actionId>");
|
|
691
|
+
const result = await undoAction(id, getProjectRoot());
|
|
692
|
+
if (result.conflict) {
|
|
693
|
+
console.log(formatUndoConflict(result));
|
|
694
|
+
process.exitCode = 3;
|
|
695
|
+
}
|
|
696
|
+
else if (!result.ok) {
|
|
697
|
+
console.log(result.message);
|
|
698
|
+
process.exitCode = 1;
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
console.log(result.message);
|
|
702
|
+
console.log(`Status: ${result.action.status}`);
|
|
703
|
+
}
|
|
704
|
+
break;
|
|
705
|
+
}
|
|
706
|
+
case "mcp:sessions":
|
|
707
|
+
await printMcpSessionReport();
|
|
708
|
+
break;
|
|
709
|
+
case "mcp:recover": {
|
|
710
|
+
const n = await markIncompleteFailed();
|
|
711
|
+
console.log(`Marked ${n} incomplete action(s) as FAILED`);
|
|
712
|
+
break;
|
|
713
|
+
}
|
|
714
|
+
case "enforce":
|
|
715
|
+
await dispatchEnforce(args.slice(1));
|
|
716
|
+
break;
|
|
717
|
+
case "policy": {
|
|
718
|
+
const sub = args[1];
|
|
719
|
+
if (sub === "init") {
|
|
720
|
+
console.log(await cliPolicyInit(getProjectRoot()));
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
if (sub === "events") {
|
|
724
|
+
const events = await listPolicyEvents();
|
|
725
|
+
console.log("REWIND POLICY EVENTS (V3 enforcement)");
|
|
726
|
+
for (const e of events) {
|
|
727
|
+
console.log(`${e.time} ${e.id} ${e.status} ${e.type} ${e.path ?? "—"}`);
|
|
728
|
+
}
|
|
729
|
+
console.log(`Total: ${events.length}`);
|
|
730
|
+
break;
|
|
731
|
+
}
|
|
732
|
+
if (sub === "set") {
|
|
733
|
+
// policy set <id> <ALLOW|ASK|DENY> [--op write] [--resource 'src/**'] [--enable]
|
|
734
|
+
const id = args[2];
|
|
735
|
+
const effect = args[3];
|
|
736
|
+
if (!id || !effect || !["ALLOW", "ASK", "DENY"].includes(effect)) {
|
|
737
|
+
throw new Error("Usage: policy set <id> <ALLOW|ASK|DENY> [--op op] [--resource glob] [--adapter filesystem] [--enable|--disable]");
|
|
738
|
+
}
|
|
739
|
+
const opIdx = args.indexOf("--op");
|
|
740
|
+
const resIdx = args.indexOf("--resource");
|
|
741
|
+
const adIdx = args.indexOf("--adapter");
|
|
742
|
+
const rule = {
|
|
743
|
+
id,
|
|
744
|
+
priority: 100,
|
|
745
|
+
effect,
|
|
746
|
+
reason: `cli policy set ${id}`,
|
|
747
|
+
enabled: !args.includes("--disable"),
|
|
748
|
+
};
|
|
749
|
+
if (opIdx >= 0 && args[opIdx + 1])
|
|
750
|
+
rule.operation = args[opIdx + 1];
|
|
751
|
+
if (resIdx >= 0 && args[resIdx + 1]) {
|
|
752
|
+
rule.resourcePattern = args[resIdx + 1];
|
|
753
|
+
}
|
|
754
|
+
if (adIdx >= 0 && args[adIdx + 1]) {
|
|
755
|
+
rule.adapter = args[adIdx + 1];
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
rule.adapter = "filesystem";
|
|
759
|
+
}
|
|
760
|
+
const { cliPolicySetRule } = await import("./cli/v5.js");
|
|
761
|
+
console.log(await cliPolicySetRule(rule, getProjectRoot()));
|
|
762
|
+
break;
|
|
763
|
+
}
|
|
764
|
+
console.log(await cliPolicy(getProjectRoot()));
|
|
765
|
+
break;
|
|
766
|
+
}
|
|
767
|
+
case "policy:events": {
|
|
768
|
+
const events = await listPolicyEvents();
|
|
769
|
+
console.log("REWIND POLICY EVENTS (V3 enforcement)");
|
|
770
|
+
for (const e of events) {
|
|
771
|
+
console.log(`${e.time} ${e.id} ${e.status} ${e.type} ${e.path ?? "—"}`);
|
|
772
|
+
}
|
|
773
|
+
console.log(`Total: ${events.length}`);
|
|
774
|
+
break;
|
|
775
|
+
}
|
|
776
|
+
case "trust": {
|
|
777
|
+
const trust = await listTrustRecords();
|
|
778
|
+
console.log("REWIND TRUST STATE");
|
|
779
|
+
for (const t of trust) {
|
|
780
|
+
console.log(`${t.state} ${t.path}`);
|
|
781
|
+
}
|
|
782
|
+
console.log(`Total: ${trust.length}`);
|
|
783
|
+
break;
|
|
784
|
+
}
|
|
785
|
+
case "cycles": {
|
|
786
|
+
const idx = args.indexOf("--count");
|
|
787
|
+
const count = idx >= 0 && args[idx + 1] ? Number(args[idx + 1]) : 10;
|
|
788
|
+
if (!Number.isFinite(count) || count < 1) {
|
|
789
|
+
throw new Error("Invalid --count");
|
|
790
|
+
}
|
|
791
|
+
await cmdCycles(count);
|
|
792
|
+
break;
|
|
793
|
+
}
|
|
794
|
+
case "help":
|
|
795
|
+
case "--help":
|
|
796
|
+
case "-h":
|
|
797
|
+
case undefined:
|
|
798
|
+
printUsage();
|
|
799
|
+
break;
|
|
800
|
+
default:
|
|
801
|
+
console.error(`Unknown command: ${cmd}`);
|
|
802
|
+
printUsage();
|
|
803
|
+
process.exitCode = 1;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
catch (err) {
|
|
807
|
+
if (err instanceof PathSafetyError) {
|
|
808
|
+
console.error("");
|
|
809
|
+
console.error("REWIND SAFETY REFUSAL");
|
|
810
|
+
console.error(err.message);
|
|
811
|
+
console.error("");
|
|
812
|
+
process.exitCode = 2;
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
console.error("");
|
|
816
|
+
console.error("REWIND ERROR");
|
|
817
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
818
|
+
console.error("");
|
|
819
|
+
process.exitCode = 1;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
await main();
|
|
823
|
+
//# sourceMappingURL=cli.js.map
|