@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.
Files changed (3) hide show
  1. package/LICENSE +1 -1
  2. package/bin/upgrade.js +81 -16
  3. package/package.json +1 -1
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Sean
3
+ Copyright (c) 2026 Sean Winter
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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 upgradeFile(relPath) {
52
- const targetPath = path.join(workspace, relPath);
53
- const templatePath = path.join(__dirname, "..", "templates", relPath);
53
+ function extractHash(content) {
54
+ const match = content.match(HASH_REGEX);
55
+ return match ? match[1] : null;
56
+ }
54
57
 
55
- if (!exists(targetPath)) return;
58
+ function stripHeaders(content) {
59
+ return content.replace(/<!--[\s\S]*?-->/g, "").trim();
60
+ }
56
61
 
57
- const installed = read(targetPath);
58
- const template = read(templatePath);
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
- const installedVersion = getInstalledVersion(installed);
91
+ function upgradeFile(relPath) {
92
+ const targetPath = path.join(workspace, relPath);
61
93
 
62
- if (installedVersion === CLI_VERSION) {
63
- console.log(`✓ ${relPath} already up to date.`);
94
+ if (!exists(targetPath)) {
95
+ console.log(`⚠ Skipping ${relPath} (not installed).`);
64
96
  return;
65
97
  }
66
98
 
67
- console.log(`Upgrading ${relPath} from v${installedVersion} → v${CLI_VERSION}`);
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 stamped = `<!-- Installed by openclaw-deterministic v${CLI_VERSION} -->\n${template}`;
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(openclawRoot) || !exists(workspace)) {
75
- console.error("OpenClaw workspace not found.");
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(`Running deterministic upgrade → CLI v${CLI_VERSION}\n`);
137
+ console.log(`\nRunning deterministic upgrade → v${CLI_VERSION}\n`);
80
138
 
81
- files.forEach(upgradeFile);
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdotwinter/openclaw-deterministic",
3
- "version": "0.17.5",
3
+ "version": "0.17.6",
4
4
  "description": "Deterministic governance and memory compaction layer for OpenClaw",
5
5
  "keywords": [
6
6
  "openclaw",