ghost-dragon 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/.github/workflows/ci.yml +23 -0
  2. package/CHANGELOG.md +96 -0
  3. package/README.md +193 -0
  4. package/bootstrap.ps1 +83 -0
  5. package/bootstrap.sh +71 -0
  6. package/dist/agent/loop.d.ts +68 -0
  7. package/dist/agent/loop.d.ts.map +1 -0
  8. package/dist/agent/loop.js +135 -0
  9. package/dist/agent/mcp.d.ts +33 -0
  10. package/dist/agent/mcp.d.ts.map +1 -0
  11. package/dist/agent/mcp.js +107 -0
  12. package/dist/agent/session.d.ts +16 -0
  13. package/dist/agent/session.d.ts.map +1 -0
  14. package/dist/agent/session.js +55 -0
  15. package/dist/agent/skills.d.ts +36 -0
  16. package/dist/agent/skills.d.ts.map +1 -0
  17. package/dist/agent/skills.js +153 -0
  18. package/dist/agent/stack.d.ts +21 -0
  19. package/dist/agent/stack.d.ts.map +1 -0
  20. package/dist/agent/stack.js +158 -0
  21. package/dist/agent/task.d.ts +21 -0
  22. package/dist/agent/task.d.ts.map +1 -0
  23. package/dist/agent/task.js +45 -0
  24. package/dist/agent/tools.d.ts +44 -0
  25. package/dist/agent/tools.d.ts.map +1 -0
  26. package/dist/agent/tools.js +262 -0
  27. package/dist/agent/trace.d.ts +34 -0
  28. package/dist/agent/trace.d.ts.map +1 -0
  29. package/dist/agent/trace.js +72 -0
  30. package/dist/agent.d.ts +46 -0
  31. package/dist/agent.d.ts.map +1 -0
  32. package/dist/agent.js +103 -0
  33. package/dist/auth.d.ts +74 -0
  34. package/dist/auth.d.ts.map +1 -0
  35. package/dist/auth.js +116 -0
  36. package/dist/brain/anthropic.d.ts +19 -0
  37. package/dist/brain/anthropic.d.ts.map +1 -0
  38. package/dist/brain/anthropic.js +74 -0
  39. package/dist/brain/claude-cli.d.ts +20 -0
  40. package/dist/brain/claude-cli.d.ts.map +1 -0
  41. package/dist/brain/claude-cli.js +79 -0
  42. package/dist/brain/ghost-ember.d.ts +28 -0
  43. package/dist/brain/ghost-ember.d.ts.map +1 -0
  44. package/dist/brain/ghost-ember.js +97 -0
  45. package/dist/brain/index.d.ts +22 -0
  46. package/dist/brain/index.d.ts.map +1 -0
  47. package/dist/brain/index.js +95 -0
  48. package/dist/brain/openai-compat.d.ts +21 -0
  49. package/dist/brain/openai-compat.d.ts.map +1 -0
  50. package/dist/brain/openai-compat.js +119 -0
  51. package/dist/brain/router/classify.d.ts +23 -0
  52. package/dist/brain/router/classify.d.ts.map +1 -0
  53. package/dist/brain/router/classify.js +160 -0
  54. package/dist/brain/router/execute.d.ts +23 -0
  55. package/dist/brain/router/execute.d.ts.map +1 -0
  56. package/dist/brain/router/execute.js +84 -0
  57. package/dist/brain/router/index.d.ts +26 -0
  58. package/dist/brain/router/index.d.ts.map +1 -0
  59. package/dist/brain/router/index.js +118 -0
  60. package/dist/brain/router/routing-memory.d.ts +27 -0
  61. package/dist/brain/router/routing-memory.d.ts.map +1 -0
  62. package/dist/brain/router/routing-memory.js +77 -0
  63. package/dist/brain/router/select.d.ts +32 -0
  64. package/dist/brain/router/select.d.ts.map +1 -0
  65. package/dist/brain/router/select.js +146 -0
  66. package/dist/brain/router/two-hop.d.ts +23 -0
  67. package/dist/brain/router/two-hop.d.ts.map +1 -0
  68. package/dist/brain/router/two-hop.js +39 -0
  69. package/dist/brain/router/verify.d.ts +37 -0
  70. package/dist/brain/router/verify.d.ts.map +1 -0
  71. package/dist/brain/router/verify.js +111 -0
  72. package/dist/brain/types.d.ts +55 -0
  73. package/dist/brain/types.d.ts.map +1 -0
  74. package/dist/brain/types.js +16 -0
  75. package/dist/brain/worker.d.ts +27 -0
  76. package/dist/brain/worker.d.ts.map +1 -0
  77. package/dist/brain/worker.js +71 -0
  78. package/dist/commands/ai.d.ts +24 -0
  79. package/dist/commands/ai.d.ts.map +1 -0
  80. package/dist/commands/ai.js +137 -0
  81. package/dist/commands/alerts.d.ts +19 -0
  82. package/dist/commands/alerts.d.ts.map +1 -0
  83. package/dist/commands/alerts.js +114 -0
  84. package/dist/commands/billing.d.ts +13 -0
  85. package/dist/commands/billing.d.ts.map +1 -0
  86. package/dist/commands/billing.js +55 -0
  87. package/dist/commands/chat.d.ts +22 -0
  88. package/dist/commands/chat.d.ts.map +1 -0
  89. package/dist/commands/chat.js +422 -0
  90. package/dist/commands/config.d.ts +18 -0
  91. package/dist/commands/config.d.ts.map +1 -0
  92. package/dist/commands/config.js +136 -0
  93. package/dist/commands/doctor.d.ts +11 -0
  94. package/dist/commands/doctor.d.ts.map +1 -0
  95. package/dist/commands/doctor.js +73 -0
  96. package/dist/commands/global.d.ts +11 -0
  97. package/dist/commands/global.d.ts.map +1 -0
  98. package/dist/commands/global.js +253 -0
  99. package/dist/commands/keep.d.ts +12 -0
  100. package/dist/commands/keep.d.ts.map +1 -0
  101. package/dist/commands/keep.js +58 -0
  102. package/dist/commands/lifecycle.d.ts +17 -0
  103. package/dist/commands/lifecycle.d.ts.map +1 -0
  104. package/dist/commands/lifecycle.js +267 -0
  105. package/dist/commands/login.d.ts +16 -0
  106. package/dist/commands/login.d.ts.map +1 -0
  107. package/dist/commands/login.js +234 -0
  108. package/dist/commands/maintenance.d.ts +12 -0
  109. package/dist/commands/maintenance.d.ts.map +1 -0
  110. package/dist/commands/maintenance.js +76 -0
  111. package/dist/commands/mcp.d.ts +16 -0
  112. package/dist/commands/mcp.d.ts.map +1 -0
  113. package/dist/commands/mcp.js +56 -0
  114. package/dist/commands/memory.d.ts +13 -0
  115. package/dist/commands/memory.d.ts.map +1 -0
  116. package/dist/commands/memory.js +218 -0
  117. package/dist/commands/osint.d.ts +14 -0
  118. package/dist/commands/osint.d.ts.map +1 -0
  119. package/dist/commands/osint.js +161 -0
  120. package/dist/commands/pentest.d.ts +13 -0
  121. package/dist/commands/pentest.d.ts.map +1 -0
  122. package/dist/commands/pentest.js +131 -0
  123. package/dist/commands/scale.d.ts +14 -0
  124. package/dist/commands/scale.d.ts.map +1 -0
  125. package/dist/commands/scale.js +191 -0
  126. package/dist/commands/serve.d.ts +16 -0
  127. package/dist/commands/serve.d.ts.map +1 -0
  128. package/dist/commands/serve.js +167 -0
  129. package/dist/commands/tui.d.ts +17 -0
  130. package/dist/commands/tui.d.ts.map +1 -0
  131. package/dist/commands/tui.js +138 -0
  132. package/dist/commands/wyrm.d.ts +20 -0
  133. package/dist/commands/wyrm.d.ts.map +1 -0
  134. package/dist/commands/wyrm.js +274 -0
  135. package/dist/config.d.ts +67 -0
  136. package/dist/config.d.ts.map +1 -0
  137. package/dist/config.js +54 -0
  138. package/dist/index.d.ts +16 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +85 -0
  141. package/dist/manifest.d.ts +31 -0
  142. package/dist/manifest.d.ts.map +1 -0
  143. package/dist/manifest.js +83 -0
  144. package/dist/ui.d.ts +57 -0
  145. package/dist/ui.d.ts.map +1 -0
  146. package/dist/ui.js +174 -0
  147. package/dist/utils.d.ts +33 -0
  148. package/dist/utils.d.ts.map +1 -0
  149. package/dist/utils.js +155 -0
  150. package/dist/wyrm/mcp.d.ts +37 -0
  151. package/dist/wyrm/mcp.d.ts.map +1 -0
  152. package/dist/wyrm/mcp.js +137 -0
  153. package/docs/SYSTEM-PREMORTEM.md +397 -0
  154. package/dragon-manifest.toml +241 -0
  155. package/dragon.py +177 -0
  156. package/install/launchd/lk.ghosts.dragonkeep.plist +57 -0
  157. package/install/systemd/dragonkeep.service +40 -0
  158. package/media/dragon-silver-lockup.svg +931 -0
  159. package/media/dragon-silver-mark.svg +931 -0
  160. package/media/dragon-silver.png +0 -0
  161. package/package.json +45 -0
  162. package/specs/001-godmode/constitution.md +54 -0
  163. package/specs/001-godmode/plan.md +30 -0
  164. package/specs/001-godmode/spec.md +64 -0
  165. package/specs/001-godmode/tasks.md +35 -0
  166. package/specs/002-premortem-positioning/premortem.md +211 -0
  167. package/src/agent/loop.ts +165 -0
  168. package/src/agent/mcp.ts +92 -0
  169. package/src/agent/session.ts +48 -0
  170. package/src/agent/skills.ts +138 -0
  171. package/src/agent/stack.ts +154 -0
  172. package/src/agent/task.ts +55 -0
  173. package/src/agent/tools.ts +255 -0
  174. package/src/agent/trace.ts +76 -0
  175. package/src/agent.ts +114 -0
  176. package/src/auth.ts +133 -0
  177. package/src/brain/anthropic.ts +83 -0
  178. package/src/brain/claude-cli.ts +78 -0
  179. package/src/brain/ghost-ember.ts +94 -0
  180. package/src/brain/index.ts +99 -0
  181. package/src/brain/openai-compat.ts +115 -0
  182. package/src/brain/router/classify.ts +167 -0
  183. package/src/brain/router/execute.ts +80 -0
  184. package/src/brain/router/index.ts +125 -0
  185. package/src/brain/router/routing-memory.ts +71 -0
  186. package/src/brain/router/select.ts +156 -0
  187. package/src/brain/router/two-hop.ts +62 -0
  188. package/src/brain/router/verify.ts +123 -0
  189. package/src/brain/types.ts +61 -0
  190. package/src/brain/worker.ts +72 -0
  191. package/src/commands/ai.ts +144 -0
  192. package/src/commands/alerts.ts +131 -0
  193. package/src/commands/billing.ts +59 -0
  194. package/src/commands/chat.ts +318 -0
  195. package/src/commands/config.ts +137 -0
  196. package/src/commands/doctor.ts +71 -0
  197. package/src/commands/global.ts +256 -0
  198. package/src/commands/keep.ts +67 -0
  199. package/src/commands/lifecycle.ts +273 -0
  200. package/src/commands/login.ts +184 -0
  201. package/src/commands/maintenance.ts +54 -0
  202. package/src/commands/mcp.ts +57 -0
  203. package/src/commands/memory.ts +229 -0
  204. package/src/commands/osint.ts +171 -0
  205. package/src/commands/pentest.ts +140 -0
  206. package/src/commands/scale.ts +185 -0
  207. package/src/commands/serve.ts +171 -0
  208. package/src/commands/tui.ts +126 -0
  209. package/src/commands/wyrm.ts +269 -0
  210. package/src/config.ts +93 -0
  211. package/src/index.ts +92 -0
  212. package/src/manifest.ts +104 -0
  213. package/src/ui.ts +188 -0
  214. package/src/utils.ts +153 -0
  215. package/src/wyrm/mcp.ts +130 -0
  216. package/test/auth.test.ts +70 -0
  217. package/test/brain.test.ts +39 -0
  218. package/test/security.test.ts +104 -0
  219. package/test/skills.test.ts +38 -0
  220. package/test/ui.test.ts +46 -0
  221. package/tsconfig.json +19 -0
  222. package/worker/package-lock.json +1527 -0
  223. package/worker/package.json +17 -0
  224. package/worker/src/index.ts +76 -0
  225. package/worker/tsconfig.json +15 -0
  226. package/worker/wrangler.toml +26 -0
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Verified hard-reasoning path (ROUTER-BLUEPRINT.md §2).
3
+ *
4
+ * Ollama exposes no logits, so confidence = ANSWER-AGREEMENT: sample the reasoner
5
+ * N times at spread temperatures, extract each final answer, and majority-vote.
6
+ * The agreement ratio is the router's confidence signal (→ later: escalate if low).
7
+ * For code/security candidates we additionally run EXECUTION-based verification
8
+ * (see execute.ts) and use pass/fail as a hard reward.
9
+ *
10
+ * optillm: if DRAGON_OPTILLM_URL is set we treat it as a drop-in OpenAI-compatible
11
+ * test-time-scaling proxy and let IT do the scaling in one call (you run optillm
12
+ * pointed at Ollama). Otherwise we do best-of-N here — self-contained, no extra
13
+ * service, which suits the 8 GB local-first box.
14
+ *
15
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
16
+ */
17
+ import { executeVerify } from './execute.js';
18
+ const OLLAMA = (base) => base.replace(/\/v1\/?$/, '').replace(/\/+$/, '');
19
+ export function toOllamaMessages(system, messages) {
20
+ const out = [{ role: 'system', content: system }];
21
+ for (const m of messages) {
22
+ if (m.role === 'tool')
23
+ out.push({ role: 'user', content: `[observed] ${m.toolName ?? 'tool'} → ${m.content}`.slice(0, 1200) });
24
+ else
25
+ out.push({ role: m.role === 'assistant' ? 'assistant' : 'user', content: m.content });
26
+ }
27
+ return out;
28
+ }
29
+ export async function ollamaChat(base, model, messages, temperature, maxTokens, signal) {
30
+ const res = await fetch(OLLAMA(base) + '/api/chat', {
31
+ method: 'POST',
32
+ headers: { 'content-type': 'application/json' },
33
+ body: JSON.stringify({ model, messages, stream: false, options: { temperature, top_p: 0.95, num_predict: maxTokens } }),
34
+ signal,
35
+ });
36
+ if (!res.ok)
37
+ throw new Error(`reasoner HTTP ${res.status}`);
38
+ const data = (await res.json());
39
+ return data.message?.content ?? '';
40
+ }
41
+ async function openaiChat(base, model, messages, temperature, maxTokens, signal) {
42
+ const res = await fetch(base.replace(/\/+$/, '') + '/chat/completions', {
43
+ method: 'POST',
44
+ headers: { 'content-type': 'application/json', authorization: 'Bearer optillm' },
45
+ body: JSON.stringify({ model, messages, temperature, top_p: 0.95, max_tokens: maxTokens, stream: false }),
46
+ signal,
47
+ });
48
+ if (!res.ok)
49
+ throw new Error(`optillm HTTP ${res.status}`);
50
+ const data = (await res.json());
51
+ return data.choices?.[0]?.message?.content ?? '';
52
+ }
53
+ export function extractAnswer(text) {
54
+ const boxed = [...text.matchAll(/\\boxed\{([^}]*)\}/g)];
55
+ if (boxed.length)
56
+ return boxed[boxed.length - 1][1].trim();
57
+ const ans = [...text.matchAll(/(?:final answer|answer)\s*(?:is|:|=)\s*([^\n.]+)/gi)];
58
+ if (ans.length)
59
+ return ans[ans.length - 1][1].trim().replace(/\.$/, '');
60
+ const nums = text.match(/-?\d[\d,]*\.?\d*/g);
61
+ return nums ? nums[nums.length - 1].replace(/,/g, '') : null;
62
+ }
63
+ const norm = (a) => (a || '').toLowerCase().replace(/\s+/g, '').replace(/\.$/, '');
64
+ /** Run the reasoner with test-time scaling. Returns the chosen turn + how confident. */
65
+ export async function verifyReasoning(localBaseURL, model, t, votes) {
66
+ const maxTokens = t.maxTokens ?? 2048;
67
+ const messages = toOllamaMessages(t.system, t.messages);
68
+ const optillm = process.env.DRAGON_OPTILLM_URL;
69
+ let chosen;
70
+ let meta;
71
+ if (optillm) {
72
+ chosen = await openaiChat(optillm, process.env.DRAGON_OPTILLM_MODEL || model, messages, 0.7, maxTokens, t.signal);
73
+ meta = { via: 'optillm', votes: 1, agreement: null };
74
+ }
75
+ else {
76
+ const temps = [0.3, 0.6, 0.8, 1.0, 1.1, 0.5, 0.9].slice(0, Math.max(1, votes));
77
+ while (temps.length < votes)
78
+ temps.push(0.7);
79
+ const samples = [];
80
+ for (const temp of temps) {
81
+ try {
82
+ samples.push(await ollamaChat(localBaseURL, model, messages, temp, maxTokens, t.signal));
83
+ }
84
+ catch { /* a failed sample just doesn't vote */ }
85
+ }
86
+ if (!samples.length)
87
+ throw new Error('reasoner produced no samples');
88
+ const dist = {};
89
+ const byNorm = {}; // normalized answer → a full sample text
90
+ for (const s of samples) {
91
+ const a = extractAnswer(s);
92
+ const key = norm(a);
93
+ if (!key)
94
+ continue;
95
+ dist[key] = (dist[key] || 0) + 1;
96
+ if (!byNorm[key])
97
+ byNorm[key] = s;
98
+ }
99
+ const winner = Object.entries(dist).sort((a, b) => b[1] - a[1])[0];
100
+ chosen = winner ? byNorm[winner[0]] : samples[0]; // no extractable answer → first sample
101
+ meta = {
102
+ via: 'self-consistency', votes: samples.length,
103
+ agreement: winner ? winner[1] / samples.length : 0, distribution: dist,
104
+ };
105
+ }
106
+ // Execution-based verification for code/security candidates (opt-in + sandboxed).
107
+ meta.exec = await executeVerify(chosen, t.signal);
108
+ t.onDelta?.(chosen);
109
+ return { turn: { text: chosen, toolCalls: [] }, meta };
110
+ }
111
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Brain abstraction — the reasoning layer behind the Dragon agent.
3
+ *
4
+ * A Brain runs ONE model turn: given the system prompt, the running message
5
+ * history, and the available tool specs, it streams text deltas and returns the
6
+ * assembled text plus any tool calls the model wants executed. The agent loop
7
+ * (src/agent/loop.ts) owns the loop; the Brain owns only "talk to the model".
8
+ *
9
+ * Tool calls are normalized to a single shape across providers so the loop is
10
+ * provider-agnostic — Anthropic content blocks and OpenAI `tool_calls` both map
11
+ * onto {id,name,arguments}.
12
+ *
13
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
14
+ */
15
+ export interface ToolSpec {
16
+ name: string;
17
+ description: string;
18
+ /** JSON Schema for the arguments object. */
19
+ parameters: Record<string, unknown>;
20
+ }
21
+ export interface ToolCall {
22
+ id: string;
23
+ name: string;
24
+ arguments: Record<string, unknown>;
25
+ }
26
+ export type Role = 'user' | 'assistant' | 'tool';
27
+ export interface BrainMessage {
28
+ role: Role;
29
+ /** Natural-language text (assistant prose, user input, or a tool result string). */
30
+ content: string;
31
+ /** Present on assistant turns that requested tools. */
32
+ toolCalls?: ToolCall[];
33
+ /** Present on role:'tool' — links the result to the assistant's call. */
34
+ toolCallId?: string;
35
+ toolName?: string;
36
+ }
37
+ export interface BrainTurn {
38
+ text: string;
39
+ toolCalls: ToolCall[];
40
+ }
41
+ export interface TurnOpts {
42
+ system: string;
43
+ messages: BrainMessage[];
44
+ tools: ToolSpec[];
45
+ onDelta?: (s: string) => void;
46
+ signal?: AbortSignal;
47
+ maxTokens?: number;
48
+ }
49
+ export interface Brain {
50
+ /** provider id: 'claude' | 'openai' | 'local' */
51
+ id: string;
52
+ model: string;
53
+ turn(opts: TurnOpts): Promise<BrainTurn>;
54
+ }
55
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/brain/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAA;AAEhD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,IAAI,CAAA;IACV,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAA;IACf,uDAAuD;IACvD,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAA;IACtB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,QAAQ,EAAE,CAAA;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,KAAK;IACpB,iDAAiD;IACjD,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA;CACzC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Brain abstraction — the reasoning layer behind the Dragon agent.
3
+ *
4
+ * A Brain runs ONE model turn: given the system prompt, the running message
5
+ * history, and the available tool specs, it streams text deltas and returns the
6
+ * assembled text plus any tool calls the model wants executed. The agent loop
7
+ * (src/agent/loop.ts) owns the loop; the Brain owns only "talk to the model".
8
+ *
9
+ * Tool calls are normalized to a single shape across providers so the loop is
10
+ * provider-agnostic — Anthropic content blocks and OpenAI `tool_calls` both map
11
+ * onto {id,name,arguments}.
12
+ *
13
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
14
+ */
15
+ export {};
16
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Worker brain — Ghost Protocol's Cloudflare Workers AI (Llama 3.3 70B) as the
3
+ * agent's reasoning brain. The free, zero-key fallback: no API key, just
4
+ * `dragon login`. Tools still execute locally in the CLI; this only does
5
+ * inference, via POST /api/v1/cli/brain (one turn, non-streaming).
6
+ *
7
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
8
+ */
9
+ import type { Brain, BrainTurn } from './types.js';
10
+ /** The raw JSON shape the /api/v1/cli/brain endpoint returns. */
11
+ export interface WorkerResponse {
12
+ response?: string;
13
+ tool_calls?: {
14
+ name?: string;
15
+ arguments?: unknown;
16
+ }[];
17
+ }
18
+ /**
19
+ * Normalize the Cloudflare brain's (Llama 3.3, fp8) raw JSON into a BrainTurn.
20
+ * Deliberately tolerant — the fp8 tool-caller is flaky: arguments arrive as a
21
+ * JSON string OR an object OR not at all, tool_calls can be nameless/garbage,
22
+ * and `response` can be missing. Every one of those degrades gracefully instead
23
+ * of throwing into the agent loop. (Pure → unit-tested in test/brain.test.ts.)
24
+ */
25
+ export declare function normalizeWorkerTurn(data: WorkerResponse): BrainTurn;
26
+ export declare function makeWorkerBrain(): Brain;
27
+ //# sourceMappingURL=worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/brain/worker.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAY,MAAM,YAAY,CAAA;AAM5D,iEAAiE;AACjE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;CACtD;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,cAAc,GAAG,SAAS,CAUnE;AAED,wBAAgB,eAAe,IAAI,KAAK,CA8BvC"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Worker brain — Ghost Protocol's Cloudflare Workers AI (Llama 3.3 70B) as the
3
+ * agent's reasoning brain. The free, zero-key fallback: no API key, just
4
+ * `dragon login`. Tools still execute locally in the CLI; this only does
5
+ * inference, via POST /api/v1/cli/brain (one turn, non-streaming).
6
+ *
7
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
8
+ */
9
+ import { resolveAuth } from '../auth.js';
10
+ function safeParse(s) {
11
+ try {
12
+ return JSON.parse(s);
13
+ }
14
+ catch {
15
+ return {};
16
+ }
17
+ }
18
+ /**
19
+ * Normalize the Cloudflare brain's (Llama 3.3, fp8) raw JSON into a BrainTurn.
20
+ * Deliberately tolerant — the fp8 tool-caller is flaky: arguments arrive as a
21
+ * JSON string OR an object OR not at all, tool_calls can be nameless/garbage,
22
+ * and `response` can be missing. Every one of those degrades gracefully instead
23
+ * of throwing into the agent loop. (Pure → unit-tested in test/brain.test.ts.)
24
+ */
25
+ export function normalizeWorkerTurn(data) {
26
+ const text = data.response ?? '';
27
+ const toolCalls = (data.tool_calls ?? [])
28
+ .filter((c) => c && c.name)
29
+ .map((c, i) => ({
30
+ id: `wc_${i}`,
31
+ name: String(c.name),
32
+ arguments: typeof c.arguments === 'string' ? safeParse(c.arguments) : (c.arguments ?? {}),
33
+ }));
34
+ return { text, toolCalls };
35
+ }
36
+ export function makeWorkerBrain() {
37
+ return {
38
+ id: 'worker',
39
+ model: 'cloudflare:llama-3.3-70b',
40
+ async turn(t) {
41
+ const { apiBase, headers, mode } = resolveAuth();
42
+ if (mode === 'none')
43
+ throw new Error('the Cloudflare brain needs sign-in — run `dragon login` (or use `--brain local`).');
44
+ let res;
45
+ try {
46
+ res = await fetch(`${apiBase}/api/v1/cli/brain`, {
47
+ method: 'POST',
48
+ headers: { 'content-type': 'application/json', ...headers },
49
+ body: JSON.stringify({ system: t.system, messages: t.messages, tools: t.tools, max_tokens: t.maxTokens ?? 1024 }),
50
+ signal: t.signal,
51
+ });
52
+ }
53
+ catch (e) {
54
+ if (e?.name === 'AbortError')
55
+ throw e;
56
+ throw new Error(`can't reach the Cloudflare brain at ${apiBase} — ${e instanceof Error ? e.message : String(e)}.`);
57
+ }
58
+ if (res.status === 401)
59
+ throw new Error('not signed in — run `dragon login`.');
60
+ if (res.status === 429)
61
+ throw new Error('daily quota reached on the Cloudflare brain — try tomorrow, or `--brain claude`/`--brain local`.');
62
+ if (!res.ok)
63
+ throw new Error(`Cloudflare brain HTTP ${res.status}: ${(await res.text().catch(() => '')).slice(0, 200)}`);
64
+ const turn = normalizeWorkerTurn((await res.json()));
65
+ if (turn.text && t.onDelta)
66
+ t.onDelta(turn.text); // non-streaming endpoint → emit the whole answer once
67
+ return turn;
68
+ },
69
+ };
70
+ }
71
+ //# sourceMappingURL=worker.js.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * dragon ai — Bridge the dragon stack to AI clients (Claude Code,
3
+ * Codex, Copilot CLI, Aider, Cursor, etc.)
4
+ *
5
+ * ai context <target> — Print a compact context dump suitable
6
+ * for splicing into any LLM system prompt
7
+ * ai mcp-config — Print Claude Desktop MCP JSON config
8
+ * ai brief <target> — Print engagement brief Markdown
9
+ * ai prompt <target> — Print operator-ready prompt with
10
+ * memory context + suggested actions
11
+ *
12
+ * Designed so an operator running Claude Code in another window can do:
13
+ *
14
+ * $ dragon ai prompt upalis.com | claude
15
+ *
16
+ * and Claude picks up the engagement context immediately.
17
+ *
18
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
19
+ * Author: Ryan Sebastian <ryan@ghosts.lk>
20
+ */
21
+ import type { Command } from 'commander';
22
+ import type { DragonConfig } from '../config.js';
23
+ export declare function registerAiCommands(program: Command, config: DragonConfig): void;
24
+ //# sourceMappingURL=ai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../src/commands/ai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAgBhD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,QAyGxE"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * dragon ai — Bridge the dragon stack to AI clients (Claude Code,
3
+ * Codex, Copilot CLI, Aider, Cursor, etc.)
4
+ *
5
+ * ai context <target> — Print a compact context dump suitable
6
+ * for splicing into any LLM system prompt
7
+ * ai mcp-config — Print Claude Desktop MCP JSON config
8
+ * ai brief <target> — Print engagement brief Markdown
9
+ * ai prompt <target> — Print operator-ready prompt with
10
+ * memory context + suggested actions
11
+ *
12
+ * Designed so an operator running Claude Code in another window can do:
13
+ *
14
+ * $ dragon ai prompt upalis.com | claude
15
+ *
16
+ * and Claude picks up the engagement context immediately.
17
+ *
18
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
19
+ * Author: Ryan Sebastian <ryan@ghosts.lk>
20
+ */
21
+ import { fetchJSON, label, error, info } from '../utils.js';
22
+ import chalk from 'chalk';
23
+ function api(config, path) {
24
+ const port = config.products.pentest.controlPort ?? 4091;
25
+ return `http://localhost:${port}${path}`;
26
+ }
27
+ export function registerAiCommands(program, config) {
28
+ const ai = program.command('ai').description('Bridge the dragon stack to AI clients');
29
+ // --- context ---
30
+ ai.command('context <target>')
31
+ .description('Print compact context for splicing into an LLM system prompt')
32
+ .action(async (target) => {
33
+ try {
34
+ const port = config.products.pentest.controlPort ?? 4091;
35
+ const res = await fetch(`http://localhost:${port}/v1/memory/target/${encodeURIComponent(target)}/context`);
36
+ if (!res.ok) {
37
+ error(`HTTP ${res.status}`);
38
+ return;
39
+ }
40
+ process.stdout.write(await res.text());
41
+ }
42
+ catch (e) {
43
+ error(String(e));
44
+ }
45
+ });
46
+ // --- prompt ---
47
+ ai.command('prompt <target>')
48
+ .description('Operator-ready prompt block: identity + memory + suggested actions')
49
+ .action(async (target) => {
50
+ try {
51
+ const port = config.products.pentest.controlPort ?? 4091;
52
+ const [ctxR, sugR] = await Promise.all([
53
+ fetch(`http://localhost:${port}/v1/memory/target/${encodeURIComponent(target)}/context`),
54
+ fetchJSON(api(config, `/v1/memory/target/${encodeURIComponent(target)}/copilot`)),
55
+ ]);
56
+ const ctx = ctxR.ok ? await ctxR.text() : "# No prior memory.";
57
+ process.stdout.write([
58
+ "You are operating inside Ghost Protocol's offensive-security stack.",
59
+ "Target context follows. Cite scan_ids when referring to prior findings.",
60
+ "",
61
+ "---",
62
+ "",
63
+ ctx,
64
+ "",
65
+ "## Suggested next actions",
66
+ "",
67
+ ...sugR.suggestions.map((s) => `- **${s.title}** — ${s.rationale}\n Command: \`${s.command}\``),
68
+ "",
69
+ "---",
70
+ "",
71
+ "When the operator asks a question, prefer concrete actions from above.",
72
+ "Never invent findings; if memory is empty, say so and propose a baseline scan.",
73
+ "",
74
+ ].join("\n"));
75
+ }
76
+ catch (e) {
77
+ error(String(e));
78
+ }
79
+ });
80
+ // --- brief ---
81
+ ai.command('brief <target>')
82
+ .description('Print engagement-brief Markdown for paste into any client')
83
+ .action(async (target) => {
84
+ try {
85
+ const port = config.products.pentest.controlPort ?? 4091;
86
+ const res = await fetch(`http://localhost:${port}/v1/memory/target/${encodeURIComponent(target)}/brief`);
87
+ if (!res.ok) {
88
+ error(`HTTP ${res.status}`);
89
+ return;
90
+ }
91
+ process.stdout.write(await res.text());
92
+ }
93
+ catch (e) {
94
+ error(String(e));
95
+ }
96
+ });
97
+ // --- mcp-config ---
98
+ ai.command('mcp-config')
99
+ .description('Print Claude Desktop MCP server configuration')
100
+ .option('--name <name>', 'Server profile name', 'dragon-stack')
101
+ .action((opts) => {
102
+ const cfg = {
103
+ mcpServers: {
104
+ "phantom-memory": {
105
+ command: "phantom-memory-mcp",
106
+ env: { PD_CONTROL_API: `http://localhost:${config.products.pentest.controlPort ?? 4091}` },
107
+ },
108
+ "dragonkeep": {
109
+ command: "dragonkeep-mcp",
110
+ },
111
+ "dragonnet": {
112
+ command: "dragonnet-mcp",
113
+ env: { DRAGONNET_API: `http://localhost:${config.products.net.apiPort ?? 4080}` },
114
+ },
115
+ },
116
+ };
117
+ console.log(label('Dragon AI'), `MCP config for ${chalk.bold(opts.name)}:\n`);
118
+ console.log(JSON.stringify(cfg, null, 2));
119
+ console.log();
120
+ info('Linux: paste into ~/.config/Claude/claude_desktop_config.json');
121
+ info('macOS: ~/Library/Application Support/Claude/claude_desktop_config.json');
122
+ info('Restart Claude Desktop after editing.');
123
+ });
124
+ // --- skills ---
125
+ ai.command('skills')
126
+ .description('Print Claude Code skill paths and an install hint')
127
+ .action(() => {
128
+ const skills = [
129
+ "scan", "memory", "osint", "chronicle", "triage",
130
+ ];
131
+ console.log(label('Dragon AI'), 'Claude Code skills available:\n');
132
+ skills.forEach((s) => console.log(` /${s.padEnd(12)} ~/.claude/skills/dragon-${s}/SKILL.md`));
133
+ console.log();
134
+ info('These are pre-installed in ~/.copilot/skills/ — symlinked into ~/.claude/skills/.');
135
+ });
136
+ }
137
+ //# sourceMappingURL=ai.js.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * dragon alerts — Webhook alert pipeline (Slack / Discord / generic)
3
+ *
4
+ * alerts list
5
+ * alerts add <url> --label NAME --kind slack|discord|generic
6
+ * alerts remove <id>
7
+ * alerts test — fire a test event to every webhook
8
+ *
9
+ * Backed by PhantomDragon Control's /v1/alerts/webhooks endpoints. Any
10
+ * defensive Critical/High finding (DragonKeep → Phantom Memory) auto-
11
+ * fan-outs to every webhook that has on_critical / on_high enabled.
12
+ *
13
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
14
+ * Author: Ryan Sebastian <ryan@ghosts.lk>
15
+ */
16
+ import type { Command } from 'commander';
17
+ import type { DragonConfig } from '../config.js';
18
+ export declare function registerAlertsCommands(program: Command, config: DragonConfig): void;
19
+ //# sourceMappingURL=alerts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../../src/commands/alerts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAmChD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,QA8E5E"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * dragon alerts — Webhook alert pipeline (Slack / Discord / generic)
3
+ *
4
+ * alerts list
5
+ * alerts add <url> --label NAME --kind slack|discord|generic
6
+ * alerts remove <id>
7
+ * alerts test — fire a test event to every webhook
8
+ *
9
+ * Backed by PhantomDragon Control's /v1/alerts/webhooks endpoints. Any
10
+ * defensive Critical/High finding (DragonKeep → Phantom Memory) auto-
11
+ * fan-outs to every webhook that has on_critical / on_high enabled.
12
+ *
13
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
14
+ * Author: Ryan Sebastian <ryan@ghosts.lk>
15
+ */
16
+ import { fetchJSON, label, success, error, info, table } from '../utils.js';
17
+ import chalk from 'chalk';
18
+ function api(config, path) {
19
+ const port = config.products.pentest.controlPort ?? 4091;
20
+ return `http://localhost:${port}${path}`;
21
+ }
22
+ async function postJSON(url, body) {
23
+ const res = await fetch(url, {
24
+ method: 'POST',
25
+ headers: { 'Content-Type': 'application/json' },
26
+ body: JSON.stringify(body),
27
+ });
28
+ if (!res.ok)
29
+ throw new Error(`HTTP ${res.status}: ${await res.text()}`);
30
+ return res.json();
31
+ }
32
+ async function del(url) {
33
+ const res = await fetch(url, { method: 'DELETE' });
34
+ if (!res.ok)
35
+ throw new Error(`HTTP ${res.status}`);
36
+ }
37
+ export function registerAlertsCommands(program, config) {
38
+ const alerts = program
39
+ .command('alerts')
40
+ .description('Webhook alert pipeline (Slack / Discord / generic)');
41
+ alerts
42
+ .command('list')
43
+ .description('List configured webhooks')
44
+ .action(async () => {
45
+ try {
46
+ const d = await fetchJSON(api(config, '/v1/alerts/webhooks'));
47
+ if (d.webhooks.length === 0) {
48
+ info('No webhooks configured. Try: dragon alerts add <url> --label slack');
49
+ return;
50
+ }
51
+ console.log(label('Alerts'), 'Webhooks:\n');
52
+ table(d.webhooks.map((w) => ({
53
+ ID: w.id,
54
+ Label: w.label,
55
+ Kind: w.kind,
56
+ 'Crit/High/Scan': `${w.on_critical ? '✓' : '·'}${w.on_high ? '✓' : '·'}${w.on_scan_complete ? '✓' : '·'}`,
57
+ Enabled: w.enabled ? 'yes' : 'no',
58
+ URL: w.url.slice(0, 50),
59
+ })));
60
+ }
61
+ catch (e) {
62
+ error(String(e));
63
+ }
64
+ });
65
+ alerts
66
+ .command('add <url>')
67
+ .description('Register a new webhook')
68
+ .option('--label <label>', 'Display label', 'webhook')
69
+ .option('--kind <kind>', 'slack | discord | generic', 'generic')
70
+ .option('--on-high', 'Also fire on HIGH (default: CRITICAL only)')
71
+ .option('--on-scan-complete', 'Fire on scan completion events')
72
+ .action(async (url, opts) => {
73
+ try {
74
+ const created = await postJSON(api(config, '/v1/alerts/webhooks'), {
75
+ label: opts.label,
76
+ url,
77
+ kind: opts.kind,
78
+ on_critical: true,
79
+ on_high: !!opts.onHigh,
80
+ on_scan_complete: !!opts.onScanComplete,
81
+ enabled: true,
82
+ });
83
+ success(`Registered webhook ${chalk.bold(created.id)} (${created.kind})`);
84
+ }
85
+ catch (e) {
86
+ error(String(e));
87
+ }
88
+ });
89
+ alerts
90
+ .command('remove <id>')
91
+ .description('Delete a webhook')
92
+ .action(async (id) => {
93
+ try {
94
+ await del(api(config, `/v1/alerts/webhooks/${encodeURIComponent(id)}`));
95
+ success(`Removed webhook ${id}`);
96
+ }
97
+ catch (e) {
98
+ error(String(e));
99
+ }
100
+ });
101
+ alerts
102
+ .command('test')
103
+ .description('Fire a test CRITICAL event to every webhook')
104
+ .action(async () => {
105
+ try {
106
+ const r = await postJSON(api(config, '/v1/alerts/test'), {});
107
+ success(`Fired to ${r.fired} of ${r.webhooks_total} webhook(s)`);
108
+ }
109
+ catch (e) {
110
+ error(String(e));
111
+ }
112
+ });
113
+ }
114
+ //# sourceMappingURL=alerts.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * dragon billing — License management (Paddle / DragonSeal)
3
+ *
4
+ * billing licenses — list active product licences
5
+ * billing check <product> — quick check whether <product> is licensed
6
+ *
7
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
8
+ * Author: Ryan Sebastian <ryan@ghosts.lk>
9
+ */
10
+ import type { Command } from 'commander';
11
+ import type { DragonConfig } from '../config.js';
12
+ export declare function registerBillingCommands(program: Command, config: DragonConfig): void;
13
+ //# sourceMappingURL=billing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billing.d.ts","sourceRoot":"","sources":["../../src/commands/billing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAShD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,QAsC7E"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * dragon billing — License management (Paddle / DragonSeal)
3
+ *
4
+ * billing licenses — list active product licences
5
+ * billing check <product> — quick check whether <product> is licensed
6
+ *
7
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
8
+ * Author: Ryan Sebastian <ryan@ghosts.lk>
9
+ */
10
+ import { fetchJSON, label, error, info } from '../utils.js';
11
+ import chalk from 'chalk';
12
+ function api(config, path) {
13
+ const port = config.products.pentest.controlPort ?? 4091;
14
+ return `http://localhost:${port}${path}`;
15
+ }
16
+ export function registerBillingCommands(program, config) {
17
+ const billing = program.command('billing').description('License management — Paddle / DragonSeal');
18
+ billing.command('licenses')
19
+ .description('Active product licences on this host')
20
+ .option('--email <email>', 'Restrict to a specific operator email')
21
+ .action(async (opts) => {
22
+ try {
23
+ const params = new URLSearchParams();
24
+ if (opts.email)
25
+ params.set('email', opts.email);
26
+ const d = await fetchJSON(api(config, `/v1/billing/licenses?${params}`));
27
+ if (d.licenses.length === 0) {
28
+ info('No active licences. Free tier active for every product.');
29
+ return;
30
+ }
31
+ console.log(label('Licenses'), 'Active products:\n');
32
+ d.licenses.forEach((p) => console.log(` ${chalk.green('●')} ${p}`));
33
+ }
34
+ catch (e) {
35
+ error(String(e));
36
+ }
37
+ });
38
+ billing.command('check <product>')
39
+ .description('Check whether <product> is licensed')
40
+ .option('--email <email>', 'Restrict to a specific operator email')
41
+ .action(async (product, opts) => {
42
+ try {
43
+ const params = new URLSearchParams({ product });
44
+ if (opts.email)
45
+ params.set('email', opts.email);
46
+ const d = await fetchJSON(api(config, `/v1/billing/check?${params}`));
47
+ const sym = d.licensed ? chalk.green('✓ licensed') : chalk.yellow('· free tier');
48
+ console.log(`${product.padEnd(28)} ${sym}`);
49
+ }
50
+ catch (e) {
51
+ error(String(e));
52
+ }
53
+ });
54
+ }
55
+ //# sourceMappingURL=billing.js.map