crbro-memory 2.9.1 → 2.11.0
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 +21 -21
- package/README.md +256 -456
- package/SECURITY.md +135 -8
- package/bin/crbro.mjs +555 -299
- package/dist/build-id.json +10 -0
- package/dist/cli/common.js +211 -0
- package/dist/cli/export.js +522 -0
- package/dist/cli/import.js +683 -0
- package/dist/cli/markdown.js +313 -0
- package/dist/cli/mcpclient.js +107 -0
- package/dist/cli/serve.js +453 -0
- package/dist/cli/view.js +358 -0
- package/dist/cli/viewer-page.js +378 -0
- package/dist/daemon/daemon.js +119 -11
- package/dist/daemon/endpoint.js +222 -21
- package/dist/daemon/lines.js +102 -13
- package/dist/daemon/proxy.js +480 -125
- package/dist/daemon/surface.js +116 -0
- package/dist/daemon/trace.js +73 -0
- package/dist/engine/atrest.js +132 -0
- package/dist/engine/backup.js +143 -19
- package/dist/engine/brain.js +22 -6
- package/dist/engine/codexconfig.js +476 -0
- package/dist/engine/cortex.js +762 -311
- package/dist/engine/doctor.js +676 -0
- package/dist/engine/evidence.js +171 -0
- package/dist/engine/heat.js +81 -37
- package/dist/engine/hippocampus.js +50 -36
- package/dist/engine/hookrefresh.js +296 -0
- package/dist/engine/hookverify.js +15 -3
- package/dist/engine/installer.js +1022 -0
- package/dist/engine/installio.js +279 -0
- package/dist/engine/keychain.js +735 -144
- package/dist/engine/maintenance.js +355 -87
- package/dist/engine/modinstall.js +98 -45
- package/dist/engine/prefrontal.js +126 -69
- package/dist/engine/projects.js +80 -0
- package/dist/engine/quarantine.js +204 -0
- package/dist/engine/secrets.js +41 -5
- package/dist/engine/shelf.js +34 -11
- package/dist/engine/synapses.js +183 -80
- package/dist/engine/triggers.js +141 -11
- package/dist/index.js +37 -3
- package/dist/mcp-surface.json +1 -0
- package/dist/miner/extractor.js +64 -17
- package/dist/miner/scheduler.js +144 -68
- package/dist/search/index.js +810 -176
- package/dist/search/quality.js +31 -0
- package/dist/search/semantic.js +208 -18
- package/dist/search/tokenize.js +44 -0
- package/dist/server.js +649 -343
- package/dist/sync/git.js +203 -72
- package/dist/sync/materialize.js +33 -8
- package/dist/sync/space.js +286 -101
- package/dist/sync/validate.js +204 -0
- package/dist/utils/budget.js +15 -2
- package/dist/utils/fs.js +40 -6
- package/dist/utils/ids.js +128 -14
- package/dist/utils/lock.js +219 -52
- package/hooks/crbro-codex.mjs +284 -0
- package/hooks/crbro-guard.mjs +281 -173
- package/hooks/crbro-hooks.shipped.json +20 -0
- package/hooks/crbro-lifecycle.mjs +16 -4
- package/hooks/crbro-subagent.mjs +180 -180
- package/package.json +12 -8
- package/dist/daemon/daemon.d.ts +0 -25
- package/dist/daemon/daemon.d.ts.map +0 -1
- package/dist/daemon/daemon.js.map +0 -1
- package/dist/daemon/endpoint.d.ts +0 -66
- package/dist/daemon/endpoint.d.ts.map +0 -1
- package/dist/daemon/endpoint.js.map +0 -1
- package/dist/daemon/lines.d.ts +0 -55
- package/dist/daemon/lines.d.ts.map +0 -1
- package/dist/daemon/lines.js.map +0 -1
- package/dist/daemon/proxy.d.ts +0 -55
- package/dist/daemon/proxy.d.ts.map +0 -1
- package/dist/daemon/proxy.js.map +0 -1
- package/dist/engine/backup.d.ts +0 -68
- package/dist/engine/backup.d.ts.map +0 -1
- package/dist/engine/backup.js.map +0 -1
- package/dist/engine/brain.d.ts +0 -79
- package/dist/engine/brain.d.ts.map +0 -1
- package/dist/engine/brain.js.map +0 -1
- package/dist/engine/cortex.d.ts +0 -670
- package/dist/engine/cortex.d.ts.map +0 -1
- package/dist/engine/cortex.js.map +0 -1
- package/dist/engine/dates.d.ts +0 -28
- package/dist/engine/dates.d.ts.map +0 -1
- package/dist/engine/dates.js.map +0 -1
- package/dist/engine/heat.d.ts +0 -28
- package/dist/engine/heat.d.ts.map +0 -1
- package/dist/engine/heat.js.map +0 -1
- package/dist/engine/hippocampus.d.ts +0 -60
- package/dist/engine/hippocampus.d.ts.map +0 -1
- package/dist/engine/hippocampus.js.map +0 -1
- package/dist/engine/hookverify.d.ts +0 -42
- package/dist/engine/hookverify.d.ts.map +0 -1
- package/dist/engine/hookverify.js.map +0 -1
- package/dist/engine/keychain.d.ts +0 -32
- package/dist/engine/keychain.d.ts.map +0 -1
- package/dist/engine/keychain.js.map +0 -1
- package/dist/engine/maintenance.d.ts +0 -218
- package/dist/engine/maintenance.d.ts.map +0 -1
- package/dist/engine/maintenance.js.map +0 -1
- package/dist/engine/modinstall.d.ts +0 -215
- package/dist/engine/modinstall.d.ts.map +0 -1
- package/dist/engine/modinstall.js.map +0 -1
- package/dist/engine/postmortem.d.ts +0 -81
- package/dist/engine/postmortem.d.ts.map +0 -1
- package/dist/engine/postmortem.js.map +0 -1
- package/dist/engine/prefrontal.d.ts +0 -61
- package/dist/engine/prefrontal.d.ts.map +0 -1
- package/dist/engine/prefrontal.js.map +0 -1
- package/dist/engine/secrets.d.ts +0 -30
- package/dist/engine/secrets.d.ts.map +0 -1
- package/dist/engine/secrets.js.map +0 -1
- package/dist/engine/shelf.d.ts +0 -156
- package/dist/engine/shelf.d.ts.map +0 -1
- package/dist/engine/shelf.js.map +0 -1
- package/dist/engine/source.d.ts +0 -6
- package/dist/engine/source.d.ts.map +0 -1
- package/dist/engine/source.js.map +0 -1
- package/dist/engine/synapses.d.ts +0 -82
- package/dist/engine/synapses.d.ts.map +0 -1
- package/dist/engine/synapses.js.map +0 -1
- package/dist/engine/triggers.d.ts +0 -53
- package/dist/engine/triggers.d.ts.map +0 -1
- package/dist/engine/triggers.js.map +0 -1
- package/dist/engine/usage.d.ts +0 -61
- package/dist/engine/usage.d.ts.map +0 -1
- package/dist/engine/usage.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/miner/extractor.d.ts +0 -12
- package/dist/miner/extractor.d.ts.map +0 -1
- package/dist/miner/extractor.js.map +0 -1
- package/dist/miner/index.d.ts +0 -61
- package/dist/miner/index.d.ts.map +0 -1
- package/dist/miner/index.js.map +0 -1
- package/dist/miner/scheduler.d.ts +0 -30
- package/dist/miner/scheduler.d.ts.map +0 -1
- package/dist/miner/scheduler.js.map +0 -1
- package/dist/miner/types.d.ts +0 -11
- package/dist/miner/types.d.ts.map +0 -1
- package/dist/miner/types.js.map +0 -1
- package/dist/search/index.d.ts +0 -323
- package/dist/search/index.d.ts.map +0 -1
- package/dist/search/index.js.map +0 -1
- package/dist/search/semantic.d.ts +0 -80
- package/dist/search/semantic.d.ts.map +0 -1
- package/dist/search/semantic.js.map +0 -1
- package/dist/search/synonyms.d.ts +0 -2
- package/dist/search/synonyms.d.ts.map +0 -1
- package/dist/search/synonyms.js.map +0 -1
- package/dist/search/tokenize.d.ts +0 -39
- package/dist/search/tokenize.d.ts.map +0 -1
- package/dist/search/tokenize.js.map +0 -1
- package/dist/server.d.ts +0 -41
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/sync/git.d.ts +0 -36
- package/dist/sync/git.d.ts.map +0 -1
- package/dist/sync/git.js.map +0 -1
- package/dist/sync/materialize.d.ts +0 -35
- package/dist/sync/materialize.d.ts.map +0 -1
- package/dist/sync/materialize.js.map +0 -1
- package/dist/sync/ops.d.ts +0 -142
- package/dist/sync/ops.d.ts.map +0 -1
- package/dist/sync/ops.js.map +0 -1
- package/dist/sync/space.d.ts +0 -116
- package/dist/sync/space.d.ts.map +0 -1
- package/dist/sync/space.js.map +0 -1
- package/dist/types/index.d.ts +0 -346
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/utils/budget.d.ts +0 -44
- package/dist/utils/budget.d.ts.map +0 -1
- package/dist/utils/budget.js.map +0 -1
- package/dist/utils/fs.d.ts +0 -59
- package/dist/utils/fs.d.ts.map +0 -1
- package/dist/utils/fs.js.map +0 -1
- package/dist/utils/hash.d.ts +0 -20
- package/dist/utils/hash.d.ts.map +0 -1
- package/dist/utils/hash.js.map +0 -1
- package/dist/utils/ids.d.ts +0 -47
- package/dist/utils/ids.d.ts.map +0 -1
- package/dist/utils/ids.js.map +0 -1
- package/dist/utils/lock.d.ts +0 -11
- package/dist/utils/lock.d.ts.map +0 -1
- package/dist/utils/lock.js.map +0 -1
- package/dist/utils/transcripts.d.ts +0 -63
- package/dist/utils/transcripts.d.ts.map +0 -1
- package/dist/utils/transcripts.js.map +0 -1
- package/dist/version.d.ts +0 -26
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ─── Installed hooks stay current on their own ───────────────────
|
|
3
|
+
//
|
|
4
|
+
// install-hooks and install-boot copy hooks/*.mjs out of the package into a
|
|
5
|
+
// stable folder — <Claude config>/crbro-hooks, <CODEX_HOME>/crbro-hooks —
|
|
6
|
+
// because the npx cache moves with every release. Until 2.11 nothing ever
|
|
7
|
+
// updated those copies: an update of CRBRO left the old guard (silent once the
|
|
8
|
+
// trigger index format moved) and the old lifecycle hook (an older copy of the
|
|
9
|
+
// secret filter) running for good.
|
|
10
|
+
//
|
|
11
|
+
// Each of those folders now keeps a manifest of what CRBRO put there: the
|
|
12
|
+
// version and the LF SHA-256 of every file. When the running package is a
|
|
13
|
+
// different build, a copy that is still byte for byte what CRBRO installed —
|
|
14
|
+
// by the manifest, or by the list of every hook CRBRO ever shipped for copies
|
|
15
|
+
// installed before manifests existed — is replaced by the package's. A copy
|
|
16
|
+
// that matches neither was edited by someone: it is left alone and reported,
|
|
17
|
+
// by doctor always and by the boot once (hooks_notice).
|
|
18
|
+
//
|
|
19
|
+
// Runs from crbro_boot (beside the mod's automatic install) and from every
|
|
20
|
+
// install command. Never creates a folder, never adds a hook to a client's
|
|
21
|
+
// settings: it only keeps current what is already there.
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.SHIPPED_HASHES = exports.HOOK_MANIFEST = void 0;
|
|
27
|
+
exports.compareVersions = compareVersions;
|
|
28
|
+
exports.hookDirs = hookDirs;
|
|
29
|
+
exports.packageHooksDir = packageHooksDir;
|
|
30
|
+
exports.readManifest = readManifest;
|
|
31
|
+
exports.shippedHashes = shippedHashes;
|
|
32
|
+
exports.writtenBy = writtenBy;
|
|
33
|
+
exports.installHookFile = installHookFile;
|
|
34
|
+
exports.refreshHooks = refreshHooks;
|
|
35
|
+
exports.hooksNotice = hooksNotice;
|
|
36
|
+
const fs_1 = require("fs");
|
|
37
|
+
const path_1 = __importDefault(require("path"));
|
|
38
|
+
const installio_js_1 = require("./installio.js");
|
|
39
|
+
exports.HOOK_MANIFEST = 'crbro-hooks.manifest.json';
|
|
40
|
+
exports.SHIPPED_HASHES = 'crbro-hooks.shipped.json';
|
|
41
|
+
/**
|
|
42
|
+
* -1, 0 or 1 on x.y.z; a prerelease is below its release; anything that is
|
|
43
|
+
* not x.y.z compares equal, which never counts as newer (same as modinstall).
|
|
44
|
+
*/
|
|
45
|
+
function compareVersions(a, b) {
|
|
46
|
+
const parse = (v) => /^(\d+)\.(\d+)\.(\d+)(-)?/.exec(v);
|
|
47
|
+
const x = parse(a);
|
|
48
|
+
const y = parse(b);
|
|
49
|
+
if (!x || !y)
|
|
50
|
+
return 0;
|
|
51
|
+
for (let i = 1; i <= 3; i++)
|
|
52
|
+
if (Number(x[i]) !== Number(y[i]))
|
|
53
|
+
return Number(x[i]) < Number(y[i]) ? -1 : 1;
|
|
54
|
+
if (!!x[4] !== !!y[4])
|
|
55
|
+
return x[4] ? -1 : 1;
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
function hookDirs(home, env = process.env) {
|
|
59
|
+
return [
|
|
60
|
+
{ client: 'claude-code', dir: path_1.default.join((0, installio_js_1.claudeDir)(home, env), 'crbro-hooks') },
|
|
61
|
+
{ client: 'codex', dir: path_1.default.join((0, installio_js_1.codexHome)(home, env), 'crbro-hooks') },
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
/** The hooks/ folder of the package this code runs from. */
|
|
65
|
+
function packageHooksDir() {
|
|
66
|
+
return path_1.default.join(__dirname, '..', '..', 'hooks');
|
|
67
|
+
}
|
|
68
|
+
function readManifest(dir) {
|
|
69
|
+
try {
|
|
70
|
+
const m = JSON.parse((0, fs_1.readFileSync)(path_1.default.join(dir, exports.HOOK_MANIFEST), 'utf8'));
|
|
71
|
+
if (m && typeof m === 'object' && m.files && typeof m.files === 'object') {
|
|
72
|
+
const files = {};
|
|
73
|
+
for (const [k, v] of Object.entries(m.files)) {
|
|
74
|
+
if (v && typeof v.sha256 === 'string')
|
|
75
|
+
files[k] = { sha256: v.sha256, version: String(v.version ?? '') };
|
|
76
|
+
}
|
|
77
|
+
return { version: String(m.version ?? ''), files, ...(Array.isArray(m.noticed) ? { noticed: m.noticed } : {}) };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch { /* none yet */ }
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
function writeManifest(dir, m) {
|
|
84
|
+
const file = path_1.default.join(dir, exports.HOOK_MANIFEST);
|
|
85
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
86
|
+
(0, fs_1.writeFileSync)(tmp, JSON.stringify(m, null, 2) + '\n', 'utf8');
|
|
87
|
+
(0, fs_1.renameSync)(tmp, file);
|
|
88
|
+
}
|
|
89
|
+
/** Every LF hash CRBRO shipped for a hook before manifests existed, with the last version that shipped it. */
|
|
90
|
+
function shippedHashes(pkgHooks) {
|
|
91
|
+
try {
|
|
92
|
+
const raw = JSON.parse((0, fs_1.readFileSync)(path_1.default.join(pkgHooks, exports.SHIPPED_HASHES), 'utf8'));
|
|
93
|
+
const out = {};
|
|
94
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
95
|
+
if (!v || typeof v !== 'object' || Array.isArray(v))
|
|
96
|
+
continue;
|
|
97
|
+
out[k] = Object.fromEntries(Object.entries(v).filter(([, x]) => typeof x === 'string'));
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Which CRBRO version wrote this copy, when it is one CRBRO wrote unmodified:
|
|
107
|
+
* by the folder's manifest, or by the list of hooks shipped before manifests.
|
|
108
|
+
* Null when it matches neither: somebody edited it.
|
|
109
|
+
*/
|
|
110
|
+
function writtenBy(name, sha, manifest, shipped) {
|
|
111
|
+
const m = manifest?.files[name];
|
|
112
|
+
if (m && m.sha256 === sha)
|
|
113
|
+
return m.version || '0.0.0';
|
|
114
|
+
return shipped[name]?.[sha] ?? null;
|
|
115
|
+
}
|
|
116
|
+
function pkgVersion(pkgHooks) {
|
|
117
|
+
try {
|
|
118
|
+
return String(JSON.parse((0, fs_1.readFileSync)(path_1.default.join(pkgHooks, '..', 'package.json'), 'utf8')).version || 'unknown');
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return 'unknown';
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function mjsIn(dir) {
|
|
125
|
+
try {
|
|
126
|
+
return (0, fs_1.readdirSync)(dir).filter(f => f.endsWith('.mjs') && (0, fs_1.statSync)(path_1.default.join(dir, f)).isFile()).sort();
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/** Copies one packaged hook into a folder (tmp + rename) and records it in that folder's manifest. */
|
|
133
|
+
function installHookFile(pkgHooks, name, targetDir) {
|
|
134
|
+
(0, fs_1.mkdirSync)(targetDir, { recursive: true });
|
|
135
|
+
const src = (0, fs_1.readFileSync)(path_1.default.join(pkgHooks, name));
|
|
136
|
+
const target = path_1.default.join(targetDir, name);
|
|
137
|
+
const version = pkgVersion(pkgHooks);
|
|
138
|
+
const before = readManifest(targetDir);
|
|
139
|
+
// A copy somebody edited is kept aside before it is replaced.
|
|
140
|
+
try {
|
|
141
|
+
const mine = (0, installio_js_1.lfSha)((0, fs_1.readFileSync)(target));
|
|
142
|
+
if (mine !== (0, installio_js_1.lfSha)(src) && writtenBy(name, mine, before, shippedHashes(pkgHooks)) === null)
|
|
143
|
+
(0, installio_js_1.backupFile)(target);
|
|
144
|
+
}
|
|
145
|
+
catch { /* not there yet */ }
|
|
146
|
+
let same = false;
|
|
147
|
+
try {
|
|
148
|
+
same = (0, fs_1.readFileSync)(target).equals(src);
|
|
149
|
+
}
|
|
150
|
+
catch { /* not there */ }
|
|
151
|
+
const entry = before?.files[name];
|
|
152
|
+
if (same && entry?.sha256 === (0, installio_js_1.lfSha)(src) && entry.version === version)
|
|
153
|
+
return target; // idempotent
|
|
154
|
+
if (!same) {
|
|
155
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
156
|
+
try {
|
|
157
|
+
(0, fs_1.writeFileSync)(tmp, src);
|
|
158
|
+
(0, fs_1.renameSync)(tmp, target);
|
|
159
|
+
}
|
|
160
|
+
catch (e) {
|
|
161
|
+
(0, fs_1.rmSync)(tmp, { force: true });
|
|
162
|
+
throw e;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const m = before ?? { version, files: {} };
|
|
166
|
+
m.version = version;
|
|
167
|
+
m.files[name] = { sha256: (0, installio_js_1.lfSha)(src), version };
|
|
168
|
+
writeManifest(targetDir, m);
|
|
169
|
+
return target;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Checks every installed CRBRO hook against the package; refreshes the
|
|
173
|
+
* unmodified ones unless dryRun. Never throws: a copy it cannot write is
|
|
174
|
+
* reported with its error and tried again next time.
|
|
175
|
+
*/
|
|
176
|
+
function refreshHooks(opts) {
|
|
177
|
+
const pkgHooks = opts.pkgHooks ?? packageHooksDir();
|
|
178
|
+
const shipped = shippedHashes(pkgHooks);
|
|
179
|
+
const version = pkgVersion(pkgHooks);
|
|
180
|
+
const out = [];
|
|
181
|
+
for (const { client, dir } of hookDirs(opts.home, opts.env ?? process.env)) {
|
|
182
|
+
if (!(0, fs_1.existsSync)(dir))
|
|
183
|
+
continue;
|
|
184
|
+
const manifest = readManifest(dir);
|
|
185
|
+
let manifestChanged = false;
|
|
186
|
+
const next = manifest ? { ...manifest, files: { ...manifest.files } } : { version, files: {} };
|
|
187
|
+
for (const name of mjsIn(dir)) {
|
|
188
|
+
let pkg;
|
|
189
|
+
try {
|
|
190
|
+
pkg = (0, fs_1.readFileSync)(path_1.default.join(pkgHooks, name));
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
continue;
|
|
194
|
+
} // not a hook of this package
|
|
195
|
+
let mine;
|
|
196
|
+
try {
|
|
197
|
+
mine = (0, fs_1.readFileSync)(path_1.default.join(dir, name));
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
const pkgSha = (0, installio_js_1.lfSha)(pkg);
|
|
203
|
+
const mineSha = (0, installio_js_1.lfSha)(mine);
|
|
204
|
+
if (mineSha === pkgSha) {
|
|
205
|
+
out.push({ client, dir, name, state: 'current' });
|
|
206
|
+
if (next.files[name]?.sha256 !== pkgSha) {
|
|
207
|
+
next.files[name] = { sha256: pkgSha, version };
|
|
208
|
+
manifestChanged = true;
|
|
209
|
+
}
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const by = writtenBy(name, mineSha, manifest, shipped);
|
|
213
|
+
if (by === null) {
|
|
214
|
+
out.push({ client, dir, name, state: 'modified' });
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
// Only a newer release replaces it.
|
|
218
|
+
if (compareVersions(version, by) <= 0) {
|
|
219
|
+
out.push({ client, dir, name, state: 'other_build' });
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const row = { client, dir, name, state: 'stale' };
|
|
223
|
+
if (!opts.dryRun) {
|
|
224
|
+
const target = path_1.default.join(dir, name);
|
|
225
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
226
|
+
try {
|
|
227
|
+
// Checked once more right before: an edit made since the read is not overwritten.
|
|
228
|
+
if ((0, installio_js_1.lfSha)((0, fs_1.readFileSync)(target)) !== mineSha)
|
|
229
|
+
throw new Error('changed while being refreshed');
|
|
230
|
+
(0, fs_1.writeFileSync)(tmp, pkg);
|
|
231
|
+
(0, fs_1.renameSync)(tmp, target);
|
|
232
|
+
row.refreshed = true;
|
|
233
|
+
next.files[name] = { sha256: pkgSha, version };
|
|
234
|
+
manifestChanged = true;
|
|
235
|
+
}
|
|
236
|
+
catch (e) {
|
|
237
|
+
(0, fs_1.rmSync)(tmp, { force: true });
|
|
238
|
+
row.error = e.message;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
out.push(row);
|
|
242
|
+
}
|
|
243
|
+
if (!opts.dryRun && manifestChanged) {
|
|
244
|
+
try {
|
|
245
|
+
next.version = version;
|
|
246
|
+
writeManifest(dir, next);
|
|
247
|
+
}
|
|
248
|
+
catch { /* the next run writes it */ }
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return out;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* The sentence for the boot, once per edited copy: the copies CRBRO did not
|
|
255
|
+
* refresh because someone changed them, and refreshes that failed. Null when
|
|
256
|
+
* there is nothing new to say. Marks what it said in the manifests.
|
|
257
|
+
*/
|
|
258
|
+
function hooksNotice(copies) {
|
|
259
|
+
const edited = copies.filter(c => c.state === 'modified');
|
|
260
|
+
const failed = copies.filter(c => c.error);
|
|
261
|
+
const fresh = [];
|
|
262
|
+
for (const c of edited) {
|
|
263
|
+
let sha = '';
|
|
264
|
+
try {
|
|
265
|
+
sha = (0, installio_js_1.lfSha)((0, fs_1.readFileSync)(path_1.default.join(c.dir, c.name)));
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
const m = readManifest(c.dir);
|
|
271
|
+
const key = `${c.name}:${sha}`;
|
|
272
|
+
if (m?.noticed?.includes(key))
|
|
273
|
+
continue;
|
|
274
|
+
fresh.push(c);
|
|
275
|
+
try {
|
|
276
|
+
const next = m ?? { version: '', files: {} };
|
|
277
|
+
next.noticed = [...(next.noticed ?? []), key].slice(-50);
|
|
278
|
+
writeManifest(c.dir, next);
|
|
279
|
+
}
|
|
280
|
+
catch { /* said again next time */ }
|
|
281
|
+
}
|
|
282
|
+
if (!fresh.length && !failed.length)
|
|
283
|
+
return null;
|
|
284
|
+
const parts = [];
|
|
285
|
+
if (fresh.length) {
|
|
286
|
+
parts.push(`CRBRO did not update ${fresh.length} installed hook file(s) because they differ from what CRBRO installed (edited by hand?): ` +
|
|
287
|
+
fresh.map(c => path_1.default.join(c.dir, c.name)).join(', ') + '.');
|
|
288
|
+
parts.push('They keep running as they are. To take this version\'s copy, run the install command again (npx crbro-memory install-hooks …, or install), which replaces them after a backup; npx crbro-memory doctor lists them.');
|
|
289
|
+
}
|
|
290
|
+
if (failed.length) {
|
|
291
|
+
parts.push(`CRBRO could not refresh ${failed.map(c => path_1.default.join(c.dir, c.name)).join(', ')} (${failed[0].error}); it tries again at the next start.`);
|
|
292
|
+
}
|
|
293
|
+
parts.push('Tell the user this once, briefly, in their language.');
|
|
294
|
+
return parts.join(' ');
|
|
295
|
+
}
|
|
296
|
+
//# sourceMappingURL=hookrefresh.js.map
|
|
@@ -24,6 +24,7 @@ const crypto_1 = require("crypto");
|
|
|
24
24
|
const fs_1 = require("fs");
|
|
25
25
|
const path_1 = __importDefault(require("path"));
|
|
26
26
|
const os_1 = __importDefault(require("os"));
|
|
27
|
+
const hookrefresh_js_1 = require("./hookrefresh.js");
|
|
27
28
|
function sha256(buf) {
|
|
28
29
|
return (0, crypto_1.createHash)('sha256').update(buf).digest('hex');
|
|
29
30
|
}
|
|
@@ -89,6 +90,13 @@ function verifyHooks(opts) {
|
|
|
89
90
|
.flatMap(scriptsIn);
|
|
90
91
|
const referenced = new Set(crbroScripts.map(p => path_1.default.basename(p)));
|
|
91
92
|
const hooks = [];
|
|
93
|
+
const known = (0, hookrefresh_js_1.shippedHashes)(opts.packageDir);
|
|
94
|
+
const manifest = (0, hookrefresh_js_1.readManifest)(opts.installedDir);
|
|
95
|
+
let version = '';
|
|
96
|
+
try {
|
|
97
|
+
version = String(JSON.parse((0, fs_1.readFileSync)(path_1.default.join(opts.packageDir, '..', 'package.json'), 'utf8')).version || '');
|
|
98
|
+
}
|
|
99
|
+
catch { /* unknown */ }
|
|
92
100
|
for (const name of shipped) {
|
|
93
101
|
const pkg = (0, fs_1.readFileSync)(path_1.default.join(opts.packageDir, name));
|
|
94
102
|
const check = { name, status: 'not_installed', package_sha256: sha256(pkg), in_settings: referenced.has(name) };
|
|
@@ -100,8 +108,10 @@ function verifyHooks(opts) {
|
|
|
100
108
|
check.status = 'same';
|
|
101
109
|
else if (lfSha(mine) === lfSha(pkg))
|
|
102
110
|
check.status = 'line_endings_only';
|
|
103
|
-
else
|
|
104
|
-
|
|
111
|
+
else {
|
|
112
|
+
const by = (0, hookrefresh_js_1.writtenBy)(name, lfSha(mine), manifest, known);
|
|
113
|
+
check.status = by === null ? 'different' : (0, hookrefresh_js_1.compareVersions)(version, by) > 0 ? 'older_release' : 'other_build';
|
|
114
|
+
}
|
|
105
115
|
}
|
|
106
116
|
hooks.push(check);
|
|
107
117
|
}
|
|
@@ -113,7 +123,7 @@ function verifyHooks(opts) {
|
|
|
113
123
|
}
|
|
114
124
|
const missing = [...new Set(crbroScripts)].filter(p => !(0, fs_1.existsSync)(p));
|
|
115
125
|
const ok = !settingsUnreadable && missing.length === 0 &&
|
|
116
|
-
hooks.every(h => h.status === 'same' || h.status === 'line_endings_only' || h.status === 'not_installed');
|
|
126
|
+
hooks.every(h => h.status === 'same' || h.status === 'line_endings_only' || h.status === 'not_installed' || h.status === 'older_release' || h.status === 'other_build');
|
|
117
127
|
return {
|
|
118
128
|
package_dir: opts.packageDir,
|
|
119
129
|
installed_dir: opts.installedDir,
|
|
@@ -127,6 +137,8 @@ function verifyHooks(opts) {
|
|
|
127
137
|
const STATUS_TEXT = {
|
|
128
138
|
same: '✅ same as the package',
|
|
129
139
|
line_endings_only: '✅ same except line endings (CRLF/LF)',
|
|
140
|
+
older_release: '⚪ from an older CRBRO release, unmodified: refreshed at the next crbro_boot',
|
|
141
|
+
other_build: '⚪ unmodified, from another CRBRO build of this version or a newer one',
|
|
130
142
|
different: '⚠️ DIFFERENT from the package',
|
|
131
143
|
not_installed: '⚪ not installed',
|
|
132
144
|
unknown: '⚠️ not a hook of this package',
|