@sdotwinter/openclaw-deterministic 0.17.5 → 0.17.6
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/LICENSE +1 -1
- package/bin/upgrade.js +81 -16
- package/package.json +1 -1
package/LICENSE
CHANGED
package/bin/upgrade.js
CHANGED
|
@@ -6,6 +6,7 @@ const path = require("path");
|
|
|
6
6
|
|
|
7
7
|
const args = process.argv.slice(2);
|
|
8
8
|
const DRY_RUN = args.includes("--dry-run");
|
|
9
|
+
const FORCE = args.includes("--force");
|
|
9
10
|
|
|
10
11
|
const pkg = require(path.join(__dirname, "..", "package.json"));
|
|
11
12
|
const CLI_VERSION = pkg.version;
|
|
@@ -15,6 +16,7 @@ const openclawRoot = path.join(home, ".openclaw");
|
|
|
15
16
|
const workspace = path.join(openclawRoot, "workspace");
|
|
16
17
|
|
|
17
18
|
const VERSION_REGEX = /Installed by openclaw-deterministic v([0-9.]+)/;
|
|
19
|
+
const HASH_REGEX = /Canonical-Hash:\s*SHA256:([a-f0-9]+)/;
|
|
18
20
|
|
|
19
21
|
const files = [
|
|
20
22
|
"OPERATING_RULES.md",
|
|
@@ -48,37 +50,100 @@ function getInstalledVersion(content) {
|
|
|
48
50
|
return match ? match[1] : null;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
function
|
|
52
|
-
const
|
|
53
|
-
|
|
53
|
+
function extractHash(content) {
|
|
54
|
+
const match = content.match(HASH_REGEX);
|
|
55
|
+
return match ? match[1] : null;
|
|
56
|
+
}
|
|
54
57
|
|
|
55
|
-
|
|
58
|
+
function stripHeaders(content) {
|
|
59
|
+
return content.replace(/<!--[\s\S]*?-->/g, "").trim();
|
|
60
|
+
}
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
const
|
|
62
|
+
function verifyIntegrity(installedContent) {
|
|
63
|
+
const storedHash = extractHash(installedContent);
|
|
64
|
+
if (!storedHash) {
|
|
65
|
+
return { valid: false, reason: "missing-canonical-hash" };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const stripped = stripHeaders(installedContent);
|
|
69
|
+
const crypto = require("crypto");
|
|
70
|
+
const currentHash = crypto
|
|
71
|
+
.createHash("sha256")
|
|
72
|
+
.update(stripped)
|
|
73
|
+
.digest("hex");
|
|
74
|
+
|
|
75
|
+
if (storedHash !== currentHash) {
|
|
76
|
+
return { valid: false, reason: "hash-mismatch" };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { valid: true };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function loadTemplate(relPath) {
|
|
83
|
+
const templatePath = path.join(__dirname, "..", "templates", relPath);
|
|
84
|
+
if (!exists(templatePath)) {
|
|
85
|
+
console.error(`Template missing: ${relPath}`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
return read(templatePath);
|
|
89
|
+
}
|
|
59
90
|
|
|
60
|
-
|
|
91
|
+
function upgradeFile(relPath) {
|
|
92
|
+
const targetPath = path.join(workspace, relPath);
|
|
61
93
|
|
|
62
|
-
if (
|
|
63
|
-
console.log(
|
|
94
|
+
if (!exists(targetPath)) {
|
|
95
|
+
console.log(`⚠ Skipping ${relPath} (not installed).`);
|
|
64
96
|
return;
|
|
65
97
|
}
|
|
66
98
|
|
|
67
|
-
|
|
99
|
+
const installed = read(targetPath);
|
|
100
|
+
const integrity = verifyIntegrity(installed);
|
|
101
|
+
|
|
102
|
+
if (!integrity.valid && !FORCE) {
|
|
103
|
+
console.error(
|
|
104
|
+
`❌ Refusing to upgrade ${relPath}: ${integrity.reason}.`
|
|
105
|
+
);
|
|
106
|
+
console.error(
|
|
107
|
+
" File has been modified or is missing canonical header."
|
|
108
|
+
);
|
|
109
|
+
console.error(" Re-run with --force to override.");
|
|
110
|
+
process.exit(2);
|
|
111
|
+
}
|
|
68
112
|
|
|
69
|
-
const
|
|
113
|
+
const template = loadTemplate(relPath);
|
|
114
|
+
|
|
115
|
+
const stamped = template.replace(
|
|
116
|
+
VERSION_REGEX,
|
|
117
|
+
`Installed by openclaw-deterministic v${CLI_VERSION}`
|
|
118
|
+
);
|
|
70
119
|
|
|
71
120
|
write(targetPath, stamped);
|
|
121
|
+
|
|
122
|
+
if (!DRY_RUN) {
|
|
123
|
+
console.log(`✅ Upgraded ${relPath} → v${CLI_VERSION}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!exists(openclawRoot)) {
|
|
128
|
+
console.error("OpenClaw not found at ~/.openclaw");
|
|
129
|
+
process.exit(1);
|
|
72
130
|
}
|
|
73
131
|
|
|
74
|
-
if (!exists(
|
|
75
|
-
console.error("OpenClaw workspace
|
|
132
|
+
if (!exists(workspace)) {
|
|
133
|
+
console.error("OpenClaw workspace missing at ~/.openclaw/workspace");
|
|
76
134
|
process.exit(1);
|
|
77
135
|
}
|
|
78
136
|
|
|
79
|
-
console.log(
|
|
137
|
+
console.log(`\nRunning deterministic upgrade → v${CLI_VERSION}\n`);
|
|
80
138
|
|
|
81
|
-
files
|
|
139
|
+
for (const rel of files) {
|
|
140
|
+
upgradeFile(rel);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (DRY_RUN) {
|
|
144
|
+
console.log("\nDry-run complete. No changes written.\n");
|
|
145
|
+
} else {
|
|
146
|
+
console.log("\nUpgrade complete.\n");
|
|
147
|
+
}
|
|
82
148
|
|
|
83
|
-
console.log("\nUpgrade complete.\n");
|
|
84
149
|
process.exit(0);
|