@totalreclaw/totalreclaw 3.3.12 → 3.3.13

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.
Files changed (128) hide show
  1. package/SKILL.md +25 -8
  2. package/{billing-cache.ts → billing/billing-cache.ts} +1 -1
  3. package/{relay-headers.ts → billing/relay-headers.ts} +1 -1
  4. package/{tr-cli-export-helper.ts → cli/tr-cli-export-helper.ts} +5 -5
  5. package/{tr-cli.ts → cli/tr-cli.ts} +20 -20
  6. package/{contradiction-sync.ts → contradiction/contradiction-sync.ts} +13 -13
  7. package/{digest-sync.ts → digest/digest-sync.ts} +32 -2
  8. package/dist/{billing-cache.js → billing/billing-cache.js} +1 -1
  9. package/dist/{relay-headers.js → billing/relay-headers.js} +1 -1
  10. package/dist/{tr-cli-export-helper.js → cli/tr-cli-export-helper.js} +5 -5
  11. package/dist/{tr-cli.js → cli/tr-cli.js} +19 -19
  12. package/dist/{contradiction-sync.js → contradiction/contradiction-sync.js} +13 -13
  13. package/dist/{digest-sync.js → digest/digest-sync.js} +32 -2
  14. package/dist/{download-ux.js → embedding/download-ux.js} +1 -1
  15. package/dist/{reranker.js → embedding/reranker.js} +13 -4
  16. package/dist/{claims-helper.js → extraction/claims-helper.js} +54 -58
  17. package/dist/extraction/consolidation.js +170 -0
  18. package/dist/{extractor.js → extraction/extractor.js} +17 -17
  19. package/dist/extraction/importance-filter.js +38 -0
  20. package/dist/{semantic-dedup.js → extraction/semantic-dedup.js} +2 -2
  21. package/dist/{batch-gate.js → import/batch-gate.js} +1 -1
  22. package/dist/import/import-runtime.js +692 -0
  23. package/dist/import-adapters/chatgpt-adapter.js +7 -7
  24. package/dist/import-adapters/gemini-adapter.js +13 -13
  25. package/dist/import-adapters/mem0-adapter.js +10 -10
  26. package/dist/index.js +111 -1094
  27. package/dist/{llm-client.js → llm/llm-client.js} +2 -2
  28. package/dist/{hot-cache-wrapper.js → memory/hot-cache-wrapper.js} +5 -5
  29. package/dist/{memory-runtime.js → memory/memory-runtime.js} +1 -1
  30. package/dist/{pin.js → memory/pin.js} +18 -18
  31. package/dist/{retype-setscope.js → memory/retype-setscope.js} +19 -19
  32. package/dist/{credential-provider.js → pairing/credential-provider.js} +2 -2
  33. package/dist/{first-run.js → pairing/first-run.js} +1 -1
  34. package/dist/{onboarding-cli.js → pairing/onboarding-cli.js} +20 -20
  35. package/dist/{pair-cli-relay.js → pairing/pair-cli-relay.js} +10 -10
  36. package/dist/{pair-crypto.js → pairing/pair-crypto.js} +1 -1
  37. package/dist/{pair-http.js → pairing/pair-http.js} +3 -3
  38. package/dist/{pair-remote-client.js → pairing/pair-remote-client.js} +10 -10
  39. package/dist/{pair-session-store.js → pairing/pair-session-store.js} +4 -4
  40. package/dist/runtime/config-schema.js +59 -0
  41. package/dist/runtime/format-helpers.js +257 -0
  42. package/dist/runtime/types.js +11 -0
  43. package/dist/{confirm-indexed.js → subgraph/confirm-indexed.js} +1 -1
  44. package/dist/{subgraph-search.js → subgraph/subgraph-search.js} +14 -14
  45. package/dist/{subgraph-store.js → subgraph/subgraph-store.js} +6 -6
  46. package/{download-ux.ts → embedding/download-ux.ts} +1 -1
  47. package/{reranker.ts → embedding/reranker.ts} +13 -4
  48. package/{claims-helper.ts → extraction/claims-helper.ts} +52 -60
  49. package/{consolidation.ts → extraction/consolidation.ts} +63 -154
  50. package/{extractor.ts → extraction/extractor.ts} +18 -17
  51. package/extraction/importance-filter.ts +50 -0
  52. package/{semantic-dedup.ts → extraction/semantic-dedup.ts} +2 -2
  53. package/{batch-gate.ts → import/batch-gate.ts} +1 -1
  54. package/import/import-runtime.ts +853 -0
  55. package/import-adapters/chatgpt-adapter.ts +7 -7
  56. package/import-adapters/gemini-adapter.ts +13 -13
  57. package/import-adapters/mem0-adapter.ts +10 -10
  58. package/index.ts +129 -1377
  59. package/{llm-client.ts → llm/llm-client.ts} +2 -2
  60. package/{hot-cache-wrapper.ts → memory/hot-cache-wrapper.ts} +5 -5
  61. package/{memory-runtime.ts → memory/memory-runtime.ts} +1 -1
  62. package/{pin.ts → memory/pin.ts} +20 -20
  63. package/{retype-setscope.ts → memory/retype-setscope.ts} +21 -21
  64. package/{tool-gating.ts → memory/tool-gating.ts} +1 -1
  65. package/package.json +6 -6
  66. package/{credential-provider.ts → pairing/credential-provider.ts} +2 -2
  67. package/{first-run.ts → pairing/first-run.ts} +1 -1
  68. package/{onboarding-cli.ts → pairing/onboarding-cli.ts} +20 -20
  69. package/{pair-cli-relay.ts → pairing/pair-cli-relay.ts} +10 -10
  70. package/{pair-crypto.ts → pairing/pair-crypto.ts} +1 -1
  71. package/{pair-http.ts → pairing/pair-http.ts} +3 -3
  72. package/{pair-remote-client.ts → pairing/pair-remote-client.ts} +10 -10
  73. package/{pair-session-store.ts → pairing/pair-session-store.ts} +4 -4
  74. package/runtime/config-schema.ts +64 -0
  75. package/runtime/format-helpers.ts +293 -0
  76. package/runtime/types.ts +118 -0
  77. package/skill.json +44 -10
  78. package/{confirm-indexed.ts → subgraph/confirm-indexed.ts} +1 -1
  79. package/{subgraph-search.ts → subgraph/subgraph-search.ts} +14 -14
  80. package/{subgraph-store.ts → subgraph/subgraph-store.ts} +10 -10
  81. package/dist/consolidation.js +0 -249
  82. /package/{api-client.ts → billing/api-client.ts} +0 -0
  83. /package/{gateway-url.ts → billing/gateway-url.ts} +0 -0
  84. /package/{inbound-user-tracker.ts → billing/inbound-user-tracker.ts} +0 -0
  85. /package/{relay.ts → billing/relay.ts} +0 -0
  86. /package/{crypto.ts → crypto/crypto.ts} +0 -0
  87. /package/{vault-crypto.ts → crypto/vault-crypto.ts} +0 -0
  88. /package/dist/{api-client.js → billing/api-client.js} +0 -0
  89. /package/dist/{gateway-url.js → billing/gateway-url.js} +0 -0
  90. /package/dist/{inbound-user-tracker.js → billing/inbound-user-tracker.js} +0 -0
  91. /package/dist/{relay.js → billing/relay.js} +0 -0
  92. /package/dist/{crypto.js → crypto/crypto.js} +0 -0
  93. /package/dist/{vault-crypto.js → crypto/vault-crypto.js} +0 -0
  94. /package/dist/{embedder-cache.js → embedding/embedder-cache.js} +0 -0
  95. /package/dist/{embedder-loader.js → embedding/embedder-loader.js} +0 -0
  96. /package/dist/{embedder-network.js → embedding/embedder-network.js} +0 -0
  97. /package/dist/{embedding.js → embedding/embedding.js} +0 -0
  98. /package/dist/{lsh.js → embedding/lsh.js} +0 -0
  99. /package/dist/{import-state-manager.js → import/import-state-manager.js} +0 -0
  100. /package/dist/{llm-profile-reader.js → llm/llm-profile-reader.js} +0 -0
  101. /package/dist/{native-memory.js → memory/native-memory.js} +0 -0
  102. /package/dist/{tool-gating.js → memory/tool-gating.js} +0 -0
  103. /package/dist/{tools.js → memory/tools.js} +0 -0
  104. /package/dist/{generate-mnemonic.js → pairing/generate-mnemonic.js} +0 -0
  105. /package/dist/{pair-cli.js → pairing/pair-cli.js} +0 -0
  106. /package/dist/{pair-page.js → pairing/pair-page.js} +0 -0
  107. /package/dist/{pair-qr.js → pairing/pair-qr.js} +0 -0
  108. /package/dist/{restart-auth.js → pairing/restart-auth.js} +0 -0
  109. /package/dist/{qa-bug-report.js → setup/qa-bug-report.js} +0 -0
  110. /package/dist/{skill-register.js → setup/skill-register.js} +0 -0
  111. /package/dist/{trajectory-poller.js → subgraph/trajectory-poller.js} +0 -0
  112. /package/{embedder-cache.ts → embedding/embedder-cache.ts} +0 -0
  113. /package/{embedder-loader.ts → embedding/embedder-loader.ts} +0 -0
  114. /package/{embedder-network.ts → embedding/embedder-network.ts} +0 -0
  115. /package/{embedding.ts → embedding/embedding.ts} +0 -0
  116. /package/{lsh.ts → embedding/lsh.ts} +0 -0
  117. /package/{import-state-manager.ts → import/import-state-manager.ts} +0 -0
  118. /package/{llm-profile-reader.ts → llm/llm-profile-reader.ts} +0 -0
  119. /package/{native-memory.ts → memory/native-memory.ts} +0 -0
  120. /package/{tools.ts → memory/tools.ts} +0 -0
  121. /package/{generate-mnemonic.ts → pairing/generate-mnemonic.ts} +0 -0
  122. /package/{pair-cli.ts → pairing/pair-cli.ts} +0 -0
  123. /package/{pair-page.ts → pairing/pair-page.ts} +0 -0
  124. /package/{pair-qr.ts → pairing/pair-qr.ts} +0 -0
  125. /package/{restart-auth.ts → pairing/restart-auth.ts} +0 -0
  126. /package/{qa-bug-report.ts → setup/qa-bug-report.ts} +0 -0
  127. /package/{skill-register.ts → setup/skill-register.ts} +0 -0
  128. /package/{trajectory-poller.ts → subgraph/trajectory-poller.ts} +0 -0
@@ -9,7 +9,7 @@
9
9
  * @huggingface/transformers). No API key needed for embeddings.
10
10
  */
11
11
 
12
- import { CONFIG } from './config.js';
12
+ import { CONFIG } from '../config.js';
13
13
 
14
14
  // ---------------------------------------------------------------------------
15
15
  // Types
@@ -991,4 +991,4 @@ export {
991
991
  configureEmbedder,
992
992
  // 3.3.3-rc.1 (#187): pre-pair bundle prefetch
993
993
  prefetchEmbedderBundle,
994
- } from './embedding.js';
994
+ } from '../embedding/embedding.js';
@@ -96,12 +96,12 @@ export class PluginHotCache {
96
96
  if (!fs.existsSync(this.cachePath)) return;
97
97
 
98
98
  try {
99
- const data = fs.readFileSync(this.cachePath);
100
- if (data.length < IV_LENGTH + TAG_LENGTH) return;
99
+ const encryptedFile = fs.readFileSync(this.cachePath);
100
+ if (encryptedFile.length < IV_LENGTH + TAG_LENGTH) return;
101
101
 
102
- const iv = data.subarray(0, IV_LENGTH);
103
- const tag = data.subarray(IV_LENGTH, IV_LENGTH + TAG_LENGTH);
104
- const ciphertext = data.subarray(IV_LENGTH + TAG_LENGTH);
102
+ const iv = encryptedFile.subarray(0, IV_LENGTH);
103
+ const tag = encryptedFile.subarray(IV_LENGTH, IV_LENGTH + TAG_LENGTH);
104
+ const ciphertext = encryptedFile.subarray(IV_LENGTH + TAG_LENGTH);
105
105
 
106
106
  const decipher = crypto.createDecipheriv('aes-256-gcm', this.key, iv);
107
107
  decipher.setAuthTag(tag);
@@ -65,7 +65,7 @@
65
65
  // (no OpenClaw type import) so the plugin compiles without depending on
66
66
  // OpenClaw's type package.
67
67
  // ---------------------------------------------------------------------------
68
- import { EXTRACTION_SYSTEM_PROMPT } from './extractor.js';
68
+ import { EXTRACTION_SYSTEM_PROMPT } from '../extraction/extractor.js';
69
69
 
70
70
  // ---------------------------------------------------------------------------
71
71
  // Types — injected caller shapes. Kept loose (no OpenClaw type import) so
@@ -16,17 +16,17 @@ import {
16
16
  mapTypeToCategory,
17
17
  readV1Blob,
18
18
  type PinStatus,
19
- } from './claims-helper.js';
19
+ } from '../extraction/claims-helper.js';
20
20
  import {
21
21
  findLoserClaimInDecisionLog,
22
22
  maybeWriteFeedbackForPin,
23
23
  type ContradictionLogger,
24
- } from './contradiction-sync.js';
25
- import { isValidMemoryType, V0_TO_V1_TYPE } from './extractor.js';
26
- import type { MemoryType, MemorySource, MemoryScope, MemoryVolatility } from './extractor.js';
27
- import { PROTOBUF_VERSION_V4 } from './subgraph-store.js';
28
- import type { SubgraphSearchFact } from './subgraph-search.js';
29
- import { confirmIndexed, type ConfirmIndexedOptions } from './confirm-indexed.js';
24
+ } from '../contradiction/contradiction-sync.js';
25
+ import { isValidMemoryType, V0_TO_V1_TYPE } from '../extraction/extractor.js';
26
+ import type { MemoryType, MemorySource, MemoryScope, MemoryVolatility } from '../extraction/extractor.js';
27
+ import { PROTOBUF_VERSION_V4 } from '../subgraph/subgraph-store.js';
28
+ import type { SubgraphSearchFact } from '../subgraph/subgraph-search.js';
29
+ import { confirmIndexed, type ConfirmIndexedOptions } from '../subgraph/confirm-indexed.js';
30
30
 
31
31
  // Lazy-load WASM core (mirrors claims-helper.ts pattern — plays nicely under
32
32
  // both the OpenClaw runtime (CJS-ish tsx) and bare Node ESM used by tests).
@@ -151,9 +151,9 @@ export interface ParsedBlob {
151
151
 
152
152
  /** Parse a decrypted blob into a canonical mutable Claim + current human status. */
153
153
  export function parseBlobForPin(decrypted: string): ParsedBlob {
154
- let obj: Record<string, unknown>;
154
+ let parsed: Record<string, unknown>;
155
155
  try {
156
- obj = JSON.parse(decrypted) as Record<string, unknown>;
156
+ parsed = JSON.parse(decrypted) as Record<string, unknown>;
157
157
  } catch {
158
158
  const shortClaim = buildCanonicalObjectFromLegacy(decrypted, {});
159
159
  return {
@@ -167,16 +167,16 @@ export function parseBlobForPin(decrypted: string): ParsedBlob {
167
167
  // v1 payload (plugin v3.0.0+): long-form fields + schema_version "1.x".
168
168
  // Preserve the v1 structure so the pin path can emit v1 on output.
169
169
  if (
170
- typeof obj.text === 'string' &&
171
- typeof obj.type === 'string' &&
172
- typeof obj.schema_version === 'string' &&
173
- obj.schema_version.startsWith('1.')
170
+ typeof parsed.text === 'string' &&
171
+ typeof parsed.type === 'string' &&
172
+ typeof parsed.schema_version === 'string' &&
173
+ parsed.schema_version.startsWith('1.')
174
174
  ) {
175
175
  const v1 = readV1Blob(decrypted);
176
176
  if (v1) {
177
177
  // Current status = pinStatus if present, else active.
178
178
  const human: HumanStatus = v1.pinStatus === 'pinned' ? 'pinned' : 'active';
179
- const shortProjection = v1ToShortKeyClaim(obj);
179
+ const shortProjection = v1ToShortKeyClaim(parsed);
180
180
  return {
181
181
  source: {
182
182
  kind: 'v1',
@@ -204,10 +204,10 @@ export function parseBlobForPin(decrypted: string): ParsedBlob {
204
204
  }
205
205
 
206
206
  // v0 canonical Claim — short keys present.
207
- if (typeof obj.t === 'string' && typeof obj.c === 'string') {
208
- const st = typeof obj.st === 'string' ? obj.st : 'a';
207
+ if (typeof parsed.t === 'string' && typeof parsed.c === 'string') {
208
+ const st = typeof parsed.st === 'string' ? parsed.st : 'a';
209
209
  const human = SHORT_TO_HUMAN[st] ?? 'active';
210
- const cloned = JSON.parse(JSON.stringify(obj)) as Record<string, unknown>;
210
+ const cloned = JSON.parse(JSON.stringify(parsed)) as Record<string, unknown>;
211
211
  return {
212
212
  source: { kind: 'v0', claim: cloned },
213
213
  claim: cloned,
@@ -217,9 +217,9 @@ export function parseBlobForPin(decrypted: string): ParsedBlob {
217
217
  }
218
218
 
219
219
  // Legacy {text, metadata: {importance: 0-1}} shape.
220
- if (typeof obj.text === 'string') {
221
- const meta = (obj.metadata as Record<string, unknown>) ?? {};
222
- const shortClaim = buildCanonicalObjectFromLegacy(obj.text, meta);
220
+ if (typeof parsed.text === 'string') {
221
+ const meta = (parsed.metadata as Record<string, unknown>) ?? {};
222
+ const shortClaim = buildCanonicalObjectFromLegacy(parsed.text, meta);
223
223
  return {
224
224
  source: { kind: 'v0', claim: shortClaim },
225
225
  claim: shortClaim,
@@ -35,21 +35,21 @@ import {
35
35
  mapTypeToCategory,
36
36
  readV1Blob,
37
37
  type PinStatus,
38
- } from './claims-helper.js';
38
+ } from '../extraction/claims-helper.js';
39
39
  import {
40
40
  isValidMemoryType,
41
41
  VALID_MEMORY_SCOPES,
42
42
  V0_TO_V1_TYPE,
43
- } from './extractor.js';
43
+ } from '../extraction/extractor.js';
44
44
  import type {
45
45
  MemoryType,
46
46
  MemorySource,
47
47
  MemoryScope,
48
48
  MemoryVolatility,
49
- } from './extractor.js';
50
- import { PROTOBUF_VERSION_V4 } from './subgraph-store.js';
51
- import type { SubgraphSearchFact } from './subgraph-search.js';
52
- import { confirmIndexed, type ConfirmIndexedOptions } from './confirm-indexed.js';
49
+ } from '../extraction/extractor.js';
50
+ import { PROTOBUF_VERSION_V4 } from '../subgraph/subgraph-store.js';
51
+ import type { SubgraphSearchFact } from '../subgraph/subgraph-search.js';
52
+ import { confirmIndexed, type ConfirmIndexedOptions } from '../subgraph/confirm-indexed.js';
53
53
 
54
54
  // Lazy-load WASM core — mirrors pin.ts pattern.
55
55
  const requireWasm = createRequire(import.meta.url);
@@ -163,19 +163,19 @@ interface NormalizedFact {
163
163
  }
164
164
 
165
165
  function projectFromDecrypted(decrypted: string): NormalizedFact | null {
166
- let obj: Record<string, unknown>;
166
+ let parsed: Record<string, unknown>;
167
167
  try {
168
- obj = JSON.parse(decrypted) as Record<string, unknown>;
168
+ parsed = JSON.parse(decrypted) as Record<string, unknown>;
169
169
  } catch {
170
170
  return null;
171
171
  }
172
172
 
173
173
  // v1 blob (schema_version "1.x")
174
174
  if (
175
- typeof obj.text === 'string' &&
176
- typeof obj.type === 'string' &&
177
- typeof obj.schema_version === 'string' &&
178
- obj.schema_version.startsWith('1.')
175
+ typeof parsed.text === 'string' &&
176
+ typeof parsed.type === 'string' &&
177
+ typeof parsed.schema_version === 'string' &&
178
+ parsed.schema_version.startsWith('1.')
179
179
  ) {
180
180
  const v1 = readV1Blob(decrypted);
181
181
  if (v1) {
@@ -198,20 +198,20 @@ function projectFromDecrypted(decrypted: string): NormalizedFact | null {
198
198
  }
199
199
 
200
200
  // v0 short-key blob — upgrade to v1 shape.
201
- if (typeof obj.t === 'string' && typeof obj.c === 'string') {
202
- const v0Type = typeof obj.c === 'string' ? obj.c : 'fact';
201
+ if (typeof parsed.t === 'string' && typeof parsed.c === 'string') {
202
+ const v0Type = typeof parsed.c === 'string' ? parsed.c : 'fact';
203
203
  const v1Type: MemoryType = (V0_TO_V1_TYPE as Record<string, MemoryType>)[v0Type] ?? 'claim';
204
- const imp = typeof obj.i === 'number' ? obj.i : 5;
205
- const conf = typeof obj.cf === 'number' ? obj.cf : 0.85;
206
- const sa = typeof obj.sa === 'string' ? obj.sa : 'user';
204
+ const imp = typeof parsed.i === 'number' ? parsed.i : 5;
205
+ const conf = typeof parsed.cf === 'number' ? parsed.cf : 0.85;
206
+ const sa = typeof parsed.sa === 'string' ? parsed.sa : 'user';
207
207
  const validSource: MemorySource = (
208
208
  ['user', 'user-inferred', 'assistant', 'external', 'derived'] as const
209
209
  ).includes(sa as MemorySource)
210
210
  ? (sa as MemorySource)
211
211
  : 'user';
212
- const ea = typeof obj.ea === 'string' ? obj.ea : new Date().toISOString();
213
- const entities = Array.isArray(obj.e)
214
- ? (obj.e as unknown[])
212
+ const ea = typeof parsed.ea === 'string' ? parsed.ea : new Date().toISOString();
213
+ const entities = Array.isArray(parsed.e)
214
+ ? (parsed.e as unknown[])
215
215
  .map((e) => {
216
216
  if (!e || typeof e !== 'object') return null;
217
217
  const entity = e as Record<string, unknown>;
@@ -224,7 +224,7 @@ function projectFromDecrypted(decrypted: string): NormalizedFact | null {
224
224
  .filter((e): e is { name: string; type: string; role?: string } => e !== null)
225
225
  : undefined;
226
226
  return {
227
- text: typeof obj.t === 'string' ? obj.t : '',
227
+ text: typeof parsed.t === 'string' ? parsed.t : '',
228
228
  type: v1Type,
229
229
  source: validSource,
230
230
  scope: undefined,
@@ -30,7 +30,7 @@
30
30
  * `resolveOnboardingState` already does; no network; no env reads.
31
31
  */
32
32
 
33
- import type { OnboardingState } from './fs-helpers.js';
33
+ import type { OnboardingState } from '../fs-helpers.js';
34
34
 
35
35
  /**
36
36
  * Tool names gated on `state=active`. These are the bundled NATIVE memory
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@totalreclaw/totalreclaw",
3
- "version": "3.3.12",
3
+ "version": "3.3.13",
4
4
  "description": "End-to-end encrypted, agent-portable memory for OpenClaw and any LLM-agent runtime. XChaCha20-Poly1305 with protobuf v4 + on-chain Memory Taxonomy v1 (claim / preference / directive / commitment / episode / summary).",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -34,8 +34,6 @@
34
34
  "@scure/bip39": "^2.2.0",
35
35
  "@totalreclaw/client": "^1.3.0",
36
36
  "@totalreclaw/core": "^2.5.3",
37
- "@types/qrcode": "^1.5.6",
38
- "@types/ws": "^8.5.12",
39
37
  "qrcode": "^1.5.4",
40
38
  "qrcode-terminal": "^0.12.0",
41
39
  "ws": "^8.18.3"
@@ -43,17 +41,19 @@
43
41
  "//": "@huggingface/transformers + onnxruntime-node deliberately omitted from runtime deps. They are heavy native bundles (~700 MB peak install RAM) that OOM-killed the OpenClaw gateway on small VPS during `openclaw plugins install` in rc.21 (issue: 3.7 GB Hetzner host). rc.22 ships them via a lazy GitHub-Releases bundle (`embedder-v1.tar.gz`) downloaded on first call to embed(). See `embedder-network.ts` + `scripts/build-embedder-bundle.mjs`. The dev-deps below are for type-checking + bundle generation only; npm install of the plugin tarball never installs them.",
44
42
  "devDependencies": {
45
43
  "@huggingface/transformers": "^4.0.1",
44
+ "@types/qrcode": "^1.5.6",
45
+ "@types/ws": "^8.5.12",
46
46
  "onnxruntime-node": "^1.24.0",
47
47
  "typescript": "^5.5.0"
48
48
  },
49
49
  "main": "./dist/index.js",
50
50
  "types": "./dist/index.d.ts",
51
51
  "bin": {
52
- "tr": "./dist/tr-cli.js"
52
+ "tr": "./dist/cli/tr-cli.js"
53
53
  },
54
54
  "files": [
55
55
  "dist/",
56
- "*.ts",
56
+ "**/*.ts",
57
57
  "import-adapters/",
58
58
  "!**/*.test.ts",
59
59
  "!pocv2-e2e-test.ts",
@@ -68,7 +68,7 @@
68
68
  "scripts": {
69
69
  "build": "rm -rf dist && tsc -p tsconfig.json --noCheck",
70
70
  "verify-tarball": "node ../scripts/verify-tarball.mjs",
71
- "test": "npx tsx batch-gate.test.ts && npx tsx manifest-shape.test.ts && npx tsx config-schema.test.ts && npx tsx config.test.ts && npx tsx relay-headers.test.ts && npx tsx scope-address-visible.test.ts && npx tsx llm-profile-reader.test.ts && npx tsx llm-client.test.ts && npx tsx llm-client-retry.test.ts && npx tsx llm-client-json-mode.test.ts && npx tsx gateway-url.test.ts && npx tsx retype-setscope.test.ts && npx tsx tool-gating.test.ts && npx tsx onboarding-noninteractive.test.ts && npx tsx pair-cli-json.test.ts && npx tsx pair-qr.test.ts && npx tsx pair-remote-client.test.ts && npx tsx pair-http.test.ts && npx tsx pair-http-route-registration.test.ts && npx tsx pair-http-init.test.ts && npx tsx qa-bug-report.test.ts && npx tsx nonce-serialization.test.ts && npx tsx initcode-lifecycle.test.ts && npx tsx phrase-safety-registry.test.ts && npx tsx test_issue_92_onnx_download_ux.test.ts && npx tsx onboard-pair-only.test.ts && npx tsx import-state.test.ts && npx tsx import-time-smoke.test.ts && npx tsx install-staging-cleanup.test.ts && npx tsx partial-install-detection.test.ts && npx tsx install-reload-idempotency.test.ts && npx tsx skill-register.test.ts && npx tsx json-stdout-cleanliness.test.ts && npx tsx url-binding.test.ts && npx tsx fs-helpers.test.ts && npx tsx pair-cli-default-mode.test.ts && npx tsx embedding-fallback-tag.test.ts && npx tsx staging-banner-gate.test.ts && npx tsx restart-auth.test.ts && npx tsx inbound-user-tracker.test.ts && npx tsx register-command-name.test.ts && npx tsx skill-md-native.test.ts &&npx tsx tr-cli-json-output.test.ts && npx tsx import-upgrade-cli.test.ts && npx tsx cli-registercli-scope.test.ts && npx tsx postinstall-validate.test.ts && npx tsx credential-provider.test.ts && npx tsx memory-runtime.test.ts && npx tsx tools.test.ts && npx tsx register-native.test.ts && npx tsx relay.test.ts && npx tsx vault-crypto.test.ts && npx tsx entry-env.test.ts && npx tsx trajectory-poller.test.ts && npx tsx billing-cache.test.ts && npx tsx dataedge-write-target.test.ts",
71
+ "test": "node run-tests.mjs",
72
72
  "smoke:dist": "npx tsx dist-esm-smoke.test.ts",
73
73
  "check-scanner": "node ../scripts/check-scanner.mjs",
74
74
  "check-version-drift": "node ../scripts/check-version-drift.mjs",
@@ -36,13 +36,13 @@
36
36
 
37
37
  import fs from 'node:fs';
38
38
 
39
- import { CONFIG } from './config.js';
39
+ import { CONFIG } from '../config.js';
40
40
  import {
41
41
  loadCredentialsJson,
42
42
  writeCredentialsJson,
43
43
  deleteCredentialsFile,
44
44
  type CredentialsFile,
45
- } from './fs-helpers.js';
45
+ } from '../fs-helpers.js';
46
46
 
47
47
  /**
48
48
  * Provider interface — three methods cover the lifecycle:
@@ -37,7 +37,7 @@
37
37
  * crypto code for a copy change.
38
38
  */
39
39
 
40
- import { loadCredentialsJson, extractBootstrapMnemonic } from './fs-helpers.js';
40
+ import { loadCredentialsJson, extractBootstrapMnemonic } from '../fs-helpers.js';
41
41
 
42
42
  // ---------------------------------------------------------------------------
43
43
  // Canonical copy — single source of truth for the welcome-on-first-run UX.
@@ -52,7 +52,7 @@ import {
52
52
  writeOnboardingState,
53
53
  loadOnboardingState,
54
54
  type OnboardingState,
55
- } from './fs-helpers.js';
55
+ } from '../fs-helpers.js';
56
56
 
57
57
  // ---------------------------------------------------------------------------
58
58
  // User-facing strings (centralised so tests can assert on them + localisation
@@ -665,15 +665,15 @@ export async function runNonInteractiveOnboard(
665
665
  }
666
666
  }
667
667
 
668
- const result: NonInteractiveOnboardResult = {
668
+ const onboardResult: NonInteractiveOnboardResult = {
669
669
  ok: true,
670
670
  action,
671
671
  credentials_path: inputs.credentialsPath,
672
672
  };
673
- if (scopeAddress) result.scope_address = scopeAddress;
674
- if (inputs.emitPhrase) result.mnemonic = mnemonic;
673
+ if (scopeAddress) onboardResult.scope_address = scopeAddress;
674
+ if (inputs.emitPhrase) onboardResult.mnemonic = mnemonic;
675
675
  void state; // Touch for clarity — state is persisted via writeCredsAndState.
676
- return result;
676
+ return onboardResult;
677
677
  }
678
678
 
679
679
  // ---------------------------------------------------------------------------
@@ -840,7 +840,7 @@ export function registerOnboardingCli(
840
840
  phrase = await readAllStdin();
841
841
  }
842
842
 
843
- const result = await runNonInteractiveOnboard({
843
+ const onboardResult = await runNonInteractiveOnboard({
844
844
  credentialsPath: opts.credentialsPath,
845
845
  statePath: opts.statePath,
846
846
  mode,
@@ -854,13 +854,13 @@ export function registerOnboardingCli(
854
854
  // the JSON payload is about to include the plaintext phrase.
855
855
  // stderr is intentional: stdout must remain a single
856
856
  // machine-parseable JSON line.
857
- if (cliOpts.emitPhrase && result.ok && result.mnemonic) {
857
+ if (cliOpts.emitPhrase && onboardResult.ok && onboardResult.mnemonic) {
858
858
  process.stderr.write(PHRASE_PRINT_DEPRECATION_WARNING);
859
859
  }
860
- process.stdout.write(JSON.stringify(result) + '\n');
860
+ process.stdout.write(JSON.stringify(onboardResult) + '\n');
861
861
  } else {
862
- if (result.ok) {
863
- if (result.mnemonic) {
862
+ if (onboardResult.ok) {
863
+ if (onboardResult.mnemonic) {
864
864
  process.stderr.write(PHRASE_PRINT_DEPRECATION_WARNING);
865
865
  process.stderr.write(
866
866
  'WARNING: --emit-phrase was set. The plaintext recovery phrase was returned.\n' +
@@ -869,15 +869,15 @@ export function registerOnboardingCli(
869
869
  );
870
870
  }
871
871
  process.stdout.write(
872
- `onboarding: ok action=${result.action} ` +
873
- (result.scope_address ? `scope_address=${result.scope_address} ` : '') +
874
- `credentials=${result.credentials_path}\n`,
872
+ `onboarding: ok action=${onboardResult.action} ` +
873
+ (onboardResult.scope_address ? `scope_address=${onboardResult.scope_address} ` : '') +
874
+ `credentials=${onboardResult.credentials_path}\n`,
875
875
  );
876
876
  } else {
877
- process.stderr.write(`onboarding: ${result.error}: ${result.error_detail ?? ''}\n`);
877
+ process.stderr.write(`onboarding: ${onboardResult.error}: ${onboardResult.error_detail ?? ''}\n`);
878
878
  }
879
879
  }
880
- process.exit(result.ok ? 0 : 1);
880
+ process.exit(onboardResult.ok ? 0 : 1);
881
881
  }
882
882
 
883
883
  if (cliOpts.json) {
@@ -890,18 +890,18 @@ export function registerOnboardingCli(
890
890
  // Interactive path — original 3.2.0 behaviour preserved in full.
891
891
  const io = buildDefaultIo();
892
892
  try {
893
- const result = await runOnboardingWizard({
893
+ const wizardResult = await runOnboardingWizard({
894
894
  credentialsPath: opts.credentialsPath,
895
895
  statePath: opts.statePath,
896
896
  io,
897
897
  });
898
- if (result.error) {
899
- opts.logger.warn(`onboarding wizard exited with error: ${result.error}`);
898
+ if (wizardResult.error) {
899
+ opts.logger.warn(`onboarding wizard exited with error: ${wizardResult.error}`);
900
900
  io.close();
901
901
  process.exit(1);
902
902
  }
903
- if (result.choice === 'generate' || result.choice === 'import') {
904
- opts.logger.info(`onboarding: state=active createdBy=${result.state?.createdBy}`);
903
+ if (wizardResult.choice === 'generate' || wizardResult.choice === 'import') {
904
+ opts.logger.info(`onboarding: state=active createdBy=${wizardResult.state?.createdBy}`);
905
905
  }
906
906
  } catch (err) {
907
907
  const msg = err instanceof Error ? err.message : String(err);
@@ -51,15 +51,15 @@ import {
51
51
  loadCredentialsJson,
52
52
  writeCredentialsJson,
53
53
  writeOnboardingState,
54
- } from './fs-helpers.js';
54
+ } from '../fs-helpers.js';
55
55
  import {
56
56
  awaitPhraseUpload,
57
57
  openRemotePairSession,
58
58
  } from './pair-remote-client.js';
59
- import { setRecoveryPhraseOverride } from './config.js';
59
+ import { setRecoveryPhraseOverride } from '../config.js';
60
60
  import { encodePng, encodeUnicode } from './pair-qr.js';
61
- import { deriveKeys, computeAuthKeyHash } from './crypto.js';
62
- import { createApiClient } from './api-client.js';
61
+ import { deriveKeys, computeAuthKeyHash } from '../crypto/crypto.js';
62
+ import { createApiClient } from '../billing/api-client.js';
63
63
  import type {
64
64
  PairCliIo,
65
65
  PairCliJsonPayload,
@@ -270,7 +270,7 @@ export async function runRelayPairCli(
270
270
  };
271
271
 
272
272
  try {
273
- const result = await awaitPhraseUpload(session, {
273
+ const completion = await awaitPhraseUpload(session, {
274
274
  phraseValidator: (p: string) => validateMnemonic(p, wordlist),
275
275
  completePairing: async ({ mnemonic }) => {
276
276
  try {
@@ -296,8 +296,8 @@ export async function runRelayPairCli(
296
296
  // serves both protocols on the same host.
297
297
  const httpsBase = opts.relayBaseUrl.replace(/^ws/, 'http').replace(/\/+$/, '');
298
298
  const apiClient = createApiClient(httpsBase);
299
- const result = await apiClient.register(authKeyHash, saltHex);
300
- registeredUserId = result.user_id;
299
+ const registerResult = await apiClient.register(authKeyHash, saltHex);
300
+ registeredUserId = registerResult.user_id;
301
301
  opts.logger.info(
302
302
  `pair-cli (relay): registered user_id=${registeredUserId} (salt + auth-key persisted)`,
303
303
  );
@@ -361,12 +361,12 @@ export async function runRelayPairCli(
361
361
  emitStatus('\nCanceled. Pairing session invalidated.\n');
362
362
  return { status: 'canceled', sid: session.token };
363
363
  }
364
- if (result.state === 'active') {
364
+ if (completion.state === 'active') {
365
365
  emitStatus('\nPairing complete. Account is active.\n');
366
366
  return { status: 'completed', sid: session.token };
367
367
  }
368
- emitStatus(`\nPairing failed: ${result.error ?? 'unknown_error'}\n`);
369
- return { status: 'error', sid: session.token, error: result.error ?? 'unknown_error' };
368
+ emitStatus(`\nPairing failed: ${completion.error ?? 'unknown_error'}\n`);
369
+ return { status: 'error', sid: session.token, error: completion.error ?? 'unknown_error' };
370
370
  } catch (err) {
371
371
  if (canceled) {
372
372
  emitStatus('\nCanceled. Pairing session invalidated.\n');
@@ -39,4 +39,4 @@ export {
39
39
  aeadEncryptWithSessionKey,
40
40
  encryptPairingPayload,
41
41
  compareSecondaryCodesCT,
42
- } from './vault-crypto.js';
42
+ } from '../crypto/vault-crypto.js';
@@ -542,7 +542,7 @@ export function buildPairRoutes(cfg: PairHttpConfig): PairRouteBundle {
542
542
  // sid/mode for log correlation.
543
543
  void (async (): Promise<void> => {
544
544
  try {
545
- const result = await awaitPhraseUpload(session, {
545
+ const completion = await awaitPhraseUpload(session, {
546
546
  phraseValidator: validate,
547
547
  timeoutMs: cfg.initAwaitTimeoutMs,
548
548
  completePairing: async ({ mnemonic }) => {
@@ -557,13 +557,13 @@ export function buildPairRoutes(cfg: PairHttpConfig): PairRouteBundle {
557
557
  return cfg.completePairing({ mnemonic, session: sessionLike });
558
558
  },
559
559
  });
560
- if (result.state === 'active') {
560
+ if (completion.state === 'active') {
561
561
  cfg.logger.info(
562
562
  `pair-http /init: session ${redactSid(session.token)} completed in-process; onboarding active`,
563
563
  );
564
564
  } else {
565
565
  cfg.logger.warn(
566
- `pair-http /init: session ${redactSid(session.token)} completion non-active: ${result.error ?? 'unknown'}`,
566
+ `pair-http /init: session ${redactSid(session.token)} completion non-active: ${completion.error ?? 'unknown'}`,
567
567
  );
568
568
  }
569
569
  } catch (err) {
@@ -259,9 +259,9 @@ export async function openRemotePairSession(
259
259
  try {
260
260
  const text = typeof raw === 'string' ? raw : Buffer.from(raw as ArrayBuffer).toString('utf-8');
261
261
  msg = JSON.parse(text);
262
- } catch {
262
+ } catch (err) {
263
263
  safeClose(ws);
264
- throw new Error('pair-remote-client: opened frame not valid JSON');
264
+ throw new Error('pair-remote-client: opened frame not valid JSON', { cause: err });
265
265
  }
266
266
 
267
267
  if (msg.type === 'error') {
@@ -331,10 +331,10 @@ export async function awaitPhraseUpload(
331
331
  try {
332
332
  const text = typeof raw === 'string' ? raw : Buffer.from(raw as ArrayBuffer).toString('utf-8');
333
333
  msg = JSON.parse(text);
334
- } catch {
334
+ } catch (err) {
335
335
  safeSend(ws, { type: 'nack', error: 'bad_json' });
336
336
  safeClose(ws);
337
- throw new Error('pair-remote-client: forward frame not valid JSON');
337
+ throw new Error('pair-remote-client: forward frame not valid JSON', { cause: err });
338
338
  }
339
339
 
340
340
  if (msg.type !== 'forward') {
@@ -395,9 +395,9 @@ export async function awaitPhraseUpload(
395
395
 
396
396
  // Hand off to the caller-supplied completion handler. Wrapped in try/finally
397
397
  // so we always drop our own reference to the mnemonic.
398
- let result: RelayCompletionResult;
398
+ let completion: RelayCompletionResult;
399
399
  try {
400
- result = await opts.completePairing({ mnemonic, session });
400
+ completion = await opts.completePairing({ mnemonic, session });
401
401
  } catch (err) {
402
402
  safeSend(ws, { type: 'nack', error: 'completion_failed' });
403
403
  safeClose(ws);
@@ -408,15 +408,15 @@ export async function awaitPhraseUpload(
408
408
  mnemonic = '';
409
409
  }
410
410
 
411
- if (result.state !== 'active') {
412
- safeSend(ws, { type: 'nack', error: result.error ?? 'completion_failed' });
411
+ if (completion.state !== 'active') {
412
+ safeSend(ws, { type: 'nack', error: completion.error ?? 'completion_failed' });
413
413
  safeClose(ws);
414
- return result;
414
+ return completion;
415
415
  }
416
416
 
417
417
  safeSend(ws, { type: 'ack' });
418
418
  safeClose(ws);
419
- return result;
419
+ return completion;
420
420
  }
421
421
 
422
422
  /**
@@ -592,16 +592,16 @@ export async function updatePairSession(
592
592
  }
593
593
  const current = pruned.file.sessions[idx];
594
594
  const next = mutate(current);
595
- let result: PairSession | null;
595
+ let mutatedSession: PairSession | null;
596
596
  if (next === null) {
597
597
  pruned.file.sessions.splice(idx, 1);
598
- result = null;
598
+ mutatedSession = null;
599
599
  } else {
600
600
  pruned.file.sessions[idx] = next;
601
- result = next;
601
+ mutatedSession = next;
602
602
  }
603
603
  writePairSessionsFileSync(sessionsPath, pruned.file);
604
- return result;
604
+ return mutatedSession;
605
605
  } finally {
606
606
  release();
607
607
  }
@@ -0,0 +1,64 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Plugin configSchema — static JSON-schema for the OpenClaw plugin config.
3
+ // Extracted from index.ts; pure data, no runtime state.
4
+ // ---------------------------------------------------------------------------
5
+
6
+ export const CONFIG_SCHEMA = {
7
+ type: 'object',
8
+ additionalProperties: false,
9
+ properties: {
10
+ publicUrl: {
11
+ type: 'string',
12
+ description:
13
+ "Public gateway URL for QR pairing (e.g. 'https://gateway.example.com:18789'). Overrides the auto-resolution cascade in buildPairingUrl.",
14
+ },
15
+ extraction: {
16
+ type: 'object',
17
+ additionalProperties: false,
18
+ properties: {
19
+ enabled: {
20
+ type: 'boolean',
21
+ description: 'Enable/disable auto-extraction (default: true)',
22
+ },
23
+ model: {
24
+ type: 'string',
25
+ description:
26
+ "Shorthand: override just the extraction model (e.g., 'glm-4.5-flash', 'gpt-4.1-mini'). For a full provider override use extraction.llm.",
27
+ },
28
+ interval: {
29
+ type: 'number',
30
+ description: 'Number of turns between automatic extractions (default: 3)',
31
+ },
32
+ maxFactsPerExtraction: {
33
+ type: 'number',
34
+ description: 'Hard cap on facts extracted per turn (default: 15)',
35
+ },
36
+ llm: {
37
+ type: 'object',
38
+ additionalProperties: false,
39
+ description:
40
+ 'Explicit LLM override block. Highest-priority tier in the extraction-provider cascade. Any subset of provider+apiKey is enough to pin a provider.',
41
+ properties: {
42
+ provider: {
43
+ type: 'string',
44
+ description:
45
+ "Provider name: zai | openai | anthropic | gemini | google | mistral | groq | deepseek | openrouter | xai | together | cerebras.",
46
+ },
47
+ model: {
48
+ type: 'string',
49
+ description: 'Explicit model id. If omitted, deriveCheapModel(provider) picks a sensible default.',
50
+ },
51
+ apiKey: {
52
+ type: 'string',
53
+ description: 'API key for the selected provider. Required for the override to take effect.',
54
+ },
55
+ baseUrl: {
56
+ type: 'string',
57
+ description: 'Override the provider base URL (self-hosted / custom gateway setups).',
58
+ },
59
+ },
60
+ },
61
+ },
62
+ },
63
+ },
64
+ } as const;