context-mode 1.0.126 → 1.0.127
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/build/server.js +7 -0
- package/build/util/project-dir.d.ts +13 -0
- package/build/util/project-dir.js +11 -2
- package/cli.bundle.mjs +3 -3
- package/hooks/core/routing.mjs +114 -22
- package/hooks/gemini-cli/sessionstart.mjs +8 -6
- package/hooks/security.bundle.mjs +1 -0
- package/hooks/sessionstart.mjs +18 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +3 -3
- package/scripts/plugin-cache-integrity.mjs +101 -21
- package/server.bundle.mjs +75 -75
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* developer ran during `pretest`.
|
|
30
30
|
*/
|
|
31
31
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
32
|
-
import { join, relative } from "node:path";
|
|
32
|
+
import { join, relative, sep } from "node:path";
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Walk a directory recursively, returning a flat list of relative file
|
|
@@ -102,22 +102,15 @@ export function derivePluginManifest({ pkg, pluginRoot }) {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
105
|
+
* LEGACY_FALLBACK — the v1.0.126 hardcoded REQUIRED_RUNTIME_SIBLINGS,
|
|
106
|
+
* preserved verbatim. Forms the union seed for the algorithmic set so
|
|
107
|
+
* the post-558 contract is strictly additive over the pre-558 contract
|
|
108
|
+
* (no required sibling ever silently disappears).
|
|
109
109
|
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* without cli.bundle.mjs `context-mode doctor` can't run.
|
|
113
|
-
* - hooks/{5 hook scripts}.mjs are spawned per Claude Code event.
|
|
114
|
-
* Missing any one produces a silent hook failure.
|
|
115
|
-
*
|
|
116
|
-
* Other files in package.json files[] (insight/, configs/, README, …)
|
|
117
|
-
* are not boot-critical, so missing them is a "warn"-class issue
|
|
118
|
-
* surfaced only via the doctor — never enough to fail-fast at boot.
|
|
110
|
+
* Also acts as a safety net when `package.json` is unreadable — the
|
|
111
|
+
* boot gate stays loud even if the publish manifest is corrupted.
|
|
119
112
|
*/
|
|
120
|
-
const
|
|
113
|
+
const LEGACY_FALLBACK = Object.freeze([
|
|
121
114
|
"server.bundle.mjs",
|
|
122
115
|
"cli.bundle.mjs",
|
|
123
116
|
join("hooks", "pretooluse.mjs"),
|
|
@@ -127,6 +120,94 @@ const REQUIRED_RUNTIME_SIBLINGS = Object.freeze([
|
|
|
127
120
|
join("hooks", "userpromptsubmit.mjs"),
|
|
128
121
|
]);
|
|
129
122
|
|
|
123
|
+
/**
|
|
124
|
+
* SOFT_FALLBACK_BUNDLES — bundles that already implement
|
|
125
|
+
* bundle-first / build-fallback resolution (via session-loaders.mjs or
|
|
126
|
+
* session-helpers.mjs). Their absence on a published install is
|
|
127
|
+
* gracefully recoverable, so they MUST NOT join the fail-fast boot
|
|
128
|
+
* gate — the gate would refuse to start a working install.
|
|
129
|
+
*
|
|
130
|
+
* The security bundle is intentionally NOT here: its absence creates a
|
|
131
|
+
* silent fail-OPEN regression (#558), so it IS boot-critical.
|
|
132
|
+
*/
|
|
133
|
+
const SOFT_FALLBACK_BUNDLES = new Set([
|
|
134
|
+
"hooks/session-extract.bundle.mjs",
|
|
135
|
+
"hooks/session-snapshot.bundle.mjs",
|
|
136
|
+
"hooks/session-db.bundle.mjs",
|
|
137
|
+
"hooks/session-attribution.bundle.mjs",
|
|
138
|
+
]);
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Algorithmically extract every esbuild output path from
|
|
142
|
+
* `package.json scripts.bundle`. The bundle script is the SINGLE
|
|
143
|
+
* SOURCE OF TRUTH for "what bundles this build produces" — parsing
|
|
144
|
+
* its `--outfile=…` arguments avoids the parallel-list trap that
|
|
145
|
+
* bit Algo-D4 v1.0.126 (the hardcoded REQUIRED list lagged the
|
|
146
|
+
* actual bundle output).
|
|
147
|
+
*
|
|
148
|
+
* Returns POSIX-style relative paths (forward slashes) for stable
|
|
149
|
+
* comparison with SOFT_FALLBACK_BUNDLES. Caller normalizes to
|
|
150
|
+
* `path.join` shape before pluginRoot-relative resolution.
|
|
151
|
+
*/
|
|
152
|
+
function extractBundleOutfiles(pkg) {
|
|
153
|
+
const script = pkg?.scripts?.bundle;
|
|
154
|
+
if (typeof script !== "string") return [];
|
|
155
|
+
const out = new Set();
|
|
156
|
+
// Match every `--outfile=<path>` token (path is whitespace-delimited
|
|
157
|
+
// because the script chains commands with `&&`).
|
|
158
|
+
const re = /--outfile=(\S+)/g;
|
|
159
|
+
let m;
|
|
160
|
+
while ((m = re.exec(script)) !== null) {
|
|
161
|
+
out.add(m[1]);
|
|
162
|
+
}
|
|
163
|
+
return [...out];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Algorithmic — derive the boot-critical sibling set as the union of:
|
|
168
|
+
* 1. LEGACY_FALLBACK (the v1.0.126 contract, preserved verbatim).
|
|
169
|
+
* 2. Every esbuild output path from `package.json scripts.bundle`
|
|
170
|
+
* that is NOT in SOFT_FALLBACK_BUNDLES.
|
|
171
|
+
*
|
|
172
|
+
* Why algorithmic instead of hardcoded:
|
|
173
|
+
*
|
|
174
|
+
* v1.0.126 shipped Algo-D4 with a hardcoded REQUIRED_RUNTIME_SIBLINGS
|
|
175
|
+
* array that omitted `hooks/security.bundle.mjs` (the bundle didn't
|
|
176
|
+
* ship until v1.0.127). The hardcoded list would need manual
|
|
177
|
+
* extension every time a runtime bundle is added — the same trap
|
|
178
|
+
* would re-bite the next bundle. Deriving from `scripts.bundle`
|
|
179
|
+
* closes the trap: any new bundle output is auto-gated unless it
|
|
180
|
+
* joins the soft-fallback whitelist (which is itself an explicit
|
|
181
|
+
* architectural decision, not a maintenance burden). (#558)
|
|
182
|
+
*
|
|
183
|
+
* Returns OS-native-separator relative paths (suitable for
|
|
184
|
+
* `path.join(pluginRoot, …)`).
|
|
185
|
+
*
|
|
186
|
+
* If `package.json` is unreadable, returns LEGACY_FALLBACK as a
|
|
187
|
+
* safety net so the boot gate never goes silent due to a parse
|
|
188
|
+
* error in the publish manifest.
|
|
189
|
+
*/
|
|
190
|
+
export function getRequiredRuntimeSiblings(pluginRoot) {
|
|
191
|
+
let pkg;
|
|
192
|
+
try {
|
|
193
|
+
pkg = JSON.parse(readFileSync(join(pluginRoot, "package.json"), "utf-8"));
|
|
194
|
+
} catch {
|
|
195
|
+
return [...LEGACY_FALLBACK];
|
|
196
|
+
}
|
|
197
|
+
const required = new Set(LEGACY_FALLBACK);
|
|
198
|
+
for (const outfile of extractBundleOutfiles(pkg)) {
|
|
199
|
+
// Normalize to POSIX for soft-fallback membership check —
|
|
200
|
+
// scripts.bundle is hand-authored with forward slashes already,
|
|
201
|
+
// but be defensive in case a Windows-authored package.json ever
|
|
202
|
+
// reaches us.
|
|
203
|
+
const posix = outfile.split(sep).join("/");
|
|
204
|
+
if (SOFT_FALLBACK_BUNDLES.has(posix)) continue;
|
|
205
|
+
// Convert back to OS-native sep for downstream filesystem ops.
|
|
206
|
+
required.add(posix.split("/").join(sep));
|
|
207
|
+
}
|
|
208
|
+
return [...required];
|
|
209
|
+
}
|
|
210
|
+
|
|
130
211
|
/**
|
|
131
212
|
* Verify boot-critical siblings exist at pluginRoot.
|
|
132
213
|
*
|
|
@@ -134,15 +215,14 @@ const REQUIRED_RUNTIME_SIBLINGS = Object.freeze([
|
|
|
134
215
|
* stderr. The caller (start.mjs at boot, src/cli.ts at doctor) decides
|
|
135
216
|
* the failure surface (fail-fast exit 2 vs. doctor diagnostic).
|
|
136
217
|
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* check fails — that's the "drift between contract and tarball" trap.
|
|
218
|
+
* Required-set is computed by `getRequiredRuntimeSiblings()` —
|
|
219
|
+
* algorithmically derived from `package.json files[]` filtered to the
|
|
220
|
+
* RUNTIME_CRITICAL_PATTERN. Drift between publish manifest and runtime
|
|
221
|
+
* contract becomes architecturally impossible (#558).
|
|
142
222
|
*/
|
|
143
223
|
export function assertPluginCacheIntegrity({ pluginRoot }) {
|
|
144
224
|
const missing = [];
|
|
145
|
-
for (const rel of
|
|
225
|
+
for (const rel of getRequiredRuntimeSiblings(pluginRoot)) {
|
|
146
226
|
const abs = join(pluginRoot, rel);
|
|
147
227
|
if (!existsSync(abs)) missing.push(abs);
|
|
148
228
|
}
|