@totalreclaw/totalreclaw 3.3.2 → 3.3.4-rc.1
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 +149 -0
- package/SKILL.md +18 -6
- package/config.ts +24 -1
- package/dist/config.js +22 -1
- package/dist/embedding.js +41 -1
- package/dist/fs-helpers.js +50 -4
- package/dist/index.js +309 -27
- package/dist/llm-client.js +3 -1
- package/dist/pair-cli-relay.js +278 -0
- package/dist/pair-cli.js +85 -17
- package/dist/subgraph-store.js +3 -2
- package/embedding.ts +43 -1
- package/fs-helpers.ts +49 -5
- package/index.ts +322 -24
- package/llm-client.ts +8 -1
- package/package.json +5 -6
- package/pair-cli-relay.ts +336 -0
- package/pair-cli.ts +118 -18
- package/skill.json +3 -3
- package/subgraph-store.ts +3 -2
- package/postinstall.mjs +0 -260
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,155 @@ 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
|
+
## [3.3.3-rc.1] — 2026-04-30
|
|
8
|
+
|
|
9
|
+
Combined RC bundle:
|
|
10
|
+
|
|
11
|
+
- Fix the OpenClaw runtime-scanner regression that blocked `openclaw plugins
|
|
12
|
+
install @totalreclaw/totalreclaw` on stable 3.3.2 (Telegram QA, OpenClaw
|
|
13
|
+
2026.4.22).
|
|
14
|
+
- Implement the codified RC=staging / stable=production environment-binding
|
|
15
|
+
rule from PR #165.
|
|
16
|
+
- Add a one-shot RC/staging banner so QA testers can't accidentally use an
|
|
17
|
+
RC build for real data.
|
|
18
|
+
- Decouple the ~700 MB embedder bundle download from the pair-completion
|
|
19
|
+
gate (issue [#187](https://github.com/p-diogo/totalreclaw-internal/issues/187)).
|
|
20
|
+
- Document the direct-node fallback for inside-gateway agents that hit
|
|
21
|
+
CLI deadlock (issue [#184](https://github.com/p-diogo/totalreclaw-internal/issues/184)).
|
|
22
|
+
|
|
23
|
+
### Fixed — OpenClaw scanner blocking install on `child_process` import
|
|
24
|
+
|
|
25
|
+
User chat QA on stable 3.3.2 hit:
|
|
26
|
+
|
|
27
|
+
> The plugin install was blocked — OpenClaw flagged it because the plugin's
|
|
28
|
+
> `postinstall.mjs` uses `child_process` (shell execution), which triggers
|
|
29
|
+
> the dangerous-code-pattern safety gate.
|
|
30
|
+
|
|
31
|
+
Workaround was `--allow-dangerous`. Real fix (this RC): drop `postinstall.mjs`
|
|
32
|
+
entirely. The runtime `register(api)` path already (since 3.3.1-rc.21 / 22)
|
|
33
|
+
sweeps `.openclaw-install-stage-*` siblings AND clears the
|
|
34
|
+
`.tr-partial-install` marker, so the postinstall script was redundant.
|
|
35
|
+
|
|
36
|
+
- `skill/plugin/postinstall.mjs` deleted.
|
|
37
|
+
- `skill/plugin/postinstall-validation.test.ts` deleted (the script it
|
|
38
|
+
exercised no longer exists; the runtime equivalents are still covered by
|
|
39
|
+
`install-staging-cleanup.test.ts` + `partial-install-detection.test.ts` +
|
|
40
|
+
`install-reload-idempotency.test.ts`).
|
|
41
|
+
- `package.json` no longer declares `scripts.postinstall` and no longer
|
|
42
|
+
ships `postinstall.mjs` in the `files` array.
|
|
43
|
+
|
|
44
|
+
Behavior preserved:
|
|
45
|
+
- `preinstall` still writes `.tr-partial-install` (uses `node -e` only — no
|
|
46
|
+
`child_process` import).
|
|
47
|
+
- The `.tr-partial-install` marker is now cleared exclusively at plugin
|
|
48
|
+
load time by `register(api)`.
|
|
49
|
+
- `.openclaw-install-stage-*` orphan sweep happens at register() time via
|
|
50
|
+
`cleanupInstallStagingDirs(pluginDir)`.
|
|
51
|
+
- Critical deps (`@scure/bip39`, `@scure/bip39/wordlists/english.js`,
|
|
52
|
+
`@totalreclaw/core`, `@totalreclaw/client`, etc.) are imported at module
|
|
53
|
+
top of `index.ts` — if any is missing, the SDK loader surfaces the
|
|
54
|
+
import error directly AND the existing `.error.json` write path drops a
|
|
55
|
+
structured marker (issue #186 in 3.3.2-rc.1). The retry-by-respawn was
|
|
56
|
+
nice-to-have, not load-bearing.
|
|
57
|
+
|
|
58
|
+
OpenClaw's runtime scanner (different code path from the plugin's local
|
|
59
|
+
`check-scanner.mjs`) does NOT honor the `// scanner-sim: allow` comment.
|
|
60
|
+
The local scanner's previous guidance ("Moving the subprocess call into a
|
|
61
|
+
separate post-install helper that OpenClaw sandboxes") turned out to be
|
|
62
|
+
incorrect — the runtime scanner inspects the full tarball and flags any
|
|
63
|
+
`child_process` import regardless of file role. The local scanner now has
|
|
64
|
+
nothing to flag because `child_process` no longer appears anywhere in the
|
|
65
|
+
shipped tarball.
|
|
66
|
+
|
|
67
|
+
### Added — ENV binding implementation (PR #165 codified rule)
|
|
68
|
+
|
|
69
|
+
| `release-type` | Default `TOTALRECLAW_SERVER_URL` | Audience |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `rc` | `https://api-staging.totalreclaw.xyz` | QA only — never point real users here |
|
|
72
|
+
| `stable` | `https://api.totalreclaw.xyz` | Production users |
|
|
73
|
+
|
|
74
|
+
User env override (`TOTALRECLAW_SERVER_URL=...`) always wins.
|
|
75
|
+
|
|
76
|
+
Implementation:
|
|
77
|
+
|
|
78
|
+
- Source-of-truth in `config.ts` / `index.ts` / `subgraph-store.ts` /
|
|
79
|
+
`skill.json` now references `api-staging.totalreclaw.xyz` everywhere.
|
|
80
|
+
RC tarballs ship the staging URL by design.
|
|
81
|
+
- Stable publish workflows (`npm-publish.yml` + `publish-clawhub.yml`)
|
|
82
|
+
add a "Bind stable artifacts to production URLs" step that
|
|
83
|
+
sed-replaces `api-staging.totalreclaw.xyz` → `api.totalreclaw.xyz`
|
|
84
|
+
across `dist/**.js`, `skill.json`, and the SKILL.md / CLAWHUB.md /
|
|
85
|
+
CHANGELOG.md / README.md prose, before pack/publish.
|
|
86
|
+
- New `skill/scripts/check-url-binding.mjs` guard runs at
|
|
87
|
+
`prepublishOnly` time + as a workflow step. It asserts the right
|
|
88
|
+
invariant for the resolved release type (RC artifact MUST contain
|
|
89
|
+
`api-staging.totalreclaw.xyz`; stable artifact MUST contain
|
|
90
|
+
`api.totalreclaw.xyz` AND ZERO staging references). Misconfigured
|
|
91
|
+
artifacts fail the publish before reaching the registry.
|
|
92
|
+
- `prepublishOnly` reads `TOTALRECLAW_RELEASE_TYPE=stable|rc` (default
|
|
93
|
+
`rc` for safety) so local `npm publish` invocations also assert the
|
|
94
|
+
invariant.
|
|
95
|
+
- New `url-binding.test.ts` regression covers both modes against a
|
|
96
|
+
synthetic artifact tree.
|
|
97
|
+
|
|
98
|
+
### Added — RC/staging banner (one-shot per gateway process)
|
|
99
|
+
|
|
100
|
+
When the bundled `serverUrl` resolves to `api-staging.totalreclaw.xyz`
|
|
101
|
+
AND the user has not overridden via env, the plugin emits a prominent
|
|
102
|
+
prependContext banner on the first non-trivial `before_agent_start`:
|
|
103
|
+
|
|
104
|
+
> ⚠️ TotalReclaw is running in RC / staging mode
|
|
105
|
+
>
|
|
106
|
+
> This build is bound to `api-staging.totalreclaw.xyz`. Staging has **no
|
|
107
|
+
> SLA** and may be wiped between QA cycles. Do **NOT** use this build for
|
|
108
|
+
> real data.
|
|
109
|
+
>
|
|
110
|
+
> For production, install the stable release: `openclaw plugins install
|
|
111
|
+
> @totalreclaw/totalreclaw` (no `@rc` suffix). To pin a custom server,
|
|
112
|
+
> set `TOTALRECLAW_SERVER_URL=https://api.totalreclaw.xyz` in your env.
|
|
113
|
+
|
|
114
|
+
Stable artifacts (where the workflow seded the URL to production) never
|
|
115
|
+
fire the banner. Per-process one-shot semantics — restart re-fires once.
|
|
116
|
+
|
|
117
|
+
### Added — `totalreclaw_preload_embedder` tool + non-blocking prefetch (issue #187)
|
|
118
|
+
|
|
119
|
+
- New tool: `totalreclaw_preload_embedder` lets the agent download the
|
|
120
|
+
embedder bundle ahead of `totalreclaw_pair`. Includes a 500 MB
|
|
121
|
+
disk-space pre-flight (refuses if the cache mount is below threshold)
|
|
122
|
+
and surfaces a structured `{ status: cache_hit | fetched | failed }`
|
|
123
|
+
response.
|
|
124
|
+
- Register-time non-blocking prefetch: `register(api)` now fires
|
|
125
|
+
`prefetchEmbedderBundle()` as a fire-and-forget Promise immediately
|
|
126
|
+
after `configureEmbedder()`. The bundle download starts on gateway
|
|
127
|
+
boot, BEFORE the user completes pair — closing the catch-22 where the
|
|
128
|
+
bundle was only fetched on the first `generateEmbedding()` call (which
|
|
129
|
+
is gated behind `requireFullSetup()`).
|
|
130
|
+
- Toggle: `TOTALRECLAW_DISABLE_EMBEDDER_PREFETCH=1` skips the auto-prefetch
|
|
131
|
+
(CI / sandboxed-network environments). The next `generateEmbedding()`
|
|
132
|
+
call still triggers the download via the same idempotent path.
|
|
133
|
+
|
|
134
|
+
### Documentation — direct-node fallback for CLI deadlock (issue #184)
|
|
135
|
+
|
|
136
|
+
- `docs/guides/openclaw-setup.md` Troubleshooting now documents the
|
|
137
|
+
filesystem-manifest probe (`.loaded.json` / `.error.json`) and the
|
|
138
|
+
`node ~/.openclaw/extensions/totalreclaw/dist/pair-cli.js
|
|
139
|
+
--url-pin-only` direct-node fallback for when the `openclaw` CLI
|
|
140
|
+
deadlocks (exit 124) inside gateway-internal agent shells.
|
|
141
|
+
- `skill/SKILL.md` mirrors the same fallbacks for the agent's own
|
|
142
|
+
instructions: prefer reading the `.loaded.json` manifest over
|
|
143
|
+
re-running `openclaw plugins list`; switch to direct-node `pair-cli.js`
|
|
144
|
+
when `totalreclaw_pair` itself hangs.
|
|
145
|
+
|
|
146
|
+
### Known issues filed during this RC
|
|
147
|
+
|
|
148
|
+
Five new observation issues filed via the QA pipeline (severity:minor,
|
|
149
|
+
not blockers for 3.3.3-rc.1 promote):
|
|
150
|
+
- [#208](https://github.com/p-diogo/totalreclaw-internal/issues/208) — Hermes auto-extraction burst pattern can trip per-model rate limits
|
|
151
|
+
- [#209](https://github.com/p-diogo/totalreclaw-internal/issues/209) — `HERMES_MODEL` env swap doesn't propagate to running daemon
|
|
152
|
+
- [#210](https://github.com/p-diogo/totalreclaw-internal/issues/210) — Hermes Docker venv ships without pip
|
|
153
|
+
- [#211](https://github.com/p-diogo/totalreclaw-internal/issues/211) — ClawHub artifact's `package.json` retains rc-version label after stable promote
|
|
154
|
+
- [#212](https://github.com/p-diogo/totalreclaw-internal/issues/212) — `wipe-qa.sh` model-pin step appends duplicate `HERMES_MODEL` lines
|
|
155
|
+
|
|
7
156
|
## [3.3.2-rc.1] — 2026-04-27
|
|
8
157
|
|
|
9
158
|
Hotfix bundle for the inside-gateway agent-flow ship-stoppers caught by the
|
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.
|
|
4
|
+
version: 3.3.4-rc.1
|
|
5
5
|
author: TotalReclaw Team
|
|
6
6
|
license: MIT
|
|
7
7
|
homepage: https://totalreclaw.xyz
|
|
@@ -30,16 +30,28 @@ metadata:
|
|
|
30
30
|
|
|
31
31
|
If `totalreclaw_*` tools are already bound to your toolset, SKIP install and jump to step 4. Otherwise run all steps in order.
|
|
32
32
|
|
|
33
|
-
1. **Install skill
|
|
33
|
+
1. **Install plugin first, skill second.** Run via your shell tool, in this exact order:
|
|
34
34
|
```
|
|
35
|
-
openclaw skills install totalreclaw
|
|
36
35
|
openclaw plugins install @totalreclaw/totalreclaw
|
|
36
|
+
openclaw skills install totalreclaw
|
|
37
37
|
```
|
|
38
|
-
For RC: replace the
|
|
38
|
+
For RC: replace the first command with `openclaw plugins install @totalreclaw/totalreclaw@rc`. **Plugin BEFORE skill is deliberate** (3.3.4-rc.1 — issue from Pedro's QA on rc.3): the skill install triggers a config-driven SIGUSR1 gateway reload that can race a concurrent plugin install. Plugin-first lets the plugin land cleanly, then the skill's reload picks it up on the next gateway loop. If you accidentally do skill-first and the plugin install dies mid-flight, just retry `openclaw plugins install @totalreclaw/totalreclaw` once — it's idempotent.
|
|
39
|
+
|
|
40
|
+
If either shell call fails with `command not found` / ENOENT / "not authorized" (managed-service or shell-restricted environment), tell the user verbatim *"Your environment doesn't expose the OpenClaw CLI to me. Please install `totalreclaw` from your service's plugins / skills UI, then reply `done`."* Wait for `done` and skip to step 3.
|
|
41
|
+
|
|
42
|
+
2. **Wait for auto-reload — then issue `/restart` autonomously if needed.** OpenClaw's default config (`gateway.reload.mode = "hybrid"`) auto-restarts the gateway via a file-watcher within 1-3 seconds. Wait ~5s, then run `openclaw plugins list | grep totalreclaw` and confirm `totalreclaw_pair` is in your toolset.
|
|
43
|
+
|
|
44
|
+
**If `totalreclaw_pair` is NOT visible after 10s:** issue OpenClaw's built-in `/restart` slash-command immediately and silently. **DO NOT ask the user "should I restart" / "do you want me to restart" / "let me check if the tool is bound" — just issue `/restart` and announce "Reloading gateway..." once.** This is in-process and drains active runs cleanly; your shell is NOT shelling out to `docker restart` or `openclaw gateway restart` (which would suicide the host). After `/restart`, wait ~3s and re-run `openclaw plugins list`.
|
|
45
|
+
|
|
46
|
+
**If `/restart` returns "You are not authorized to use this command":** the gateway's `channels.<your-channel>.allowFrom` allowlist is empty. **Do NOT keep retrying.** Surface this one-line user-facing fix and stop:
|
|
47
|
+
|
|
48
|
+
> Your channel needs to be allowlisted for slash commands. Run on the gateway host: `jq '.channels.<channel>.allowFrom = ["<your-id>"]' ~/.openclaw/openclaw.json | sponge ~/.openclaw/openclaw.json && docker restart <your-container-name>` (Docker self-host) — substitute `<channel>` with your channel name (e.g. `telegram`, `slack`), `<your-id>` with your user ID for that channel (Telegram: `@username` or numeric `chat_id`; Slack: workspace member ID), and `<your-container-name>` with your gateway container name (`docker ps` shows it). On a managed service, ask the service's support to enable slash commands for your channel. Reply `done` once it's back up.
|
|
49
|
+
|
|
50
|
+
For non-Docker / native deployments substitute `openclaw gateway restart` for the `docker restart` portion. As a session-level fallback while restart is gated, try `/new` (starts a fresh session within the same gateway process, may pick up freshly-bound tools without a full restart).
|
|
39
51
|
|
|
40
|
-
|
|
52
|
+
**Forbidden vocabulary** (these are anti-patterns when a tool is missing post-install): "Should I /restart", "Do you have a public URL", "Want me to restart", "Let me check if the tool is bound", "I need permission to restart". Just issue `/restart` immediately and announce.
|
|
41
53
|
|
|
42
|
-
3. **Verify `totalreclaw_pair` is bound.** If still missing after the user said `done
|
|
54
|
+
3. **Verify `totalreclaw_pair` is bound.** If still missing after `/restart` (or after the user fixed allowFrom and said `done`), tell them the restart didn't take effect and ask them to retry. Do not loop the install.
|
|
43
55
|
|
|
44
56
|
4. **Check for existing credentials.** Run `test -s ~/.totalreclaw/credentials.json`. If it exists and is non-empty, confirm *"TotalReclaw is already set up."* and stop.
|
|
45
57
|
|
package/config.ts
CHANGED
|
@@ -138,7 +138,15 @@ export const CONFIG = {
|
|
|
138
138
|
get sessionId(): string | null {
|
|
139
139
|
return getSessionId();
|
|
140
140
|
},
|
|
141
|
-
|
|
141
|
+
// 3.3.3-rc.1: source default is `api-staging.totalreclaw.xyz` per the
|
|
142
|
+
// codified RC=staging / stable=production rule (PR #165). The workflow
|
|
143
|
+
// step "Bind stable to production URLs" in `npm-publish.yml` /
|
|
144
|
+
// `publish-clawhub.yml` sed-replaces `api-staging.totalreclaw.xyz` ->
|
|
145
|
+
// `api.totalreclaw.xyz` across the built `dist/` tree (and skill.json /
|
|
146
|
+
// SKILL.md / CHANGELOG / CLAWHUB.md / package.json) when
|
|
147
|
+
// `release-type=stable`. RC publishes leave the staging URL untouched.
|
|
148
|
+
// User overrides via `TOTALRECLAW_SERVER_URL=...` always win.
|
|
149
|
+
serverUrl: (process.env.TOTALRECLAW_SERVER_URL || 'https://api-staging.totalreclaw.xyz').replace(/\/+$/, ''),
|
|
142
150
|
selfHosted: process.env.TOTALRECLAW_SELF_HOSTED === 'true',
|
|
143
151
|
credentialsPath: process.env.TOTALRECLAW_CREDENTIALS_PATH || path.join(home, '.totalreclaw', 'credentials.json'),
|
|
144
152
|
// 3.2.0 onboarding state file — separate from credentials.json so it
|
|
@@ -184,6 +192,21 @@ export const CONFIG = {
|
|
|
184
192
|
entryPointAddress: process.env.TOTALRECLAW_ENTRYPOINT_ADDRESS || '',
|
|
185
193
|
rpcUrl: process.env.TOTALRECLAW_RPC_URL || '',
|
|
186
194
|
|
|
195
|
+
// 3.3.3-rc.1 (issue #187 — ONNX decouple): kill switch for the
|
|
196
|
+
// non-blocking embedder bundle prefetch fired from register(). Set to
|
|
197
|
+
// `1` in CI / sandboxed environments where the GitHub-Releases CDN is
|
|
198
|
+
// unreachable. The next call to generateEmbedding() still triggers the
|
|
199
|
+
// download via the same idempotent path.
|
|
200
|
+
embedderPrefetchDisabled: process.env.TOTALRECLAW_DISABLE_EMBEDDER_PREFETCH === '1',
|
|
201
|
+
|
|
202
|
+
// 3.3.3-rc.1 (PR #165 implementation): observable form of "did the user
|
|
203
|
+
// explicitly override the bundled-default server URL via env?". Used
|
|
204
|
+
// by the RC-staging banner check in index.ts so the banner suppresses
|
|
205
|
+
// when the user has pinned a custom URL (production or self-hosted).
|
|
206
|
+
// Lives here so index.ts stays free of process.env reads (scanner
|
|
207
|
+
// env-harvesting rule).
|
|
208
|
+
serverUrlEnvOverridden: !!process.env.TOTALRECLAW_SERVER_URL,
|
|
209
|
+
|
|
187
210
|
// Tuning knobs — default values used only as local fallback for
|
|
188
211
|
// self-hosted mode. Managed-service clients override these from the relay
|
|
189
212
|
// billing response via `resolveTuning(...)`.
|
package/dist/config.js
CHANGED
|
@@ -123,7 +123,15 @@ export const CONFIG = {
|
|
|
123
123
|
get sessionId() {
|
|
124
124
|
return getSessionId();
|
|
125
125
|
},
|
|
126
|
-
|
|
126
|
+
// 3.3.3-rc.1: source default is `api-staging.totalreclaw.xyz` per the
|
|
127
|
+
// codified RC=staging / stable=production rule (PR #165). The workflow
|
|
128
|
+
// step "Bind stable to production URLs" in `npm-publish.yml` /
|
|
129
|
+
// `publish-clawhub.yml` sed-replaces `api-staging.totalreclaw.xyz` ->
|
|
130
|
+
// `api.totalreclaw.xyz` across the built `dist/` tree (and skill.json /
|
|
131
|
+
// SKILL.md / CHANGELOG / CLAWHUB.md / package.json) when
|
|
132
|
+
// `release-type=stable`. RC publishes leave the staging URL untouched.
|
|
133
|
+
// User overrides via `TOTALRECLAW_SERVER_URL=...` always win.
|
|
134
|
+
serverUrl: (process.env.TOTALRECLAW_SERVER_URL || 'https://api-staging.totalreclaw.xyz').replace(/\/+$/, ''),
|
|
127
135
|
selfHosted: process.env.TOTALRECLAW_SELF_HOSTED === 'true',
|
|
128
136
|
credentialsPath: process.env.TOTALRECLAW_CREDENTIALS_PATH || path.join(home, '.totalreclaw', 'credentials.json'),
|
|
129
137
|
// 3.2.0 onboarding state file — separate from credentials.json so it
|
|
@@ -166,6 +174,19 @@ export const CONFIG = {
|
|
|
166
174
|
dataEdgeAddress: process.env.TOTALRECLAW_DATA_EDGE_ADDRESS || '',
|
|
167
175
|
entryPointAddress: process.env.TOTALRECLAW_ENTRYPOINT_ADDRESS || '',
|
|
168
176
|
rpcUrl: process.env.TOTALRECLAW_RPC_URL || '',
|
|
177
|
+
// 3.3.3-rc.1 (issue #187 — ONNX decouple): kill switch for the
|
|
178
|
+
// non-blocking embedder bundle prefetch fired from register(). Set to
|
|
179
|
+
// `1` in CI / sandboxed environments where the GitHub-Releases CDN is
|
|
180
|
+
// unreachable. The next call to generateEmbedding() still triggers the
|
|
181
|
+
// download via the same idempotent path.
|
|
182
|
+
embedderPrefetchDisabled: process.env.TOTALRECLAW_DISABLE_EMBEDDER_PREFETCH === '1',
|
|
183
|
+
// 3.3.3-rc.1 (PR #165 implementation): observable form of "did the user
|
|
184
|
+
// explicitly override the bundled-default server URL via env?". Used
|
|
185
|
+
// by the RC-staging banner check in index.ts so the banner suppresses
|
|
186
|
+
// when the user has pinned a custom URL (production or self-hosted).
|
|
187
|
+
// Lives here so index.ts stays free of process.env reads (scanner
|
|
188
|
+
// env-harvesting rule).
|
|
189
|
+
serverUrlEnvOverridden: !!process.env.TOTALRECLAW_SERVER_URL,
|
|
169
190
|
// Tuning knobs — default values used only as local fallback for
|
|
170
191
|
// self-hosted mode. Managed-service clients override these from the relay
|
|
171
192
|
// billing response via `resolveTuning(...)`.
|
package/dist/embedding.js
CHANGED
|
@@ -53,10 +53,50 @@ export function configureEmbedder(cfg) {
|
|
|
53
53
|
function defaultCacheRoot() {
|
|
54
54
|
return path.join(os.homedir(), '.totalreclaw', 'embedder');
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Last-known-good embedder bundle tag. Used ONLY as a hard-fallback when
|
|
58
|
+
* `configureEmbedder()` is never called by the orchestrator (defensive
|
|
59
|
+
* path — production code always wires it via index.ts register()).
|
|
60
|
+
*
|
|
61
|
+
* 3.3.4-rc.1 — pinned to v3.3.3-rc.1 because that is the most recent
|
|
62
|
+
* release at fix-time with a published `embedder-v1.tar.gz` asset. Earlier
|
|
63
|
+
* fallback `'0.0.0-dev'` (rc.22 → 3.3.3-rc.1) hard-coded a placeholder
|
|
64
|
+
* that resolved to a 404 GitHub Release URL; QA on 3.3.3-rc.1 (Pedro
|
|
65
|
+
* 2026-04-30) caught it because the cascade-cause (broken
|
|
66
|
+
* `readPluginVersion()` resolution) made the fallback fire on every cold
|
|
67
|
+
* start. Bumping this constant per RC is fine — the publish workflow auto-
|
|
68
|
+
* publishes the bundle for every RC tag (see scripts/build-embedder-
|
|
69
|
+
* bundle.mjs in the public repo).
|
|
70
|
+
*/
|
|
71
|
+
const LAST_KNOWN_GOOD_RC_TAG = '3.3.3-rc.1';
|
|
56
72
|
function activeRuntimeConfig() {
|
|
57
73
|
if (runtimeConfig)
|
|
58
74
|
return runtimeConfig;
|
|
59
|
-
return { cacheRoot: defaultCacheRoot(), rcTag:
|
|
75
|
+
return { cacheRoot: defaultCacheRoot(), rcTag: LAST_KNOWN_GOOD_RC_TAG };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 3.3.3-rc.1 (issue #187 — ONNX decouple): prefetch the embedder bundle
|
|
79
|
+
* WITHOUT loading the model into memory. Used to download the
|
|
80
|
+
* ~700 MB tarball pre-pair so the user does not hit the network round-trip
|
|
81
|
+
* mid-conversation. Idempotent — subsequent calls are cache-hit no-ops.
|
|
82
|
+
*
|
|
83
|
+
* Returns:
|
|
84
|
+
* - `'cache_hit'` if the bundle was already extracted + verified.
|
|
85
|
+
* - `'fetched'` if the bundle was downloaded this call.
|
|
86
|
+
* - throws on transport / extraction failure.
|
|
87
|
+
*
|
|
88
|
+
* Pre-flight is the caller's job (disk-space, network reachability) — this
|
|
89
|
+
* function focuses on the cache-resolve + fetch-on-miss path so it can also
|
|
90
|
+
* be reused as a fast cache-validation probe.
|
|
91
|
+
*/
|
|
92
|
+
export async function prefetchEmbedderBundle(opts) {
|
|
93
|
+
const cfg = activeRuntimeConfig();
|
|
94
|
+
const loaded = await loadEmbedder({
|
|
95
|
+
cacheRoot: cfg.cacheRoot,
|
|
96
|
+
rcTag: cfg.rcTag,
|
|
97
|
+
log: opts?.log,
|
|
98
|
+
});
|
|
99
|
+
return loaded.wasFetched ? 'fetched' : 'cache_hit';
|
|
60
100
|
}
|
|
61
101
|
/** Lazily initialized state. */
|
|
62
102
|
let pipelineExtractor = null;
|
package/dist/fs-helpers.js
CHANGED
|
@@ -74,9 +74,17 @@ export function ensureMemoryHeaderFile(workspace, header, markerSubstring = 'Tot
|
|
|
74
74
|
* Read the plugin's own version string from `package.json`.
|
|
75
75
|
*
|
|
76
76
|
* Behaviour:
|
|
77
|
-
* -
|
|
77
|
+
* - Tries `package.json` next to the caller-provided directory first
|
|
78
78
|
* (typically `path.dirname(fileURLToPath(import.meta.url))` from the
|
|
79
|
-
* caller).
|
|
79
|
+
* caller — i.e., the directory of the running ESM module).
|
|
80
|
+
* - If that misses, walks up to 5 parent directories looking for a
|
|
81
|
+
* `package.json` whose `name` is `@totalreclaw/totalreclaw`. This
|
|
82
|
+
* covers the OpenClaw plugin sandbox case where the loaded module
|
|
83
|
+
* lives at `<pluginRoot>/dist/index.js` while `package.json` lives
|
|
84
|
+
* at `<pluginRoot>/package.json` (3.3.4-rc.1 fix — without this
|
|
85
|
+
* walk-up, the `.loaded.json` manifest gets `version=unknown` and
|
|
86
|
+
* all RC-gated logic that depends on the version string fails
|
|
87
|
+
* silently in production OpenClaw deployments).
|
|
80
88
|
* - Returns the `version` field, or `null` on any I/O / parse error.
|
|
81
89
|
*
|
|
82
90
|
* Used by the RC-gated `totalreclaw_report_qa_bug` tool registration in
|
|
@@ -87,13 +95,51 @@ export function ensureMemoryHeaderFile(workspace, header, markerSubstring = 'Tot
|
|
|
87
95
|
* helper — see the file-header guardrail.
|
|
88
96
|
*/
|
|
89
97
|
export function readPluginVersion(packageJsonDir) {
|
|
98
|
+
// Direct hit (source-tree dev path; tests).
|
|
99
|
+
const direct = tryReadPluginPackageJson(path.join(packageJsonDir, 'package.json'));
|
|
100
|
+
if (direct)
|
|
101
|
+
return direct;
|
|
102
|
+
// Walk up — the running ESM module typically lives at
|
|
103
|
+
// `<pluginRoot>/dist/index.js`, so `packageJsonDir` is `<pluginRoot>/dist`
|
|
104
|
+
// and `package.json` is one level up. Bound the walk so a misconfigured
|
|
105
|
+
// path doesn't traverse the entire filesystem; 5 levels is more than
|
|
106
|
+
// enough for any realistic plugin layout (dist/, dist/cjs/, build/lib/).
|
|
107
|
+
let current = packageJsonDir;
|
|
108
|
+
for (let depth = 0; depth < 5; depth++) {
|
|
109
|
+
const parent = path.dirname(current);
|
|
110
|
+
if (parent === current)
|
|
111
|
+
break; // root reached
|
|
112
|
+
const candidate = path.join(parent, 'package.json');
|
|
113
|
+
const version = tryReadPluginPackageJson(candidate);
|
|
114
|
+
if (version)
|
|
115
|
+
return version;
|
|
116
|
+
current = parent;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Try to read `package.json` at `pkgPath`. Returns the `version` only if
|
|
122
|
+
* the file's `name` field matches `@totalreclaw/totalreclaw` — guards
|
|
123
|
+
* against accidentally returning the version of an outer host-package
|
|
124
|
+
* (e.g. when the plugin is bundled inside a parent app's tree).
|
|
125
|
+
*
|
|
126
|
+
* If `name` is absent (legacy / minimal package.json), accept the version
|
|
127
|
+
* anyway as a fallback — this is the existing behaviour preserved for
|
|
128
|
+
* anyone who manually trimmed their package.json.
|
|
129
|
+
*/
|
|
130
|
+
function tryReadPluginPackageJson(pkgPath) {
|
|
90
131
|
try {
|
|
91
|
-
const pkgPath = path.join(packageJsonDir, 'package.json');
|
|
92
132
|
if (!fs.existsSync(pkgPath))
|
|
93
133
|
return null;
|
|
94
134
|
const raw = fs.readFileSync(pkgPath, 'utf-8');
|
|
95
135
|
const parsed = JSON.parse(raw);
|
|
96
|
-
|
|
136
|
+
if (typeof parsed.version !== 'string')
|
|
137
|
+
return null;
|
|
138
|
+
if (typeof parsed.name === 'string' && parsed.name !== '@totalreclaw/totalreclaw') {
|
|
139
|
+
// Wrong package — keep walking.
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
return parsed.version;
|
|
97
143
|
}
|
|
98
144
|
catch {
|
|
99
145
|
return null;
|