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,56 @@
1
+ /**
2
+ * dragon mcp — manage extra MCP servers wired into the agent.
3
+ *
4
+ * dragon mcp add <name> <command> [args...] # e.g. dragon mcp add fs npx -- -y @modelcontextprotocol/server-filesystem ~/code
5
+ * dragon mcp list
6
+ * dragon mcp remove <name>
7
+ *
8
+ * Configured servers connect on `dragon chat` and their tools become agent tools
9
+ * (namespaced <server>__<tool>). Wyrm is built-in and not managed here.
10
+ *
11
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
12
+ */
13
+ import { loadConfig, saveConfig } from '../config.js';
14
+ import { C, success, error, info } from '../utils.js';
15
+ import { panel, chrome, statusDot } from '../ui.js';
16
+ export function registerMcpCommands(program, _config) {
17
+ const mcp = program.command('mcp').description('Manage MCP servers wired into the agent');
18
+ mcp
19
+ .command('add <name> <command> [args...]')
20
+ .description('Add an MCP server (stdio). Args after the command are passed to it.')
21
+ .action((name, command, args = []) => {
22
+ const c = loadConfig();
23
+ c.mcpServers = { ...(c.mcpServers ?? {}), [name]: { command, args } };
24
+ saveConfig(c);
25
+ success(`mcp server "${name}" added → ${command} ${args.join(' ')}`);
26
+ info('it connects on `dragon chat`; its tools appear namespaced as ' + C.info(`${name}__<tool>`));
27
+ });
28
+ mcp
29
+ .command('remove <name>')
30
+ .description('Remove a configured MCP server')
31
+ .action((name) => {
32
+ const c = loadConfig();
33
+ if (!c.mcpServers?.[name]) {
34
+ error(`no MCP server named "${name}"`);
35
+ process.exitCode = 1;
36
+ return;
37
+ }
38
+ delete c.mcpServers[name];
39
+ saveConfig(c);
40
+ success(`removed "${name}"`);
41
+ });
42
+ mcp
43
+ .command('list')
44
+ .description('List configured MCP servers')
45
+ .action(() => {
46
+ const servers = loadConfig().mcpServers ?? {};
47
+ const names = Object.keys(servers);
48
+ const lines = names.length
49
+ ? names.map((n) => `${statusDot(true)} ${C.info(n.padEnd(12))} ${C.faint(servers[n].command + ' ' + (servers[n].args ?? []).join(' '))}`)
50
+ : [C.faint('none — add one with `dragon mcp add <name> <command> [args...]`')];
51
+ lines.push('', C.faint('Wyrm memory is built-in (not listed here).'));
52
+ console.log();
53
+ console.log(panel(lines, { title: chrome('MCP SERVERS') }));
54
+ });
55
+ }
56
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * dragon memory — Phantom Memory (the moat)
3
+ *
4
+ * - memory list — every known target with summary
5
+ * - memory show <target> — full dossier
6
+ * - memory context <target> — plaintext brain-context prompt
7
+ * - memory reindex — rebuild memory from reports/
8
+ * - memory verify — diff reports/ vs Phantom Memory
9
+ */
10
+ import type { Command } from 'commander';
11
+ import type { DragonConfig } from '../config.js';
12
+ export declare function registerMemoryCommands(program: Command, config: DragonConfig): void;
13
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/commands/memory.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAqBhD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,QAoM5E"}
@@ -0,0 +1,218 @@
1
+ /**
2
+ * dragon memory — Phantom Memory (the moat)
3
+ *
4
+ * - memory list — every known target with summary
5
+ * - memory show <target> — full dossier
6
+ * - memory context <target> — plaintext brain-context prompt
7
+ * - memory reindex — rebuild memory from reports/
8
+ * - memory verify — diff reports/ vs Phantom Memory
9
+ */
10
+ import { fetchJSON, label, success, error, info, warn, table } 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 registerMemoryCommands(program, config) {
17
+ const mem = program.command('memory').description('Phantom Memory — persistent engagement memory');
18
+ // --- list ---
19
+ mem
20
+ .command('list')
21
+ .description('List every target Phantom Memory has ever touched')
22
+ .action(async () => {
23
+ console.log(label('Phantom Memory'), 'Known targets:\n');
24
+ try {
25
+ const d = await fetchJSON(api(config, '/v1/memory'));
26
+ if (d.targets.length === 0) {
27
+ info('No targets in memory yet — run a scan to seed.');
28
+ return;
29
+ }
30
+ table(d.targets.map((t) => ({
31
+ Target: t.display,
32
+ Scans: String(t.scan_count),
33
+ Findings: String(t.latest_findings ?? '—'),
34
+ Risk: t.latest_risk ?? '—',
35
+ OSINT: String(t.osint_count),
36
+ Entities: String(t.entity_count),
37
+ Creds: String(t.cred_count),
38
+ 'Last seen': new Date(t.last_seen_at).toLocaleDateString(),
39
+ })));
40
+ }
41
+ catch (e) {
42
+ error('Could not reach control_api. Is PhantomDragon Control running? `dragon serve`');
43
+ info(String(e));
44
+ }
45
+ });
46
+ // --- show ---
47
+ mem
48
+ .command('show <target>')
49
+ .description('Full memory dossier on a target')
50
+ .action(async (target) => {
51
+ console.log(label('Phantom Memory'), `Dossier on ${chalk.white(target)}\n`);
52
+ try {
53
+ const d = await fetchJSON(api(config, `/v1/memory/target/${encodeURIComponent(target)}`));
54
+ if (!d.exists) {
55
+ info(`No memory for ${target} yet.`);
56
+ return;
57
+ }
58
+ const s = d.summary;
59
+ info(`First seen ${d.first_seen_at}, last touched ${d.last_seen_at}`);
60
+ info(`${s.scan_count} scans · ${s.persistent_finding_count} persistent findings · ${s.tech_count} techs · ${s.investigation_count} OSINT investigations · ${s.entity_count} entities · ${s.credential_exposure_count} cred exposures\n`);
61
+ if (d.scans?.length) {
62
+ console.log(chalk.dim('── Scans ──'));
63
+ table(d.scans.slice(0, 5).map((sc) => ({
64
+ ID: sc.scan_id.slice(-22),
65
+ Mode: sc.mode || '?',
66
+ Findings: String(sc.total_findings),
67
+ Critical: String(sc.severity_counts?.CRITICAL ?? 0),
68
+ High: String(sc.severity_counts?.HIGH ?? 0),
69
+ Risk: sc.risk_rating ?? '?',
70
+ Started: sc.started_at?.slice(0, 16) ?? '?',
71
+ })));
72
+ console.log();
73
+ }
74
+ if (d.persistent_findings?.length) {
75
+ console.log(chalk.dim('── Persistent findings (≥2 scans) ──'));
76
+ d.persistent_findings.slice(0, 10).forEach((f) => {
77
+ console.log(` ${chalk.bold(f.severity?.padEnd(8))} ${f.title} ${chalk.dim('· ' + f.category)}`);
78
+ });
79
+ console.log();
80
+ }
81
+ if (d.new_findings?.length) {
82
+ console.log(chalk.red('── Regressions (new in latest scan) ──'));
83
+ d.new_findings.slice(0, 8).forEach((f) => {
84
+ console.log(` ${chalk.red(f.severity?.padEnd(8))} ${f.title}`);
85
+ });
86
+ console.log();
87
+ }
88
+ if (d.resolved_findings?.length) {
89
+ console.log(chalk.green('── Resolved (vs prior scan) ──'));
90
+ d.resolved_findings.slice(0, 8).forEach((f) => {
91
+ console.log(` ${chalk.green(f.severity?.padEnd(8))} ${f.title}`);
92
+ });
93
+ console.log();
94
+ }
95
+ if (d.tech_observations?.length) {
96
+ console.log(chalk.dim('── Tech observations ──'));
97
+ console.log(' ' + d.tech_observations.map((t) => `${t.tech}×${t.occurrences}`).join(', '));
98
+ console.log();
99
+ }
100
+ if (d.osint_entity_type_counts && Object.keys(d.osint_entity_type_counts).length) {
101
+ console.log(chalk.dim('── OSINT entities by type ──'));
102
+ console.log(' ' + Object.entries(d.osint_entity_type_counts)
103
+ .sort((a, b) => b[1] - a[1])
104
+ .map(([t, n]) => `${t}×${n}`).join(', '));
105
+ }
106
+ }
107
+ catch (e) {
108
+ error('Fetch failed.');
109
+ info(String(e));
110
+ }
111
+ });
112
+ // --- context ---
113
+ mem
114
+ .command('context <target>')
115
+ .description('Plaintext brain-context prompt for the AI brain')
116
+ .action(async (target) => {
117
+ try {
118
+ const port = config.products.pentest.controlPort ?? 4091;
119
+ const res = await fetch(`http://localhost:${port}/v1/memory/target/${encodeURIComponent(target)}/context`);
120
+ if (!res.ok) {
121
+ error(`HTTP ${res.status}`);
122
+ return;
123
+ }
124
+ const txt = await res.text();
125
+ console.log(txt);
126
+ }
127
+ catch (e) {
128
+ error('Could not reach control_api.');
129
+ info(String(e));
130
+ }
131
+ });
132
+ // --- reindex ---
133
+ mem
134
+ .command('reindex')
135
+ .description('Rebuild Phantom Memory from reports/ directory')
136
+ .action(async () => {
137
+ console.log(label('Phantom Memory'), 'Reindexing...\n');
138
+ try {
139
+ const port = config.products.pentest.controlPort ?? 4091;
140
+ const res = await fetch(`http://localhost:${port}/v1/memory/reindex`, { method: 'POST' });
141
+ if (!res.ok) {
142
+ error(`HTTP ${res.status}`);
143
+ return;
144
+ }
145
+ const d = await res.json();
146
+ success(`Ingested ${d.scans_ingested} scans (skipped ${d.skipped})`);
147
+ }
148
+ catch (e) {
149
+ error('Reindex failed.');
150
+ info(String(e));
151
+ }
152
+ });
153
+ // --- copilot ---
154
+ mem
155
+ .command('copilot <target>')
156
+ .description('What next? — rule-based suggestions from Phantom Memory state')
157
+ .action(async (target) => {
158
+ try {
159
+ const d = await fetchJSON(api(config, `/v1/memory/target/${encodeURIComponent(target)}/copilot`));
160
+ console.log(label('Dragon Copilot'), `Next actions for ${chalk.white(target)}:\n`);
161
+ d.suggestions.forEach((s) => {
162
+ const icon = s.verb === 'alert' ? chalk.red('▲') :
163
+ s.verb === 'scan' ? chalk.green('▶') :
164
+ s.verb === 'osint' ? chalk.cyan('🔍') :
165
+ chalk.dim('●');
166
+ console.log(` ${icon} ${chalk.bold(s.title)}`);
167
+ console.log(` ${chalk.dim(s.rationale)}`);
168
+ console.log(` ${chalk.cyan('$')} ${s.command}\n`);
169
+ });
170
+ }
171
+ catch (e) {
172
+ error('Copilot fetch failed.');
173
+ info(String(e));
174
+ }
175
+ });
176
+ // --- brief ---
177
+ mem
178
+ .command('brief <target>')
179
+ .description('Generate client-deliverable engagement brief (Markdown)')
180
+ .option('--as-of <date>', 'Time-travel: brief as of given ISO date')
181
+ .action(async (target, opts) => {
182
+ try {
183
+ const port = config.products.pentest.controlPort ?? 4091;
184
+ const params = new URLSearchParams();
185
+ if (opts.asOf)
186
+ params.set('as_of', opts.asOf);
187
+ const url = `http://localhost:${port}/v1/memory/target/${encodeURIComponent(target)}/brief${params.size ? `?${params}` : ''}`;
188
+ const res = await fetch(url);
189
+ if (!res.ok) {
190
+ error(`HTTP ${res.status}`);
191
+ return;
192
+ }
193
+ const md = await res.text();
194
+ process.stdout.write(md);
195
+ }
196
+ catch (e) {
197
+ error('Brief fetch failed.');
198
+ info(String(e));
199
+ }
200
+ });
201
+ // --- verify ---
202
+ mem
203
+ .command('verify')
204
+ .description('Diff reports/ vs Phantom Memory (TODO: full implementation; for now triggers reindex)')
205
+ .action(async () => {
206
+ warn('Full diff verification is a deferred feature. Triggering reindex as the safe surrogate.');
207
+ try {
208
+ const port = config.products.pentest.controlPort ?? 4091;
209
+ const res = await fetch(`http://localhost:${port}/v1/memory/reindex`, { method: 'POST' });
210
+ const d = await res.json();
211
+ info(`Reindex result: ${d.scans_ingested} scans ingested, ${d.skipped} skipped.`);
212
+ }
213
+ catch (e) {
214
+ error(String(e));
215
+ }
216
+ });
217
+ }
218
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * dragon osint — DragonNet OSINT Investigation Platform
3
+ *
4
+ * - osint open <target> — open a new investigation seeded with target
5
+ * - osint list — list known investigations
6
+ * - osint show <id> — fetch entities + edges for an investigation
7
+ * - osint search <q> — cross-investigation entity search
8
+ * - osint serve — start the DragonNet API + dashboard
9
+ * - osint legal-posture <jdx> — print per-collector legal posture
10
+ */
11
+ import type { Command } from 'commander';
12
+ import type { DragonConfig } from '../config.js';
13
+ export declare function registerOsintCommands(program: Command, config: DragonConfig): void;
14
+ //# sourceMappingURL=osint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"osint.d.ts","sourceRoot":"","sources":["../../src/commands/osint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAoChD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,QA0H3E"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * dragon osint — DragonNet OSINT Investigation Platform
3
+ *
4
+ * - osint open <target> — open a new investigation seeded with target
5
+ * - osint list — list known investigations
6
+ * - osint show <id> — fetch entities + edges for an investigation
7
+ * - osint search <q> — cross-investigation entity search
8
+ * - osint serve — start the DragonNet API + dashboard
9
+ * - osint legal-posture <jdx> — print per-collector legal posture
10
+ */
11
+ import { getProductPath } from '../config.js';
12
+ import { run, fetchJSON, label, success, error, info, warn, table } from '../utils.js';
13
+ import chalk from 'chalk';
14
+ function api(config, path) {
15
+ const port = config.products.net.apiPort ?? 4080;
16
+ return `http://localhost:${port}${path}`;
17
+ }
18
+ function ui(config, path) {
19
+ const port = config.products.net.uiPort ?? 4081;
20
+ return `http://localhost:${port}${path}`;
21
+ }
22
+ const LEGAL_POSTURES = {
23
+ lk: {
24
+ TORWATCH: "defensible if seeds are public services and operator records authorization (CCA 2007)",
25
+ PASTEDRAGNET: "defensible (public paste sites only)",
26
+ TELEGAZER: "defensible (public previews only); GDPR if EU subjects appear",
27
+ NIGHTGLASS: "operator-bears-responsibility; corpus provenance required",
28
+ },
29
+ us: {
30
+ TORWATCH: "defensible under CFAA if no authentication is bypassed; document seeds",
31
+ PASTEDRAGNET: "defensible (public surface)",
32
+ TELEGAZER: "defensible (public previews)",
33
+ NIGHTGLASS: "operator-bears-responsibility; possessing breach data is grey-area",
34
+ },
35
+ eu: {
36
+ TORWATCH: "defensible; GDPR data-controller obligations apply on EU subjects",
37
+ PASTEDRAGNET: "defensible; honour right-to-erasure",
38
+ TELEGAZER: "defensible; GDPR data-controller obligations apply",
39
+ NIGHTGLASS: "operator-bears-responsibility; GDPR retention rules apply",
40
+ },
41
+ };
42
+ export function registerOsintCommands(program, config) {
43
+ const osint = program.command('osint').description('DragonNet — OSINT investigations (no external API deps)');
44
+ // --- open ---
45
+ osint
46
+ .command('open <target>')
47
+ .description('Open a new DragonNet investigation seeded with the target (opens browser)')
48
+ .action(async (target) => {
49
+ const url = ui(config, `/?seed_target=${encodeURIComponent(target)}&autocreate=1`);
50
+ console.log(label('DragonNet'), `Opening investigation seeded with ${chalk.white(target)}`);
51
+ console.log(` → ${chalk.dim(url)}`);
52
+ // Best-effort open in browser
53
+ const opener = process.platform === 'darwin' ? 'open' :
54
+ process.platform === 'win32' ? 'start' : 'xdg-open';
55
+ try {
56
+ await run(opener, [url], process.cwd());
57
+ }
58
+ catch { /* no opener */ }
59
+ success('Browser launched (or URL printed)');
60
+ });
61
+ // --- list ---
62
+ osint
63
+ .command('list')
64
+ .description('List investigations (requires DragonNet API auth via browser session)')
65
+ .action(async () => {
66
+ console.log(label('DragonNet'), 'Investigations:\n');
67
+ try {
68
+ const d = await fetchJSON(api(config, '/v1/investigations'));
69
+ if (d.investigations.length === 0) {
70
+ info('No investigations yet — try: dragon osint open <target>');
71
+ return;
72
+ }
73
+ table(d.investigations.map((i) => ({
74
+ ID: i.id.slice(-12),
75
+ Name: i.name,
76
+ Status: i.status,
77
+ Created: new Date(i.created_at).toLocaleDateString(),
78
+ })));
79
+ }
80
+ catch (e) {
81
+ error('Could not reach DragonNet API. Is it running? `dragon osint serve`');
82
+ info(String(e));
83
+ }
84
+ });
85
+ // --- show ---
86
+ osint
87
+ .command('show <id>')
88
+ .description('Show an investigation graph summary (entities + edges)')
89
+ .action(async (id) => {
90
+ console.log(label('DragonNet'), `Investigation ${chalk.white(id.slice(-8))}\n`);
91
+ try {
92
+ const d = await fetchJSON(api(config, `/v1/entities/investigation/${id}/graph`));
93
+ info(`Entities: ${d.counts.entities} Edges: ${d.counts.edges}`);
94
+ const byType = {};
95
+ for (const e of d.entities)
96
+ byType[e.type] = (byType[e.type] ?? 0) + 1;
97
+ Object.entries(byType).sort((a, b) => b[1] - a[1]).forEach(([t, n]) => {
98
+ console.log(` ${chalk.green(t.padEnd(20))} ${n}`);
99
+ });
100
+ }
101
+ catch (e) {
102
+ error('Fetch failed (auth required, or DragonNet API offline).');
103
+ info(String(e));
104
+ }
105
+ });
106
+ // --- search ---
107
+ osint
108
+ .command('search <q>')
109
+ .description('Cross-investigation entity search')
110
+ .option('-t, --type <type>', 'Filter by entity type')
111
+ .action(async (q, opts) => {
112
+ const params = new URLSearchParams({ q, limit: '50' });
113
+ if (opts.type)
114
+ params.set('type', opts.type);
115
+ console.log(label('DragonNet'), `Searching for: ${chalk.white(q)}\n`);
116
+ try {
117
+ const d = await fetchJSON(api(config, `/v1/entities/search?${params}`));
118
+ if (d.results.length === 0) {
119
+ info('No hits');
120
+ return;
121
+ }
122
+ table(d.results.map((r) => ({
123
+ Type: r.type,
124
+ Name: r.display_name,
125
+ Investigation: r.investigation_name,
126
+ Created: new Date(r.created_at).toLocaleDateString(),
127
+ })));
128
+ }
129
+ catch (e) {
130
+ error('Search failed (auth required, or DragonNet API offline).');
131
+ info(String(e));
132
+ }
133
+ });
134
+ // --- serve ---
135
+ osint
136
+ .command('serve')
137
+ .description('Start DragonNet API + dashboard (foreground; Ctrl-C to stop)')
138
+ .action(async () => {
139
+ const path = getProductPath(config, 'net');
140
+ console.log(label('DragonNet'), 'Starting API + dashboard...\n');
141
+ info(`API: http://localhost:${config.products.net.apiPort}`);
142
+ info(`Dashboard: http://localhost:${config.products.net.uiPort}\n`);
143
+ await run('pnpm', ['dev'], path);
144
+ });
145
+ // --- legal-posture ---
146
+ osint
147
+ .command('legal-posture [jurisdiction]')
148
+ .description('Print per-collector legal posture for a jurisdiction (lk|us|eu)')
149
+ .action((jurisdiction = 'lk') => {
150
+ const jdx = jurisdiction.toLowerCase();
151
+ const postures = LEGAL_POSTURES[jdx];
152
+ if (!postures) {
153
+ error(`Unknown jurisdiction "${jdx}". Supported: ${Object.keys(LEGAL_POSTURES).join(', ')}`);
154
+ return;
155
+ }
156
+ console.log(label('Legal Posture'), `Jurisdiction: ${chalk.white(jdx.toUpperCase())}\n`);
157
+ table(Object.entries(postures).map(([k, v]) => ({ Collector: k, Posture: v })));
158
+ warn('This is not legal advice. The operator is responsible for compliance in their jurisdiction.');
159
+ });
160
+ }
161
+ //# sourceMappingURL=osint.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * dragon pentest — PhantomDragon Penetration Testing
3
+ *
4
+ * Manages security scans:
5
+ * - Run scans against targets
6
+ * - View/export reports
7
+ * - Update payloads
8
+ * - Configure scan profiles
9
+ */
10
+ import type { Command } from 'commander';
11
+ import type { DragonConfig } from '../config.js';
12
+ export declare function registerPentestCommands(program: Command, config: DragonConfig): void;
13
+ //# sourceMappingURL=pentest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pentest.d.ts","sourceRoot":"","sources":["../../src/commands/pentest.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAOhD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,QAyH7E"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * dragon pentest — PhantomDragon Penetration Testing
3
+ *
4
+ * Manages security scans:
5
+ * - Run scans against targets
6
+ * - View/export reports
7
+ * - Update payloads
8
+ * - Configure scan profiles
9
+ */
10
+ import { getProductPath } from '../config.js';
11
+ import { run, label, success, error, info } from '../utils.js';
12
+ import chalk from 'chalk';
13
+ import { existsSync, readdirSync } from 'fs';
14
+ import { join } from 'path';
15
+ export function registerPentestCommands(program, config) {
16
+ const pentest = program
17
+ .command('pentest')
18
+ .description('PhantomDragon — Web application security scanner');
19
+ // --- scan ---
20
+ pentest
21
+ .command('scan <target>')
22
+ .description('Run a security scan against a target URL')
23
+ .option('-p, --profile <profile>', 'Scan profile: quick|standard|full|api_only|auth_only', 'standard')
24
+ .option('-m, --mode <mode>', 'Scan mode: safe|standard|aggressive|chaos', 'safe')
25
+ .option('-o, --output <dir>', 'Output directory for reports')
26
+ .option('--no-spider', 'Skip crawling/spidering')
27
+ .action(async (target, opts) => {
28
+ const path = getProductPath(config, 'pentest');
29
+ console.log(label('PhantomDragon'), `Scanning ${chalk.white(target)}`);
30
+ console.log(` Profile: ${chalk.dim(opts.profile)} Mode: ${chalk.dim(opts.mode)}\n`);
31
+ const args = ['phantomdragon.py', '-t', target, '--profile', opts.profile, '--mode', opts.mode];
32
+ if (opts.output)
33
+ args.push('-o', opts.output);
34
+ if (opts.noSpider === false)
35
+ args.push('--no-spider');
36
+ const code = await run('python3', args, path);
37
+ code === 0 ? success('Scan complete') : error(`Scan failed (exit ${code})`);
38
+ });
39
+ // --- quick ---
40
+ pentest
41
+ .command('quick <target>')
42
+ .description('Quick scan (shortcut for --profile quick --mode safe)')
43
+ .action(async (target) => {
44
+ const path = getProductPath(config, 'pentest');
45
+ console.log(label('PhantomDragon'), `Quick scan: ${chalk.white(target)}\n`);
46
+ const code = await run('python3', ['phantomdragon.py', '-t', target, '--profile', 'quick', '--mode', 'safe'], path);
47
+ code === 0 ? success('Quick scan complete') : error(`Scan failed (exit ${code})`);
48
+ });
49
+ // --- reports ---
50
+ pentest
51
+ .command('reports')
52
+ .description('List generated reports')
53
+ .action(async () => {
54
+ const path = getProductPath(config, 'pentest');
55
+ const reportsDir = join(path, 'reports');
56
+ if (!existsSync(reportsDir)) {
57
+ info('No reports directory');
58
+ return;
59
+ }
60
+ console.log(label('PhantomDragon'), 'Reports:\n');
61
+ const entries = readdirSync(reportsDir, { withFileTypes: true });
62
+ const dirs = entries.filter(e => e.isDirectory()).sort((a, b) => b.name.localeCompare(a.name));
63
+ const files = entries.filter(e => e.isFile() && e.name.endsWith('.md'));
64
+ for (const d of dirs) {
65
+ console.log(` ${chalk.green('📁')} ${d.name}`);
66
+ }
67
+ for (const f of files) {
68
+ console.log(` ${chalk.dim('📄')} ${f.name}`);
69
+ }
70
+ if (dirs.length === 0 && files.length === 0) {
71
+ info('No reports found');
72
+ }
73
+ });
74
+ // --- payloads ---
75
+ pentest
76
+ .command('payloads')
77
+ .description('Show payload statistics')
78
+ .action(async () => {
79
+ const path = getProductPath(config, 'pentest');
80
+ const payloadsDir = join(path, 'payloads');
81
+ if (!existsSync(payloadsDir)) {
82
+ info('No payloads directory');
83
+ return;
84
+ }
85
+ console.log(label('PhantomDragon'), 'Payload inventory:\n');
86
+ const categories = readdirSync(payloadsDir, { withFileTypes: true }).filter(e => e.isDirectory());
87
+ const { readFileSync: rf } = await import('fs');
88
+ let total = 0;
89
+ for (const cat of categories) {
90
+ const catPath = join(payloadsDir, cat.name);
91
+ const files = readdirSync(catPath).filter(f => f.endsWith('.txt') || f.endsWith('.json'));
92
+ let count = 0;
93
+ for (const f of files) {
94
+ try {
95
+ // Safe line count — no shell interpolation of filename
96
+ const body = rf(join(catPath, f), 'utf-8');
97
+ count += body.length === 0 ? 0 : body.split('\n').length;
98
+ }
99
+ catch { /* skip */ }
100
+ }
101
+ total += count;
102
+ console.log(` ${chalk.green(cat.name.padEnd(20))} ${chalk.white(String(count).padStart(5))} payloads ${chalk.dim(`(${files.length} files)`)}`);
103
+ }
104
+ console.log(chalk.dim(`\n Total: ${total} payloads`));
105
+ });
106
+ // --- config ---
107
+ pentest
108
+ .command('config')
109
+ .description('Show current scan configuration')
110
+ .action(async () => {
111
+ const path = getProductPath(config, 'pentest');
112
+ const configFile = join(path, 'config/pentest.json');
113
+ if (!existsSync(configFile)) {
114
+ error('Config not found: config/pentest.json');
115
+ return;
116
+ }
117
+ console.log(label('PhantomDragon'), 'Configuration:\n');
118
+ await run('cat', [configFile], path);
119
+ });
120
+ // --- update ---
121
+ pentest
122
+ .command('update')
123
+ .description('Update PhantomDragon (git pull)')
124
+ .action(async () => {
125
+ const path = getProductPath(config, 'pentest');
126
+ console.log(label('PhantomDragon'), 'Updating...\n');
127
+ const code = await run('git', ['pull', '--rebase'], path);
128
+ code === 0 ? success('Updated') : error(`Update failed (exit ${code})`);
129
+ });
130
+ }
131
+ //# sourceMappingURL=pentest.js.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * dragon scale — DragonScale Commerce Platform management
3
+ *
4
+ * Manages Upalis/DragonScale deployments:
5
+ * - Status & health checks via API
6
+ * - Order management
7
+ * - Menu management
8
+ * - Database operations (backup, seed)
9
+ * - Deployment (pull, update)
10
+ */
11
+ import type { Command } from 'commander';
12
+ import type { DragonConfig } from '../config.js';
13
+ export declare function registerScaleCommands(program: Command, config: DragonConfig): void;
14
+ //# sourceMappingURL=scale.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scale.d.ts","sourceRoot":"","sources":["../../src/commands/scale.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAOhD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,QAqK3E"}