@totalreclaw/totalreclaw 3.3.2 → 3.3.3-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 +1 -1
- package/config.ts +24 -1
- package/dist/config.js +22 -1
- package/dist/embedding.js +24 -0
- package/dist/index.js +197 -16
- package/dist/llm-client.js +3 -1
- package/dist/subgraph-store.js +3 -2
- package/embedding.ts +25 -0
- package/index.ts +206 -13
- package/llm-client.ts +8 -1
- package/package.json +5 -6
- 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.3-rc.1
|
|
5
5
|
author: TotalReclaw Team
|
|
6
6
|
license: MIT
|
|
7
7
|
homepage: https://totalreclaw.xyz
|
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
|
@@ -58,6 +58,30 @@ function activeRuntimeConfig() {
|
|
|
58
58
|
return runtimeConfig;
|
|
59
59
|
return { cacheRoot: defaultCacheRoot(), rcTag: '0.0.0-dev' };
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* 3.3.3-rc.1 (issue #187 — ONNX decouple): prefetch the embedder bundle
|
|
63
|
+
* WITHOUT loading the model into memory. Used to download the
|
|
64
|
+
* ~700 MB tarball pre-pair so the user does not hit the network round-trip
|
|
65
|
+
* mid-conversation. Idempotent — subsequent calls are cache-hit no-ops.
|
|
66
|
+
*
|
|
67
|
+
* Returns:
|
|
68
|
+
* - `'cache_hit'` if the bundle was already extracted + verified.
|
|
69
|
+
* - `'fetched'` if the bundle was downloaded this call.
|
|
70
|
+
* - throws on transport / extraction failure.
|
|
71
|
+
*
|
|
72
|
+
* Pre-flight is the caller's job (disk-space, network reachability) — this
|
|
73
|
+
* function focuses on the cache-resolve + fetch-on-miss path so it can also
|
|
74
|
+
* be reused as a fast cache-validation probe.
|
|
75
|
+
*/
|
|
76
|
+
export async function prefetchEmbedderBundle(opts) {
|
|
77
|
+
const cfg = activeRuntimeConfig();
|
|
78
|
+
const loaded = await loadEmbedder({
|
|
79
|
+
cacheRoot: cfg.cacheRoot,
|
|
80
|
+
rcTag: cfg.rcTag,
|
|
81
|
+
log: opts?.log,
|
|
82
|
+
});
|
|
83
|
+
return loaded.wasFetched ? 'fetched' : 'cache_hit';
|
|
84
|
+
}
|
|
61
85
|
/** Lazily initialized state. */
|
|
62
86
|
let pipelineExtractor = null;
|
|
63
87
|
let autoTokenizer = null;
|
package/dist/index.js
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
import { deriveKeys, deriveLshSeed, computeAuthKeyHash, encrypt, decrypt, generateBlindIndices, generateContentFingerprint, } from './crypto.js';
|
|
49
49
|
import { createApiClient } from './api-client.js';
|
|
50
50
|
import { extractFacts, extractDebrief, isValidMemoryType, parseEntity, VALID_MEMORY_TYPES, LEGACY_V0_MEMORY_TYPES, VALID_MEMORY_SOURCES, VALID_MEMORY_SCOPES, EXTRACTION_SYSTEM_PROMPT, extractFactsForCompaction, } from './extractor.js';
|
|
51
|
-
import { initLLMClient, resolveLLMConfig, chatCompletion, generateEmbedding, getEmbeddingDims, getEmbeddingModelId, configureEmbedder, } from './llm-client.js';
|
|
51
|
+
import { initLLMClient, resolveLLMConfig, chatCompletion, generateEmbedding, getEmbeddingDims, getEmbeddingModelId, configureEmbedder, prefetchEmbedderBundle, } from './llm-client.js';
|
|
52
52
|
import { defaultAuthProfilesRoot, readAllProfileKeys, dedupeByProvider, } from './llm-profile-reader.js';
|
|
53
53
|
import { LSHHasher } from './lsh.js';
|
|
54
54
|
import { rerank, cosineSimilarity, detectQueryIntent, INTENT_WEIGHTS } from './reranker.js';
|
|
@@ -501,6 +501,20 @@ let firstRunAfterInit = true;
|
|
|
501
501
|
* skips. A fresh gateway restart resets it back to `false`.
|
|
502
502
|
*/
|
|
503
503
|
let firstRunWelcomeShown = false;
|
|
504
|
+
/**
|
|
505
|
+
* 3.3.3-rc.1 — RC-mode staging-only banner (PR #165 implementation).
|
|
506
|
+
*
|
|
507
|
+
* Fires ONCE per gateway process when:
|
|
508
|
+
* - the bundled-default `serverUrl` resolves to `api-staging.totalreclaw.xyz`
|
|
509
|
+
* (RC artifact, not stable), AND
|
|
510
|
+
* - the user has NOT overridden via `TOTALRECLAW_SERVER_URL=...` env.
|
|
511
|
+
*
|
|
512
|
+
* Goal: a fresh QA tester can't accidentally use an RC build for real data
|
|
513
|
+
* without seeing a clear "RC = staging, no SLA, may be wiped" warning.
|
|
514
|
+
* One-shot at the first `before_agent_start` after register(); never spams
|
|
515
|
+
* per-turn. A fresh gateway restart re-fires it once.
|
|
516
|
+
*/
|
|
517
|
+
let stagingBannerShown = false;
|
|
504
518
|
/**
|
|
505
519
|
* Derive keys from the recovery phrase, load credentials, and register with
|
|
506
520
|
* the server if this is the first run.
|
|
@@ -514,7 +528,9 @@ let firstRunWelcomeShown = false;
|
|
|
514
528
|
* directs the caller to `openclaw totalreclaw onboard`.
|
|
515
529
|
*/
|
|
516
530
|
async function initialize(logger) {
|
|
517
|
-
|
|
531
|
+
// 3.3.3-rc.1: staging is the source default per #165. Stable build-time
|
|
532
|
+
// sed-replace flips `api-staging` -> `api` in dist/.
|
|
533
|
+
const serverUrl = CONFIG.serverUrl || 'https://api-staging.totalreclaw.xyz';
|
|
518
534
|
let masterPassword = CONFIG.recoveryPhrase;
|
|
519
535
|
// 3.2.0: if the env var is unset, probe credentials.json for a
|
|
520
536
|
// pre-existing mnemonic (written either by the CLI wizard on this machine
|
|
@@ -2407,13 +2423,36 @@ const plugin = {
|
|
|
2407
2423
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2408
2424
|
api.logger.warn(`TotalReclaw: configureEmbedder failed (will use defaults): ${msg}`);
|
|
2409
2425
|
}
|
|
2426
|
+
// 3.3.3-rc.1 (issue #187 — ONNX decouple): kick off a non-blocking
|
|
2427
|
+
// bundle prefetch so the ~700 MB embedder tarball starts streaming
|
|
2428
|
+
// as soon as the gateway boots, BEFORE the user completes
|
|
2429
|
+
// `totalreclaw_pair`. Decouples the model download from the
|
|
2430
|
+
// pair-completion gate the previous flow imposed via
|
|
2431
|
+
// `requireFullSetup()` -> first `generateEmbedding()` call.
|
|
2432
|
+
// Fire-and-forget — never awaits, never throws on failure (the next
|
|
2433
|
+
// `generateEmbedding()` call retries via the same idempotent path).
|
|
2434
|
+
// Disabled when `TOTALRECLAW_DISABLE_EMBEDDER_PREFETCH=1` (CI / tests
|
|
2435
|
+
// where the network is sandboxed away). The env read lives in
|
|
2436
|
+
// config.ts; we read the resolved CONFIG flag here so this file
|
|
2437
|
+
// stays scanner-clean (no env lookups in index.ts).
|
|
2438
|
+
if (!CONFIG.embedderPrefetchDisabled) {
|
|
2439
|
+
prefetchEmbedderBundle({ log: (msg) => api.logger.info(msg) })
|
|
2440
|
+
.then((result) => {
|
|
2441
|
+
api.logger.info(`TotalReclaw: embedder prefetch ${result === 'fetched' ? 'completed (downloaded bundle)' : 'cache hit'}`);
|
|
2442
|
+
})
|
|
2443
|
+
.catch((err) => {
|
|
2444
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2445
|
+
api.logger.warn(`TotalReclaw: embedder prefetch failed (non-fatal — will retry on first generateEmbedding): ${msg}`);
|
|
2446
|
+
});
|
|
2447
|
+
}
|
|
2410
2448
|
// 3.3.1-rc.22 (rc.21 finding #5): self-heal partial-install marker.
|
|
2411
|
-
// The `preinstall` npm script writes `.tr-partial-install`;
|
|
2412
|
-
//
|
|
2413
|
-
//
|
|
2414
|
-
//
|
|
2415
|
-
//
|
|
2416
|
-
// Clear it so the next retry's
|
|
2449
|
+
// The `preinstall` npm script writes `.tr-partial-install`; clearing
|
|
2450
|
+
// it has been the runtime's job since 3.3.3-rc.1 dropped postinstall.mjs
|
|
2451
|
+
// (OpenClaw scanner blocked the install on the subprocess-spawn import
|
|
2452
|
+
// — see 3.3.3-rc.1 PR). If we have gotten this far the loader did
|
|
2453
|
+
// register us — meaning the install succeeded enough to be useful —
|
|
2454
|
+
// so any lingering marker is stale. Clear it so the next retry's
|
|
2455
|
+
// detector does not see a false positive.
|
|
2417
2456
|
//
|
|
2418
2457
|
// 3.3.1-rc.22 (rc.21 finding #6) — gateway/reload upstream caveat:
|
|
2419
2458
|
// OpenClaw's config-watcher fires `gateway/reload` when
|
|
@@ -3509,6 +3548,114 @@ const plugin = {
|
|
|
3509
3548
|
},
|
|
3510
3549
|
}, { name: 'totalreclaw_status' });
|
|
3511
3550
|
// ---------------------------------------------------------------
|
|
3551
|
+
// Tool: totalreclaw_preload_embedder (3.3.3-rc.1 — issue #187)
|
|
3552
|
+
// ---------------------------------------------------------------
|
|
3553
|
+
//
|
|
3554
|
+
// Decouples the ~700 MB embedder bundle download from the pair-
|
|
3555
|
+
// completion gate. The agent can call this BEFORE
|
|
3556
|
+
// `totalreclaw_pair` to pre-flight disk space, kick off the
|
|
3557
|
+
// download, and surface the completion state. The non-blocking
|
|
3558
|
+
// prefetch in `register()` already starts the download
|
|
3559
|
+
// unconditionally; this tool is an explicit on-demand hook for
|
|
3560
|
+
// agents that want to confirm completion (or trigger a retry on
|
|
3561
|
+
// network failure) without first completing pair.
|
|
3562
|
+
//
|
|
3563
|
+
// Behavior:
|
|
3564
|
+
// - Disk-space pre-flight: refuses if free disk < 500 MB on the
|
|
3565
|
+
// embedder cache mount. Surfaces the path + free bytes so the
|
|
3566
|
+
// user can clear space.
|
|
3567
|
+
// - Triggers `prefetchEmbedderBundle()` (idempotent — cache hit
|
|
3568
|
+
// returns immediately).
|
|
3569
|
+
// - Returns a structured success message with status:
|
|
3570
|
+
// `cache_hit | fetched | failed`.
|
|
3571
|
+
//
|
|
3572
|
+
// Phrase-safety: this tool does NOT touch credentials.json,
|
|
3573
|
+
// mnemonics, or keys. It only touches `~/.totalreclaw/embedder/`
|
|
3574
|
+
// and the GitHub Releases CDN.
|
|
3575
|
+
api.registerTool({
|
|
3576
|
+
name: 'totalreclaw_preload_embedder',
|
|
3577
|
+
label: 'Preload Embedder',
|
|
3578
|
+
description: 'Download the local-embedding model bundle ahead of pair completion. Use this when the user wants to set up TotalReclaw on a slow connection or run an offline-after-setup workflow. Returns success once the ~700 MB bundle is cached at ~/.totalreclaw/embedder/.',
|
|
3579
|
+
parameters: {
|
|
3580
|
+
type: 'object',
|
|
3581
|
+
properties: {},
|
|
3582
|
+
additionalProperties: false,
|
|
3583
|
+
},
|
|
3584
|
+
async execute() {
|
|
3585
|
+
try {
|
|
3586
|
+
// Disk-space pre-flight: refuse if < 500 MB free on the
|
|
3587
|
+
// embedder cache mount. Best-effort — if statfs fails, we
|
|
3588
|
+
// proceed without the pre-flight rather than blocking.
|
|
3589
|
+
const fsModule = await import('node:fs');
|
|
3590
|
+
const cacheRoot = CONFIG.embedderCachePath;
|
|
3591
|
+
const REQUIRED_BYTES = 500 * 1024 * 1024;
|
|
3592
|
+
try {
|
|
3593
|
+
// Find the deepest existing parent so statfs has a real
|
|
3594
|
+
// mount to measure (loadEmbedder will mkdir under it
|
|
3595
|
+
// anyway).
|
|
3596
|
+
let probeDir = cacheRoot;
|
|
3597
|
+
while (true) {
|
|
3598
|
+
try {
|
|
3599
|
+
fsModule.statSync(probeDir);
|
|
3600
|
+
break;
|
|
3601
|
+
}
|
|
3602
|
+
catch {
|
|
3603
|
+
const parent = nodePath.dirname(probeDir);
|
|
3604
|
+
if (parent === probeDir)
|
|
3605
|
+
break;
|
|
3606
|
+
probeDir = parent;
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
const stats = fsModule.statfsSync?.(probeDir);
|
|
3610
|
+
if (stats) {
|
|
3611
|
+
const bavail = typeof stats.bavail === 'bigint' ? Number(stats.bavail) : stats.bavail;
|
|
3612
|
+
const bsize = typeof stats.bsize === 'bigint' ? Number(stats.bsize) : stats.bsize;
|
|
3613
|
+
const freeBytes = bavail * bsize;
|
|
3614
|
+
if (freeBytes > 0 && freeBytes < REQUIRED_BYTES) {
|
|
3615
|
+
const freeMb = Math.round(freeBytes / (1024 * 1024));
|
|
3616
|
+
return {
|
|
3617
|
+
content: [{
|
|
3618
|
+
type: 'text',
|
|
3619
|
+
text: `Insufficient free disk space for embedder bundle. Required: 500 MB. Available at ${probeDir}: ${freeMb} MB. Free up space and retry.`,
|
|
3620
|
+
}],
|
|
3621
|
+
details: { status: 'failed', reason: 'disk_space', free_mb: freeMb, required_mb: 500, cache_root: cacheRoot },
|
|
3622
|
+
};
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
}
|
|
3626
|
+
catch {
|
|
3627
|
+
// statfs probe failed — surface a soft warning in logs
|
|
3628
|
+
// but proceed with the download anyway.
|
|
3629
|
+
api.logger.info('totalreclaw_preload_embedder: disk-space probe unavailable, proceeding without pre-flight');
|
|
3630
|
+
}
|
|
3631
|
+
// Trigger the prefetch. This is idempotent (cache hit returns
|
|
3632
|
+
// immediately) so it's safe to invoke even when the
|
|
3633
|
+
// background prefetch from register() already completed.
|
|
3634
|
+
const result = await prefetchEmbedderBundle({
|
|
3635
|
+
log: (msg) => api.logger.info(msg),
|
|
3636
|
+
});
|
|
3637
|
+
const human = result === 'fetched'
|
|
3638
|
+
? `Embedder bundle downloaded and cached at ${cacheRoot}. Subsequent embedding calls run in-memory.`
|
|
3639
|
+
: `Embedder bundle already cached at ${cacheRoot} — no download needed.`;
|
|
3640
|
+
return {
|
|
3641
|
+
content: [{ type: 'text', text: human }],
|
|
3642
|
+
details: { status: result, cache_root: cacheRoot },
|
|
3643
|
+
};
|
|
3644
|
+
}
|
|
3645
|
+
catch (err) {
|
|
3646
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3647
|
+
api.logger.error(`totalreclaw_preload_embedder failed: ${message}`);
|
|
3648
|
+
return {
|
|
3649
|
+
content: [{
|
|
3650
|
+
type: 'text',
|
|
3651
|
+
text: `Embedder bundle preload failed: ${humanizeError(message)}. The plugin will retry on first embedding call.`,
|
|
3652
|
+
}],
|
|
3653
|
+
details: { status: 'failed', reason: 'fetch_error', message },
|
|
3654
|
+
};
|
|
3655
|
+
}
|
|
3656
|
+
},
|
|
3657
|
+
}, { name: 'totalreclaw_preload_embedder' });
|
|
3658
|
+
// ---------------------------------------------------------------
|
|
3512
3659
|
// Tool: totalreclaw_consolidate
|
|
3513
3660
|
// ---------------------------------------------------------------
|
|
3514
3661
|
api.registerTool({
|
|
@@ -4840,6 +4987,38 @@ const plugin = {
|
|
|
4840
4987
|
if (!evt?.prompt || evt.prompt.length < 5) {
|
|
4841
4988
|
return undefined;
|
|
4842
4989
|
}
|
|
4990
|
+
// 3.3.3-rc.1 — RC-staging banner (PR #165 implementation).
|
|
4991
|
+
// Build a one-shot prefix when the bundled default points at staging
|
|
4992
|
+
// AND the user hasn't overridden via env. This prefix is prepended
|
|
4993
|
+
// to whichever context block the rest of the hook produces.
|
|
4994
|
+
let stagingBannerBlock = '';
|
|
4995
|
+
if (!stagingBannerShown) {
|
|
4996
|
+
try {
|
|
4997
|
+
const usingStagingDefault = CONFIG.serverUrl.includes('api-staging.totalreclaw.xyz');
|
|
4998
|
+
const userOverrode = CONFIG.serverUrlEnvOverridden;
|
|
4999
|
+
if (usingStagingDefault && !userOverrode) {
|
|
5000
|
+
stagingBannerBlock =
|
|
5001
|
+
'## ⚠️ TotalReclaw is running in RC / staging mode\n\n' +
|
|
5002
|
+
'This build is bound to `api-staging.totalreclaw.xyz`. Staging has **no SLA** ' +
|
|
5003
|
+
'and may be wiped between QA cycles. Do **NOT** use this build for real data.\n\n' +
|
|
5004
|
+
'For production, install the stable release: `openclaw plugins install ' +
|
|
5005
|
+
'@totalreclaw/totalreclaw` (no `@rc` suffix). To pin a custom server, set ' +
|
|
5006
|
+
'`TOTALRECLAW_SERVER_URL=https://api.totalreclaw.xyz` in your env.\n\n';
|
|
5007
|
+
stagingBannerShown = true;
|
|
5008
|
+
api.logger.warn('TotalReclaw: RC/staging build active (api-staging.totalreclaw.xyz). ' +
|
|
5009
|
+
'See docs/guides/release-process.md for the RC=staging / stable=production rule.');
|
|
5010
|
+
}
|
|
5011
|
+
else {
|
|
5012
|
+
// Non-RC artifact OR user override — never fire the banner this
|
|
5013
|
+
// gateway-process lifetime.
|
|
5014
|
+
stagingBannerShown = true;
|
|
5015
|
+
}
|
|
5016
|
+
}
|
|
5017
|
+
catch {
|
|
5018
|
+
// Best-effort; never block session start on banner derivation.
|
|
5019
|
+
stagingBannerShown = true;
|
|
5020
|
+
}
|
|
5021
|
+
}
|
|
4843
5022
|
await ensureInitialized(api.logger);
|
|
4844
5023
|
// 3.2.0 onboarding pending: emit a non-secret guidance banner so
|
|
4845
5024
|
// the LLM knows how to respond when the user asks about setup.
|
|
@@ -4868,7 +5047,8 @@ const plugin = {
|
|
|
4868
5047
|
api.logger.warn(`First-run welcome check failed: ${msg}`);
|
|
4869
5048
|
}
|
|
4870
5049
|
return {
|
|
4871
|
-
prependContext:
|
|
5050
|
+
prependContext: stagingBannerBlock +
|
|
5051
|
+
welcomeBlock +
|
|
4872
5052
|
'## TotalReclaw setup pending\n\n' +
|
|
4873
5053
|
'TotalReclaw encrypted memory is installed but not yet set up on this machine. ' +
|
|
4874
5054
|
'If the user asks about memory features or wants to configure TotalReclaw, ' +
|
|
@@ -4966,7 +5146,8 @@ const plugin = {
|
|
|
4966
5146
|
if (injectResult.promptText) {
|
|
4967
5147
|
api.logger.info(`Digest injection: state=${injectResult.state}`);
|
|
4968
5148
|
return {
|
|
4969
|
-
prependContext:
|
|
5149
|
+
prependContext: stagingBannerBlock +
|
|
5150
|
+
`## Your Memory\n\n${injectResult.promptText}` + welcomeBack + billingWarning,
|
|
4970
5151
|
};
|
|
4971
5152
|
}
|
|
4972
5153
|
}
|
|
@@ -5007,7 +5188,7 @@ const plugin = {
|
|
|
5007
5188
|
const querySimilarity = cosineSimilarity(queryEmbedding, lastQueryEmbedding);
|
|
5008
5189
|
if (querySimilarity > SEMANTIC_SKIP_THRESHOLD) {
|
|
5009
5190
|
const lines = cachedFacts.slice(0, 8).map((f, i) => `${i + 1}. ${f.text} (importance: ${f.importance}/10, cached)`);
|
|
5010
|
-
return { prependContext: `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
5191
|
+
return { prependContext: stagingBannerBlock + `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
5011
5192
|
}
|
|
5012
5193
|
}
|
|
5013
5194
|
// 3. Merge trapdoors — always include word trapdoors for small-dataset coverage.
|
|
@@ -5016,7 +5197,7 @@ const plugin = {
|
|
|
5016
5197
|
// If we have cached facts and no trapdoors, return cached facts.
|
|
5017
5198
|
if (allTrapdoors.length === 0 && cachedFacts.length > 0) {
|
|
5018
5199
|
const lines = cachedFacts.slice(0, 8).map((f, i) => `${i + 1}. ${f.text} (importance: ${f.importance}/10, cached)`);
|
|
5019
|
-
return { prependContext: `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
5200
|
+
return { prependContext: stagingBannerBlock + `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
5020
5201
|
}
|
|
5021
5202
|
if (allTrapdoors.length === 0)
|
|
5022
5203
|
return undefined;
|
|
@@ -5031,7 +5212,7 @@ const plugin = {
|
|
|
5031
5212
|
// Subgraph query failed -- fall back to cached facts if available.
|
|
5032
5213
|
if (cachedFacts.length > 0) {
|
|
5033
5214
|
const lines = cachedFacts.slice(0, 8).map((f, i) => `${i + 1}. ${f.text} (importance: ${f.importance}/10, cached)`);
|
|
5034
|
-
return { prependContext: `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
5215
|
+
return { prependContext: stagingBannerBlock + `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
5035
5216
|
}
|
|
5036
5217
|
return undefined;
|
|
5037
5218
|
}
|
|
@@ -5055,7 +5236,7 @@ const plugin = {
|
|
|
5055
5236
|
// If subgraph returned no results but we have cache, use cache.
|
|
5056
5237
|
if (subgraphResults.length === 0) {
|
|
5057
5238
|
const lines = cachedFacts.slice(0, 8).map((f, i) => `${i + 1}. ${f.text} (importance: ${f.importance}/10, cached)`);
|
|
5058
|
-
return { prependContext: `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
5239
|
+
return { prependContext: stagingBannerBlock + `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
5059
5240
|
}
|
|
5060
5241
|
// 5. Decrypt subgraph results and build reranker input.
|
|
5061
5242
|
const rerankerCandidates = [];
|
|
@@ -5130,7 +5311,7 @@ const plugin = {
|
|
|
5130
5311
|
return `${i + 1}. ${typeTag}${m.text} (importance: ${importance}/10, ${age})`;
|
|
5131
5312
|
});
|
|
5132
5313
|
const contextString = `## Relevant Memories\n\n${lines.join('\n')}`;
|
|
5133
|
-
return { prependContext: contextString + welcomeBack + billingWarning };
|
|
5314
|
+
return { prependContext: stagingBannerBlock + contextString + welcomeBack + billingWarning };
|
|
5134
5315
|
}
|
|
5135
5316
|
// --- Server mode (existing behavior) ---
|
|
5136
5317
|
// 1. Generate word trapdoors from the user prompt.
|
|
@@ -5212,7 +5393,7 @@ const plugin = {
|
|
|
5212
5393
|
return `${i + 1}. ${m.text} (importance: ${importance}/10, ${age})`;
|
|
5213
5394
|
});
|
|
5214
5395
|
const contextString = `## Relevant Memories\n\n${lines.join('\n')}`;
|
|
5215
|
-
return { prependContext: contextString + welcomeBack + billingWarning };
|
|
5396
|
+
return { prependContext: stagingBannerBlock + contextString + welcomeBack + billingWarning };
|
|
5216
5397
|
}
|
|
5217
5398
|
catch (err) {
|
|
5218
5399
|
// The hook must NEVER throw -- log and return undefined.
|
package/dist/llm-client.js
CHANGED
|
@@ -684,4 +684,6 @@ async function chatCompletionAnthropic(config, messages, maxTokens, temperature,
|
|
|
684
684
|
// (Harrier-OSS-v1-270M ONNX model). No API key needed. The native deps +
|
|
685
685
|
// model are lazy-fetched from a pinned GitHub Release on first call —
|
|
686
686
|
// see embedding.ts + embedder-loader.ts.
|
|
687
|
-
export { generateEmbedding, getEmbeddingDims, getEmbeddingModelId, configureEmbedder
|
|
687
|
+
export { generateEmbedding, getEmbeddingDims, getEmbeddingModelId, configureEmbedder,
|
|
688
|
+
// 3.3.3-rc.1 (#187): pre-pair bundle prefetch
|
|
689
|
+
prefetchEmbedderBundle, } from './embedding.js';
|
package/dist/subgraph-store.js
CHANGED
|
@@ -675,7 +675,7 @@ export function isSubgraphMode() {
|
|
|
675
675
|
*
|
|
676
676
|
* After the v1 env var cleanup, clients only need:
|
|
677
677
|
* - TOTALRECLAW_RECOVERY_PHRASE -- BIP-39 mnemonic
|
|
678
|
-
* - TOTALRECLAW_SERVER_URL -- relay server URL (default: https://api.totalreclaw.xyz)
|
|
678
|
+
* - TOTALRECLAW_SERVER_URL -- relay server URL (source default: https://api-staging.totalreclaw.xyz; stable build: https://api.totalreclaw.xyz — swapped in at publish time per PR #165)
|
|
679
679
|
* - TOTALRECLAW_SELF_HOSTED -- set "true" to use self-hosted server (default: managed service)
|
|
680
680
|
*
|
|
681
681
|
* Chain ID is no longer configurable via env — it is auto-detected from the
|
|
@@ -683,7 +683,8 @@ export function isSubgraphMode() {
|
|
|
683
683
|
*/
|
|
684
684
|
export function getSubgraphConfig() {
|
|
685
685
|
return {
|
|
686
|
-
|
|
686
|
+
// 3.3.3-rc.1: staging by default in source; stable workflow seds.
|
|
687
|
+
relayUrl: CONFIG.serverUrl || 'https://api-staging.totalreclaw.xyz',
|
|
687
688
|
mnemonic: CONFIG.recoveryPhrase,
|
|
688
689
|
cachePath: CONFIG.cachePath,
|
|
689
690
|
chainId: CONFIG.chainId,
|
package/embedding.ts
CHANGED
|
@@ -91,6 +91,31 @@ function activeRuntimeConfig(): EmbedderRuntimeConfig {
|
|
|
91
91
|
return { cacheRoot: defaultCacheRoot(), rcTag: '0.0.0-dev' };
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* 3.3.3-rc.1 (issue #187 — ONNX decouple): prefetch the embedder bundle
|
|
96
|
+
* WITHOUT loading the model into memory. Used to download the
|
|
97
|
+
* ~700 MB tarball pre-pair so the user does not hit the network round-trip
|
|
98
|
+
* mid-conversation. Idempotent — subsequent calls are cache-hit no-ops.
|
|
99
|
+
*
|
|
100
|
+
* Returns:
|
|
101
|
+
* - `'cache_hit'` if the bundle was already extracted + verified.
|
|
102
|
+
* - `'fetched'` if the bundle was downloaded this call.
|
|
103
|
+
* - throws on transport / extraction failure.
|
|
104
|
+
*
|
|
105
|
+
* Pre-flight is the caller's job (disk-space, network reachability) — this
|
|
106
|
+
* function focuses on the cache-resolve + fetch-on-miss path so it can also
|
|
107
|
+
* be reused as a fast cache-validation probe.
|
|
108
|
+
*/
|
|
109
|
+
export async function prefetchEmbedderBundle(opts?: { log?: (msg: string) => void }): Promise<'cache_hit' | 'fetched'> {
|
|
110
|
+
const cfg = activeRuntimeConfig();
|
|
111
|
+
const loaded = await loadEmbedder({
|
|
112
|
+
cacheRoot: cfg.cacheRoot,
|
|
113
|
+
rcTag: cfg.rcTag,
|
|
114
|
+
log: opts?.log,
|
|
115
|
+
});
|
|
116
|
+
return loaded.wasFetched ? 'fetched' : 'cache_hit';
|
|
117
|
+
}
|
|
118
|
+
|
|
94
119
|
/** Lazily initialized state. */
|
|
95
120
|
let pipelineExtractor: any = null;
|
|
96
121
|
let autoTokenizer: any = null;
|