wyrm-mcp 7.2.0 → 7.2.2

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 (156) hide show
  1. package/LICENSE +26 -667
  2. package/NOTICE +14 -33
  3. package/dist/activation.d.ts.map +1 -1
  4. package/dist/activation.js +1 -44
  5. package/dist/activation.js.map +1 -1
  6. package/dist/agent-daemon.js +4 -281
  7. package/dist/agent-loop.js +7 -332
  8. package/dist/analytics.js +13 -236
  9. package/dist/attribution.js +1 -49
  10. package/dist/audit.js +2 -457
  11. package/dist/auto-capture.js +3 -138
  12. package/dist/auto-orchestrator.js +1 -325
  13. package/dist/autoconfig.js +39 -840
  14. package/dist/buddy-runner.js +1 -109
  15. package/dist/buddy.js +14 -564
  16. package/dist/build-flags.js +1 -17
  17. package/dist/capabilities.js +3 -183
  18. package/dist/capture.js +1 -56
  19. package/dist/causality.js +6 -107
  20. package/dist/cli.js +20 -281
  21. package/dist/cloud/cli.js +5 -541
  22. package/dist/cloud/client.js +1 -221
  23. package/dist/cloud/crypto.js +1 -85
  24. package/dist/cloud/machine-id.js +2 -113
  25. package/dist/cloud/recovery.js +1 -60
  26. package/dist/cloud/sync-engine.js +7 -543
  27. package/dist/cloud-backup.js +5 -579
  28. package/dist/cloud-profile.js +1 -138
  29. package/dist/cloud-sync-entrypoint.js +1 -47
  30. package/dist/cloud-sync.js +2 -309
  31. package/dist/constellation.js +12 -168
  32. package/dist/context-build-budgeted.js +4 -144
  33. package/dist/context-ranking.js +1 -69
  34. package/dist/crypto.js +1 -179
  35. package/dist/daemon-write-endpoint.js +1 -290
  36. package/dist/daemon-writer.js +2 -406
  37. package/dist/database.js +43 -1110
  38. package/dist/deprecations.js +2 -162
  39. package/dist/design.js +13 -141
  40. package/dist/event-replication.js +1 -112
  41. package/dist/events-sse.js +7 -43
  42. package/dist/events.js +6 -238
  43. package/dist/failure-patterns.js +42 -659
  44. package/dist/federation.js +12 -236
  45. package/dist/goals.js +13 -101
  46. package/dist/golden.js +3 -355
  47. package/dist/handlers/agent.js +4 -165
  48. package/dist/handlers/alias-adapters.js +1 -129
  49. package/dist/handlers/aliases.js +1 -171
  50. package/dist/handlers/audit.js +1 -87
  51. package/dist/handlers/boundary.js +1 -221
  52. package/dist/handlers/capture.js +73 -1109
  53. package/dist/handlers/causality.js +7 -114
  54. package/dist/handlers/cloud.js +85 -382
  55. package/dist/handlers/companion.js +28 -459
  56. package/dist/handlers/datalake.js +7 -187
  57. package/dist/handlers/dispatch-context.js +0 -22
  58. package/dist/handlers/entity.js +25 -256
  59. package/dist/handlers/events.js +16 -335
  60. package/dist/handlers/failure.js +13 -340
  61. package/dist/handlers/goals.js +4 -296
  62. package/dist/handlers/intelligence.js +126 -674
  63. package/dist/handlers/invoicing.js +1 -70
  64. package/dist/handlers/mcpclient.js +6 -137
  65. package/dist/handlers/orchestration.js +40 -125
  66. package/dist/handlers/output-schemas.js +1 -24
  67. package/dist/handlers/presence.js +3 -99
  68. package/dist/handlers/project.js +28 -182
  69. package/dist/handlers/prompts.js +6 -157
  70. package/dist/handlers/quest.js +4 -224
  71. package/dist/handlers/recall.js +11 -218
  72. package/dist/handlers/registry.js +1 -167
  73. package/dist/handlers/resources.js +1 -288
  74. package/dist/handlers/review.js +11 -74
  75. package/dist/handlers/run.js +17 -487
  76. package/dist/handlers/search.js +15 -326
  77. package/dist/handlers/session.js +28 -615
  78. package/dist/handlers/share.js +8 -184
  79. package/dist/handlers/shims.js +1 -464
  80. package/dist/handlers/skill.js +67 -449
  81. package/dist/handlers/survivors.js +1 -120
  82. package/dist/handlers/symbols.js +8 -109
  83. package/dist/handlers/syncops.js +4 -302
  84. package/dist/handlers/types.js +1 -27
  85. package/dist/harvest.js +5 -191
  86. package/dist/hours.js +7 -156
  87. package/dist/http-auth.js +3 -321
  88. package/dist/http-fast.js +21 -1137
  89. package/dist/icons.js +1 -47
  90. package/dist/index.js +2 -924
  91. package/dist/indexer.js +4 -145
  92. package/dist/intelligence.js +31 -261
  93. package/dist/internal-dispatch.js +3 -212
  94. package/dist/keyset.js +1 -110
  95. package/dist/knowledge-graph.js +12 -176
  96. package/dist/license.d.ts +11 -0
  97. package/dist/license.d.ts.map +1 -1
  98. package/dist/license.js +2 -414
  99. package/dist/license.js.map +1 -1
  100. package/dist/logger.js +2 -199
  101. package/dist/maintenance.js +2 -148
  102. package/dist/mcp-client.js +6 -262
  103. package/dist/memory-artifacts.js +30 -449
  104. package/dist/migrate-prompt.js +2 -124
  105. package/dist/migrations.js +40 -655
  106. package/dist/performance.js +1 -228
  107. package/dist/presence.js +11 -140
  108. package/dist/priority-embed.js +5 -164
  109. package/dist/providers/embedding-provider.js +1 -196
  110. package/dist/readonly-gate.js +1 -29
  111. package/dist/rehydration.js +9 -157
  112. package/dist/reindex.js +1 -88
  113. package/dist/render-target.js +21 -514
  114. package/dist/render.js +4 -280
  115. package/dist/repl-guard.js +1 -173
  116. package/dist/replication-daemon-entrypoint.js +1 -31
  117. package/dist/replication-daemon.js +2 -262
  118. package/dist/resilience.js +1 -591
  119. package/dist/reverse-bridge.js +5 -360
  120. package/dist/security.js +1 -244
  121. package/dist/session-seen.js +3 -51
  122. package/dist/setup.js +1 -260
  123. package/dist/skill-author.js +5 -168
  124. package/dist/spec-kit.js +1 -191
  125. package/dist/sqlite-busy.js +1 -154
  126. package/dist/statusline.js +11 -315
  127. package/dist/sub-agent.js +13 -262
  128. package/dist/summarizer.js +13 -139
  129. package/dist/symbols.js +7 -283
  130. package/dist/sync.js +5 -359
  131. package/dist/tasks-dispatch.js +1 -84
  132. package/dist/tasks.js +1 -282
  133. package/dist/token-budget.js +1 -143
  134. package/dist/tool-analytics.js +7 -129
  135. package/dist/tool-annotations.js +1 -365
  136. package/dist/tool-manifest-v2.json +1 -1
  137. package/dist/tool-manifest.json +1 -1
  138. package/dist/tool-profiles.js +1 -75
  139. package/dist/trace-harvest.js +6 -244
  140. package/dist/types.js +1 -30
  141. package/dist/ui-dashboard.js +41 -50
  142. package/dist/ulid.js +1 -81
  143. package/dist/validate.js +1 -129
  144. package/dist/vault.js +1 -534
  145. package/dist/vectors.js +3 -184
  146. package/dist/version-check.js +4 -136
  147. package/dist/visibility.js +19 -155
  148. package/dist/wyrm-cli.js +98 -2451
  149. package/dist/wyrm-cli.js.map +1 -1
  150. package/dist/wyrm-guard.js +14 -424
  151. package/dist/wyrm-loop.js +3 -150
  152. package/dist/wyrm-manifest.json +1 -1
  153. package/dist/wyrm-statusline-daemon.js +1 -11
  154. package/dist/wyrm-statusline.js +4 -56
  155. package/dist/wyrm-ui.js +9 -77
  156. package/package.json +4 -2
package/dist/cloud/cli.js CHANGED
@@ -1,135 +1,4 @@
1
- /**
2
- * `wyrm cloud …` subcommand dispatcher.
3
- *
4
- * wyrm cloud login authenticate via Google/GitHub OAuth
5
- * wyrm cloud logout revoke this session
6
- * wyrm cloud status account + tier + storage + devices
7
- * wyrm cloud devices list registered devices
8
- * wyrm cloud devices revoke <id> revoke a device
9
- * wyrm cloud sync [--dry-run] push local changes + pull peer deltas
10
- * wyrm cloud export <path> download full account dump (encrypted blobs included)
11
- * wyrm cloud delete --confirm permanently delete the account on Wyrm Cloud
12
- *
13
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
14
- */
15
- import { randomBytes } from 'node:crypto';
16
- import { writeFileSync } from 'node:fs';
17
- import { hostname } from 'node:os';
18
- import { resolve } from 'node:path';
19
- import { createInterface } from 'node:readline';
20
- import { CloudClient, CloudError, DEFAULT_BASE, SESSION_FILE, loadSession, saveSession, clearSession } from './client.js';
21
- import { getMasterKey, keyExists, keyFilePath } from './crypto.js';
22
- import { computeMachineFp, classifySession } from './machine-id.js';
23
- import { runSync, CopiedSessionError } from './sync-engine.js';
24
- import { initializeLicense, hasFeature, getTier } from '../license.js';
25
- /**
26
- * Gate the data-movement cloud commands behind a Pro+ license.
27
- *
28
- * Cloud sync is a paid feature (the `cloud_backup` flag, granted at pro
29
- * tier and above). The MCP tools already enforce this; without the same
30
- * check here, the CLI was a free bypass of the paid tier. Setup/visibility
31
- * commands (login, logout, status, devices, recovery, delete) stay free so
32
- * users can authenticate, see the upsell, and always retain the right to
33
- * delete their own data.
34
- */
35
- function requireCloudLicense() {
36
- initializeLicense(); // loads ~/.wyrm/license.json (no-op → free tier)
37
- if (!hasFeature('cloud_backup')) {
38
- console.error('󱅝 Wyrm Cloud sync requires a Pro license or higher.');
39
- console.error(` Current tier: ${getTier()}`);
40
- console.error(' Upgrade: https://ghosts.lk/wyrm');
41
- console.error(' Already paid? Activate your license (wyrm_license tool, or place');
42
- console.error(' the signed JSON at ~/.wyrm/license.json) and retry.');
43
- process.exit(1);
44
- }
45
- }
46
- function fmtBytes(n) {
47
- if (n < 1024)
48
- return `${n} B`;
49
- if (n < 1024 * 1024)
50
- return `${(n / 1024).toFixed(1)} KiB`;
51
- if (n < 1024 * 1024 * 1024)
52
- return `${(n / (1024 * 1024)).toFixed(1)} MiB`;
53
- return `${(n / (1024 * 1024 * 1024)).toFixed(2)} GiB`;
54
- }
55
- function fmtTime(unixMs) {
56
- if (!unixMs)
57
- return '—';
58
- return new Date(unixMs).toISOString().replace('T', ' ').slice(0, 16) + ' UTC';
59
- }
60
- export async function cmdCloud(args) {
61
- const sub = args[0];
62
- const rest = args.slice(1);
63
- try {
64
- switch (sub) {
65
- case undefined:
66
- case 'help':
67
- case '--help':
68
- case '-h':
69
- return cmdHelp();
70
- case 'login': return await cmdLogin(rest);
71
- case 'logout': return await cmdLogout();
72
- case 'status': return await cmdStatus();
73
- case 'doctor': return cmdDoctor();
74
- case 'devices': return await cmdDevices(rest);
75
- case 'sync': return await cmdSync(rest);
76
- case 'export': return await cmdExport(rest);
77
- case 'delete': return await cmdDelete(rest);
78
- case 'recovery': return await cmdRecovery(rest);
79
- default:
80
- console.error(`Unknown subcommand: wyrm cloud ${sub}`);
81
- console.error(`Try \`wyrm cloud help\``);
82
- process.exit(1);
83
- }
84
- }
85
- catch (err) {
86
- // Translate common errors to friendly CLI output. Falls through
87
- // to a generic message for unexpected exceptions.
88
- if (err instanceof CopiedSessionError) {
89
- // The detailed loud guidance was already written to stderr by the
90
- // sync engine. Just exit non-zero without burying it under a
91
- // generic message.
92
- process.exit(1);
93
- }
94
- if (err instanceof CloudError) {
95
- if (err.status === 401) {
96
- console.error('✘ Not authenticated. Run `wyrm cloud login`.');
97
- }
98
- else if (err.status === 403) {
99
- console.error(`✘ Forbidden: ${err.message}`);
100
- }
101
- else if (err.status === 0) {
102
- // Network / timeout error from fetch.
103
- console.error(`✘ ${err.message}`);
104
- console.error(' Check your internet connection. If wyrm.ghosts.lk is unreachable, see status at https://www.cloudflarestatus.com');
105
- }
106
- else if (err.status === 501) {
107
- console.error(`✘ Service not configured: ${err.message}`);
108
- }
109
- else {
110
- console.error(`✘ ${err.message} (HTTP ${err.status})`);
111
- }
112
- }
113
- else if (err instanceof Error) {
114
- const msg = err.message;
115
- if (msg.includes('Not logged in')) {
116
- console.error('✘ Not logged in. Run `wyrm cloud login`.');
117
- }
118
- else if (msg.includes('Wyrm Cloud key')) {
119
- console.error(`✘ Encryption-key error: ${msg}`);
120
- }
121
- else {
122
- console.error(`✘ ${msg}`);
123
- }
124
- }
125
- else {
126
- console.error('✘ Unexpected error:', err);
127
- }
128
- process.exit(1);
129
- }
130
- }
131
- function cmdHelp() {
132
- console.log(`󱅝 wyrm cloud — sync your Wyrm memory across devices
1
+ import{randomBytes as D}from"node:crypto";import{writeFileSync as I}from"node:fs";import{hostname as N}from"node:os";import{resolve as P}from"node:path";import{createInterface as A}from"node:readline";import{CloudClient as a,CloudError as k,DEFAULT_BASE as p,SESSION_FILE as f,loadSession as d,saveSession as S,clearSession as b}from"./client.js";import{getMasterKey as R,keyExists as h,keyFilePath as g}from"./crypto.js";import{computeMachineFp as E,classifySession as O}from"./machine-id.js";import{runSync as L,CopiedSessionError as W}from"./sync-engine.js";import{initializeLicense as F,hasFeature as M,getTier as B}from"../license.js";function T(){F(),M("cloud_backup")||(console.error("\u{F115D} Wyrm Cloud sync requires a Pro license or higher."),console.error(` Current tier: ${B()}`),console.error(" Upgrade: https://ghosts.lk/wyrm"),console.error(" Already paid? Activate your license (wyrm_license tool, or place"),console.error(" the signed JSON at ~/.wyrm/license.json) and retry."),process.exit(1))}function w(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KiB`:e<1024*1024*1024?`${(e/(1024*1024)).toFixed(1)} MiB`:`${(e/(1024*1024*1024)).toFixed(2)} GiB`}function G(e){return e?new Date(e).toISOString().replace("T"," ").slice(0,16)+" UTC":"\u2014"}async function ie(e){const n=e[0],s=e.slice(1);try{switch(n){case void 0:case"help":case"--help":case"-h":return U();case"login":return await j(s);case"logout":return await K();case"status":return await H();case"doctor":return Y();case"devices":return await q(s);case"sync":return await z(s);case"export":return await J(s);case"delete":return await Q(s);case"recovery":return await V(s);default:console.error(`Unknown subcommand: wyrm cloud ${n}`),console.error("Try `wyrm cloud help`"),process.exit(1)}}catch(o){if(o instanceof W&&process.exit(1),o instanceof k)o.status===401?console.error("\u2718 Not authenticated. Run `wyrm cloud login`."):o.status===403?console.error(`\u2718 Forbidden: ${o.message}`):o.status===0?(console.error(`\u2718 ${o.message}`),console.error(" Check your internet connection. If wyrm.ghosts.lk is unreachable, see status at https://www.cloudflarestatus.com")):o.status===501?console.error(`\u2718 Service not configured: ${o.message}`):console.error(`\u2718 ${o.message} (HTTP ${o.status})`);else if(o instanceof Error){const r=o.message;r.includes("Not logged in")?console.error("\u2718 Not logged in. Run `wyrm cloud login`."):r.includes("Wyrm Cloud key")?console.error(`\u2718 Encryption-key error: ${r}`):console.error(`\u2718 ${r}`)}else console.error("\u2718 Unexpected error:",o);process.exit(1)}}function U(){console.log(`\u{F115D} wyrm cloud \u2014 sync your Wyrm memory across devices
133
2
 
134
3
  USAGE:
135
4
  wyrm cloud <subcommand> [args]
@@ -155,7 +24,7 @@ SUBCOMMANDS:
155
24
 
156
25
  CONFIG:
157
26
  Session file: ~/.wyrm/cloud.json (0600)
158
- Master key: ~/.wyrm/cloud.key (0600) never sent to server
27
+ Master key: ~/.wyrm/cloud.key (0600) \u2190 never sent to server
159
28
 
160
29
  CONSTITUTION:
161
30
  All sync payloads AES-256-GCM encrypted client-side before upload.
@@ -163,411 +32,6 @@ CONSTITUTION:
163
32
 
164
33
  DOMAIN:
165
34
  https://wyrm.ghosts.lk
166
- `);
167
- }
168
- // ── login ───────────────────────────────────────────────────────────────────
169
- async function cmdLogin(_args) {
170
- if (loadSession()) {
171
- console.error('Already logged in. Run `wyrm cloud logout` first to switch accounts.');
172
- process.exit(1);
173
- }
174
- const client = new CloudClient(DEFAULT_BASE);
175
- const pollToken = randomBytes(32).toString('hex'); // 64 chars
176
- let init;
177
- try {
178
- init = await client.cliInit(pollToken);
179
- }
180
- catch (err) {
181
- console.error(`✘ Could not start login: ${err instanceof Error ? err.message : err}`);
182
- process.exit(1);
183
- }
184
- console.log('');
185
- console.log('󱅝 Sign in to Wyrm Cloud');
186
- console.log('');
187
- console.log(` 1. Open this URL on any device:`);
188
- console.log(` ${init.browser_url}`);
189
- console.log('');
190
- console.log(` 2. Type this code when prompted:`);
191
- console.log(` ${init.code}`);
192
- console.log('');
193
- console.log(` 3. Sign in with Google or GitHub.`);
194
- console.log('');
195
- console.log(` Waiting (expires in ${Math.round(init.expires_in_sec / 60)} min)…`);
196
- // Poll up to expires_in_sec, every 3s.
197
- const start = Date.now();
198
- const deadline = start + init.expires_in_sec * 1000;
199
- let session;
200
- let accountId;
201
- while (Date.now() < deadline) {
202
- await new Promise((r) => { setTimeout(r, 3000); });
203
- try {
204
- const poll = await client.cliPoll(pollToken);
205
- if (poll.ready && poll.session) {
206
- session = poll.session;
207
- accountId = poll.account_id;
208
- break;
209
- }
210
- }
211
- catch (err) {
212
- // The pending state is a 200 with ready:false (handled above). A
213
- // 404 here means the poll_token isn't in the DB — link expired or
214
- // server lost state. Fail loud; don't spin until deadline.
215
- console.error(`\n✘ Login failed: ${err instanceof Error ? err.message : err}`);
216
- process.exit(1);
217
- }
218
- }
219
- if (!session || !accountId) {
220
- console.error('\n✘ Login timed out. Run `wyrm cloud login` again.');
221
- process.exit(1);
222
- }
223
- // Save session, fetch account info for the welcome message.
224
- saveSession({
225
- base: DEFAULT_BASE,
226
- session,
227
- account_id: accountId,
228
- created_at: Date.now(),
229
- });
230
- const authed = new CloudClient(DEFAULT_BASE, session);
231
- let email = '';
232
- try {
233
- const me = await authed.me();
234
- email = me.account.email;
235
- }
236
- catch { /* whatever, login still succeeded */ }
237
- // Register this machine as a device (idempotent: every fresh login creates one).
238
- const deviceName = `${hostname()} (${process.platform})`;
239
- let deviceId;
240
- try {
241
- const reg = await authed.registerDevice(deviceName);
242
- deviceId = reg.device.id;
243
- // Update session file with device info + this machine's fingerprint.
244
- // The fp binds the session to THIS machine so a later copy of
245
- // cloud.json to a second box is detected at sync (failure #40).
246
- const s = loadSession();
247
- if (s)
248
- saveSession({ ...s, email, device_id: deviceId, device_name: deviceName, machine_fp: computeMachineFp() });
249
- }
250
- catch (err) {
251
- console.error(`\n⚠ Logged in but could not register device: ${err instanceof Error ? err.message : err}`);
252
- console.error(' Re-run `wyrm cloud login` to retry.');
253
- return;
254
- }
255
- console.log('');
256
- console.log(`✓ Signed in as ${email}`);
257
- console.log(`✓ Device registered: ${deviceName}`);
258
- console.log(`✓ Session saved to ${SESSION_FILE}`);
259
- // Eagerly materialise the master key so `wyrm cloud recovery show`
260
- // works immediately after login. Without this, the key only gets
261
- // created lazily on the first encrypt(), and the post-login UX of
262
- // "save your recovery phrase right now" doesn't actually work.
263
- const keyWasFresh = !keyExists();
264
- getMasterKey(); // creates the file if missing
265
- if (keyWasFresh) {
266
- console.log(`✓ Encryption key generated at ${keyFilePath()} (0600)`);
267
- console.log('');
268
- console.log(' ⚠ Back up the key file. Losing it means losing access to your synced data.');
269
- console.log(' The Wyrm Cloud server CANNOT recover it — that\'s the operator-owns-data');
270
- console.log(' guarantee. Run `wyrm cloud recovery show` now to get a 24-word phrase,');
271
- console.log(' or copy the key file to a password manager / encrypted backup.');
272
- }
273
- else {
274
- console.log(`✓ Encryption key reused from ${keyFilePath()}`);
275
- }
276
- console.log('');
277
- console.log(`Next: \`wyrm cloud sync\` to push your memory.`);
278
- }
279
- // ── logout ──────────────────────────────────────────────────────────────────
280
- async function cmdLogout() {
281
- const s = loadSession();
282
- if (!s) {
283
- console.log('Not logged in.');
284
- return;
285
- }
286
- try {
287
- const client = new CloudClient(s.base, s.session);
288
- await client.logout();
289
- console.log('✓ Session revoked server-side.');
290
- }
291
- catch (err) {
292
- console.error(`⚠ Could not revoke server-side: ${err instanceof Error ? err.message : err}`);
293
- console.error(' Local session cleared anyway.');
294
- }
295
- clearSession();
296
- console.log(`✓ Local session removed (${SESSION_FILE}).`);
297
- console.log(' Note: ~/.wyrm/cloud.key kept. Delete manually if you want a clean slate.');
298
- }
299
- // ── status ──────────────────────────────────────────────────────────────────
300
- async function cmdStatus() {
301
- const s = loadSession();
302
- if (!s) {
303
- console.log('Not logged in. Run `wyrm cloud login`.');
304
- return;
305
- }
306
- const client = new CloudClient(s.base, s.session);
307
- try {
308
- const me = await client.me();
309
- const st = await client.syncStatus();
310
- console.log('');
311
- console.log(`󱅝 Wyrm Cloud — ${me.account.email}`);
312
- console.log('');
313
- console.log(` Tier: ${st.tier}`);
314
- console.log(` Storage: ${fmtBytes(st.storage.used_bytes)} / ${fmtBytes(st.storage.limit_bytes)} (${st.storage.percent}%)`);
315
- console.log(` Deltas: ${st.deltas}`);
316
- console.log(` Devices: ${st.devices}`);
317
- console.log(` Providers:${me.identities.map((i) => ' ' + i.provider).join(',')}`);
318
- console.log('');
319
- console.log(` This machine: ${s.device_name ?? '(not registered)'}`);
320
- console.log(` Device ID: ${s.device_id ? s.device_id.slice(0, 8) + '…' : '(none)'}`);
321
- console.log(` Session age: ${Math.round((Date.now() - s.created_at) / (24 * 60 * 60 * 1000))} day(s)`);
322
- printIdentityHealth(s);
323
- console.log('');
324
- // Encryption-key health. The whole operator-owns-data guarantee rests
325
- // on this file existing — if it's gone, no pulled blob can be decrypted.
326
- if (!keyExists()) {
327
- console.log(' ⚠ ENCRYPTION KEY MISSING');
328
- console.log(` Expected at: ${keyFilePath()}`);
329
- console.log(` Without this file, deltas already pushed cannot be decrypted.`);
330
- console.log(` If you have a backup, restore it before running sync.`);
331
- console.log('');
332
- }
333
- else if (st.deltas > 0) {
334
- console.log(` Key: ${keyFilePath()} (0600)`);
335
- console.log(` ⚠ Back this file up. Losing it = losing access to your ${st.deltas} deltas.`);
336
- console.log('');
337
- }
338
- }
339
- catch (err) {
340
- if (err instanceof CloudError && err.status === 401) {
341
- console.error('Session expired or invalid. Run `wyrm cloud login` again.');
342
- process.exit(1);
343
- }
344
- throw err;
345
- }
346
- }
347
- /**
348
- * Print the device-identity health line(s) for a session: whether the
349
- * stored machine fingerprint matches THIS machine. A mismatch is the
350
- * copied-session / device_id-collision symptom (failure #40). Shared by
351
- * `status` and `doctor`.
352
- */
353
- function printIdentityHealth(s) {
354
- const verdict = classifySession(s.machine_fp);
355
- if (verdict.state === 'match') {
356
- console.log(' Identity: ✓ machine fingerprint matches (not a copied session)');
357
- }
358
- else if (verdict.state === 'adopt') {
359
- console.log(' Identity: — no fingerprint yet (pre-7.0.3 session; adopted on next sync)');
360
- }
361
- else {
362
- console.log(' Identity: ⚠ FINGERPRINT MISMATCH — this session looks COPIED');
363
- console.log(` stored ${verdict.stored.slice(0, 12)}… vs this machine ${verdict.current.slice(0, 12)}…`);
364
- console.log(' device_id collision → cross-device pull returns 0 silently.');
365
- console.log(' Fix: rm ~/.wyrm/cloud.json ~/.wyrm/cloud-cursor.json (KEEP cloud.key),');
366
- console.log(' then `wyrm cloud login`. (Or `wyrm cloud sync --force` if intended.)');
367
- }
368
- }
369
- // ── doctor ────────────────────────────────────────────────────────────────────
370
- function cmdDoctor() {
371
- const s = loadSession();
372
- console.log('');
373
- console.log('󱅝 Wyrm Cloud — device identity doctor');
374
- console.log('');
375
- if (!s) {
376
- console.log(' Not logged in. Run `wyrm cloud login`.');
377
- console.log('');
378
- return;
379
- }
380
- console.log(` Account: ${s.email ?? s.account_id.slice(0, 8) + '…'}`);
381
- console.log(` Device ID: ${s.device_id ? s.device_id.slice(0, 8) + '…' : '(none — re-run `wyrm cloud login`)'}`);
382
- console.log(` Server: ${s.base}`);
383
- console.log(` Stored fp: ${s.machine_fp ? s.machine_fp.slice(0, 12) + '…' : '(none — pre-7.0.3 session)'}`);
384
- console.log(` This box fp: ${computeMachineFp().slice(0, 12)}…`);
385
- printIdentityHealth(s);
386
- console.log('');
387
- console.log(' Only ~/.wyrm/cloud.key is meant to be shared across your machines.');
388
- console.log(' cloud.json + cloud-cursor.json + machine-id are per-device.');
389
- console.log('');
390
- }
391
- // ── devices ─────────────────────────────────────────────────────────────────
392
- async function cmdDevices(args) {
393
- const action = args[0];
394
- const client = CloudClient.fromSession();
395
- if (action === 'revoke') {
396
- const id = args[1];
397
- if (!id) {
398
- console.error('Usage: wyrm cloud devices revoke <device-id>');
399
- process.exit(1);
400
- }
401
- await client.revokeDevice(id);
402
- console.log(`✓ Device ${id.slice(0, 8)}… revoked.`);
403
- return;
404
- }
405
- const { devices } = await client.listDevices();
406
- const session = loadSession();
407
- const thisId = session?.device_id;
408
- console.log('');
409
- console.log(`󱅝 Devices (${devices.length})`);
410
- console.log('');
411
- for (const d of devices) {
412
- const marker = d.id === thisId ? '★' : ' ';
413
- const status = d.revoked ? '(revoked)' : '';
414
- console.log(` ${marker} ${d.id.slice(0, 8)}… ${d.name.padEnd(30)} last_seen=${fmtTime(d.last_seen_at)} ${status}`);
415
- }
416
- console.log('');
417
- console.log(' ★ = this machine');
418
- console.log(' Revoke: wyrm cloud devices revoke <device-id>');
419
- console.log('');
420
- }
421
- // ── sync ────────────────────────────────────────────────────────────────────
422
- async function cmdSync(args) {
423
- requireCloudLicense();
424
- const dryRun = args.includes('--dry-run');
425
- const all = args.includes('--all');
426
- const force = args.includes('--force');
427
- if (!keyExists()) {
428
- console.error('⚠ Encryption key not found at', keyFilePath());
429
- console.error(' A new key will be generated, but it will NOT decrypt deltas');
430
- console.error(' pushed by other devices using a different key. If you have a backup,');
431
- console.error(' restore it to ~/.wyrm/cloud.key (0600) before running sync.');
432
- console.error(' Continuing in 5 seconds — press Ctrl+C to abort.');
433
- await new Promise((r) => { setTimeout(r, 5000); });
434
- }
435
- if (all) {
436
- console.log('🌐 --all mode: syncing every semantic table (ignoring visibility flags).');
437
- console.log(' Skipped: per-device logs, FTS shadows, sync internals.');
438
- console.log('');
439
- }
440
- const result = await runSync({ dryRun, all, force });
441
- console.log('');
442
- console.log('󱅝 Sync complete');
443
- console.log('');
444
- console.log(` Pushed: ${result.pushed} row(s)${dryRun ? ' (dry run)' : ''}`);
445
- console.log(` Pulled: ${result.pulled} row(s)`);
446
- console.log(` Deleted: ${result.deleted_local} row(s) (peer tombstones)`);
447
- if (result.errors.length > 0) {
448
- console.log('');
449
- console.log(' Errors:');
450
- for (const e of result.errors)
451
- console.log(` - ${e}`);
452
- }
453
- console.log('');
454
- }
455
- // ── export ──────────────────────────────────────────────────────────────────
456
- async function cmdExport(args) {
457
- requireCloudLicense();
458
- const client = CloudClient.fromSession();
459
- const targetArg = args.find((a) => !a.startsWith('-'));
460
- const target = resolve(targetArg ?? `wyrm-cloud-export-${new Date().toISOString().slice(0, 10)}.json`);
461
- console.log(' Fetching account export…');
462
- const dump = await client.accountExport();
463
- const body = JSON.stringify(dump, null, 2);
464
- writeFileSync(target, body, { mode: 0o600 });
465
- console.log('');
466
- console.log(`✓ Exported to ${target}`);
467
- console.log('');
468
- console.log(` Account: ${dump.account.email} (${dump.account.id.slice(0, 8)}…)`);
469
- console.log(` Devices: ${dump.devices.length}`);
470
- console.log(` Identities: ${dump.identities.length}`);
471
- console.log(` Orgs: ${dump.orgs.length}`);
472
- console.log(` Deltas: ${dump.deltas.length}`);
473
- console.log(` Size: ${fmtBytes(body.length)}`);
474
- console.log('');
475
- console.log(' Deltas are still AES-256-GCM ciphertext. To decrypt them you');
476
- console.log(` need the key at ${keyFilePath()}. Back that up alongside this file.`);
477
- console.log('');
478
- }
479
- // ── delete ──────────────────────────────────────────────────────────────────
480
- async function cmdDelete(args) {
481
- const confirmed = args.includes('--confirm');
482
- if (!confirmed) {
483
- console.error('✘ This permanently deletes your Wyrm Cloud account.');
484
- console.error(' Re-run with --confirm to proceed:');
485
- console.error(' wyrm cloud delete --confirm');
486
- process.exit(1);
487
- }
488
- const s = loadSession();
489
- if (!s) {
490
- console.error('✘ Not logged in. Nothing to delete.');
491
- process.exit(1);
492
- }
493
- console.log('');
494
- console.log('⚠ You are about to PERMANENTLY DELETE your Wyrm Cloud account.');
495
- console.log('');
496
- console.log(` Account: ${s.email ?? s.account_id}`);
497
- console.log(` Server: ${s.base}`);
498
- console.log('');
499
- console.log(' This wipes all encrypted blobs, devices, identities, and org');
500
- console.log(' memberships you own. The local ~/.wyrm/cloud.key is kept so');
501
- console.log(' any backups remain decryptable. This action CANNOT be undone.');
502
- console.log('');
503
- const phrase = await prompt(' Type "DELETE my account" to confirm: ');
504
- if (phrase.trim() !== 'DELETE my account') {
505
- console.error('✘ Confirmation phrase did not match. Aborting.');
506
- process.exit(1);
507
- }
508
- const client = new CloudClient(s.base, s.session);
509
- await client.accountDelete();
510
- clearSession();
511
- console.log('');
512
- console.log('✓ Account deleted on server.');
513
- console.log(`✓ Local session removed (${SESSION_FILE}).`);
514
- console.log(` Kept: ${keyFilePath()} (in case you need to decrypt an old export).`);
515
- console.log('');
516
- }
517
- function prompt(question) {
518
- return new Promise((resolveP) => {
519
- const rl = createInterface({ input: process.stdin, output: process.stdout });
520
- rl.question(question, (answer) => { rl.close(); resolveP(answer); });
521
- });
522
- }
523
- // ── recovery ────────────────────────────────────────────────────────────────
524
- async function cmdRecovery(args) {
525
- const action = args[0];
526
- if (action === 'show') {
527
- const { keyToMnemonic } = await import('./recovery.js');
528
- const phrase = keyToMnemonic();
529
- const words = phrase.split(' ');
530
- console.log('');
531
- console.log('🔑 Wyrm Cloud recovery phrase (BIP39, 24 words)');
532
- console.log('');
533
- // 4 rows × 6 columns — easier to write down without losing place.
534
- for (let row = 0; row < 4; row++) {
535
- const line = words
536
- .slice(row * 6, row * 6 + 6)
537
- .map((w, i) => `${String(row * 6 + i + 1).padStart(2)}. ${w.padEnd(10)}`)
538
- .join(' ');
539
- console.log(` ${line}`);
540
- }
541
- console.log('');
542
- console.log(' Write these down on PAPER and keep them somewhere safe.');
543
- console.log(' Anyone with this phrase can decrypt your synced Wyrm memory.');
544
- console.log(' Wyrm Cloud has NO COPY of this. Lose it = lose your data.');
545
- console.log('');
546
- return;
547
- }
548
- if (action === 'restore') {
549
- const overwrite = args.includes('--overwrite');
550
- console.log('');
551
- console.log('🔑 Restore master key from BIP39 recovery phrase');
552
- console.log('');
553
- console.log(' Enter your 24 words separated by spaces (case-insensitive).');
554
- console.log(' Press Enter when done.');
555
- console.log('');
556
- const phrase = await prompt(' > ');
557
- const { mnemonicToKey } = await import('./recovery.js');
558
- const { path, overwrote } = mnemonicToKey(phrase, { overwrite });
559
- console.log('');
560
- console.log(`✓ Master key restored to ${path} (0600)`);
561
- if (overwrote) {
562
- console.log(' Replaced existing key (you passed --overwrite).');
563
- }
564
- console.log(' Run `wyrm cloud sync` to pull deltas with the restored key.');
565
- console.log('');
566
- return;
567
- }
568
- console.error('Usage:');
569
- console.error(' wyrm cloud recovery show');
570
- console.error(' wyrm cloud recovery restore [--overwrite]');
571
- process.exit(1);
572
- }
573
- //# sourceMappingURL=cli.js.map
35
+ `)}async function j(e){d()&&(console.error("Already logged in. Run `wyrm cloud logout` first to switch accounts."),process.exit(1));const n=new a(p),s=D(32).toString("hex");let o;try{o=await n.cliInit(s)}catch(t){console.error(`\u2718 Could not start login: ${t instanceof Error?t.message:t}`),process.exit(1)}console.log(""),console.log("\u{F115D} Sign in to Wyrm Cloud"),console.log(""),console.log(" 1. Open this URL on any device:"),console.log(` ${o.browser_url}`),console.log(""),console.log(" 2. Type this code when prompted:"),console.log(` ${o.code}`),console.log(""),console.log(" 3. Sign in with Google or GitHub."),console.log(""),console.log(` Waiting (expires in ${Math.round(o.expires_in_sec/60)} min)\u2026`);const l=Date.now()+o.expires_in_sec*1e3;let c,i;for(;Date.now()<l;){await new Promise(t=>{setTimeout(t,3e3)});try{const t=await n.cliPoll(s);if(t.ready&&t.session){c=t.session,i=t.account_id;break}}catch(t){console.error(`
36
+ \u2718 Login failed: ${t instanceof Error?t.message:t}`),process.exit(1)}}(!c||!i)&&(console.error("\n\u2718 Login timed out. Run `wyrm cloud login` again."),process.exit(1)),S({base:p,session:c,account_id:i,created_at:Date.now()});const u=new a(p,c);let y="";try{y=(await u.me()).account.email}catch{}const m=`${N()} (${process.platform})`;let v;try{v=(await u.registerDevice(m)).device.id;const $=d();$&&S({...$,email:y,device_id:v,device_name:m,machine_fp:E()})}catch(t){console.error(`
37
+ \u26A0 Logged in but could not register device: ${t instanceof Error?t.message:t}`),console.error(" Re-run `wyrm cloud login` to retry.");return}console.log(""),console.log(`\u2713 Signed in as ${y}`),console.log(`\u2713 Device registered: ${m}`),console.log(`\u2713 Session saved to ${f}`);const C=!h();R(),C?(console.log(`\u2713 Encryption key generated at ${g()} (0600)`),console.log(""),console.log(" \u26A0 Back up the key file. Losing it means losing access to your synced data."),console.log(" The Wyrm Cloud server CANNOT recover it \u2014 that's the operator-owns-data"),console.log(" guarantee. Run `wyrm cloud recovery show` now to get a 24-word phrase,"),console.log(" or copy the key file to a password manager / encrypted backup.")):console.log(`\u2713 Encryption key reused from ${g()}`),console.log(""),console.log("Next: `wyrm cloud sync` to push your memory.")}async function K(){const e=d();if(!e){console.log("Not logged in.");return}try{await new a(e.base,e.session).logout(),console.log("\u2713 Session revoked server-side.")}catch(n){console.error(`\u26A0 Could not revoke server-side: ${n instanceof Error?n.message:n}`),console.error(" Local session cleared anyway.")}b(),console.log(`\u2713 Local session removed (${f}).`),console.log(" Note: ~/.wyrm/cloud.key kept. Delete manually if you want a clean slate.")}async function H(){const e=d();if(!e){console.log("Not logged in. Run `wyrm cloud login`.");return}const n=new a(e.base,e.session);try{const s=await n.me(),o=await n.syncStatus();console.log(""),console.log(`\u{F115D} Wyrm Cloud \u2014 ${s.account.email}`),console.log(""),console.log(` Tier: ${o.tier}`),console.log(` Storage: ${w(o.storage.used_bytes)} / ${w(o.storage.limit_bytes)} (${o.storage.percent}%)`),console.log(` Deltas: ${o.deltas}`),console.log(` Devices: ${o.devices}`),console.log(` Providers:${s.identities.map(r=>" "+r.provider).join(",")}`),console.log(""),console.log(` This machine: ${e.device_name??"(not registered)"}`),console.log(` Device ID: ${e.device_id?e.device_id.slice(0,8)+"\u2026":"(none)"}`),console.log(` Session age: ${Math.round((Date.now()-e.created_at)/(1440*60*1e3))} day(s)`),x(e),console.log(""),h()?o.deltas>0&&(console.log(` Key: ${g()} (0600)`),console.log(` \u26A0 Back this file up. Losing it = losing access to your ${o.deltas} deltas.`),console.log("")):(console.log(" \u26A0 ENCRYPTION KEY MISSING"),console.log(` Expected at: ${g()}`),console.log(" Without this file, deltas already pushed cannot be decrypted."),console.log(" If you have a backup, restore it before running sync."),console.log(""))}catch(s){throw s instanceof k&&s.status===401&&(console.error("Session expired or invalid. Run `wyrm cloud login` again."),process.exit(1)),s}}function x(e){const n=O(e.machine_fp);n.state==="match"?console.log(" Identity: \u2713 machine fingerprint matches (not a copied session)"):n.state==="adopt"?console.log(" Identity: \u2014 no fingerprint yet (pre-7.0.3 session; adopted on next sync)"):(console.log(" Identity: \u26A0 FINGERPRINT MISMATCH \u2014 this session looks COPIED"),console.log(` stored ${n.stored.slice(0,12)}\u2026 vs this machine ${n.current.slice(0,12)}\u2026`),console.log(" device_id collision \u2192 cross-device pull returns 0 silently."),console.log(" Fix: rm ~/.wyrm/cloud.json ~/.wyrm/cloud-cursor.json (KEEP cloud.key),"),console.log(" then `wyrm cloud login`. (Or `wyrm cloud sync --force` if intended.)"))}function Y(){const e=d();if(console.log(""),console.log("\u{F115D} Wyrm Cloud \u2014 device identity doctor"),console.log(""),!e){console.log(" Not logged in. Run `wyrm cloud login`."),console.log("");return}console.log(` Account: ${e.email??e.account_id.slice(0,8)+"\u2026"}`),console.log(` Device ID: ${e.device_id?e.device_id.slice(0,8)+"\u2026":"(none \u2014 re-run `wyrm cloud login`)"}`),console.log(` Server: ${e.base}`),console.log(` Stored fp: ${e.machine_fp?e.machine_fp.slice(0,12)+"\u2026":"(none \u2014 pre-7.0.3 session)"}`),console.log(` This box fp: ${E().slice(0,12)}\u2026`),x(e),console.log(""),console.log(" Only ~/.wyrm/cloud.key is meant to be shared across your machines."),console.log(" cloud.json + cloud-cursor.json + machine-id are per-device."),console.log("")}async function q(e){const n=e[0],s=a.fromSession();if(n==="revoke"){const c=e[1];c||(console.error("Usage: wyrm cloud devices revoke <device-id>"),process.exit(1)),await s.revokeDevice(c),console.log(`\u2713 Device ${c.slice(0,8)}\u2026 revoked.`);return}const{devices:o}=await s.listDevices(),l=d()?.device_id;console.log(""),console.log(`\u{F115D} Devices (${o.length})`),console.log("");for(const c of o){const i=c.id===l?"\u2605":" ",u=c.revoked?"(revoked)":"";console.log(` ${i} ${c.id.slice(0,8)}\u2026 ${c.name.padEnd(30)} last_seen=${G(c.last_seen_at)} ${u}`)}console.log(""),console.log(" \u2605 = this machine"),console.log(" Revoke: wyrm cloud devices revoke <device-id>"),console.log("")}async function z(e){T();const n=e.includes("--dry-run"),s=e.includes("--all"),o=e.includes("--force");h()||(console.error("\u26A0 Encryption key not found at",g()),console.error(" A new key will be generated, but it will NOT decrypt deltas"),console.error(" pushed by other devices using a different key. If you have a backup,"),console.error(" restore it to ~/.wyrm/cloud.key (0600) before running sync."),console.error(" Continuing in 5 seconds \u2014 press Ctrl+C to abort."),await new Promise(l=>{setTimeout(l,5e3)})),s&&(console.log("\u{1F310} --all mode: syncing every semantic table (ignoring visibility flags)."),console.log(" Skipped: per-device logs, FTS shadows, sync internals."),console.log(""));const r=await L({dryRun:n,all:s,force:o});if(console.log(""),console.log("\u{F115D} Sync complete"),console.log(""),console.log(` Pushed: ${r.pushed} row(s)${n?" (dry run)":""}`),console.log(` Pulled: ${r.pulled} row(s)`),console.log(` Deleted: ${r.deleted_local} row(s) (peer tombstones)`),r.errors.length>0){console.log(""),console.log(" Errors:");for(const l of r.errors)console.log(` - ${l}`)}console.log("")}async function J(e){T();const n=a.fromSession(),s=e.find(c=>!c.startsWith("-")),o=P(s??`wyrm-cloud-export-${new Date().toISOString().slice(0,10)}.json`);console.log(" Fetching account export\u2026");const r=await n.accountExport(),l=JSON.stringify(r,null,2);I(o,l,{mode:384}),console.log(""),console.log(`\u2713 Exported to ${o}`),console.log(""),console.log(` Account: ${r.account.email} (${r.account.id.slice(0,8)}\u2026)`),console.log(` Devices: ${r.devices.length}`),console.log(` Identities: ${r.identities.length}`),console.log(` Orgs: ${r.orgs.length}`),console.log(` Deltas: ${r.deltas.length}`),console.log(` Size: ${w(l.length)}`),console.log(""),console.log(" Deltas are still AES-256-GCM ciphertext. To decrypt them you"),console.log(` need the key at ${g()}. Back that up alongside this file.`),console.log("")}async function Q(e){e.includes("--confirm")||(console.error("\u2718 This permanently deletes your Wyrm Cloud account."),console.error(" Re-run with --confirm to proceed:"),console.error(" wyrm cloud delete --confirm"),process.exit(1));const s=d();s||(console.error("\u2718 Not logged in. Nothing to delete."),process.exit(1)),console.log(""),console.log("\u26A0 You are about to PERMANENTLY DELETE your Wyrm Cloud account."),console.log(""),console.log(` Account: ${s.email??s.account_id}`),console.log(` Server: ${s.base}`),console.log(""),console.log(" This wipes all encrypted blobs, devices, identities, and org"),console.log(" memberships you own. The local ~/.wyrm/cloud.key is kept so"),console.log(" any backups remain decryptable. This action CANNOT be undone."),console.log(""),(await _(' Type "DELETE my account" to confirm: ')).trim()!=="DELETE my account"&&(console.error("\u2718 Confirmation phrase did not match. Aborting."),process.exit(1)),await new a(s.base,s.session).accountDelete(),b(),console.log(""),console.log("\u2713 Account deleted on server."),console.log(`\u2713 Local session removed (${f}).`),console.log(` Kept: ${g()} (in case you need to decrypt an old export).`),console.log("")}function _(e){return new Promise(n=>{const s=A({input:process.stdin,output:process.stdout});s.question(e,o=>{s.close(),n(o)})})}async function V(e){const n=e[0];if(n==="show"){const{keyToMnemonic:s}=await import("./recovery.js"),r=s().split(" ");console.log(""),console.log("\u{1F511} Wyrm Cloud recovery phrase (BIP39, 24 words)"),console.log("");for(let l=0;l<4;l++){const c=r.slice(l*6,l*6+6).map((i,u)=>`${String(l*6+u+1).padStart(2)}. ${i.padEnd(10)}`).join(" ");console.log(` ${c}`)}console.log(""),console.log(" Write these down on PAPER and keep them somewhere safe."),console.log(" Anyone with this phrase can decrypt your synced Wyrm memory."),console.log(" Wyrm Cloud has NO COPY of this. Lose it = lose your data."),console.log("");return}if(n==="restore"){const s=e.includes("--overwrite");console.log(""),console.log("\u{1F511} Restore master key from BIP39 recovery phrase"),console.log(""),console.log(" Enter your 24 words separated by spaces (case-insensitive)."),console.log(" Press Enter when done."),console.log("");const o=await _(" > "),{mnemonicToKey:r}=await import("./recovery.js"),{path:l,overwrote:c}=r(o,{overwrite:s});console.log(""),console.log(`\u2713 Master key restored to ${l} (0600)`),c&&console.log(" Replaced existing key (you passed --overwrite)."),console.log(" Run `wyrm cloud sync` to pull deltas with the restored key."),console.log("");return}console.error("Usage:"),console.error(" wyrm cloud recovery show"),console.error(" wyrm cloud recovery restore [--overwrite]"),process.exit(1)}export{ie as cmdCloud};