wyrm-mcp 7.2.1 → 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 (150) hide show
  1. package/LICENSE +26 -667
  2. package/NOTICE +14 -33
  3. package/dist/activation.js +1 -60
  4. package/dist/agent-daemon.js +4 -281
  5. package/dist/agent-loop.js +7 -332
  6. package/dist/analytics.js +13 -236
  7. package/dist/attribution.js +1 -49
  8. package/dist/audit.js +2 -457
  9. package/dist/auto-capture.js +3 -138
  10. package/dist/auto-orchestrator.js +1 -325
  11. package/dist/autoconfig.js +39 -840
  12. package/dist/buddy-runner.js +1 -109
  13. package/dist/buddy.js +14 -564
  14. package/dist/build-flags.js +1 -17
  15. package/dist/capabilities.js +3 -183
  16. package/dist/capture.js +1 -56
  17. package/dist/causality.js +6 -107
  18. package/dist/cli.js +20 -281
  19. package/dist/cloud/cli.js +5 -541
  20. package/dist/cloud/client.js +1 -221
  21. package/dist/cloud/crypto.js +1 -85
  22. package/dist/cloud/machine-id.js +2 -113
  23. package/dist/cloud/recovery.js +1 -60
  24. package/dist/cloud/sync-engine.js +7 -543
  25. package/dist/cloud-backup.js +5 -579
  26. package/dist/cloud-profile.js +1 -138
  27. package/dist/cloud-sync-entrypoint.js +1 -47
  28. package/dist/cloud-sync.js +2 -309
  29. package/dist/constellation.js +12 -168
  30. package/dist/context-build-budgeted.js +4 -144
  31. package/dist/context-ranking.js +1 -69
  32. package/dist/crypto.js +1 -179
  33. package/dist/daemon-write-endpoint.js +1 -290
  34. package/dist/daemon-writer.js +2 -406
  35. package/dist/database.js +43 -1110
  36. package/dist/deprecations.js +2 -162
  37. package/dist/design.js +13 -141
  38. package/dist/event-replication.js +1 -112
  39. package/dist/events-sse.js +7 -43
  40. package/dist/events.js +6 -238
  41. package/dist/failure-patterns.js +42 -659
  42. package/dist/federation.js +12 -236
  43. package/dist/goals.js +13 -101
  44. package/dist/golden.js +3 -355
  45. package/dist/handlers/agent.js +4 -165
  46. package/dist/handlers/alias-adapters.js +1 -129
  47. package/dist/handlers/aliases.js +1 -171
  48. package/dist/handlers/audit.js +1 -87
  49. package/dist/handlers/boundary.js +1 -221
  50. package/dist/handlers/capture.js +73 -1109
  51. package/dist/handlers/causality.js +7 -114
  52. package/dist/handlers/cloud.js +85 -382
  53. package/dist/handlers/companion.js +28 -459
  54. package/dist/handlers/datalake.js +7 -187
  55. package/dist/handlers/dispatch-context.js +0 -22
  56. package/dist/handlers/entity.js +25 -256
  57. package/dist/handlers/events.js +16 -335
  58. package/dist/handlers/failure.js +13 -340
  59. package/dist/handlers/goals.js +4 -296
  60. package/dist/handlers/intelligence.js +126 -674
  61. package/dist/handlers/invoicing.js +1 -70
  62. package/dist/handlers/mcpclient.js +6 -137
  63. package/dist/handlers/orchestration.js +40 -125
  64. package/dist/handlers/output-schemas.js +1 -24
  65. package/dist/handlers/presence.js +3 -99
  66. package/dist/handlers/project.js +28 -182
  67. package/dist/handlers/prompts.js +6 -157
  68. package/dist/handlers/quest.js +4 -224
  69. package/dist/handlers/recall.js +11 -218
  70. package/dist/handlers/registry.js +1 -167
  71. package/dist/handlers/resources.js +1 -288
  72. package/dist/handlers/review.js +11 -74
  73. package/dist/handlers/run.js +17 -487
  74. package/dist/handlers/search.js +15 -326
  75. package/dist/handlers/session.js +28 -615
  76. package/dist/handlers/share.js +8 -184
  77. package/dist/handlers/shims.js +1 -464
  78. package/dist/handlers/skill.js +67 -449
  79. package/dist/handlers/survivors.js +1 -120
  80. package/dist/handlers/symbols.js +8 -109
  81. package/dist/handlers/syncops.js +4 -302
  82. package/dist/handlers/types.js +1 -27
  83. package/dist/harvest.js +5 -191
  84. package/dist/hours.js +7 -156
  85. package/dist/http-auth.js +3 -321
  86. package/dist/http-fast.js +21 -1137
  87. package/dist/icons.js +1 -47
  88. package/dist/index.js +2 -924
  89. package/dist/indexer.js +4 -145
  90. package/dist/intelligence.js +31 -261
  91. package/dist/internal-dispatch.js +3 -212
  92. package/dist/keyset.js +1 -110
  93. package/dist/knowledge-graph.js +12 -176
  94. package/dist/license.js +2 -441
  95. package/dist/logger.js +2 -199
  96. package/dist/maintenance.js +2 -148
  97. package/dist/mcp-client.js +6 -262
  98. package/dist/memory-artifacts.js +30 -449
  99. package/dist/migrate-prompt.js +2 -124
  100. package/dist/migrations.js +40 -655
  101. package/dist/performance.js +1 -228
  102. package/dist/presence.js +11 -140
  103. package/dist/priority-embed.js +5 -164
  104. package/dist/providers/embedding-provider.js +1 -196
  105. package/dist/readonly-gate.js +1 -29
  106. package/dist/rehydration.js +9 -157
  107. package/dist/reindex.js +1 -88
  108. package/dist/render-target.js +21 -514
  109. package/dist/render.js +4 -280
  110. package/dist/repl-guard.js +1 -173
  111. package/dist/replication-daemon-entrypoint.js +1 -31
  112. package/dist/replication-daemon.js +2 -262
  113. package/dist/resilience.js +1 -591
  114. package/dist/reverse-bridge.js +5 -360
  115. package/dist/security.js +1 -244
  116. package/dist/session-seen.js +3 -51
  117. package/dist/setup.js +1 -260
  118. package/dist/skill-author.js +5 -168
  119. package/dist/spec-kit.js +1 -191
  120. package/dist/sqlite-busy.js +1 -154
  121. package/dist/statusline.js +11 -315
  122. package/dist/sub-agent.js +13 -262
  123. package/dist/summarizer.js +13 -139
  124. package/dist/symbols.js +7 -283
  125. package/dist/sync.js +5 -359
  126. package/dist/tasks-dispatch.js +1 -84
  127. package/dist/tasks.js +1 -282
  128. package/dist/token-budget.js +1 -143
  129. package/dist/tool-analytics.js +7 -129
  130. package/dist/tool-annotations.js +1 -365
  131. package/dist/tool-manifest-v2.json +1 -1
  132. package/dist/tool-manifest.json +1 -1
  133. package/dist/tool-profiles.js +1 -75
  134. package/dist/trace-harvest.js +6 -244
  135. package/dist/types.js +1 -30
  136. package/dist/ui-dashboard.js +41 -50
  137. package/dist/ulid.js +1 -81
  138. package/dist/validate.js +1 -129
  139. package/dist/vault.js +1 -534
  140. package/dist/vectors.js +3 -184
  141. package/dist/version-check.js +4 -136
  142. package/dist/visibility.js +19 -155
  143. package/dist/wyrm-cli.js +98 -2464
  144. package/dist/wyrm-guard.js +14 -424
  145. package/dist/wyrm-loop.js +3 -150
  146. package/dist/wyrm-manifest.json +1 -1
  147. package/dist/wyrm-statusline-daemon.js +1 -11
  148. package/dist/wyrm-statusline.js +4 -56
  149. package/dist/wyrm-ui.js +9 -77
  150. package/package.json +4 -2
package/dist/license.js CHANGED
@@ -1,442 +1,3 @@
1
- /**
2
- * Wyrm License System
3
- * ED25519 license key generation, signing, and offline verification
4
- *
5
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
6
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
7
- * @module license
8
- * @version 3.2.0
9
- */
10
- import { createPrivateKey, createPublicKey, sign, verify, generateKeyPairSync, randomBytes, } from 'crypto';
11
- import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync } from 'fs';
12
- import { homedir } from 'os';
13
- import { join } from 'path';
14
- // ==================== CONSTANTS ====================
15
- /** Base32 alphabet without visually-ambiguous characters (no I, L, O, 1) */
16
- const BASE32_ALPHABET = 'ABCDEFGHJKMNPQRSTUVWXYZ234567890';
17
- /** License key prefix */
18
- const KEY_PREFIX = 'WRM';
19
- /** Bytes of entropy per key segment (4 chars each) */
20
- const KEY_SEGMENT_COUNT = 4;
21
- const KEY_SEGMENT_LENGTH = 4;
22
- /** License file location */
23
- const WYRM_DIR = join(homedir(), '.wyrm');
24
- const LICENSE_PATH = join(WYRM_DIR, 'license.json');
25
- /**
26
- * Feature gates per tier.
27
- * Each tier inherits all features from lower tiers plus its own additions.
28
- */
29
- export const TIER_FEATURES = {
30
- free: ['local_storage', 'all_tools', 'fts_search', 'unlimited_projects'],
31
- pro: ['cloud_backup', 'encryption', 'analytics', 'priority_support'],
32
- team: ['shared_memory', 'workspaces', 'admin_dashboard', 'slack_integration'],
33
- enterprise: ['unlimited_seats', 'sso_saml', 'custom_sla', 'on_premise'],
34
- };
35
- /** Ordered tiers for cumulative feature resolution */
36
- const TIER_ORDER = ['free', 'pro', 'team', 'enterprise'];
37
- /** Default device limits per tier */
38
- export const TIER_DEVICE_LIMITS = {
39
- free: 1,
40
- pro: 1,
41
- team: 25,
42
- enterprise: -1,
43
- };
44
- /**
45
- * Embedded public key for offline verification.
46
- *
47
- * Generated 2026-05-18 alongside the wyrm-billing signing keypair.
48
- * The matching private key lives ONLY in wyrm-billing's
49
- * LICENSE_SIGNING_KEY_PRIVATE_PEM_B64 env var. This public half is
50
- * shipped to every Wyrm client so they can verify license JWTs
51
- * offline without phoning home.
52
- *
53
- * Rotation: regenerate the keypair, replace the private in
54
- * wyrm-billing's env, replace this constant, ship a new wyrm-mcp
55
- * release. Issued licenses signed under the OLD key will reject
56
- * after the new client rolls out — schedule a grace window.
57
- */
58
- const WYRM_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
1
+ import{createPrivateKey as S,createPublicKey as T,sign as w,verify as k,generateKeyPairSync as I,randomBytes as A}from"crypto";import{readFileSync as K,writeFileSync as h,mkdirSync as L,existsSync as m,chmodSync as b}from"fs";import{homedir as B}from"os";import{join as _}from"path";const x="ABCDEFGHJKMNPQRSTUVWXYZ234567890",N="WRM",F=4,R=4,l=_(B(),".wyrm"),c=_(l,"license.json"),n={free:["local_storage","all_tools","fts_search","unlimited_projects"],pro:["cloud_backup","encryption","analytics","priority_support"],team:["shared_memory","workspaces","admin_dashboard","slack_integration"],enterprise:["unlimited_seats","sso_saml","custom_sla","on_premise"]},d=["free","pro","team","enterprise"],M={free:1,pro:1,team:25,enterprise:-1},P=`-----BEGIN PUBLIC KEY-----
59
2
  MCowBQYDK2VwAyEA5WkbNjI9tclD+Mps13IzEXH5FhCBnblwy4+bjYJHpOk=
60
- -----END PUBLIC KEY-----`;
61
- // ==================== SINGLETON STATE ====================
62
- let currentLicense = null;
63
- let currentTier = 'free';
64
- let currentFeatures = new Set(TIER_FEATURES.free);
65
- // ==================== HELPERS ====================
66
- /**
67
- * Resolve the cumulative feature set for a given tier.
68
- * E.g. `team` gets free + pro + team features.
69
- */
70
- export function resolveFeaturesForTier(tier) {
71
- const idx = TIER_ORDER.indexOf(tier);
72
- const features = [];
73
- for (let i = 0; i <= idx; i++) {
74
- features.push(...TIER_FEATURES[TIER_ORDER[i]]);
75
- }
76
- return [...new Set(features)];
77
- }
78
- /**
79
- * Build the canonical string representation of a license for signing/verification.
80
- * Field order is fixed so both signer and verifier produce identical payloads.
81
- */
82
- function buildCanonicalPayload(license) {
83
- return [
84
- license.key,
85
- license.product,
86
- license.tier,
87
- license.issued_to,
88
- license.issued_at,
89
- license.expires_at ?? 'perpetual',
90
- String(license.max_devices),
91
- license.features.sort().join(','),
92
- license.hardware_id ?? '',
93
- ].join('|');
94
- }
95
- /**
96
- * Encode random bytes into a Base32 string of the given length
97
- * using our custom visually-unambiguous alphabet.
98
- */
99
- function randomBase32(length) {
100
- const bytes = randomBytes(length);
101
- let result = '';
102
- for (let i = 0; i < length; i++) {
103
- result += BASE32_ALPHABET[bytes[i] % BASE32_ALPHABET.length];
104
- }
105
- return result;
106
- }
107
- // ==================== KEY GENERATION (SERVER-SIDE) ====================
108
- /**
109
- * Generate an Ed25519 key pair for license signing.
110
- *
111
- * **Server-side only** — the private key must never be shipped in the client.
112
- *
113
- * @returns PEM-encoded public and private keys
114
- */
115
- export function generateKeyPair() {
116
- const { publicKey, privateKey } = generateKeyPairSync('ed25519', {
117
- publicKeyEncoding: { type: 'spki', format: 'pem' },
118
- privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
119
- });
120
- return { publicKey, privateKey };
121
- }
122
- /**
123
- * Generate a license key in `WRM-XXXX-XXXX-XXXX-XXXX` format.
124
- *
125
- * Uses `crypto.randomBytes` mapped onto a Base32 alphabet that excludes
126
- * visually-ambiguous characters (I, L, O, 1).
127
- *
128
- * **Server-side only.**
129
- */
130
- export function generateLicenseKey() {
131
- const segments = [];
132
- for (let i = 0; i < KEY_SEGMENT_COUNT; i++) {
133
- segments.push(randomBase32(KEY_SEGMENT_LENGTH));
134
- }
135
- return `${KEY_PREFIX}-${segments.join('-')}`;
136
- }
137
- /**
138
- * Create a new {@link WyrmLicense} with sane defaults for the given tier.
139
- *
140
- * **Server-side only.**
141
- *
142
- * @param email - Licensee email
143
- * @param tier - Desired tier
144
- * @param options - Optional overrides (expiry, hardware binding, extra features)
145
- */
146
- export function createLicense(email, tier, options = {}) {
147
- const features = resolveFeaturesForTier(tier);
148
- if (options.extraFeatures) {
149
- for (const f of options.extraFeatures) {
150
- if (!features.includes(f))
151
- features.push(f);
152
- }
153
- }
154
- return {
155
- key: generateLicenseKey(),
156
- product: 'wyrm',
157
- tier,
158
- issued_to: email,
159
- issued_at: new Date().toISOString(),
160
- expires_at: options.expiresAt ?? null,
161
- max_devices: options.maxDevices ?? TIER_DEVICE_LIMITS[tier],
162
- features,
163
- hardware_id: options.hardwareId,
164
- };
165
- }
166
- /**
167
- * Sign a {@link WyrmLicense} with an Ed25519 private key.
168
- *
169
- * **Server-side only.**
170
- *
171
- * @param license - The license data to sign
172
- * @param privateKeyPem - PEM-encoded Ed25519 private key
173
- * @returns The license bundled with its base64 signature
174
- */
175
- export function signLicense(license, privateKeyPem) {
176
- const payload = buildCanonicalPayload(license);
177
- const key = createPrivateKey(privateKeyPem);
178
- const sig = sign(null, Buffer.from(payload, 'utf-8'), key);
179
- return {
180
- license,
181
- signature: sig.toString('base64'),
182
- };
183
- }
184
- // ==================== VERIFICATION (CLIENT-SIDE / OFFLINE) ====================
185
- /**
186
- * Verify a {@link SignedLicense} against an Ed25519 public key.
187
- *
188
- * Performs three checks:
189
- * 1. Signature validity (Ed25519)
190
- * 2. Product field matches "wyrm"
191
- * 3. Expiry date (if present)
192
- *
193
- * Works fully offline with zero external dependencies.
194
- *
195
- * @param signed - The signed license bundle
196
- * @param publicKeyPem - Optional PEM override; defaults to the embedded key
197
- */
198
- export function verifyLicense(signed, publicKeyPem) {
199
- const freeFallback = {
200
- valid: false,
201
- tier: 'free',
202
- features: [...TIER_FEATURES.free],
203
- expiresAt: null,
204
- };
205
- try {
206
- const { license, signature } = signed;
207
- // Product check
208
- if (license.product !== 'wyrm') {
209
- return { ...freeFallback, error: 'Invalid product identifier' };
210
- }
211
- // Tier validity
212
- if (!TIER_ORDER.includes(license.tier)) {
213
- return { ...freeFallback, error: `Unknown tier: ${license.tier}` };
214
- }
215
- // Signature verification
216
- const payload = buildCanonicalPayload(license);
217
- const pem = publicKeyPem ?? WYRM_PUBLIC_KEY;
218
- let key;
219
- try {
220
- key = createPublicKey(pem);
221
- }
222
- catch {
223
- return { ...freeFallback, error: 'Invalid public key' };
224
- }
225
- const isValid = verify(null, Buffer.from(payload, 'utf-8'), key, Buffer.from(signature, 'base64'));
226
- if (!isValid) {
227
- return { ...freeFallback, error: 'Invalid signature — license may be tampered' };
228
- }
229
- // Expiry check
230
- if (license.expires_at !== null) {
231
- const expiry = new Date(license.expires_at);
232
- if (isNaN(expiry.getTime())) {
233
- return { ...freeFallback, error: 'Malformed expiry date' };
234
- }
235
- if (expiry.getTime() < Date.now()) {
236
- return {
237
- valid: false,
238
- tier: license.tier,
239
- features: [...TIER_FEATURES.free],
240
- expiresAt: license.expires_at,
241
- error: `License expired on ${license.expires_at}`,
242
- };
243
- }
244
- }
245
- return {
246
- valid: true,
247
- tier: license.tier,
248
- features: [...license.features],
249
- expiresAt: license.expires_at,
250
- };
251
- }
252
- catch (err) {
253
- const message = err instanceof Error ? err.message : String(err);
254
- return { ...freeFallback, error: `Verification failed: ${message}` };
255
- }
256
- }
257
- // ==================== TAMPER-EVIDENCE (KAT) ====================
258
- /**
259
- * Known-Answer Test for the official build: confirm the verifier still REJECTS a
260
- * structurally-valid but badly-signed license. If a fork stubs `verifyLicense` to
261
- * always-accept (so it can run on a forged `license.json` and bypass the activation
262
- * gate), this self-test trips. It's a deterrent layer — a determined forker can
263
- * remove this too — but it raises the effort beyond a one-line edit. Pure + offline.
264
- */
265
- export function selfTestVerifier() {
266
- try {
267
- const forged = {
268
- license: {
269
- key: 'WRM-KAT0-KAT0-KAT0-KAT0', product: 'wyrm', tier: 'enterprise',
270
- issued_to: 'integrity@selftest', issued_at: '2026-01-01T00:00:00.000Z',
271
- expires_at: null, max_devices: -1, features: [...TIER_FEATURES.enterprise],
272
- },
273
- signature: Buffer.from('integrity-self-test-not-a-valid-signature').toString('base64'),
274
- };
275
- if (verifyLicense(forged).valid)
276
- return { ok: false, reason: 'verifier_accepts_forged_signature' };
277
- return { ok: true };
278
- }
279
- catch {
280
- // An unexpected crypto error shouldn't hard-fail an honest build → "can't confirm".
281
- return { ok: true };
282
- }
283
- }
284
- // ==================== FEATURE GATES ====================
285
- /**
286
- * Check whether a specific feature is available in the current license.
287
- *
288
- * @param feature - Feature flag name (e.g. `"encryption"`, `"sso_saml"`)
289
- */
290
- export function hasFeature(feature) {
291
- return currentFeatures.has(feature);
292
- }
293
- /**
294
- * Get the current active license tier.
295
- * Defaults to `"free"` when no valid license is loaded.
296
- */
297
- export function getTier() {
298
- return currentTier;
299
- }
300
- /**
301
- * Return a summary of the currently-loaded license state.
302
- */
303
- export function getLicenseInfo() {
304
- if (!currentLicense) {
305
- return {
306
- tier: 'free',
307
- features: [...TIER_FEATURES.free],
308
- expiresAt: null,
309
- valid: false,
310
- issuedTo: null,
311
- key: null,
312
- };
313
- }
314
- const validation = verifyLicense(currentLicense);
315
- return {
316
- tier: validation.tier,
317
- features: validation.features,
318
- expiresAt: validation.expiresAt,
319
- valid: validation.valid,
320
- issuedTo: currentLicense.license.issued_to,
321
- key: currentLicense.license.key,
322
- };
323
- }
324
- // ==================== PERSISTENCE ====================
325
- /**
326
- * Load a signed license from `~/.wyrm/license.json`.
327
- *
328
- * @returns The parsed {@link SignedLicense} or `null` if not found / unparseable
329
- */
330
- export function loadLicense() {
331
- try {
332
- if (!existsSync(LICENSE_PATH))
333
- return null;
334
- const raw = readFileSync(LICENSE_PATH, 'utf-8');
335
- const parsed = JSON.parse(raw);
336
- // Basic shape check
337
- if (!parsed.license || !parsed.signature)
338
- return null;
339
- if (!parsed.license.key || !parsed.license.tier)
340
- return null;
341
- return parsed;
342
- }
343
- catch {
344
- return null;
345
- }
346
- }
347
- /**
348
- * Persist a signed license to `~/.wyrm/license.json`.
349
- * File permissions are set to `0o600` (owner read/write only).
350
- *
351
- * @param signed - The signed license to save
352
- */
353
- export function saveLicense(signed) {
354
- if (!existsSync(WYRM_DIR)) {
355
- mkdirSync(WYRM_DIR, { recursive: true });
356
- }
357
- const json = JSON.stringify(signed, null, 2);
358
- writeFileSync(LICENSE_PATH, json, { encoding: 'utf-8', mode: 0o600 });
359
- // Ensure permissions even if file existed previously
360
- chmodSync(LICENSE_PATH, 0o600);
361
- }
362
- /**
363
- * Activate a license from a JSON string (e.g. pasted by the user).
364
- *
365
- * Parses, verifies, and — if valid — persists the license and updates
366
- * the in-memory singleton state.
367
- *
368
- * @param licenseString - JSON-encoded {@link SignedLicense}
369
- * @returns Validation result
370
- */
371
- export function activateLicense(licenseString) {
372
- let signed;
373
- try {
374
- signed = JSON.parse(licenseString);
375
- }
376
- catch {
377
- return {
378
- valid: false,
379
- tier: 'free',
380
- features: [...TIER_FEATURES.free],
381
- expiresAt: null,
382
- error: 'Invalid license format — expected JSON',
383
- };
384
- }
385
- if (!signed.license || !signed.signature) {
386
- return {
387
- valid: false,
388
- tier: 'free',
389
- features: [...TIER_FEATURES.free],
390
- expiresAt: null,
391
- error: 'Malformed license — missing license data or signature',
392
- };
393
- }
394
- const validation = verifyLicense(signed);
395
- if (validation.valid) {
396
- saveLicense(signed);
397
- currentLicense = signed;
398
- currentTier = validation.tier;
399
- currentFeatures = new Set(validation.features);
400
- }
401
- return validation;
402
- }
403
- // ==================== INITIALIZATION ====================
404
- /**
405
- * Initialize the license subsystem on startup.
406
- *
407
- * Attempts to load and verify `~/.wyrm/license.json`.
408
- * Falls back to the free tier on any failure.
409
- *
410
- * Call this once during MCP server boot.
411
- */
412
- export function initializeLicense() {
413
- const signed = loadLicense();
414
- if (!signed) {
415
- currentLicense = null;
416
- currentTier = 'free';
417
- currentFeatures = new Set(TIER_FEATURES.free);
418
- return;
419
- }
420
- const validation = verifyLicense(signed);
421
- if (validation.valid) {
422
- currentLicense = signed;
423
- currentTier = validation.tier;
424
- currentFeatures = new Set(validation.features);
425
- }
426
- else {
427
- // Invalid / expired — degrade gracefully
428
- currentLicense = null;
429
- currentTier = 'free';
430
- currentFeatures = new Set(TIER_FEATURES.free);
431
- }
432
- }
433
- /**
434
- * Reset the singleton state to free tier.
435
- * Useful for testing or license deactivation.
436
- */
437
- export function resetLicense() {
438
- currentLicense = null;
439
- currentTier = 'free';
440
- currentFeatures = new Set(TIER_FEATURES.free);
441
- }
442
- //# sourceMappingURL=license.js.map
3
+ -----END PUBLIC KEY-----`;let s=null,o="free",u=new Set(n.free);function D(e){const r=d.indexOf(e),i=[];for(let t=0;t<=r;t++)i.push(...n[d[t]]);return[...new Set(i)]}function g(e){return[e.key,e.product,e.tier,e.issued_to,e.issued_at,e.expires_at??"perpetual",String(e.max_devices),e.features.sort().join(","),e.hardware_id??""].join("|")}function Y(e){const r=A(e);let i="";for(let t=0;t<e;t++)i+=x[r[t]%x.length];return i}function G(){const{publicKey:e,privateKey:r}=I("ed25519",{publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem"}});return{publicKey:e,privateKey:r}}function C(){const e=[];for(let r=0;r<F;r++)e.push(Y(R));return`${N}-${e.join("-")}`}function $(e,r,i={}){const t=D(r);if(i.extraFeatures)for(const a of i.extraFeatures)t.includes(a)||t.push(a);return{key:C(),product:"wyrm",tier:r,issued_to:e,issued_at:new Date().toISOString(),expires_at:i.expiresAt??null,max_devices:i.maxDevices??M[r],features:t,hardware_id:i.hardwareId}}function X(e,r){const i=g(e),t=S(r),a=w(null,Buffer.from(i,"utf-8"),t);return{license:e,signature:a.toString("base64")}}function f(e,r){const i={valid:!1,tier:"free",features:[...n.free],expiresAt:null};try{const{license:t,signature:a}=e;if(t.product!=="wyrm")return{...i,error:"Invalid product identifier"};if(!d.includes(t.tier))return{...i,error:`Unknown tier: ${t.tier}`};const v=g(t),E=r??P;let p;try{p=T(E)}catch{return{...i,error:"Invalid public key"}}if(!k(null,Buffer.from(v,"utf-8"),p,Buffer.from(a,"base64")))return{...i,error:"Invalid signature \u2014 license may be tampered"};if(t.expires_at!==null){const y=new Date(t.expires_at);if(isNaN(y.getTime()))return{...i,error:"Malformed expiry date"};if(y.getTime()<Date.now())return{valid:!1,tier:t.tier,features:[...n.free],expiresAt:t.expires_at,error:`License expired on ${t.expires_at}`}}return{valid:!0,tier:t.tier,features:[...t.features],expiresAt:t.expires_at}}catch(t){const a=t instanceof Error?t.message:String(t);return{...i,error:`Verification failed: ${a}`}}}function z(){try{const e={license:{key:"WRM-KAT0-KAT0-KAT0-KAT0",product:"wyrm",tier:"enterprise",issued_to:"integrity@selftest",issued_at:"2026-01-01T00:00:00.000Z",expires_at:null,max_devices:-1,features:[...n.enterprise]},signature:Buffer.from("integrity-self-test-not-a-valid-signature").toString("base64")};return f(e).valid?{ok:!1,reason:"verifier_accepts_forged_signature"}:{ok:!0}}catch{return{ok:!0}}}function Q(e){return u.has(e)}function Z(){return o}function q(){if(!s)return{tier:"free",features:[...n.free],expiresAt:null,valid:!1,issuedTo:null,key:null};const e=f(s);return{tier:e.tier,features:e.features,expiresAt:e.expiresAt,valid:e.valid,issuedTo:s.license.issued_to,key:s.license.key}}function j(){try{if(!m(c))return null;const e=K(c,"utf-8"),r=JSON.parse(e);return!r.license||!r.signature||!r.license.key||!r.license.tier?null:r}catch{return null}}function O(e){m(l)||L(l,{recursive:!0});const r=JSON.stringify(e,null,2);h(c,r,{encoding:"utf-8",mode:384}),b(c,384)}function ee(e){let r;try{r=JSON.parse(e)}catch{return{valid:!1,tier:"free",features:[...n.free],expiresAt:null,error:"Invalid license format \u2014 expected JSON"}}if(!r.license||!r.signature)return{valid:!1,tier:"free",features:[...n.free],expiresAt:null,error:"Malformed license \u2014 missing license data or signature"};const i=f(r);return i.valid&&(O(r),s=r,o=i.tier,u=new Set(i.features)),i}function re(){const e=j();if(!e){s=null,o="free",u=new Set(n.free);return}const r=f(e);r.valid?(s=e,o=r.tier,u=new Set(r.features)):(s=null,o="free",u=new Set(n.free))}function te(){s=null,o="free",u=new Set(n.free)}export{M as TIER_DEVICE_LIMITS,n as TIER_FEATURES,ee as activateLicense,$ as createLicense,G as generateKeyPair,C as generateLicenseKey,q as getLicenseInfo,Z as getTier,Q as hasFeature,re as initializeLicense,j as loadLicense,te as resetLicense,D as resolveFeaturesForTier,O as saveLicense,z as selfTestVerifier,X as signLicense,f as verifyLicense};
package/dist/logger.js CHANGED
@@ -1,199 +1,2 @@
1
- /**
2
- * Wyrm Logger - Structured logging with log levels
3
- *
4
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
5
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
6
- * @module logger
7
- * @version 3.0.0
8
- */
9
- import { appendFileSync, existsSync, mkdirSync } from 'fs';
10
- import { homedir } from 'os';
11
- import { join } from 'path';
12
- const LOG_LEVELS = {
13
- debug: 0,
14
- info: 1,
15
- warn: 2,
16
- error: 3,
17
- };
18
- const LOG_COLORS = {
19
- debug: '\x1b[36m', // Cyan
20
- info: '\x1b[32m', // Green
21
- warn: '\x1b[33m', // Yellow
22
- error: '\x1b[31m', // Red
23
- };
24
- const RESET = '\x1b[0m';
25
- const BOLD = '\x1b[1m';
26
- const DIM = '\x1b[2m';
27
- /**
28
- * Wyrm Logger - Structured logging for the memory system
29
- */
30
- export class WyrmLogger {
31
- config;
32
- correlationId;
33
- logDir;
34
- constructor(config) {
35
- this.config = {
36
- level: config?.level ?? 'info',
37
- console: config?.console ?? true,
38
- json: config?.json ?? false,
39
- file: config?.file,
40
- };
41
- this.logDir = join(homedir(), '.wyrm', 'logs');
42
- if (!existsSync(this.logDir)) {
43
- mkdirSync(this.logDir, { recursive: true });
44
- }
45
- }
46
- /**
47
- * Set correlation ID for request tracing
48
- */
49
- setCorrelationId(id) {
50
- this.correlationId = id;
51
- }
52
- /**
53
- * Generate a new correlation ID
54
- */
55
- generateCorrelationId() {
56
- this.correlationId = `wyrm-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
57
- return this.correlationId;
58
- }
59
- /**
60
- * Check if a log level should be logged
61
- */
62
- shouldLog(level) {
63
- return LOG_LEVELS[level] >= LOG_LEVELS[this.config.level];
64
- }
65
- /**
66
- * Format log entry
67
- */
68
- format(entry) {
69
- if (this.config.json) {
70
- return JSON.stringify(entry);
71
- }
72
- const color = LOG_COLORS[entry.level];
73
- const levelStr = entry.level.toUpperCase().padEnd(5);
74
- const time = entry.timestamp.split('T')[1]?.split('.')[0] || entry.timestamp;
75
- let msg = `${DIM}${time}${RESET} ${color}${BOLD}${levelStr}${RESET} ${entry.message}`;
76
- if (entry.context && Object.keys(entry.context).length > 0) {
77
- msg += ` ${DIM}${JSON.stringify(entry.context)}${RESET}`;
78
- }
79
- if (entry.correlationId) {
80
- msg += ` ${DIM}[${entry.correlationId}]${RESET}`;
81
- }
82
- return msg;
83
- }
84
- /**
85
- * Write log entry
86
- */
87
- log(level, message, context) {
88
- if (!this.shouldLog(level))
89
- return;
90
- const entry = {
91
- timestamp: new Date().toISOString(),
92
- level,
93
- message,
94
- context,
95
- correlationId: this.correlationId,
96
- };
97
- const formatted = this.format(entry);
98
- if (this.config.console) {
99
- // EVERY level sinks to stderr: stdout is the MCP wire when running as
100
- // the stdio server, and a single console.log on the CallTool path
101
- // corrupts JSON-RPC framing (F1 adversarial review: warn/info/debug
102
- // used to route through console.log). Pinned by
103
- // tests/hygiene-security-floor.test.ts + tests/edge-cases.test.ts.
104
- console.error(formatted);
105
- }
106
- if (this.config.file) {
107
- try {
108
- const logPath = join(this.logDir, this.config.file);
109
- appendFileSync(logPath, (this.config.json ? formatted : JSON.stringify(entry)) + '\n');
110
- }
111
- catch {
112
- // Silent fail for file writes
113
- }
114
- }
115
- }
116
- // Log level methods
117
- debug(message, context) {
118
- this.log('debug', message, context);
119
- }
120
- info(message, context) {
121
- this.log('info', message, context);
122
- }
123
- warn(message, context) {
124
- this.log('warn', message, context);
125
- }
126
- error(message, context) {
127
- this.log('error', message, context);
128
- }
129
- /**
130
- * Log with timing measurement
131
- */
132
- time(label, fn) {
133
- const start = performance.now();
134
- try {
135
- const result = fn();
136
- const duration = performance.now() - start;
137
- this.debug(`${label} completed`, { duration: `${duration.toFixed(2)}ms` });
138
- return result;
139
- }
140
- catch (error) {
141
- const duration = performance.now() - start;
142
- this.error(`${label} failed`, { duration: `${duration.toFixed(2)}ms`, error: String(error) });
143
- throw error;
144
- }
145
- }
146
- /**
147
- * Async timing
148
- */
149
- async timeAsync(label, fn) {
150
- const start = performance.now();
151
- try {
152
- const result = await fn();
153
- const duration = performance.now() - start;
154
- this.debug(`${label} completed`, { duration: `${duration.toFixed(2)}ms` });
155
- return result;
156
- }
157
- catch (error) {
158
- const duration = performance.now() - start;
159
- this.error(`${label} failed`, { duration: `${duration.toFixed(2)}ms`, error: String(error) });
160
- throw error;
161
- }
162
- }
163
- /**
164
- * Create a child logger with additional context
165
- */
166
- child(context) {
167
- const child = new WyrmLogger(this.config);
168
- child.correlationId = this.correlationId;
169
- const originalLog = child.log.bind(child);
170
- child.log = (level, message, ctx) => {
171
- originalLog(level, message, { ...context, ...ctx });
172
- };
173
- return child;
174
- }
175
- }
176
- // Default logger instance
177
- let defaultLogger = null;
178
- export function getLogger() {
179
- if (!defaultLogger) {
180
- defaultLogger = new WyrmLogger({
181
- level: process.env.WYRM_LOG_LEVEL || 'info',
182
- console: process.env.WYRM_LOG_CONSOLE !== 'false',
183
- json: process.env.WYRM_LOG_JSON === 'true',
184
- file: 'wyrm.log',
185
- });
186
- }
187
- return defaultLogger;
188
- }
189
- export function createLogger(config) {
190
- return new WyrmLogger(config);
191
- }
192
- // Convenience exports
193
- export const logger = {
194
- debug: (msg, ctx) => getLogger().debug(msg, ctx),
195
- info: (msg, ctx) => getLogger().info(msg, ctx),
196
- warn: (msg, ctx) => getLogger().warn(msg, ctx),
197
- error: (msg, ctx) => getLogger().error(msg, ctx),
198
- };
199
- //# sourceMappingURL=logger.js.map
1
+ import{appendFileSync as h,existsSync as u,mkdirSync as p}from"fs";import{homedir as $}from"os";import{join as f}from"path";const m={debug:0,info:1,warn:2,error:3},w={debug:"\x1B[36m",info:"\x1B[32m",warn:"\x1B[33m",error:"\x1B[31m"},s="\x1B[0m",x="\x1B[1m",g="\x1B[2m";class l{config;correlationId;logDir;constructor(o){this.config={level:o?.level??"info",console:o?.console??!0,json:o?.json??!1,file:o?.file},this.logDir=f($(),".wyrm","logs"),u(this.logDir)||p(this.logDir,{recursive:!0})}setCorrelationId(o){this.correlationId=o}generateCorrelationId(){return this.correlationId=`wyrm-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,this.correlationId}shouldLog(o){return m[o]>=m[this.config.level]}format(o){if(this.config.json)return JSON.stringify(o);const r=w[o.level],n=o.level.toUpperCase().padEnd(5),t=o.timestamp.split("T")[1]?.split(".")[0]||o.timestamp;let e=`${g}${t}${s} ${r}${x}${n}${s} ${o.message}`;return o.context&&Object.keys(o.context).length>0&&(e+=` ${g}${JSON.stringify(o.context)}${s}`),o.correlationId&&(e+=` ${g}[${o.correlationId}]${s}`),e}log(o,r,n){if(!this.shouldLog(o))return;const t={timestamp:new Date().toISOString(),level:o,message:r,context:n,correlationId:this.correlationId},e=this.format(t);if(this.config.console&&console.error(e),this.config.file)try{const a=f(this.logDir,this.config.file);h(a,(this.config.json?e:JSON.stringify(t))+`
2
+ `)}catch{}}debug(o,r){this.log("debug",o,r)}info(o,r){this.log("info",o,r)}warn(o,r){this.log("warn",o,r)}error(o,r){this.log("error",o,r)}time(o,r){const n=performance.now();try{const t=r(),e=performance.now()-n;return this.debug(`${o} completed`,{duration:`${e.toFixed(2)}ms`}),t}catch(t){const e=performance.now()-n;throw this.error(`${o} failed`,{duration:`${e.toFixed(2)}ms`,error:String(t)}),t}}async timeAsync(o,r){const n=performance.now();try{const t=await r(),e=performance.now()-n;return this.debug(`${o} completed`,{duration:`${e.toFixed(2)}ms`}),t}catch(t){const e=performance.now()-n;throw this.error(`${o} failed`,{duration:`${e.toFixed(2)}ms`,error:String(t)}),t}}child(o){const r=new l(this.config);r.correlationId=this.correlationId;const n=r.log.bind(r);return r.log=(t,e,a)=>{n(t,e,{...o,...a})},r}}let d=null;function c(){return d||(d=new l({level:process.env.WYRM_LOG_LEVEL||"info",console:process.env.WYRM_LOG_CONSOLE!=="false",json:process.env.WYRM_LOG_JSON==="true",file:"wyrm.log"})),d}function b(i){return new l(i)}const I={debug:(i,o)=>c().debug(i,o),info:(i,o)=>c().info(i,o),warn:(i,o)=>c().warn(i,o),error:(i,o)=>c().error(i,o)};export{l as WyrmLogger,b as createLogger,c as getLogger,I as logger};