wyrm-mcp 7.3.2 → 7.3.3

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 (184) hide show
  1. package/README.md +8 -14
  2. package/dist/activation.js +59 -1
  3. package/dist/agent-daemon.js +281 -4
  4. package/dist/agent-loop.js +332 -7
  5. package/dist/analytics.js +236 -13
  6. package/dist/attribution.js +49 -1
  7. package/dist/audit.js +457 -2
  8. package/dist/auto-capture.js +138 -3
  9. package/dist/auto-orchestrator.js +325 -1
  10. package/dist/autoconfig.d.ts +50 -0
  11. package/dist/autoconfig.d.ts.map +1 -1
  12. package/dist/autoconfig.js +1115 -39
  13. package/dist/autoconfig.js.map +1 -1
  14. package/dist/buddy-runner.js +109 -1
  15. package/dist/buddy.js +564 -14
  16. package/dist/build-flags.js +15 -1
  17. package/dist/capabilities.js +183 -3
  18. package/dist/capture.js +56 -1
  19. package/dist/causality.js +148 -8
  20. package/dist/cli.js +281 -20
  21. package/dist/cloud/cli.js +541 -5
  22. package/dist/cloud/client.js +221 -1
  23. package/dist/cloud/crypto.js +85 -1
  24. package/dist/cloud/machine-id.js +113 -2
  25. package/dist/cloud/recovery.js +60 -1
  26. package/dist/cloud/sync-engine.js +543 -7
  27. package/dist/cloud-backup.js +579 -5
  28. package/dist/cloud-profile.js +138 -1
  29. package/dist/cloud-sync-entrypoint.js +47 -1
  30. package/dist/cloud-sync.js +309 -2
  31. package/dist/connectors/bridge-source.d.ts +46 -0
  32. package/dist/connectors/bridge-source.d.ts.map +1 -0
  33. package/dist/connectors/bridge-source.js +77 -0
  34. package/dist/connectors/bridge-source.js.map +1 -0
  35. package/dist/connectors/index.d.ts +24 -0
  36. package/dist/connectors/index.d.ts.map +1 -0
  37. package/dist/connectors/index.js +69 -0
  38. package/dist/connectors/index.js.map +1 -0
  39. package/dist/connectors/ingest.d.ts +16 -0
  40. package/dist/connectors/ingest.d.ts.map +1 -0
  41. package/dist/connectors/ingest.js +116 -0
  42. package/dist/connectors/ingest.js.map +1 -0
  43. package/dist/connectors/types.d.ts +99 -0
  44. package/dist/connectors/types.d.ts.map +1 -0
  45. package/dist/connectors/types.js +17 -0
  46. package/dist/connectors/types.js.map +1 -0
  47. package/dist/constellation.js +168 -12
  48. package/dist/content-signature.js +45 -1
  49. package/dist/context-build-budgeted.js +144 -4
  50. package/dist/context-ranking.js +69 -1
  51. package/dist/crypto.js +179 -1
  52. package/dist/daemon-write-endpoint.js +290 -1
  53. package/dist/daemon-writer.js +406 -2
  54. package/dist/database.js +1278 -53
  55. package/dist/deprecations.js +162 -2
  56. package/dist/design.js +141 -13
  57. package/dist/event-replication.js +112 -1
  58. package/dist/events-sse.js +43 -7
  59. package/dist/events.js +238 -6
  60. package/dist/failure-patterns.d.ts +107 -0
  61. package/dist/failure-patterns.d.ts.map +1 -1
  62. package/dist/failure-patterns.js +924 -43
  63. package/dist/failure-patterns.js.map +1 -1
  64. package/dist/federation.js +236 -12
  65. package/dist/goals.js +101 -13
  66. package/dist/golden.js +355 -3
  67. package/dist/handlers/agent.js +165 -4
  68. package/dist/handlers/alias-adapters.js +129 -1
  69. package/dist/handlers/aliases.js +171 -1
  70. package/dist/handlers/audit.js +87 -1
  71. package/dist/handlers/boundary.js +221 -1
  72. package/dist/handlers/capture.js +1114 -73
  73. package/dist/handlers/causality.js +119 -9
  74. package/dist/handlers/cloud.js +382 -85
  75. package/dist/handlers/companion.js +459 -28
  76. package/dist/handlers/datalake.js +187 -7
  77. package/dist/handlers/dispatch-context.js +22 -0
  78. package/dist/handlers/entity.js +256 -25
  79. package/dist/handlers/events.js +335 -16
  80. package/dist/handlers/failure.d.ts.map +1 -1
  81. package/dist/handlers/failure.js +408 -13
  82. package/dist/handlers/failure.js.map +1 -1
  83. package/dist/handlers/goals.js +296 -4
  84. package/dist/handlers/intelligence.js +681 -126
  85. package/dist/handlers/invoicing.js +70 -1
  86. package/dist/handlers/mcpclient.js +137 -6
  87. package/dist/handlers/orchestration.js +125 -40
  88. package/dist/handlers/output-schemas.js +24 -1
  89. package/dist/handlers/presence.js +99 -3
  90. package/dist/handlers/project.js +182 -28
  91. package/dist/handlers/prompts.js +157 -6
  92. package/dist/handlers/quest.js +224 -4
  93. package/dist/handlers/recall.js +237 -13
  94. package/dist/handlers/registry.js +167 -1
  95. package/dist/handlers/resources.js +288 -1
  96. package/dist/handlers/review.js +74 -11
  97. package/dist/handlers/run.js +498 -16
  98. package/dist/handlers/search.js +338 -15
  99. package/dist/handlers/session.js +643 -31
  100. package/dist/handlers/share.js +184 -8
  101. package/dist/handlers/shims.js +464 -1
  102. package/dist/handlers/skill.js +449 -67
  103. package/dist/handlers/survivors.js +120 -1
  104. package/dist/handlers/symbols.js +109 -8
  105. package/dist/handlers/syncops.js +302 -4
  106. package/dist/handlers/types.js +27 -1
  107. package/dist/harvest.js +191 -5
  108. package/dist/hours.js +156 -7
  109. package/dist/http-auth.js +321 -3
  110. package/dist/http-fast.js +1302 -22
  111. package/dist/icons.js +47 -1
  112. package/dist/importers.js +268 -1
  113. package/dist/index.js +840 -2
  114. package/dist/indexer.js +145 -4
  115. package/dist/intelligence.js +261 -31
  116. package/dist/internal-dispatch.js +212 -3
  117. package/dist/keyset.js +110 -1
  118. package/dist/knowledge-graph.js +176 -12
  119. package/dist/license.js +441 -2
  120. package/dist/logger.js +199 -2
  121. package/dist/maintenance.js +148 -2
  122. package/dist/mcp-client.js +262 -6
  123. package/dist/memory-artifacts.js +596 -32
  124. package/dist/migrate-prompt.js +124 -2
  125. package/dist/migrations.d.ts.map +1 -1
  126. package/dist/migrations.js +799 -42
  127. package/dist/migrations.js.map +1 -1
  128. package/dist/performance.js +228 -1
  129. package/dist/presence.js +140 -11
  130. package/dist/priority-embed.js +164 -5
  131. package/dist/providers/embedding-provider.js +196 -1
  132. package/dist/readonly-gate.js +29 -1
  133. package/dist/receipt.js +43 -1
  134. package/dist/rehydration.js +157 -9
  135. package/dist/reindex.js +88 -1
  136. package/dist/render-target.js +544 -21
  137. package/dist/render.js +280 -4
  138. package/dist/repl-guard.js +173 -1
  139. package/dist/replication-daemon-entrypoint.js +31 -1
  140. package/dist/replication-daemon.js +262 -2
  141. package/dist/rerank.js +142 -1
  142. package/dist/resilience.js +591 -1
  143. package/dist/reverse-bridge.js +360 -5
  144. package/dist/security.js +244 -1
  145. package/dist/session-seen.js +51 -3
  146. package/dist/setup.js +260 -1
  147. package/dist/skill-author.js +168 -5
  148. package/dist/spec-kit.js +191 -1
  149. package/dist/sqlite-busy.js +154 -1
  150. package/dist/statusline.js +315 -11
  151. package/dist/sub-agent.js +262 -13
  152. package/dist/summarizer.js +139 -13
  153. package/dist/symbols.js +283 -7
  154. package/dist/sync.js +359 -5
  155. package/dist/tasks-dispatch.js +84 -1
  156. package/dist/tasks.js +282 -1
  157. package/dist/token-budget.js +143 -1
  158. package/dist/tool-analytics.js +129 -7
  159. package/dist/tool-annotations.js +365 -1
  160. package/dist/tool-manifest-v2.json +1 -1
  161. package/dist/tool-manifest.json +1 -1
  162. package/dist/tool-profiles.js +75 -1
  163. package/dist/trace-harvest.js +244 -6
  164. package/dist/types.js +30 -1
  165. package/dist/ui-dashboard.js +50 -41
  166. package/dist/ulid.js +81 -1
  167. package/dist/usage-tracker.js +66 -1
  168. package/dist/validate.js +129 -1
  169. package/dist/vault.js +534 -1
  170. package/dist/vector-init.js +67 -1
  171. package/dist/vectors.js +184 -3
  172. package/dist/version-check.js +136 -4
  173. package/dist/visibility.js +155 -19
  174. package/dist/wyrm-cli.js +2845 -101
  175. package/dist/wyrm-cli.js.map +1 -1
  176. package/dist/wyrm-guard.d.ts.map +1 -1
  177. package/dist/wyrm-guard.js +475 -14
  178. package/dist/wyrm-guard.js.map +1 -1
  179. package/dist/wyrm-loop.js +150 -3
  180. package/dist/wyrm-manifest.json +1 -1
  181. package/dist/wyrm-statusline-daemon.js +11 -1
  182. package/dist/wyrm-statusline.js +56 -4
  183. package/dist/wyrm-ui.js +77 -9
  184. package/package.json +1 -1
@@ -1,9 +1,119 @@
1
- import{TOOL_ANNOTATIONS as u}from"../tool-annotations.js";import{staleFoundationWarning as f}from"../causality.js";const $=[{name:"wyrm_decided_because",description:"Use to link a choice or decision to the assumption or evidence it was based on - records a causal edge (truth/quest/artifact/session) so stale foundations invalidate downstream.",inputSchema:{type:"object",properties:{projectPath:{type:"string"},from_kind:{type:"string",enum:["truth","artifact","quest","session","data"]},from_id:{type:"number"},to_kind:{type:"string",enum:["truth","artifact","quest","session","data"]},to_id:{type:"number"},relation:{type:"string",enum:["because_of","supersedes","refutes","verifies"],description:"Default because_of"},rationale:{type:"string",description:"Why"},confidence:{type:"number",description:"0-1, default 1.0"}},required:["projectPath","from_kind","from_id","to_kind","to_id"]},annotations:u.wyrm_decided_because,aliases:[],handler:async(a,d)=>{const{causality:r,db:o}=d,{projectPath:i,from_kind:e,from_id:s,to_kind:t,to_id:c,relation:n,rationale:m,confidence:y}=a,p=o.getProject(i);if(!p)return{content:[{type:"text",text:"Project not found"}],isError:!0};const l=r.link({project_id:p.id,from_kind:e,from_id:s,to_kind:t,to_id:c,relation:n,rationale:m,confidence:y});return{content:[{type:"text",text:`\u{F115D} Edge #${l.id} \u2014 ${e}#${s} ${l.relation} ${t}#${c}`}]}}},{name:"wyrm_decision_downstream",description:"List all decisions that depend on the given source. Use before invalidating a truth to see the blast radius.",inputSchema:{type:"object",properties:{kind:{type:"string",enum:["truth","artifact","quest","session","data"]},id:{type:"number"}},required:["kind","id"]},annotations:u.wyrm_decision_downstream,aliases:[],handler:async(a,d)=>{const{causality:r}=d,{kind:o,id:i}=a,e=r.downstreamOf(o,i);if(e.length===0)return{content:[{type:"text",text:`\u{F115D} No active dependents on ${o}#${i}.`}]};const s=e.map(t=>`\u2022 ${t.from_kind}#${t.from_id} (${t.relation}, conf ${t.confidence})${t.rationale?` \u2014 ${t.rationale}`:""}`).join(`
2
- `);return{content:[{type:"text",text:`\u{F115D} ${e.length} dependent(s):
3
- ${s}`}]}}},{name:"wyrm_decision_upstream",description:"List all sources a decision was based on (incoming edges). Walks the 'why did we choose this?' chain.",inputSchema:{type:"object",properties:{kind:{type:"string",enum:["truth","artifact","quest","session","data"]},id:{type:"number"}},required:["kind","id"]},annotations:u.wyrm_decision_upstream,aliases:[],handler:async(a,d)=>{const{causality:r}=d,{kind:o,id:i}=a,e=r.upstreamOf(o,i),s=f(r.staleFoundations(o,i)),t=s?`${s}
4
-
5
- `:"";if(e.length===0)return{content:[{type:"text",text:`${t}\u{F115D} No recorded basis for ${o}#${i}.`}]};const c=e.map(n=>`\u2022 ${n.to_kind}#${n.to_id} (${n.relation}, conf ${n.confidence})${n.rationale?` \u2014 ${n.rationale}`:""}`).join(`
6
- `);return{content:[{type:"text",text:`${t}\u{F115D} ${e.length} basis edge(s):
7
- ${c}`}]}}},{name:"wyrm_decision_invalidate",description:"Cascade-invalidate all decisions downstream of a source (recursive). Use when a foundational truth turns out to be wrong. Returns the set of affected (kind, id) pairs.",inputSchema:{type:"object",properties:{kind:{type:"string",enum:["truth","artifact","quest","session","data"]},id:{type:"number"},reason:{type:"string",description:"Why the source was invalidated"}},required:["kind","id"]},annotations:u.wyrm_decision_invalidate,aliases:[],handler:async(a,d)=>{const{causality:r}=d,o=a||{},{kind:i,id:e,reason:s}=a,t=r.invalidateDownstream(i,e,s);if(t.invalidated===0)return{content:[{type:"text",text:`\u{F115D} No dependents to invalidate from ${i}#${e}.`}]};const c=t.affected.map(n=>`\u2022 ${n.kind}#${n.id} (edge #${n.edge_id})`).join(`
8
- `);return{content:[{type:"text",text:`\u{F115D} Invalidated ${t.invalidated} downstream decision(s):
9
- ${c}`}]}}}];export{$ as causalityToolSpecs};
1
+ /**
2
+ * @copyright 2026 Ghost Protocol (Pvt) Ltd.
3
+ * @license Proprietary (c) 2026 Ghost Protocol (Pvt) Ltd. All rights reserved. See LICENSE.
4
+ */
5
+ import { TOOL_ANNOTATIONS } from "../tool-annotations.js";
6
+ import { staleFoundationWarning } from "../causality.js";
7
+ export const causalityToolSpecs = [
8
+ {
9
+ name: "wyrm_decided_because",
10
+ description: "Use to link a choice or decision to the assumption or evidence it was based on - records a causal edge (truth/quest/artifact/session) so stale foundations invalidate downstream.",
11
+ inputSchema: {
12
+ type: "object",
13
+ properties: {
14
+ projectPath: { type: "string" },
15
+ from_kind: { type: "string", enum: ["truth", "artifact", "quest", "session", "data"] },
16
+ from_id: { type: "number" },
17
+ to_kind: { type: "string", enum: ["truth", "artifact", "quest", "session", "data"] },
18
+ to_id: { type: "number" },
19
+ relation: { type: "string", enum: ["because_of", "supersedes", "refutes", "verifies"], description: "Default because_of" },
20
+ rationale: { type: "string", description: "Why" },
21
+ confidence: { type: "number", description: "0-1, default 1.0" },
22
+ },
23
+ required: ["projectPath", "from_kind", "from_id", "to_kind", "to_id"],
24
+ },
25
+ annotations: TOOL_ANNOTATIONS["wyrm_decided_because"],
26
+ aliases: [],
27
+ handler: async (args, ctx) => {
28
+ const { causality, db } = ctx;
29
+ const { projectPath, from_kind, from_id, to_kind, to_id, relation, rationale, confidence, } = args;
30
+ const project = db.getProject(projectPath);
31
+ if (!project)
32
+ return { content: [{ type: "text", text: "Project not found" }], isError: true };
33
+ const edge = causality.link({
34
+ project_id: project.id,
35
+ from_kind, from_id, to_kind, to_id, relation, rationale, confidence,
36
+ });
37
+ return { content: [{ type: "text", text: `󱅝 Edge #${edge.id} — ${from_kind}#${from_id} ${edge.relation} ${to_kind}#${to_id}` }] };
38
+ },
39
+ },
40
+ {
41
+ name: "wyrm_decision_downstream",
42
+ description: "List all decisions that depend on the given source. Use before invalidating a truth to see the blast radius.",
43
+ inputSchema: {
44
+ type: "object",
45
+ properties: {
46
+ kind: { type: "string", enum: ["truth", "artifact", "quest", "session", "data"] },
47
+ id: { type: "number" },
48
+ },
49
+ required: ["kind", "id"],
50
+ },
51
+ annotations: TOOL_ANNOTATIONS["wyrm_decision_downstream"],
52
+ aliases: [],
53
+ handler: async (args, ctx) => {
54
+ const { causality } = ctx;
55
+ const { kind, id } = args;
56
+ const edges = causality.downstreamOf(kind, id);
57
+ if (edges.length === 0) {
58
+ return { content: [{ type: "text", text: `󱅝 No active dependents on ${kind}#${id}.` }] };
59
+ }
60
+ const text = edges.map(e => `• ${e.from_kind}#${e.from_id} (${e.relation}, conf ${e.confidence})${e.rationale ? ` — ${e.rationale}` : ''}`).join('\n');
61
+ return { content: [{ type: "text", text: `󱅝 ${edges.length} dependent(s):\n${text}` }] };
62
+ },
63
+ },
64
+ {
65
+ name: "wyrm_decision_upstream",
66
+ description: "List all sources a decision was based on (incoming edges). Walks the 'why did we choose this?' chain.",
67
+ inputSchema: {
68
+ type: "object",
69
+ properties: {
70
+ kind: { type: "string", enum: ["truth", "artifact", "quest", "session", "data"] },
71
+ id: { type: "number" },
72
+ },
73
+ required: ["kind", "id"],
74
+ },
75
+ annotations: TOOL_ANNOTATIONS["wyrm_decision_upstream"],
76
+ aliases: [],
77
+ handler: async (args, ctx) => {
78
+ const { causality } = ctx;
79
+ const { kind, id } = args;
80
+ const edges = causality.upstreamOf(kind, id);
81
+ // Warn-at-recall: surface any foundation that has gone stale (superseded
82
+ // / refuted) so the agent RE-EVALUATES before relying on this decision.
83
+ const warn = staleFoundationWarning(causality.staleFoundations(kind, id));
84
+ const banner = warn ? `${warn}\n\n` : '';
85
+ if (edges.length === 0) {
86
+ return { content: [{ type: "text", text: `${banner}󱅝 No recorded basis for ${kind}#${id}.` }] };
87
+ }
88
+ const text = edges.map(e => `• ${e.to_kind}#${e.to_id} (${e.relation}, conf ${e.confidence})${e.rationale ? ` — ${e.rationale}` : ''}`).join('\n');
89
+ return { content: [{ type: "text", text: `${banner}󱅝 ${edges.length} basis edge(s):\n${text}` }] };
90
+ },
91
+ },
92
+ {
93
+ name: "wyrm_decision_invalidate",
94
+ description: "Cascade-invalidate all decisions downstream of a source (recursive). Use when a foundational truth turns out to be wrong. Returns the set of affected (kind, id) pairs.",
95
+ inputSchema: {
96
+ type: "object",
97
+ properties: {
98
+ kind: { type: "string", enum: ["truth", "artifact", "quest", "session", "data"] },
99
+ id: { type: "number" },
100
+ reason: { type: "string", description: "Why the source was invalidated" },
101
+ },
102
+ required: ["kind", "id"],
103
+ },
104
+ annotations: TOOL_ANNOTATIONS["wyrm_decision_invalidate"],
105
+ aliases: [],
106
+ handler: async (args, ctx) => {
107
+ const { causality } = ctx;
108
+ const a = args || {};
109
+ const { kind, id, reason } = args;
110
+ const result = causality.invalidateDownstream(kind, id, reason);
111
+ if (result.invalidated === 0) {
112
+ return { content: [{ type: "text", text: `󱅝 No dependents to invalidate from ${kind}#${id}.` }] };
113
+ }
114
+ const text = result.affected.map(a => `• ${a.kind}#${a.id} (edge #${a.edge_id})`).join('\n');
115
+ return { content: [{ type: "text", text: `󱅝 Invalidated ${result.invalidated} downstream decision(s):\n${text}` }] };
116
+ },
117
+ },
118
+ ];
119
+ //# sourceMappingURL=causality.js.map
@@ -1,85 +1,382 @@
1
- import{TOOL_ANNOTATIONS as u}from"../tool-annotations.js";import{CloudSyncDaemon as m}from"../cloud-sync.js";import{getCrypto as h,initializeCrypto as k}from"../crypto.js";import{activateLicense as f,getLicenseInfo as b,getTier as x,hasFeature as d}from"../license.js";const S=[{name:"wyrm_license",description:"View current license status, tier, and available features",inputSchema:{type:"object",properties:{}},annotations:u.wyrm_license,aliases:[],handler:async(o,i)=>{const{cachedResponse:a,sync:r}=i,c=o||{},n=b(),e=x();let t=`\u{F115D} **Wyrm License Status**
2
-
3
- `;if(t+=`- **Tier:** ${e.charAt(0).toUpperCase()+e.slice(1)}
4
- `,n.valid&&n.key){t+=`- **Key:** ${n.key}
5
- `,t+=`- **Issued To:** ${n.issuedTo||"Unknown"}
6
- `,t+=`- **Expires:** ${n.expiresAt?new Date(n.expiresAt).toLocaleDateString():"Never"}
7
- `,t+=`- **Status:** \u2713 Valid
8
-
9
- `,t+=`### Features
10
- `;for(const l of n.features)t+=`- \u2713 ${l}
11
- `}else t+=`- **Status:** Free tier (no license key)
12
-
13
- `,t+=`### Free Features
14
- `,t+=`- \u2713 Local SQLite storage
15
- `,t+=`- \u2713 32 MCP tools
16
- `,t+=`- \u2713 FTS5 search
17
- `,t+=`- \u2713 File-based sync
18
-
19
- `,t+=`### Upgrade to Pro
20
- `,t+=`- Analytics dashboard & cost tracking
21
- `,t+=`- Cloud backup to R2
22
- `,t+=`- Encryption at rest
23
- `,t+=`- Priority support
24
-
25
- `,t+=`### Already have a license?
26
- `,t+="Activate it \u2014 paste the license JSON into the `wyrm_activate` tool, or save it to `~/.wyrm/license.json` and restart Wyrm.\n",t+="Manage or download your license at https://account.ghosts.lk \xB7 buy one at https://ghosts.lk/wyrm.";return a(t)}},{name:"wyrm_activate",description:"Activate a license key to unlock Pro/Team/Enterprise features",inputSchema:{type:"object",properties:{license:{type:"string",description:"License JSON string (signed license from Ghost Protocol)"}},required:["license"]},annotations:u.wyrm_activate,aliases:[],handler:async(o,i)=>{const a=o?.license;if(!a)throw new Error("License JSON string required");try{const r=f(a);return r.valid?{content:[{type:"text",text:`\u{F115D} \u2713 License activated!
27
-
28
- - **Tier:** ${r.tier}
29
- - **Features:** ${r.features.join(", ")}
30
-
31
- Restart Wyrm to apply all features.`}]}:{content:[{type:"text",text:`\u{F115D} \u2717 License activation failed: ${r.error||"unknown error"}`}],isError:!0}}catch{return{content:[{type:"text",text:"\u{F115D} \u2717 Invalid license format. Please verify your license key."}],isError:!0}}}},{name:"wyrm_analytics_dashboard",description:"View usage analytics dashboard \u2014 tool calls, tokens, costs, cache efficiency (Pro+)",inputSchema:{type:"object",properties:{days:{type:"number",description:"Number of days to analyze (default: 30)"}}},annotations:u.wyrm_analytics_dashboard,aliases:[],handler:async(o,i)=>{const{analytics:a}=i,r=o||{};if(!d("analytics"))return{content:[{type:"text",text:`\u{F115D} Analytics requires a Pro license or higher.
32
- Visit https://ghosts.lk/wyrm to upgrade.`}]};const c=o?.days||30,n=a.dashboard(c);let e=`\u{F115D} **Usage Analytics** (last ${c} days)
33
-
34
- `;if(e+=`### Overview
35
- `,e+=`- **Total Tool Calls:** ${n.summary.total_calls.toLocaleString()}
36
- `,e+=`- **Unique Tools Used:** ${n.summary.unique_tools}
37
- `,e+=`- **Total Tokens (In):** ${n.summary.total_tokens_in.toLocaleString()}
38
- `,e+=`- **Total Tokens (Out):** ${n.summary.total_tokens_out.toLocaleString()}
39
- `,e+=`- **Cache Hit Rate:** ${(n.summary.cache_hit_rate*100).toFixed(1)}%
40
- `,e+=`- **Estimated Cost:** $${n.summary.estimated_cost_usd.toFixed(4)}
41
-
42
- `,n.top_tools.length>0){e+=`### Top Tools
43
- `;for(const t of n.top_tools.slice(0,10))e+=`- **${t.tool}** \u2014 ${t.calls} calls, ${t.tokens.toLocaleString()} tokens
44
- `;e+=`
45
- `}if(n.daily.length>0){e+=`### Recent Activity
46
- `;for(const t of n.daily.slice(-7))e+=`- ${t.date}: ${t.calls} calls, ${t.tokens.toLocaleString()} tokens
47
- `}return{content:[{type:"text",text:e}]}}},{name:"wyrm_cost_report",description:"View estimated API cost report for current billing period (Pro+)",inputSchema:{type:"object",properties:{period:{type:"string",description:"Period in YYYY-MM format (default: current month)"}}},annotations:u.wyrm_cost_report,aliases:[],handler:async(o,i)=>{const{analytics:a}=i,r=o||{};if(!d("analytics"))return{content:[{type:"text",text:`\u{F115D} Cost reports require a Pro license or higher.
48
- Visit https://ghosts.lk/wyrm to upgrade.`}]};const c=o?.period,n=a.costReport(c);let e=`\u{F115D} **Cost Report** \u2014 ${n.period}
49
-
50
- `;if(e+=`- **Total Cost:** $${n.total_cost_usd.toFixed(4)}
51
- `,e+=`- **Projected Monthly:** $${n.projected_monthly_usd.toFixed(2)}
52
-
53
- `,n.tools.length>0){e+=`### By Tool
54
- `;for(const t of n.tools.slice(0,10))e+=`- **${t.tool}** \u2014 ${t.calls} calls, $${t.cost_usd.toFixed(4)}
55
- `}return{content:[{type:"text",text:e}]}}},{name:"wyrm_cloud_backup",description:"Backup Wyrm database to encrypted cloud storage (Pro+)",inputSchema:{type:"object",properties:{action:{type:"string",description:"Action: backup, restore, list, prune",enum:["backup","restore","list","prune"]},backupKey:{type:"string",description:"Backup key for restore (from list)"},keepCount:{type:"number",description:"Number of backups to keep when pruning (default: 10)"}},required:["action"]},annotations:u.wyrm_cloud_backup,aliases:[],handler:async(o,i)=>{const{cloudBackup:a,db:r}=i,c=o||{};if(!d("cloud_backup"))return{content:[{type:"text",text:`\u{F115D} Cloud backup requires a Pro license or higher.
56
- Visit https://ghosts.lk/wyrm to upgrade.`}]};const n=o?.action;switch(n){case"backup":{const e=await a.backup(r.getDatabasePath()),t=e.metadata;return{content:[{type:"text",text:`\u{F115D} \u2713 Backup complete!
57
-
58
- - **Key:** ${e.key}
59
- - **Size:** ${(t.db_size/1024).toFixed(1)} KB \u2192 ${(t.compressed_size/1024).toFixed(1)} KB compressed
60
- - **Encrypted:** ${t.encrypted?"Yes":"No"}
61
- - **Checksum:** ${t.checksum.slice(0,16)}...`}]}}case"restore":{const e=o?.backupKey;if(!e)throw new Error("backupKey required for restore");const t=await a.restore(e,r.getDatabasePath());return{content:[{type:"text",text:`\u{F115D} \u2713 Restore complete!
62
-
63
- - **Restored:** ${t.restored?"Yes":"No"}
64
- - **Size:** ${(t.size/1024).toFixed(1)} KB
65
- - **Previous DB backed up to:** ${r.getDatabasePath()}.bak
66
-
67
- \u26A0\uFE0F Restart Wyrm to use the restored database.`}]}}case"list":{const e=await a.listBackups();if(e.length===0)return{content:[{type:"text",text:"\u{F115D} No backups found."}]};let t=`\u{F115D} **Cloud Backups** (${e.length} found)
68
-
69
- `;for(const l of e)t+=`- **${l.timestamp}** \u2014 ${(l.db_size/1024).toFixed(1)} KB, encrypted: ${l.encrypted}, machine: ${l.machine_id.slice(0,8)}...
70
- `;return{content:[{type:"text",text:t}]}}case"prune":{const e=o?.keepCount||10;return{content:[{type:"text",text:`\u{F115D} \u2713 Pruned ${(await a.pruneBackups(e)).deleted} old backup(s). Kept ${e} most recent.`}]}}default:return{content:[{type:"text",text:`Unknown backup action: ${n}. Use: backup, restore, list, prune`}],isError:!0}}}},{name:"wyrm_cloud_sync",description:"Start, stop, or check the cloud-sync daemon \u2014 Phase 2 multi-device sync. Periodically backs up the DB to R2 and restores on startup if a newer snapshot exists. Last-write-wins by mtime.",inputSchema:{type:"object",properties:{action:{type:"string",description:"Action: start, stop, restart, status, force-sync",enum:["start","stop","restart","status","force-sync"]},interval_minutes:{type:"number",description:"Sync interval (default: 10)"},keep_count:{type:"number",description:"Snapshots to retain after pruning (default: 20)"}},required:["action"]},annotations:u.wyrm_cloud_sync,aliases:[],handler:async(o,i)=>{const{cloudBackup:a,cloudSyncManager:r,db:c,sync:n}=i,e=o||{};if(!d("cloud_backup"))return{content:[{type:"text",text:`\u{F115D} Cloud sync requires a Pro license or higher.
71
- Visit https://ghosts.lk/wyrm to upgrade.`}]};const t=o?.action,l=o?.interval_minutes,y=o?.keep_count;switch(t){case"start":{const s=r.start({interval_minutes:l,keep_count:y});return s.ok?{content:[{type:"text",text:`\u{F115D} \u2713 cloud-sync daemon started (pid ${s.pid}). Logs: ~/.wyrm/wyrm-cloud-sync.log`}]}:{content:[{type:"text",text:`\u{F115D} cloud-sync: ${s.reason??"failed to start"}${s.pid?` (pid ${s.pid})`:""}`}],isError:!s.pid}}case"stop":{const s=r.stop();return{content:[{type:"text",text:s.ok?"\u{F115D} \u2713 cloud-sync daemon stopped":`\u{F115D} cloud-sync: ${s.reason??"failed to stop"}`}],isError:!s.ok}}case"restart":{r.stop(),await new Promise(p=>{setTimeout(p,500)});const s=r.start({interval_minutes:l,keep_count:y});return{content:[{type:"text",text:s.ok?`\u{F115D} \u2713 cloud-sync daemon restarted (pid ${s.pid})`:`\u{F115D} cloud-sync restart failed: ${s.reason??"unknown"}`}],isError:!s.ok}}case"status":{const s=r.status(),p=["\u{F115D} **Cloud Sync Status**"];return p.push(`- Running: ${s.running?"\u2713 yes":"\u2717 no"}`),s.pid&&p.push(`- PID: ${s.pid}`),s.state&&(s.state.last_uploaded_ts&&p.push(`- Last upload: ${new Date(s.state.last_uploaded_ts).toISOString()}${s.state.last_uploaded_key?` (${s.state.last_uploaded_key})`:""}`),s.state.last_restored_key&&p.push(`- Last restore: ${s.state.last_restored_key}`),s.state.last_check_ts&&p.push(`- Last check: ${new Date(s.state.last_check_ts).toISOString()}`),p.push(`- Machine: ${s.state.machine}`)),{content:[{type:"text",text:p.join(`
72
- `)}]}}case"force-sync":{const p=await new m(a,c.getDatabasePath()).tick();return{content:[{type:"text",text:`\u{F115D} force-sync: ${JSON.stringify(p,null,2)}`}]}}default:return{content:[{type:"text",text:`Unknown cloud-sync action: ${t}. Use: start, stop, restart, status, force-sync`}],isError:!0}}}},{name:"wyrm_encrypt_setup",description:"Set up or check encryption status for sensitive data (Pro+)",inputSchema:{type:"object",properties:{action:{type:"string",description:"Action: status, enable, test",enum:["status","enable","test"]},password:{type:"string",description:"Encryption password (min 8 chars, required for enable)"}},required:["action"]},annotations:u.wyrm_encrypt_setup,aliases:[],handler:async(o,i)=>{const a=o||{};if(!d("encryption"))return{content:[{type:"text",text:`\u{F115D} Encryption setup requires a Pro license or higher.
73
- Visit https://ghosts.lk/wyrm to upgrade.`}]};const r=o?.action,c=h();switch(r){case"status":{const n=c.isEnabled();let e=`\u{F115D} **Encryption Status**
74
-
75
- `;return e+=`- **Enabled:** ${n?"\u2713 Yes":"\u2717 No"}
76
- `,e+=`- **Algorithm:** AES-256-GCM
77
- `,n?e+=`- **Status:** Active \u2014 new data is encrypted at rest
78
- `:e+='\nTo enable, run: `wyrm_encrypt_setup` with action "enable" and a strong password.\n',{content:[{type:"text",text:e}]}}case"enable":{const n=o?.password;return!n||n.length<8?{content:[{type:"text",text:"\u{F115D} Password must be at least 8 characters."}],isError:!0}:(k(n),{content:[{type:"text",text:`\u{F115D} \u2713 Encryption enabled!
79
-
80
- - **Algorithm:** AES-256-GCM
81
- - **Key derived from:** your password (PBKDF2)
82
-
83
- \u26A0\uFE0F **Store your password safely.** Lost passwords cannot be recovered.
84
- New data lake entries will be encrypted at rest.`}]})}case"test":{if(!c.isEnabled())return{content:[{type:"text",text:'\u{F115D} Encryption not enabled. Run with action "enable" first.'}]};const n="Wyrm encryption test "+Date.now(),e=c.encrypt(n),t=c.decrypt(e);return{content:[{type:"text",text:`\u{F115D} Encryption test: ${t===n?"\u2713 PASSED":"\u2717 FAILED"}
85
- - Encrypted ${n.length} chars \u2192 decrypted back to ${t.length} chars`}]}}default:return{content:[{type:"text",text:`Unknown encrypt action: ${r}. Use: status, enable, test`}],isError:!0}}}}];export{S as cloudToolSpecs};
1
+ /**
2
+ * @copyright 2026 Ghost Protocol (Pvt) Ltd.
3
+ * @license Proprietary — (c) 2026 Ghost Protocol (Pvt) Ltd. All rights reserved. See LICENSE.
4
+ */
5
+ import { TOOL_ANNOTATIONS } from "../tool-annotations.js";
6
+ import { CloudSyncDaemon } from "../cloud-sync.js";
7
+ import { getCrypto, initializeCrypto } from "../crypto.js";
8
+ import { activateLicense, getLicenseInfo, getTier, hasFeature } from "../license.js";
9
+ export const cloudToolSpecs = [
10
+ {
11
+ name: "wyrm_license",
12
+ description: "View current license status, tier, and available features",
13
+ inputSchema: {
14
+ type: "object",
15
+ properties: {},
16
+ },
17
+ annotations: TOOL_ANNOTATIONS["wyrm_license"],
18
+ aliases: [],
19
+ handler: async (args, ctx) => {
20
+ const { cachedResponse, sync } = ctx;
21
+ const a = args || {};
22
+ const license = getLicenseInfo();
23
+ const tier = getTier();
24
+ let text = `󱅝 **Wyrm License Status**\n\n`;
25
+ text += `- **Tier:** ${tier.charAt(0).toUpperCase() + tier.slice(1)}\n`;
26
+ if (license.valid && license.key) {
27
+ text += `- **Key:** ${license.key}\n`;
28
+ text += `- **Issued To:** ${license.issuedTo || 'Unknown'}\n`;
29
+ text += `- **Expires:** ${license.expiresAt ? new Date(license.expiresAt).toLocaleDateString() : 'Never'}\n`;
30
+ text += `- **Status:** ✓ Valid\n\n`;
31
+ text += `### Features\n`;
32
+ for (const f of license.features) {
33
+ text += `- ✓ ${f}\n`;
34
+ }
35
+ }
36
+ else {
37
+ text += `- **Status:** Free tier (no license key)\n\n`;
38
+ text += `### Free Features\n`;
39
+ text += `- Local SQLite storage\n`;
40
+ text += `- ✓ 32 MCP tools\n`;
41
+ text += `- ✓ FTS5 search\n`;
42
+ text += `- ✓ File-based sync\n\n`;
43
+ text += `### Upgrade to Pro\n`;
44
+ text += `- Analytics dashboard & cost tracking\n`;
45
+ text += `- Cloud backup to R2\n`;
46
+ text += `- Encryption at rest\n`;
47
+ text += `- Priority support\n\n`;
48
+ text += `### Already have a license?\n`;
49
+ text += `Activate it — paste the license JSON into the \`wyrm_activate\` tool, or save it to \`~/.wyrm/license.json\` and restart Wyrm.\n`;
50
+ text += `Manage or download your license at https://account.ghosts.lk · buy one at https://ghosts.lk/wyrm.`;
51
+ }
52
+ return cachedResponse(text);
53
+ },
54
+ },
55
+ {
56
+ name: "wyrm_activate",
57
+ description: "Activate a license key to unlock Pro/Team/Enterprise features",
58
+ inputSchema: {
59
+ type: "object",
60
+ properties: {
61
+ license: { type: "string", description: "License JSON string (signed license from Ghost Protocol)" },
62
+ },
63
+ required: ["license"],
64
+ },
65
+ annotations: TOOL_ANNOTATIONS["wyrm_activate"],
66
+ aliases: [],
67
+ handler: async (args, ctx) => {
68
+ const licenseStr = args?.license;
69
+ if (!licenseStr)
70
+ throw new Error("License JSON string required");
71
+ try {
72
+ const result = activateLicense(licenseStr);
73
+ if (result.valid) {
74
+ return {
75
+ content: [{ type: "text", text: `󱅝 ✓ License activated!\n\n- **Tier:** ${result.tier}\n- **Features:** ${result.features.join(', ')}\n\nRestart Wyrm to apply all features.` }],
76
+ };
77
+ }
78
+ else {
79
+ return {
80
+ content: [{ type: "text", text: `󱅝 ✗ License activation failed: ${result.error || 'unknown error'}` }],
81
+ isError: true,
82
+ };
83
+ }
84
+ }
85
+ catch (e) {
86
+ return {
87
+ content: [{ type: "text", text: `󱅝 ✗ Invalid license format. Please verify your license key.` }],
88
+ isError: true,
89
+ };
90
+ }
91
+ },
92
+ },
93
+ {
94
+ name: "wyrm_analytics_dashboard",
95
+ description: "View usage analytics dashboard — tool calls, tokens, costs, cache efficiency (Pro+)",
96
+ inputSchema: {
97
+ type: "object",
98
+ properties: {
99
+ days: { type: "number", description: "Number of days to analyze (default: 30)" },
100
+ },
101
+ },
102
+ annotations: TOOL_ANNOTATIONS["wyrm_analytics_dashboard"],
103
+ aliases: [],
104
+ handler: async (args, ctx) => {
105
+ const { analytics } = ctx;
106
+ const a = args || {};
107
+ if (!hasFeature('analytics')) {
108
+ return {
109
+ content: [{ type: "text", text: `󱅝 Analytics requires a Pro license or higher.\nVisit https://ghosts.lk/wyrm to upgrade.` }],
110
+ };
111
+ }
112
+ const days = args?.days || 30;
113
+ const dashboard = analytics.dashboard(days);
114
+ let text = `󱅝 **Usage Analytics** (last ${days} days)\n\n`;
115
+ text += `### Overview\n`;
116
+ text += `- **Total Tool Calls:** ${dashboard.summary.total_calls.toLocaleString()}\n`;
117
+ text += `- **Unique Tools Used:** ${dashboard.summary.unique_tools}\n`;
118
+ text += `- **Total Tokens (In):** ${dashboard.summary.total_tokens_in.toLocaleString()}\n`;
119
+ text += `- **Total Tokens (Out):** ${dashboard.summary.total_tokens_out.toLocaleString()}\n`;
120
+ text += `- **Cache Hit Rate:** ${(dashboard.summary.cache_hit_rate * 100).toFixed(1)}%\n`;
121
+ text += `- **Estimated Cost:** $${dashboard.summary.estimated_cost_usd.toFixed(4)}\n\n`;
122
+ if (dashboard.top_tools.length > 0) {
123
+ text += `### Top Tools\n`;
124
+ for (const t of dashboard.top_tools.slice(0, 10)) {
125
+ text += `- **${t.tool}** — ${t.calls} calls, ${t.tokens.toLocaleString()} tokens\n`;
126
+ }
127
+ text += `\n`;
128
+ }
129
+ if (dashboard.daily.length > 0) {
130
+ text += `### Recent Activity\n`;
131
+ for (const d of dashboard.daily.slice(-7)) {
132
+ text += `- ${d.date}: ${d.calls} calls, ${d.tokens.toLocaleString()} tokens\n`;
133
+ }
134
+ }
135
+ return { content: [{ type: "text", text }] };
136
+ },
137
+ },
138
+ {
139
+ name: "wyrm_cost_report",
140
+ description: "View estimated API cost report for current billing period (Pro+)",
141
+ inputSchema: {
142
+ type: "object",
143
+ properties: {
144
+ period: { type: "string", description: "Period in YYYY-MM format (default: current month)" },
145
+ },
146
+ },
147
+ annotations: TOOL_ANNOTATIONS["wyrm_cost_report"],
148
+ aliases: [],
149
+ handler: async (args, ctx) => {
150
+ const { analytics } = ctx;
151
+ const a = args || {};
152
+ if (!hasFeature('analytics')) {
153
+ return {
154
+ content: [{ type: "text", text: `󱅝 Cost reports require a Pro license or higher.\nVisit https://ghosts.lk/wyrm to upgrade.` }],
155
+ };
156
+ }
157
+ const period = args?.period;
158
+ const report = analytics.costReport(period);
159
+ let text = `󱅝 **Cost Report** — ${report.period}\n\n`;
160
+ text += `- **Total Cost:** $${report.total_cost_usd.toFixed(4)}\n`;
161
+ text += `- **Projected Monthly:** $${report.projected_monthly_usd.toFixed(2)}\n\n`;
162
+ if (report.tools.length > 0) {
163
+ text += `### By Tool\n`;
164
+ for (const t of report.tools.slice(0, 10)) {
165
+ text += `- **${t.tool}** — ${t.calls} calls, $${t.cost_usd.toFixed(4)}\n`;
166
+ }
167
+ }
168
+ return { content: [{ type: "text", text }] };
169
+ },
170
+ },
171
+ {
172
+ name: "wyrm_cloud_backup",
173
+ description: "Backup Wyrm database to encrypted cloud storage (Pro+)",
174
+ inputSchema: {
175
+ type: "object",
176
+ properties: {
177
+ action: { type: "string", description: "Action: backup, restore, list, prune", enum: ["backup", "restore", "list", "prune"] },
178
+ backupKey: { type: "string", description: "Backup key for restore (from list)" },
179
+ keepCount: { type: "number", description: "Number of backups to keep when pruning (default: 10)" },
180
+ },
181
+ required: ["action"],
182
+ },
183
+ annotations: TOOL_ANNOTATIONS["wyrm_cloud_backup"],
184
+ aliases: [],
185
+ handler: async (args, ctx) => {
186
+ const { cloudBackup, db } = ctx;
187
+ const a = args || {};
188
+ if (!hasFeature('cloud_backup')) {
189
+ return {
190
+ content: [{ type: "text", text: `󱅝 Cloud backup requires a Pro license or higher.\nVisit https://ghosts.lk/wyrm to upgrade.` }],
191
+ };
192
+ }
193
+ const action = args?.action;
194
+ switch (action) {
195
+ case "backup": {
196
+ const result = await cloudBackup.backup(db.getDatabasePath());
197
+ const meta = result.metadata;
198
+ return {
199
+ content: [{ type: "text", text: `󱅝 ✓ Backup complete!\n\n- **Key:** ${result.key}\n- **Size:** ${(meta.db_size / 1024).toFixed(1)} KB → ${(meta.compressed_size / 1024).toFixed(1)} KB compressed\n- **Encrypted:** ${meta.encrypted ? 'Yes' : 'No'}\n- **Checksum:** ${meta.checksum.slice(0, 16)}...` }],
200
+ };
201
+ }
202
+ case "restore": {
203
+ const backupKey = args?.backupKey;
204
+ if (!backupKey)
205
+ throw new Error("backupKey required for restore");
206
+ const result = await cloudBackup.restore(backupKey, db.getDatabasePath());
207
+ return {
208
+ content: [{ type: "text", text: `󱅝 ✓ Restore complete!\n\n- **Restored:** ${result.restored ? 'Yes' : 'No'}\n- **Size:** ${(result.size / 1024).toFixed(1)} KB\n- **Previous DB backed up to:** ${db.getDatabasePath()}.bak\n\n⚠️ Restart Wyrm to use the restored database.` }],
209
+ };
210
+ }
211
+ case "list": {
212
+ const backups = await cloudBackup.listBackups();
213
+ if (backups.length === 0) {
214
+ return { content: [{ type: "text", text: `󱅝 No backups found.` }] };
215
+ }
216
+ let text = `󱅝 **Cloud Backups** (${backups.length} found)\n\n`;
217
+ for (const b of backups) {
218
+ text += `- **${b.timestamp}** — ${(b.db_size / 1024).toFixed(1)} KB, encrypted: ${b.encrypted}, machine: ${b.machine_id.slice(0, 8)}...\n`;
219
+ }
220
+ return { content: [{ type: "text", text }] };
221
+ }
222
+ case "prune": {
223
+ const keepCount = args?.keepCount || 10;
224
+ const result = await cloudBackup.pruneBackups(keepCount);
225
+ return {
226
+ content: [{ type: "text", text: `󱅝 ✓ Pruned ${result.deleted} old backup(s). Kept ${keepCount} most recent.` }],
227
+ };
228
+ }
229
+ default:
230
+ return { content: [{ type: "text", text: `Unknown backup action: ${action}. Use: backup, restore, list, prune` }], isError: true };
231
+ }
232
+ },
233
+ },
234
+ {
235
+ name: "wyrm_cloud_sync",
236
+ description: "Start, stop, or check the cloud-sync daemon — Phase 2 multi-device sync. Periodically backs up the DB to R2 and restores on startup if a newer snapshot exists. Last-write-wins by mtime.",
237
+ inputSchema: {
238
+ type: "object",
239
+ properties: {
240
+ action: { type: "string", description: "Action: start, stop, restart, status, force-sync", enum: ["start", "stop", "restart", "status", "force-sync"] },
241
+ interval_minutes: { type: "number", description: "Sync interval (default: 10)" },
242
+ keep_count: { type: "number", description: "Snapshots to retain after pruning (default: 20)" },
243
+ },
244
+ required: ["action"],
245
+ },
246
+ annotations: TOOL_ANNOTATIONS["wyrm_cloud_sync"],
247
+ aliases: [],
248
+ handler: async (args, ctx) => {
249
+ const { cloudBackup, cloudSyncManager, db, sync } = ctx;
250
+ const a = args || {};
251
+ if (!hasFeature('cloud_backup')) {
252
+ return {
253
+ content: [{ type: "text", text: `󱅝 Cloud sync requires a Pro license or higher.\nVisit https://ghosts.lk/wyrm to upgrade.` }],
254
+ };
255
+ }
256
+ const action = args?.action;
257
+ const interval_minutes = args?.interval_minutes;
258
+ const keep_count = args?.keep_count;
259
+ switch (action) {
260
+ case "start": {
261
+ const r = cloudSyncManager.start({ interval_minutes, keep_count });
262
+ if (!r.ok) {
263
+ return { content: [{ type: "text", text: `󱅝 cloud-sync: ${r.reason ?? 'failed to start'}${r.pid ? ` (pid ${r.pid})` : ''}` }], isError: !r.pid };
264
+ }
265
+ return { content: [{ type: "text", text: `󱅝 ✓ cloud-sync daemon started (pid ${r.pid}). Logs: ~/.wyrm/wyrm-cloud-sync.log` }] };
266
+ }
267
+ case "stop": {
268
+ const r = cloudSyncManager.stop();
269
+ return {
270
+ content: [{ type: "text", text: r.ok
271
+ ? `󱅝 ✓ cloud-sync daemon stopped`
272
+ : `󱅝 cloud-sync: ${r.reason ?? 'failed to stop'}` }],
273
+ isError: !r.ok,
274
+ };
275
+ }
276
+ case "restart": {
277
+ cloudSyncManager.stop();
278
+ // Brief grace period for the daemon's final tick before re-spawning
279
+ await new Promise((res) => { setTimeout(res, 500); });
280
+ const r = cloudSyncManager.start({ interval_minutes, keep_count });
281
+ return {
282
+ content: [{ type: "text", text: r.ok
283
+ ? `󱅝 ✓ cloud-sync daemon restarted (pid ${r.pid})`
284
+ : `󱅝 cloud-sync restart failed: ${r.reason ?? 'unknown'}` }],
285
+ isError: !r.ok,
286
+ };
287
+ }
288
+ case "status": {
289
+ const s = cloudSyncManager.status();
290
+ const lines = [`󱅝 **Cloud Sync Status**`];
291
+ lines.push(`- Running: ${s.running ? '✓ yes' : '✗ no'}`);
292
+ if (s.pid)
293
+ lines.push(`- PID: ${s.pid}`);
294
+ if (s.state) {
295
+ if (s.state.last_uploaded_ts) {
296
+ lines.push(`- Last upload: ${new Date(s.state.last_uploaded_ts).toISOString()}${s.state.last_uploaded_key ? ` (${s.state.last_uploaded_key})` : ''}`);
297
+ }
298
+ if (s.state.last_restored_key)
299
+ lines.push(`- Last restore: ${s.state.last_restored_key}`);
300
+ if (s.state.last_check_ts)
301
+ lines.push(`- Last check: ${new Date(s.state.last_check_ts).toISOString()}`);
302
+ lines.push(`- Machine: ${s.state.machine}`);
303
+ }
304
+ return { content: [{ type: "text", text: lines.join('\n') }] };
305
+ }
306
+ case "force-sync": {
307
+ // One-shot tick without the long-running daemon. Uses the in-process
308
+ // cloudBackup client, so works even when the daemon is stopped.
309
+ const daemon = new CloudSyncDaemon(cloudBackup, db.getDatabasePath());
310
+ const result = await daemon.tick();
311
+ return { content: [{ type: "text", text: `󱅝 force-sync: ${JSON.stringify(result, null, 2)}` }] };
312
+ }
313
+ default:
314
+ return { content: [{ type: "text", text: `Unknown cloud-sync action: ${action}. Use: start, stop, restart, status, force-sync` }], isError: true };
315
+ }
316
+ },
317
+ },
318
+ {
319
+ name: "wyrm_encrypt_setup",
320
+ description: "Set up or check encryption status for sensitive data (Pro+)",
321
+ inputSchema: {
322
+ type: "object",
323
+ properties: {
324
+ action: { type: "string", description: "Action: status, enable, test", enum: ["status", "enable", "test"] },
325
+ password: { type: "string", description: "Encryption password (min 8 chars, required for enable)" },
326
+ },
327
+ required: ["action"],
328
+ },
329
+ annotations: TOOL_ANNOTATIONS["wyrm_encrypt_setup"],
330
+ aliases: [],
331
+ handler: async (args, ctx) => {
332
+ const a = args || {};
333
+ if (!hasFeature('encryption')) {
334
+ return {
335
+ content: [{ type: "text", text: `󱅝 Encryption setup requires a Pro license or higher.\nVisit https://ghosts.lk/wyrm to upgrade.` }],
336
+ };
337
+ }
338
+ const encAction = args?.action;
339
+ const cryptoInstance = getCrypto();
340
+ switch (encAction) {
341
+ case "status": {
342
+ const enabled = cryptoInstance.isEnabled();
343
+ let text = `󱅝 **Encryption Status**\n\n`;
344
+ text += `- **Enabled:** ${enabled ? '✓ Yes' : '✗ No'}\n`;
345
+ text += `- **Algorithm:** AES-256-GCM\n`;
346
+ if (enabled) {
347
+ text += `- **Status:** Active — new data is encrypted at rest\n`;
348
+ }
349
+ else {
350
+ text += `\nTo enable, run: \`wyrm_encrypt_setup\` with action "enable" and a strong password.\n`;
351
+ }
352
+ return { content: [{ type: "text", text }] };
353
+ }
354
+ case "enable": {
355
+ const password = args?.password;
356
+ if (!password || password.length < 8) {
357
+ return { content: [{ type: "text", text: `󱅝 Password must be at least 8 characters.` }], isError: true };
358
+ }
359
+ initializeCrypto(password);
360
+ return {
361
+ content: [{ type: "text", text: `󱅝 ✓ Encryption enabled!\n\n- **Algorithm:** AES-256-GCM\n- **Key derived from:** your password (PBKDF2)\n\n⚠️ **Store your password safely.** Lost passwords cannot be recovered.\nNew data lake entries will be encrypted at rest.` }],
362
+ };
363
+ }
364
+ case "test": {
365
+ if (!cryptoInstance.isEnabled()) {
366
+ return { content: [{ type: "text", text: `󱅝 Encryption not enabled. Run with action "enable" first.` }] };
367
+ }
368
+ const testData = "Wyrm encryption test " + Date.now();
369
+ const encrypted = cryptoInstance.encrypt(testData);
370
+ const decrypted = cryptoInstance.decrypt(encrypted);
371
+ const ok = decrypted === testData;
372
+ return {
373
+ content: [{ type: "text", text: `󱅝 Encryption test: ${ok ? '✓ PASSED' : '✗ FAILED'}\n- Encrypted ${testData.length} chars → decrypted back to ${decrypted.length} chars` }],
374
+ };
375
+ }
376
+ default:
377
+ return { content: [{ type: "text", text: `Unknown encrypt action: ${encAction}. Use: status, enable, test` }], isError: true };
378
+ }
379
+ },
380
+ },
381
+ ];
382
+ //# sourceMappingURL=cloud.js.map