@totalreclaw/totalreclaw 3.3.2-rc.1 → 3.3.2-rc.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/CHANGELOG.md +77 -26
- package/SKILL.md +1 -1
- package/dist/fs-helpers.js +101 -0
- package/dist/index.js +2856 -2757
- package/fs-helpers.ts +124 -0
- package/index.ts +101 -0
- package/package.json +13 -7
- package/postinstall.mjs +260 -0
- package/skill.json +14 -1
- package/preinstall.mjs +0 -65
package/CHANGELOG.md
CHANGED
|
@@ -4,32 +4,83 @@ All notable changes to `@totalreclaw/totalreclaw` (the OpenClaw plugin) are docu
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
-
## [
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
7
|
+
## [3.3.2-rc.1] — 2026-04-27
|
|
8
|
+
|
|
9
|
+
Hotfix bundle for the inside-gateway agent-flow ship-stoppers caught by the
|
|
10
|
+
2026-04-27 user QA against stable 3.3.1 (umbrella issue #182). The four fixes
|
|
11
|
+
combined unblock the agent-driven canonical install path.
|
|
12
|
+
|
|
13
|
+
### Added — filesystem load manifest (issue #186)
|
|
14
|
+
|
|
15
|
+
The plugin now writes `.loaded.json` and `.error.json` into its own
|
|
16
|
+
extension directory at register-time. The agent has no working CLI inside
|
|
17
|
+
the gateway (issue #182 finding F1 — `openclaw plugins list` hangs in
|
|
18
|
+
some Docker setups), so the manifests are the canonical filesystem signal
|
|
19
|
+
that register() ran to completion AND which tools the SDK saw.
|
|
20
|
+
|
|
21
|
+
- `~/.openclaw/extensions/totalreclaw/.loaded.json` —
|
|
22
|
+
`{loadedAt: <ms>, tools: [<name>...], version: <semver>}`. Written
|
|
23
|
+
synchronously at the end of `register(api)`. Captures every tool name
|
|
24
|
+
passed to `api.registerTool` during the call.
|
|
25
|
+
- `~/.openclaw/extensions/totalreclaw/.error.json` —
|
|
26
|
+
`{loadedAt, error, stack?, version?}`. Written from the try/catch
|
|
27
|
+
surrounding the register() body when register() throws. Successful
|
|
28
|
+
boots clear any stale `.error.json`; failed boots preserve any prior
|
|
29
|
+
`.loaded.json` so the agent can compare timestamps.
|
|
30
|
+
|
|
31
|
+
Synchronous writes only (same constraint as `registerHttpRoute` — the SDK
|
|
32
|
+
freezes plugin registries the moment register() returns; an async write
|
|
33
|
+
would race that freeze and the manifest could miss late tool
|
|
34
|
+
registrations).
|
|
35
|
+
|
|
36
|
+
Regression: `load-manifest.test.ts` (22 assertions).
|
|
37
|
+
|
|
38
|
+
### Added — `totalreclaw_pair` declared in skill manifest (issue #185)
|
|
39
|
+
|
|
40
|
+
The `totalreclaw_pair` tool is now advertised in `skill.json` alongside
|
|
41
|
+
`totalreclaw_remember`/`recall`/`forget`/`export`/`status`/`consolidate`/
|
|
42
|
+
`upgrade`/`import_from`. Previously plugin-only — if the plugin runtime
|
|
43
|
+
load failed silently (e.g. dep race in #188), the tool never appeared
|
|
44
|
+
in the agent's toolset and the canonical setup flow was unreachable.
|
|
45
|
+
|
|
46
|
+
The skill-side declaration ensures the tool name is visible in the
|
|
47
|
+
skill registry advertisement even when plugin runtime issues prevent
|
|
48
|
+
binding. The implementation remains in the plugin (browser-side
|
|
49
|
+
e2e-encrypted recovery-phrase flow); only the declaration moves up.
|
|
50
|
+
|
|
51
|
+
### Added — atomic dependency validation in postinstall (issue #188)
|
|
52
|
+
|
|
53
|
+
`postinstall.mjs` is now a real lifecycle script (replacing the inline
|
|
54
|
+
`node -e` shim). After `npm install`, it require()s every critical dep
|
|
55
|
+
(`@scure/bip39`, `@scure/bip39/wordlists/english.js`, `@totalreclaw/core`,
|
|
56
|
+
`@totalreclaw/client`, `qrcode`, `ws`). On first-attempt failure: clears
|
|
57
|
+
`node_modules`, re-runs `npm install --ignore-scripts` once, re-validates.
|
|
58
|
+
If retry also fails, exits non-zero so the install surfaces the failure
|
|
59
|
+
instead of writing `enabled: true` over a broken half-state.
|
|
60
|
+
|
|
61
|
+
The retry loop can be skipped with `TOTALRECLAW_SKIP_POSTINSTALL_RETRY=1`
|
|
62
|
+
for sandboxed CI / restricted-network environments.
|
|
63
|
+
|
|
64
|
+
Phrase-safety: the script does NOT touch credentials.json, mnemonics,
|
|
65
|
+
or any phrase code path. Only validates module loading and cleans
|
|
66
|
+
staging directories.
|
|
67
|
+
|
|
68
|
+
### Added — install-stage cleanup at install-time (issue #190)
|
|
69
|
+
|
|
70
|
+
`postinstall.mjs` extends the rc.21 staging-cleanup behavior (which ran
|
|
71
|
+
at register-time) to ALSO sweep `<extensions>/.openclaw-install-stage-*`
|
|
72
|
+
siblings during the post-install step itself. Goal: a re-install starts
|
|
73
|
+
from a clean parent dir, eliminating the
|
|
74
|
+
"duplicate plugin id detected; global plugin will be overridden by global
|
|
75
|
+
plugin" warning during the install. Safety: skipped when the plugin's
|
|
76
|
+
parent dir is not an `extensions/` directory (dev checkouts) so no random
|
|
77
|
+
siblings are deleted.
|
|
78
|
+
|
|
79
|
+
Regression: `postinstall-validation.test.ts` (17 assertions) covers
|
|
80
|
+
happy path, marker clearing, staging sweep, idempotent re-runs,
|
|
81
|
+
unrelated-dotfile preservation, and dev-checkout safety.
|
|
82
|
+
|
|
83
|
+
|
|
33
84
|
|
|
34
85
|
### Install / runtime hygiene (issues #126, #128)
|
|
35
86
|
|
package/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: totalreclaw
|
|
3
3
|
description: "Install + set up TotalReclaw encrypted memory for OpenClaw, then use totalreclaw_remember / totalreclaw_recall. Trigger on 'install TotalReclaw', 'set up TotalReclaw', 'restore my recovery phrase', or any remember / recall request."
|
|
4
|
-
version: 3.3.2-rc.
|
|
4
|
+
version: 3.3.2-rc.3
|
|
5
5
|
author: TotalReclaw Team
|
|
6
6
|
license: MIT
|
|
7
7
|
homepage: https://totalreclaw.xyz
|
package/dist/fs-helpers.js
CHANGED
|
@@ -420,6 +420,107 @@ export function wipePartialInstall(pluginRootDir) {
|
|
|
420
420
|
return false;
|
|
421
421
|
}
|
|
422
422
|
}
|
|
423
|
+
// ---------------------------------------------------------------------------
|
|
424
|
+
// Plugin load manifest (.loaded.json / .error.json) — 3.3.2-rc.1 #186
|
|
425
|
+
// ---------------------------------------------------------------------------
|
|
426
|
+
/**
|
|
427
|
+
* Filenames written into the plugin root dir at the end of register() and
|
|
428
|
+
* (on failure) from the surrounding try/catch. The presence of `.loaded.json`
|
|
429
|
+
* is the canonical filesystem signal that the plugin's register() body ran
|
|
430
|
+
* to completion AND the SDK tool/route/hook registries received calls.
|
|
431
|
+
*
|
|
432
|
+
* Acceptance criteria (issue #186):
|
|
433
|
+
* - `cat ~/.openclaw/extensions/totalreclaw/.loaded.json` shows
|
|
434
|
+
* `{loadedAt, tools, version}` after every successful gateway start.
|
|
435
|
+
* - `cat ~/.openclaw/extensions/totalreclaw/.error.json` shows
|
|
436
|
+
* `{loadedAt, error, stack}` if register() threw.
|
|
437
|
+
* - Both are overwritten on each register() call so the agent can rely
|
|
438
|
+
* on the timestamp matching the most recent gateway start.
|
|
439
|
+
*
|
|
440
|
+
* Why these MUST be synchronous writes (same constraint as
|
|
441
|
+
* `registerHttpRoute` per the comment in index.ts around the route
|
|
442
|
+
* registration site): the SDK loader treats register() returning as the
|
|
443
|
+
* signal to freeze the registries. An async `fs.promises.writeFile` would
|
|
444
|
+
* settle one microtask AFTER the loader has moved on, so the manifest
|
|
445
|
+
* could miss tools that registered late OR drop entirely if the gateway
|
|
446
|
+
* exits before the microtask runs. `writeFileSync` is the only safe choice.
|
|
447
|
+
*/
|
|
448
|
+
export const PLUGIN_LOADED_MANIFEST = '.loaded.json';
|
|
449
|
+
export const PLUGIN_ERROR_MANIFEST = '.error.json';
|
|
450
|
+
/**
|
|
451
|
+
* Resolve the plugin root dir from the loaded module's directory. The plugin
|
|
452
|
+
* is shipped with `dist/index.js` as the entry, so `import.meta.url` resolves
|
|
453
|
+
* to `<root>/dist/`. We walk up one level to put the manifests next to
|
|
454
|
+
* `package.json`. Defensive: if the caller already passed the root (no
|
|
455
|
+
* trailing `dist`), we still return a sensible path.
|
|
456
|
+
*/
|
|
457
|
+
function resolvePluginRootForManifest(pluginDir) {
|
|
458
|
+
const base = path.basename(pluginDir);
|
|
459
|
+
return base === 'dist' ? path.resolve(pluginDir, '..') : pluginDir;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Write the success manifest. SYNCHRONOUS — the SDK freezes the plugin
|
|
463
|
+
* registries the moment register() returns; `fs.promises.writeFile` would
|
|
464
|
+
* race that freeze and the manifest could miss late tool registrations or
|
|
465
|
+
* never land at all if the process exits before the microtask runs.
|
|
466
|
+
*
|
|
467
|
+
* Best-effort: returns `true` on success, `false` on any I/O error. Never
|
|
468
|
+
* throws — failing to write the manifest is a diagnostic loss, not a
|
|
469
|
+
* correctness loss, so we don't propagate.
|
|
470
|
+
*
|
|
471
|
+
* The manifest is written at mode 0644 (world-readable). It contains no
|
|
472
|
+
* secrets — only a timestamp, the (publicly known) tool names, and the
|
|
473
|
+
* plugin version. Cleared first so a stale `.error.json` from a previous
|
|
474
|
+
* failed boot doesn't survive a successful boot.
|
|
475
|
+
*/
|
|
476
|
+
export function writePluginManifest(pluginDir, manifest) {
|
|
477
|
+
try {
|
|
478
|
+
const root = resolvePluginRootForManifest(pluginDir);
|
|
479
|
+
if (!fs.existsSync(root))
|
|
480
|
+
return false;
|
|
481
|
+
const loadedPath = path.join(root, PLUGIN_LOADED_MANIFEST);
|
|
482
|
+
const errorPath = path.join(root, PLUGIN_ERROR_MANIFEST);
|
|
483
|
+
// Best-effort error-file cleanup — a successful boot supersedes any
|
|
484
|
+
// prior failure marker. If the unlink fails (e.g. permission), the
|
|
485
|
+
// .loaded.json timestamp still tells the agent which is current.
|
|
486
|
+
try {
|
|
487
|
+
if (fs.existsSync(errorPath))
|
|
488
|
+
fs.unlinkSync(errorPath);
|
|
489
|
+
}
|
|
490
|
+
catch {
|
|
491
|
+
// Swallow — best-effort.
|
|
492
|
+
}
|
|
493
|
+
fs.writeFileSync(loadedPath, JSON.stringify(manifest, null, 2));
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
catch {
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Write the error manifest. SYNCHRONOUS for the same reason as
|
|
502
|
+
* `writePluginManifest`. Called from the try/catch surrounding the
|
|
503
|
+
* register() body so the agent has a filesystem signal that the plugin
|
|
504
|
+
* registered AT LEAST attempted to load and failed in a specific way.
|
|
505
|
+
*
|
|
506
|
+
* Does NOT clear `.loaded.json` from a prior successful boot — keeping
|
|
507
|
+
* the older success marker around lets the agent see "last good boot was
|
|
508
|
+
* X, current boot failed at Y" without spelunking logs. The newer
|
|
509
|
+
* `.error.json` timestamp wins as "current state".
|
|
510
|
+
*/
|
|
511
|
+
export function writePluginError(pluginDir, error) {
|
|
512
|
+
try {
|
|
513
|
+
const root = resolvePluginRootForManifest(pluginDir);
|
|
514
|
+
if (!fs.existsSync(root))
|
|
515
|
+
return false;
|
|
516
|
+
const errorPath = path.join(root, PLUGIN_ERROR_MANIFEST);
|
|
517
|
+
fs.writeFileSync(errorPath, JSON.stringify(error, null, 2));
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
catch {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
423
524
|
/**
|
|
424
525
|
* Drop the `.tr-partial-install` marker into `pluginRootDir`. Idempotent
|
|
425
526
|
* (overwrites any existing marker) and best-effort — returns `true` on
|