@karmaniverous/jeeves 0.6.0-2 → 0.6.0-3
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/README.md +10 -4
- package/dist/cli/jeeves/index.js +57 -22
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -226,10 +226,13 @@ OpenClaw must already be installed; `jeeves` checks for it and never installs it
|
|
|
226
226
|
|
|
227
227
|
1. Renders the SOUL.md/AGENTS.md managed blocks (your content outside the markers is kept) and the core config if it's missing. It never writes TOOLS.md, HEARTBEAT.md, anything under `skills/` or the spec templates (those ship with the jeeves-design skill in jeeves-tools).
|
|
228
228
|
2. For each plugin (default: `runner`, `watcher`, `server`, `meta` at `latest`), resolves an exact version with `npm view`, then runs `openclaw plugins install npm:<pkg>@<version> --pin --accept-capabilities --force`. `--force` is required for any non-ClawHub source, and it also overwrites an existing install, which is how updates land. The install is skipped when that exact version is already installed. The CLI reads OpenClaw's install records once with `openclaw plugins inspect --all --json` (no plugin code is loaded) and skips a plugin only if its record has `source: "npm"`, names the same package, and records the same version, and the loaded plugin reports that version too. A v0.x path install, a leftover legacy copy, or any record it cannot read means a reinstall. `--force-reinstall` always reinstalls. Steps 3 and 4 run either way.
|
|
229
|
+
|
|
230
|
+
Each plugin's config (`plugins.entries.<id>.config.<key>`: `configRoot`, `apiUrl`, the server `pluginKey`, and any other value the CLI resolves, see [Plugin config](#plugin-config)) is written **before** that plugin's install, with its own `openclaw config set --batch-file` call, and each install is followed by a migration sweep (see below). With a running gateway, `openclaw plugins install` activates the plugin at once, and the plugins read their config (e.g. `configRoot`) at registration: config written afterwards is too late, and a manifest default (the watcher's `configRoot`) would win. `plugins.entries` is also the only source CLI contexts such as `openclaw doctor --lint` see (they don't have `JEEVES_CONFIG_ROOT`). The order per plugin is config batch, install, sweep. Config for a plugin that is already installed at its version, and every value that is already set, is handled in step 4.
|
|
231
|
+
|
|
229
232
|
3. Removes any legacy `<openclaw dir>/extensions/<id>` copy left by the v0.x installer, but only if its `package.json` names the expected package.
|
|
230
|
-
4.
|
|
233
|
+
4. Sets `plugins.entries.<id>.hooks.allowConversationAccess: true` for plugins that [declare conversation hooks](#declaring-conversation-hooks) (after the install: OpenClaw checks the installed package), plus the config of plugins that were not installed in this run, with one final `openclaw config set --batch-file <file>` call. When nothing was installed, a migration sweep (`openclaw plugins inspect --all --json`; its output is not shown and a failure is only logged) runs first. Every batch file is created owner-only in a fresh temp directory (mode `0600` in a `0700` directory on Linux/macOS; on Windows the directory ACL is reduced to the current user with `icacls`) and deleted afterwards, so no value, secret or not, appears on a command line. Every write targets a leaf path, so unrelated keys are kept. `plugins.installs` is never written.
|
|
231
234
|
|
|
232
|
-
Right after an install, OpenClaw can refuse to edit a plugin's config until it finishes that plugin's data/settings upgrade (`Plugin "<id>" data/settings upgrade is unfinished: ... has not converged`). This happens when the gateway started with `plugins.entries.<id>` configured before the package was installed: OpenClaw records a pending migration for that plugin, installing it does not clear the record, and a refused `openclaw config set` doesn't either. OpenClaw resolves such records in the startup preflight of ordinary CLI commands (not `config`, and not `plugins list`), which is why
|
|
235
|
+
Right after an install, OpenClaw can refuse to edit a plugin's config until it finishes that plugin's data/settings upgrade (`Plugin "<id>" data/settings upgrade is unfinished: ... has not converged`). This happens when the gateway started with `plugins.entries.<id>` configured before the package was installed: OpenClaw records a pending migration for that plugin, installing it does not clear the record, and a refused `openclaw config set` doesn't either. OpenClaw resolves such records in the startup preflight of ordinary CLI commands (not `config`, and not `plugins list`), which is why a sweep runs `plugins inspect` right after each install. Every config batch (before an install and the final one) goes through the same retry: only that refusal is retried: the sweep runs again, then the same batch file is resubmitted after 2s, 4s, 8s, 16s, then every 30s, for up to 120s of waiting in total, with one log line per retry. If the plugin still hasn't converged, the command fails and names the plugin; wait and rerun it (the writes are idempotent). Any other error fails at once. The `openclaw config unset` repairs after an uninstall follow the same rule. A dry run never retries.
|
|
233
236
|
|
|
234
237
|
Plugin specs can be short (`watcher`, `watcher@1.2.3`, `runner@^1`) or full (`@karmaniverous/jeeves-watcher-openclaw@1.2.3`). Only `@karmaniverous/jeeves-*-openclaw` packages are accepted. `--content-only` skips the plugins.
|
|
235
238
|
|
|
@@ -304,8 +307,8 @@ $ jeeves install watcher --dry-run
|
|
|
304
307
|
@karmaniverous/jeeves-watcher-openclaw@0.16.0 (legacy copy found; conversation hooks: before_prompt_build)
|
|
305
308
|
…
|
|
306
309
|
[dry-run] openclaw plugins install npm:@karmaniverous/jeeves-watcher-openclaw@0.16.0 --pin --accept-capabilities --force
|
|
307
|
-
[dry-run] remove legacy plugin copy: /home/jeeves/.openclaw/extensions/jeeves-watcher-openclaw
|
|
308
310
|
[dry-run] openclaw plugins inspect --all --json (lets OpenClaw clear pending plugin migrations)
|
|
311
|
+
[dry-run] remove legacy plugin copy: /home/jeeves/.openclaw/extensions/jeeves-watcher-openclaw
|
|
309
312
|
[dry-run] openclaw config set --batch-file <private temp file>
|
|
310
313
|
[dry-run] batch file content: [{"path":"plugins.entries.jeeves-watcher-openclaw.hooks.allowConversationAccess","value":true}]
|
|
311
314
|
```
|
|
@@ -323,9 +326,12 @@ Plugin config:
|
|
|
323
326
|
…
|
|
324
327
|
[dry-run] set keys._plugin = <redacted> in /srv/jeeves/config/jeeves-server/config.json (currently unset; backup /srv/jeeves/config/jeeves-server/config.json.bak-<timestamp> first, then atomic write; restart jeeves-server afterwards)
|
|
325
328
|
…
|
|
326
|
-
[dry-run] openclaw plugins inspect --all --json (lets OpenClaw clear pending plugin migrations)
|
|
327
329
|
[dry-run] openclaw config set --batch-file <private temp file>
|
|
328
330
|
[dry-run] batch file content: [{"path":"plugins.entries.jeeves-server-openclaw.config.configRoot","value":"/srv/jeeves/config"},{"path":"plugins.entries.jeeves-server-openclaw.config.apiUrl","value":"http://127.0.0.1:1934"},{"path":"plugins.entries.jeeves-server-openclaw.config.pluginKey","value":"<redacted>"}]
|
|
331
|
+
[dry-run] openclaw plugins install npm:@karmaniverous/jeeves-server-openclaw@0.14.0 --pin --accept-capabilities --force
|
|
332
|
+
[dry-run] openclaw plugins inspect --all --json (lets OpenClaw clear pending plugin migrations)
|
|
333
|
+
[dry-run] openclaw config set --batch-file <private temp file>
|
|
334
|
+
[dry-run] batch file content: [{"path":"plugins.entries.jeeves-server-openclaw.hooks.allowConversationAccess","value":true}]
|
|
329
335
|
```
|
|
330
336
|
|
|
331
337
|
A live run stops at the first failing step. A non-zero exit from any `openclaw` or `npm` command makes `jeeves` exit 1 and print the command and its error output. Commands are spawned with an argument vector and no shell, so the same invocation works on Linux, macOS and Windows.
|
package/dist/cli/jeeves/index.js
CHANGED
|
@@ -140,14 +140,14 @@ const DEFAULT_PORTS = {
|
|
|
140
140
|
* Core library version, inlined at build time.
|
|
141
141
|
*
|
|
142
142
|
* @remarks
|
|
143
|
-
* The `0.6.0-
|
|
143
|
+
* The `0.6.0-2` placeholder is replaced by
|
|
144
144
|
* `@rollup/plugin-replace` during the build with the actual version
|
|
145
145
|
* from `package.json`. This ensures the correct version survives
|
|
146
146
|
* when consumers bundle core into their own dist (where runtime
|
|
147
147
|
* `import.meta.url`-based resolution would find the wrong package.json).
|
|
148
148
|
*/
|
|
149
149
|
/** The core library version from package.json (inlined at build time). */
|
|
150
|
-
const CORE_VERSION = '0.6.0-
|
|
150
|
+
const CORE_VERSION = '0.6.0-2';
|
|
151
151
|
|
|
152
152
|
/**
|
|
153
153
|
* Shared internal utility functions.
|
|
@@ -3185,7 +3185,9 @@ function installOptionsFromCli(opts, configRoot) {
|
|
|
3185
3185
|
* install records, declared hooks, legacy directories) and then either
|
|
3186
3186
|
* printed (`--dry-run`) or executed by {@link executePlan}. Step order encodes
|
|
3187
3187
|
* the safety rules: legacy copies are removed only after the npm install
|
|
3188
|
-
* succeeded; config
|
|
3188
|
+
* succeeded; a plugin's config is written before its install (a running
|
|
3189
|
+
* gateway activates it at once); config repair runs only after uninstall
|
|
3190
|
+
* succeeded. Config writes
|
|
3189
3191
|
* are `configSetBatch` steps, run as `openclaw config set --batch-file` with an
|
|
3190
3192
|
* owner-only temp file, so no value (secret or not) is on a command line.
|
|
3191
3193
|
*
|
|
@@ -3196,6 +3198,32 @@ const openclaw = (args) => ({
|
|
|
3196
3198
|
command: OPENCLAW_BIN,
|
|
3197
3199
|
args,
|
|
3198
3200
|
});
|
|
3201
|
+
/** A config batch step (secrets attached for redaction when present). */
|
|
3202
|
+
const configBatch = (ops, secrets) => ({
|
|
3203
|
+
kind: 'configSetBatch',
|
|
3204
|
+
ops,
|
|
3205
|
+
...(secrets.length > 0 ? { redact: [...secrets] } : {}),
|
|
3206
|
+
});
|
|
3207
|
+
/**
|
|
3208
|
+
* Split resolved config ops into per-plugin groups for the given plugin ids
|
|
3209
|
+
* and the rest.
|
|
3210
|
+
*
|
|
3211
|
+
* @param ops - Resolved `plugins.entries.<id>.config.<key>` operations.
|
|
3212
|
+
* @param pluginIds - Plugins whose config is written before their install.
|
|
3213
|
+
* @returns Ops by plugin id, and the ops not claimed by any of them.
|
|
3214
|
+
*/
|
|
3215
|
+
function splitConfigOps(ops, pluginIds) {
|
|
3216
|
+
const byPlugin = new Map();
|
|
3217
|
+
const rest = [];
|
|
3218
|
+
for (const op of ops) {
|
|
3219
|
+
const owner = pluginIds.find((id) => op.path.startsWith(configValuePath(id, '')));
|
|
3220
|
+
if (owner === undefined)
|
|
3221
|
+
rest.push(op);
|
|
3222
|
+
else
|
|
3223
|
+
byPlugin.set(owner, [...(byPlugin.get(owner) ?? []), op]);
|
|
3224
|
+
}
|
|
3225
|
+
return { byPlugin, rest };
|
|
3226
|
+
}
|
|
3199
3227
|
/**
|
|
3200
3228
|
* Build the install/update plan.
|
|
3201
3229
|
*
|
|
@@ -3203,22 +3231,33 @@ const openclaw = (args) => ({
|
|
|
3203
3231
|
* @param plugins - Current `plugins` config slice.
|
|
3204
3232
|
* @param config - Resolved plugin config to write (optional).
|
|
3205
3233
|
* @returns The server `keys._plugin` write (if planned; first, so a failure
|
|
3206
|
-
* there, e.g. a held lock, leaves OpenClaw untouched)
|
|
3207
|
-
*
|
|
3208
|
-
* (when
|
|
3209
|
-
*
|
|
3210
|
-
*
|
|
3211
|
-
*
|
|
3212
|
-
*
|
|
3234
|
+
* there, e.g. a held lock, leaves OpenClaw untouched); then, per target not
|
|
3235
|
+
* yet installed at its version: a config batch with that plugin's resolved
|
|
3236
|
+
* `plugins.entries.<id>.config` values (when any need writing), the
|
|
3237
|
+
* `openclaw plugins install`, and a migration sweep. A running gateway
|
|
3238
|
+
* activates each plugin as soon as it is installed and plugins read their
|
|
3239
|
+
* config (e.g. `configRoot`) at registration, so the config must already
|
|
3240
|
+
* be there; it also overrides manifest defaults (e.g. the watcher's
|
|
3241
|
+
* `configRoot`). Then legacy removals, then one final batch (preceded by
|
|
3242
|
+
* a sweep when nothing was installed) with hook access (only for targets
|
|
3243
|
+
* that declare conversation hooks; OpenClaw checks the installed package)
|
|
3244
|
+
* and the config of already installed targets. Re-running after a later
|
|
3245
|
+
* failure converges: the server then has the key and the plugin side
|
|
3246
|
+
* copies it.
|
|
3213
3247
|
*/
|
|
3214
3248
|
function buildInstallPlan(targets, plugins, config) {
|
|
3215
3249
|
const steps = config?.serverKeyWrite
|
|
3216
3250
|
? [{ kind: 'serverKeyWrite', write: config.serverKeyWrite }]
|
|
3217
3251
|
: [];
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3252
|
+
const secrets = config?.secrets ?? [];
|
|
3253
|
+
const toInstall = targets.filter((t) => t.installed !== true);
|
|
3254
|
+
const { byPlugin, rest } = splitConfigOps(config?.ops ?? [], toInstall.map((t) => t.pluginId));
|
|
3255
|
+
for (const t of toInstall) {
|
|
3256
|
+
const ops = byPlugin.get(t.pluginId);
|
|
3257
|
+
if (ops)
|
|
3258
|
+
steps.push(configBatch(ops, secrets));
|
|
3259
|
+
steps.push(openclaw(pluginInstallArgs(t.packageName, t.version)));
|
|
3260
|
+
steps.push({ kind: 'migrationSweep' });
|
|
3222
3261
|
}
|
|
3223
3262
|
for (const t of targets) {
|
|
3224
3263
|
if (t.legacyDir)
|
|
@@ -3228,16 +3267,12 @@ function buildInstallPlan(targets, plugins, config) {
|
|
|
3228
3267
|
...computeHookAccessOps(plugins, targets
|
|
3229
3268
|
.filter((t) => t.conversationHooks.length > 0)
|
|
3230
3269
|
.map((t) => t.pluginId)),
|
|
3231
|
-
...
|
|
3270
|
+
...rest,
|
|
3232
3271
|
];
|
|
3233
3272
|
if (ops.length > 0) {
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
steps.push(
|
|
3237
|
-
kind: 'configSetBatch',
|
|
3238
|
-
ops,
|
|
3239
|
-
...(secrets.length > 0 ? { redact: [...secrets] } : {}),
|
|
3240
|
-
});
|
|
3273
|
+
if (toInstall.length === 0)
|
|
3274
|
+
steps.push({ kind: 'migrationSweep' });
|
|
3275
|
+
steps.push(configBatch(ops, secrets));
|
|
3241
3276
|
}
|
|
3242
3277
|
return steps;
|
|
3243
3278
|
}
|
package/dist/index.js
CHANGED
|
@@ -142,14 +142,14 @@ const DEFAULT_PORTS = {
|
|
|
142
142
|
* Core library version, inlined at build time.
|
|
143
143
|
*
|
|
144
144
|
* @remarks
|
|
145
|
-
* The `0.6.0-
|
|
145
|
+
* The `0.6.0-2` placeholder is replaced by
|
|
146
146
|
* `@rollup/plugin-replace` during the build with the actual version
|
|
147
147
|
* from `package.json`. This ensures the correct version survives
|
|
148
148
|
* when consumers bundle core into their own dist (where runtime
|
|
149
149
|
* `import.meta.url`-based resolution would find the wrong package.json).
|
|
150
150
|
*/
|
|
151
151
|
/** The core library version from package.json (inlined at build time). */
|
|
152
|
-
const CORE_VERSION = '0.6.0-
|
|
152
|
+
const CORE_VERSION = '0.6.0-2';
|
|
153
153
|
|
|
154
154
|
/**
|
|
155
155
|
* Workspace and config root initialization.
|