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.
- package/.github/workflows/ci.yml +23 -0
- package/CHANGELOG.md +96 -0
- package/README.md +193 -0
- package/bootstrap.ps1 +83 -0
- package/bootstrap.sh +71 -0
- package/dist/agent/loop.d.ts +68 -0
- package/dist/agent/loop.d.ts.map +1 -0
- package/dist/agent/loop.js +135 -0
- package/dist/agent/mcp.d.ts +33 -0
- package/dist/agent/mcp.d.ts.map +1 -0
- package/dist/agent/mcp.js +107 -0
- package/dist/agent/session.d.ts +16 -0
- package/dist/agent/session.d.ts.map +1 -0
- package/dist/agent/session.js +55 -0
- package/dist/agent/skills.d.ts +36 -0
- package/dist/agent/skills.d.ts.map +1 -0
- package/dist/agent/skills.js +153 -0
- package/dist/agent/stack.d.ts +21 -0
- package/dist/agent/stack.d.ts.map +1 -0
- package/dist/agent/stack.js +158 -0
- package/dist/agent/task.d.ts +21 -0
- package/dist/agent/task.d.ts.map +1 -0
- package/dist/agent/task.js +45 -0
- package/dist/agent/tools.d.ts +44 -0
- package/dist/agent/tools.d.ts.map +1 -0
- package/dist/agent/tools.js +262 -0
- package/dist/agent/trace.d.ts +34 -0
- package/dist/agent/trace.d.ts.map +1 -0
- package/dist/agent/trace.js +72 -0
- package/dist/agent.d.ts +46 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +103 -0
- package/dist/auth.d.ts +74 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +116 -0
- package/dist/brain/anthropic.d.ts +19 -0
- package/dist/brain/anthropic.d.ts.map +1 -0
- package/dist/brain/anthropic.js +74 -0
- package/dist/brain/claude-cli.d.ts +20 -0
- package/dist/brain/claude-cli.d.ts.map +1 -0
- package/dist/brain/claude-cli.js +79 -0
- package/dist/brain/ghost-ember.d.ts +28 -0
- package/dist/brain/ghost-ember.d.ts.map +1 -0
- package/dist/brain/ghost-ember.js +97 -0
- package/dist/brain/index.d.ts +22 -0
- package/dist/brain/index.d.ts.map +1 -0
- package/dist/brain/index.js +95 -0
- package/dist/brain/openai-compat.d.ts +21 -0
- package/dist/brain/openai-compat.d.ts.map +1 -0
- package/dist/brain/openai-compat.js +119 -0
- package/dist/brain/router/classify.d.ts +23 -0
- package/dist/brain/router/classify.d.ts.map +1 -0
- package/dist/brain/router/classify.js +160 -0
- package/dist/brain/router/execute.d.ts +23 -0
- package/dist/brain/router/execute.d.ts.map +1 -0
- package/dist/brain/router/execute.js +84 -0
- package/dist/brain/router/index.d.ts +26 -0
- package/dist/brain/router/index.d.ts.map +1 -0
- package/dist/brain/router/index.js +118 -0
- package/dist/brain/router/routing-memory.d.ts +27 -0
- package/dist/brain/router/routing-memory.d.ts.map +1 -0
- package/dist/brain/router/routing-memory.js +77 -0
- package/dist/brain/router/select.d.ts +32 -0
- package/dist/brain/router/select.d.ts.map +1 -0
- package/dist/brain/router/select.js +146 -0
- package/dist/brain/router/two-hop.d.ts +23 -0
- package/dist/brain/router/two-hop.d.ts.map +1 -0
- package/dist/brain/router/two-hop.js +39 -0
- package/dist/brain/router/verify.d.ts +37 -0
- package/dist/brain/router/verify.d.ts.map +1 -0
- package/dist/brain/router/verify.js +111 -0
- package/dist/brain/types.d.ts +55 -0
- package/dist/brain/types.d.ts.map +1 -0
- package/dist/brain/types.js +16 -0
- package/dist/brain/worker.d.ts +27 -0
- package/dist/brain/worker.d.ts.map +1 -0
- package/dist/brain/worker.js +71 -0
- package/dist/commands/ai.d.ts +24 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +137 -0
- package/dist/commands/alerts.d.ts +19 -0
- package/dist/commands/alerts.d.ts.map +1 -0
- package/dist/commands/alerts.js +114 -0
- package/dist/commands/billing.d.ts +13 -0
- package/dist/commands/billing.d.ts.map +1 -0
- package/dist/commands/billing.js +55 -0
- package/dist/commands/chat.d.ts +22 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +422 -0
- package/dist/commands/config.d.ts +18 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +136 -0
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +73 -0
- package/dist/commands/global.d.ts +11 -0
- package/dist/commands/global.d.ts.map +1 -0
- package/dist/commands/global.js +253 -0
- package/dist/commands/keep.d.ts +12 -0
- package/dist/commands/keep.d.ts.map +1 -0
- package/dist/commands/keep.js +58 -0
- package/dist/commands/lifecycle.d.ts +17 -0
- package/dist/commands/lifecycle.d.ts.map +1 -0
- package/dist/commands/lifecycle.js +267 -0
- package/dist/commands/login.d.ts +16 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +234 -0
- package/dist/commands/maintenance.d.ts +12 -0
- package/dist/commands/maintenance.d.ts.map +1 -0
- package/dist/commands/maintenance.js +76 -0
- package/dist/commands/mcp.d.ts +16 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +56 -0
- package/dist/commands/memory.d.ts +13 -0
- package/dist/commands/memory.d.ts.map +1 -0
- package/dist/commands/memory.js +218 -0
- package/dist/commands/osint.d.ts +14 -0
- package/dist/commands/osint.d.ts.map +1 -0
- package/dist/commands/osint.js +161 -0
- package/dist/commands/pentest.d.ts +13 -0
- package/dist/commands/pentest.d.ts.map +1 -0
- package/dist/commands/pentest.js +131 -0
- package/dist/commands/scale.d.ts +14 -0
- package/dist/commands/scale.d.ts.map +1 -0
- package/dist/commands/scale.js +191 -0
- package/dist/commands/serve.d.ts +16 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +167 -0
- package/dist/commands/tui.d.ts +17 -0
- package/dist/commands/tui.d.ts.map +1 -0
- package/dist/commands/tui.js +138 -0
- package/dist/commands/wyrm.d.ts +20 -0
- package/dist/commands/wyrm.d.ts.map +1 -0
- package/dist/commands/wyrm.js +274 -0
- package/dist/config.d.ts +67 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +54 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +85 -0
- package/dist/manifest.d.ts +31 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +83 -0
- package/dist/ui.d.ts +57 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +174 -0
- package/dist/utils.d.ts +33 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +155 -0
- package/dist/wyrm/mcp.d.ts +37 -0
- package/dist/wyrm/mcp.d.ts.map +1 -0
- package/dist/wyrm/mcp.js +137 -0
- package/docs/SYSTEM-PREMORTEM.md +397 -0
- package/dragon-manifest.toml +241 -0
- package/dragon.py +177 -0
- package/install/launchd/lk.ghosts.dragonkeep.plist +57 -0
- package/install/systemd/dragonkeep.service +40 -0
- package/media/dragon-silver-lockup.svg +931 -0
- package/media/dragon-silver-mark.svg +931 -0
- package/media/dragon-silver.png +0 -0
- package/package.json +45 -0
- package/specs/001-godmode/constitution.md +54 -0
- package/specs/001-godmode/plan.md +30 -0
- package/specs/001-godmode/spec.md +64 -0
- package/specs/001-godmode/tasks.md +35 -0
- package/specs/002-premortem-positioning/premortem.md +211 -0
- package/src/agent/loop.ts +165 -0
- package/src/agent/mcp.ts +92 -0
- package/src/agent/session.ts +48 -0
- package/src/agent/skills.ts +138 -0
- package/src/agent/stack.ts +154 -0
- package/src/agent/task.ts +55 -0
- package/src/agent/tools.ts +255 -0
- package/src/agent/trace.ts +76 -0
- package/src/agent.ts +114 -0
- package/src/auth.ts +133 -0
- package/src/brain/anthropic.ts +83 -0
- package/src/brain/claude-cli.ts +78 -0
- package/src/brain/ghost-ember.ts +94 -0
- package/src/brain/index.ts +99 -0
- package/src/brain/openai-compat.ts +115 -0
- package/src/brain/router/classify.ts +167 -0
- package/src/brain/router/execute.ts +80 -0
- package/src/brain/router/index.ts +125 -0
- package/src/brain/router/routing-memory.ts +71 -0
- package/src/brain/router/select.ts +156 -0
- package/src/brain/router/two-hop.ts +62 -0
- package/src/brain/router/verify.ts +123 -0
- package/src/brain/types.ts +61 -0
- package/src/brain/worker.ts +72 -0
- package/src/commands/ai.ts +144 -0
- package/src/commands/alerts.ts +131 -0
- package/src/commands/billing.ts +59 -0
- package/src/commands/chat.ts +318 -0
- package/src/commands/config.ts +137 -0
- package/src/commands/doctor.ts +71 -0
- package/src/commands/global.ts +256 -0
- package/src/commands/keep.ts +67 -0
- package/src/commands/lifecycle.ts +273 -0
- package/src/commands/login.ts +184 -0
- package/src/commands/maintenance.ts +54 -0
- package/src/commands/mcp.ts +57 -0
- package/src/commands/memory.ts +229 -0
- package/src/commands/osint.ts +171 -0
- package/src/commands/pentest.ts +140 -0
- package/src/commands/scale.ts +185 -0
- package/src/commands/serve.ts +171 -0
- package/src/commands/tui.ts +126 -0
- package/src/commands/wyrm.ts +269 -0
- package/src/config.ts +93 -0
- package/src/index.ts +92 -0
- package/src/manifest.ts +104 -0
- package/src/ui.ts +188 -0
- package/src/utils.ts +153 -0
- package/src/wyrm/mcp.ts +130 -0
- package/test/auth.test.ts +70 -0
- package/test/brain.test.ts +39 -0
- package/test/security.test.ts +104 -0
- package/test/skills.test.ts +38 -0
- package/test/ui.test.ts +46 -0
- package/tsconfig.json +19 -0
- package/worker/package-lock.json +1527 -0
- package/worker/package.json +17 -0
- package/worker/src/index.ts +76 -0
- package/worker/tsconfig.json +15 -0
- package/worker/wrangler.toml +26 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dragon wyrm — Wyrm AI Memory System bridge.
|
|
3
|
+
*
|
|
4
|
+
* dragon wyrm status — health + stats from the running HTTP API
|
|
5
|
+
* dragon wyrm serve — start the Wyrm HTTP API on :3333
|
|
6
|
+
* dragon wyrm mcp — start the Wyrm MCP server over stdio
|
|
7
|
+
* dragon wyrm projects — list registered projects
|
|
8
|
+
* dragon wyrm scan <path> — discover git projects under <path>
|
|
9
|
+
* dragon wyrm search <q> — search across all memory
|
|
10
|
+
* dragon wyrm quests — list active quests
|
|
11
|
+
* dragon wyrm maintenance — vacuum + archive the database
|
|
12
|
+
*
|
|
13
|
+
* Endpoints + binary paths match Wyrm v5.x (packages/mcp-server). The
|
|
14
|
+
* earlier shape (`/api/health`, `npx wyrm`, `wyrm-deploy`) was written
|
|
15
|
+
* against a v3-era layout that no longer exists.
|
|
16
|
+
*/
|
|
17
|
+
import { getProductPath } from '../config.js';
|
|
18
|
+
import { run, label, success, error, info, warn, table } from '../utils.js';
|
|
19
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import { homedir } from 'node:os';
|
|
22
|
+
import chalk from 'chalk';
|
|
23
|
+
function wyrmUrl(config) {
|
|
24
|
+
const port = config.products.wyrm?.port || 3333;
|
|
25
|
+
return `http://127.0.0.1:${port}`;
|
|
26
|
+
}
|
|
27
|
+
// The Wyrm HTTP server enforces Bearer auth. The raw key is shown once
|
|
28
|
+
// at `wyrm-setup` time and expected to be exported as WYRM_API_KEY. We
|
|
29
|
+
// also accept a key stashed in ~/.wyrm/api-key for convenience.
|
|
30
|
+
function wyrmToken() {
|
|
31
|
+
if (process.env.WYRM_API_KEY)
|
|
32
|
+
return process.env.WYRM_API_KEY.trim();
|
|
33
|
+
const f = join(homedir(), '.wyrm', 'api-key');
|
|
34
|
+
if (existsSync(f)) {
|
|
35
|
+
try {
|
|
36
|
+
return readFileSync(f, 'utf-8').trim();
|
|
37
|
+
}
|
|
38
|
+
catch { /* fall through */ }
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
async function wyrmFetch(url, init = {}, timeoutMs = 5000) {
|
|
43
|
+
const token = wyrmToken();
|
|
44
|
+
const headers = {
|
|
45
|
+
'Accept': 'application/json',
|
|
46
|
+
...(init.headers ?? {}),
|
|
47
|
+
};
|
|
48
|
+
if (token)
|
|
49
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
50
|
+
if (init.body && !headers['Content-Type'])
|
|
51
|
+
headers['Content-Type'] = 'application/json';
|
|
52
|
+
const controller = new AbortController();
|
|
53
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
54
|
+
try {
|
|
55
|
+
const res = await fetch(url, { ...init, headers, signal: controller.signal });
|
|
56
|
+
if (res.status === 401) {
|
|
57
|
+
throw new Error('401 unauthorized — set WYRM_API_KEY (`wyrm-setup` prints one on first run)');
|
|
58
|
+
}
|
|
59
|
+
if (!res.ok)
|
|
60
|
+
throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
61
|
+
return res.json();
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Resolve the runtime entry. Prefer the built file in the installed
|
|
68
|
+
// monorepo over `npx`, because Wyrm has no root package.json and
|
|
69
|
+
// `npx wyrm` will try to fetch from the public registry.
|
|
70
|
+
function wyrmEntry(path, name) {
|
|
71
|
+
const candidates = [
|
|
72
|
+
join(path, 'packages', 'mcp-server', 'dist', `${name}.js`),
|
|
73
|
+
join(path, 'dist', `${name}.js`),
|
|
74
|
+
];
|
|
75
|
+
return candidates.find(existsSync) ?? null;
|
|
76
|
+
}
|
|
77
|
+
export function registerWyrmCommands(program, config) {
|
|
78
|
+
const wyrm = program
|
|
79
|
+
.command('wyrm')
|
|
80
|
+
.description('Wyrm — persistent AI memory (HTTP API + MCP server)');
|
|
81
|
+
// --- status ---
|
|
82
|
+
wyrm
|
|
83
|
+
.command('status')
|
|
84
|
+
.description('Check Wyrm server health & stats')
|
|
85
|
+
.action(async () => {
|
|
86
|
+
const url = wyrmUrl(config);
|
|
87
|
+
console.log(label('Wyrm'), `Probing ${chalk.dim(url)}\n`);
|
|
88
|
+
try {
|
|
89
|
+
const health = await wyrmFetch(`${url}/health`);
|
|
90
|
+
let stats = {};
|
|
91
|
+
try {
|
|
92
|
+
stats = await wyrmFetch(`${url}/stats`);
|
|
93
|
+
}
|
|
94
|
+
catch { /* stats may need auth */ }
|
|
95
|
+
console.log(` Server: ${chalk.green('● online')} ${chalk.dim(`v${health.version ?? '?'}`)}`);
|
|
96
|
+
console.log(` Projects: ${stats.projects ?? chalk.dim('—')}`);
|
|
97
|
+
console.log(` Sessions: ${stats.sessions ?? chalk.dim('—')}`);
|
|
98
|
+
console.log(` Quests: ${stats.quests ?? chalk.dim('—')}`);
|
|
99
|
+
if (stats.data_entries !== undefined) {
|
|
100
|
+
console.log(` Data Lake: ${stats.data_entries} entries`);
|
|
101
|
+
}
|
|
102
|
+
console.log();
|
|
103
|
+
success('Wyrm is running');
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
error(`Wyrm not reachable — ${e.message}`);
|
|
107
|
+
info(`Start it with: ${chalk.green('dragon wyrm serve')}`);
|
|
108
|
+
process.exitCode = 1;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
// --- serve (HTTP API) ---
|
|
112
|
+
wyrm
|
|
113
|
+
.command('serve')
|
|
114
|
+
.description('Start the Wyrm HTTP API server')
|
|
115
|
+
.option('-p, --port <port>', 'Port number', '3333')
|
|
116
|
+
.action(async (opts) => {
|
|
117
|
+
const path = getProductPath(config, 'wyrm');
|
|
118
|
+
const entry = wyrmEntry(path, 'http-server');
|
|
119
|
+
if (!entry) {
|
|
120
|
+
error(`No built Wyrm at ${path}`);
|
|
121
|
+
info(`Run: ${chalk.green('dragon install wyrm')} first`);
|
|
122
|
+
process.exit(2);
|
|
123
|
+
}
|
|
124
|
+
console.log(label('Wyrm'), `Starting HTTP API on :${opts.port}…\n`);
|
|
125
|
+
const code = await run('node', [entry], path, { env: { ...process.env, WYRM_PORT: String(opts.port) } });
|
|
126
|
+
if (code !== 0)
|
|
127
|
+
error(`Server exited with code ${code}`);
|
|
128
|
+
});
|
|
129
|
+
// --- mcp (stdio) ---
|
|
130
|
+
wyrm
|
|
131
|
+
.command('mcp')
|
|
132
|
+
.description('Start the Wyrm MCP server over stdio')
|
|
133
|
+
.action(async () => {
|
|
134
|
+
const path = getProductPath(config, 'wyrm');
|
|
135
|
+
const entry = wyrmEntry(path, 'index');
|
|
136
|
+
if (!entry) {
|
|
137
|
+
error(`No built Wyrm at ${path}`);
|
|
138
|
+
info(`Run: ${chalk.green('dragon install wyrm')} first`);
|
|
139
|
+
process.exit(2);
|
|
140
|
+
}
|
|
141
|
+
// Don't print a label — MCP clients read JSON-RPC from stdout.
|
|
142
|
+
const code = await run('node', [entry], path);
|
|
143
|
+
if (code !== 0)
|
|
144
|
+
error(`MCP server exited with code ${code}`);
|
|
145
|
+
});
|
|
146
|
+
// --- projects ---
|
|
147
|
+
wyrm
|
|
148
|
+
.command('projects')
|
|
149
|
+
.description('List registered projects')
|
|
150
|
+
.action(async () => {
|
|
151
|
+
const url = wyrmUrl(config);
|
|
152
|
+
try {
|
|
153
|
+
const data = await wyrmFetch(`${url}/projects`);
|
|
154
|
+
const list = data.projects ?? data ?? [];
|
|
155
|
+
if (Array.isArray(list) && list.length) {
|
|
156
|
+
table(list.map((p) => ({
|
|
157
|
+
Name: p.name ?? p.id ?? '—',
|
|
158
|
+
Path: p.path ?? '—',
|
|
159
|
+
Language: p.language ?? '—',
|
|
160
|
+
Sessions: String(p.session_count ?? p.sessions ?? 0),
|
|
161
|
+
})));
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
info('No projects registered. Run: dragon wyrm scan <path>');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
error(`Failed: ${e.message}`);
|
|
169
|
+
process.exitCode = 1;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
// --- scan ---
|
|
173
|
+
wyrm
|
|
174
|
+
.command('scan <path>')
|
|
175
|
+
.description('Scan directory for git projects')
|
|
176
|
+
.action(async (scanPath) => {
|
|
177
|
+
const url = wyrmUrl(config);
|
|
178
|
+
console.log(label('Wyrm'), `Scanning ${chalk.dim(scanPath)}…\n`);
|
|
179
|
+
try {
|
|
180
|
+
const data = await wyrmFetch(`${url}/projects/scan`, {
|
|
181
|
+
method: 'POST',
|
|
182
|
+
body: JSON.stringify({ path: scanPath }),
|
|
183
|
+
});
|
|
184
|
+
success(`Found ${data.projects_found ?? data.found ?? 0} project(s)`);
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
error(`Failed: ${e.message}`);
|
|
188
|
+
process.exitCode = 1;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
// --- search ---
|
|
192
|
+
wyrm
|
|
193
|
+
.command('search <query>')
|
|
194
|
+
.description('Search across all memory')
|
|
195
|
+
.action(async (query) => {
|
|
196
|
+
const url = wyrmUrl(config);
|
|
197
|
+
try {
|
|
198
|
+
const data = await wyrmFetch(`${url}/search?q=${encodeURIComponent(query)}`);
|
|
199
|
+
const results = data.results ?? data ?? [];
|
|
200
|
+
if (Array.isArray(results) && results.length) {
|
|
201
|
+
for (const r of results) {
|
|
202
|
+
const snippet = (r.content ?? r.snippet ?? '').slice(0, 120);
|
|
203
|
+
console.log(` ${chalk.green(r.project ?? 'global')} ${chalk.dim('—')} ${snippet}`);
|
|
204
|
+
}
|
|
205
|
+
console.log(chalk.dim(`\n ${results.length} result(s)`));
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
info('No results');
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (e) {
|
|
212
|
+
error(`Failed: ${e.message}`);
|
|
213
|
+
process.exitCode = 1;
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
// --- quests ---
|
|
217
|
+
wyrm
|
|
218
|
+
.command('quests')
|
|
219
|
+
.description('List active quests/tasks')
|
|
220
|
+
.option('-a, --all', 'Include completed quests')
|
|
221
|
+
.action(async (opts) => {
|
|
222
|
+
const url = wyrmUrl(config);
|
|
223
|
+
try {
|
|
224
|
+
const data = await wyrmFetch(`${url}/quests${opts.all ? '?all=1' : ''}`);
|
|
225
|
+
const quests = data.quests ?? data ?? [];
|
|
226
|
+
if (Array.isArray(quests) && quests.length) {
|
|
227
|
+
for (const q of quests) {
|
|
228
|
+
const icon = q.status === 'completed' ? chalk.green('✓') : chalk.dim('○');
|
|
229
|
+
console.log(` ${icon} ${q.title ?? q.name ?? q.id} ${chalk.dim(q.project ?? '')}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
info('No active quests');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (e) {
|
|
237
|
+
error(`Failed: ${e.message}`);
|
|
238
|
+
process.exitCode = 1;
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
// --- maintenance ---
|
|
242
|
+
wyrm
|
|
243
|
+
.command('maintenance')
|
|
244
|
+
.description('Run database maintenance (vacuum, archive)')
|
|
245
|
+
.action(async () => {
|
|
246
|
+
const url = wyrmUrl(config);
|
|
247
|
+
console.log(label('Wyrm'), 'Running maintenance…\n');
|
|
248
|
+
try {
|
|
249
|
+
const data = await wyrmFetch(`${url}/maintenance`, { method: 'POST' });
|
|
250
|
+
success(data.message ?? 'Maintenance complete');
|
|
251
|
+
}
|
|
252
|
+
catch (e) {
|
|
253
|
+
error(`Failed: ${e.message}`);
|
|
254
|
+
process.exitCode = 1;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
// Default action — short summary instead of dumping help.
|
|
258
|
+
wyrm.action(() => {
|
|
259
|
+
const tokened = wyrmToken() !== null;
|
|
260
|
+
console.log();
|
|
261
|
+
console.log(` ${label('Wyrm')} ${chalk.dim('persistent AI memory · HTTP :3333 · MCP stdio')}`);
|
|
262
|
+
console.log();
|
|
263
|
+
console.log(` ${chalk.dim('dragon wyrm status')} ${chalk.dim('— server health + stats')}`);
|
|
264
|
+
console.log(` ${chalk.dim('dragon wyrm serve')} ${chalk.dim('— start the HTTP API')}`);
|
|
265
|
+
console.log(` ${chalk.dim('dragon wyrm mcp')} ${chalk.dim('— start the MCP server (stdio)')}`);
|
|
266
|
+
console.log();
|
|
267
|
+
if (!tokened) {
|
|
268
|
+
warn('WYRM_API_KEY not set — auth-protected endpoints will 401');
|
|
269
|
+
info(`Print one with: ${chalk.green('cd ~/Git\\ Projects/Wyrm/packages/mcp-server && node dist/setup.js')}`);
|
|
270
|
+
}
|
|
271
|
+
console.log();
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=wyrm.js.map
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dragon CLI configuration
|
|
3
|
+
* Stores product paths & settings in ~/.dragon/config.json
|
|
4
|
+
*/
|
|
5
|
+
export interface DragonConfig {
|
|
6
|
+
products: {
|
|
7
|
+
scale: {
|
|
8
|
+
path: string;
|
|
9
|
+
url?: string;
|
|
10
|
+
};
|
|
11
|
+
wyrm: {
|
|
12
|
+
path: string;
|
|
13
|
+
port?: number;
|
|
14
|
+
};
|
|
15
|
+
pentest: {
|
|
16
|
+
path: string;
|
|
17
|
+
controlPort?: number;
|
|
18
|
+
controlUiPort?: number;
|
|
19
|
+
};
|
|
20
|
+
keep: {
|
|
21
|
+
path: string;
|
|
22
|
+
};
|
|
23
|
+
net: {
|
|
24
|
+
path: string;
|
|
25
|
+
apiPort?: number;
|
|
26
|
+
uiPort?: number;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
defaults: {
|
|
30
|
+
editor: string;
|
|
31
|
+
};
|
|
32
|
+
/** Dragon assistant (account.ghosts.lk) auth for the hosted portal tool. */
|
|
33
|
+
auth?: {
|
|
34
|
+
apiBase?: string;
|
|
35
|
+
token?: string;
|
|
36
|
+
session?: string;
|
|
37
|
+
email?: string;
|
|
38
|
+
};
|
|
39
|
+
/** The agent's reasoning brain (tools always run locally; this is the model). */
|
|
40
|
+
brain?: {
|
|
41
|
+
provider?: 'claude' | 'openai' | 'local' | 'ghost' | 'worker' | 'custom' | 'router';
|
|
42
|
+
model?: string;
|
|
43
|
+
localModel?: string;
|
|
44
|
+
localBaseURL?: string;
|
|
45
|
+
ghostModel?: string;
|
|
46
|
+
ghostBaseURL?: string;
|
|
47
|
+
customBaseURL?: string;
|
|
48
|
+
customModel?: string;
|
|
49
|
+
keys?: {
|
|
50
|
+
anthropic?: string;
|
|
51
|
+
openai?: string;
|
|
52
|
+
};
|
|
53
|
+
routerWorkhorse?: string;
|
|
54
|
+
routerReasoner?: string;
|
|
55
|
+
routerCheap?: string;
|
|
56
|
+
};
|
|
57
|
+
/** Extra MCP servers wired into the agent (`dragon mcp add`). */
|
|
58
|
+
mcpServers?: Record<string, {
|
|
59
|
+
command: string;
|
|
60
|
+
args?: string[];
|
|
61
|
+
env?: Record<string, string>;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
export declare function loadConfig(): DragonConfig;
|
|
65
|
+
export declare function saveConfig(config: DragonConfig): void;
|
|
66
|
+
export declare function getProductPath(config: DragonConfig, product: keyof DragonConfig['products']): string;
|
|
67
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE;QACR,KAAK,EAAI;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,GAAG,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;QACvC,IAAI,EAAK;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;QACxC,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;QACvE,IAAI,EAAK;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;QACzB,GAAG,EAAM;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAC7D,CAAA;IACD,QAAQ,EAAE;QACR,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;IACD,4EAA4E;IAC5E,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;IACD,iFAAiF;IACjF,KAAK,CAAC,EAAE;QACN,QAAQ,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;QACnF,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,IAAI,CAAC,EAAE;YAAE,SAAS,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;QAE9C,eAAe,CAAC,EAAE,MAAM,CAAA;QACxB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,CAAA;IACD,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC,CAAA;CAChG;AAkBD,wBAAgB,UAAU,IAAI,YAAY,CAazC;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAMrD;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC,UAAU,CAAC,GAAG,MAAM,CAMpG"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dragon CLI configuration
|
|
3
|
+
* Stores product paths & settings in ~/.dragon/config.json
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync } from 'fs';
|
|
6
|
+
import { homedir } from 'os';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
const CONFIG_DIR = join(homedir(), '.dragon');
|
|
9
|
+
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
10
|
+
const DEFAULT_CONFIG = {
|
|
11
|
+
products: {
|
|
12
|
+
scale: { path: '' },
|
|
13
|
+
wyrm: { path: '', port: 3333 },
|
|
14
|
+
pentest: { path: '', controlPort: 4091, controlUiPort: 4090 },
|
|
15
|
+
keep: { path: '' },
|
|
16
|
+
net: { path: '', apiPort: 4080, uiPort: 4081 },
|
|
17
|
+
},
|
|
18
|
+
defaults: {
|
|
19
|
+
editor: process.env.EDITOR || 'code',
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
export function loadConfig() {
|
|
23
|
+
if (!existsSync(CONFIG_FILE)) {
|
|
24
|
+
return DEFAULT_CONFIG;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const raw = readFileSync(CONFIG_FILE, 'utf-8');
|
|
28
|
+
const merged = { ...DEFAULT_CONFIG, ...JSON.parse(raw) };
|
|
29
|
+
// Deep-merge products so new keys appear with defaults
|
|
30
|
+
merged.products = { ...DEFAULT_CONFIG.products, ...(merged.products || {}) };
|
|
31
|
+
return merged;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return DEFAULT_CONFIG;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function saveConfig(config) {
|
|
38
|
+
// The config may hold a bearer token / session — lock it down on EVERY write
|
|
39
|
+
// path (not just after login), so it's never briefly world-readable.
|
|
40
|
+
mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
41
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\n', { mode: 0o600 });
|
|
42
|
+
try {
|
|
43
|
+
chmodSync(CONFIG_FILE, 0o600);
|
|
44
|
+
}
|
|
45
|
+
catch { /* best-effort on odd filesystems */ }
|
|
46
|
+
}
|
|
47
|
+
export function getProductPath(config, product) {
|
|
48
|
+
const p = config.products[product].path;
|
|
49
|
+
if (!p) {
|
|
50
|
+
throw new Error(`${product} path not configured. Run: dragon init`);
|
|
51
|
+
}
|
|
52
|
+
return p;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=config.js.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Dragon CLI — Unified management for Ghost Protocol products.
|
|
4
|
+
*
|
|
5
|
+
* dragon list — print the stack + live install / running state
|
|
6
|
+
* dragon up — clone + install every product (fresh-machine bootstrap)
|
|
7
|
+
* dragon install — install one product
|
|
8
|
+
* dragon update — git pull every product
|
|
9
|
+
* dragon serve — boot the dragon stack daemons
|
|
10
|
+
* dragon down — stop everything
|
|
11
|
+
* dragon doctor — quick health check across the stack
|
|
12
|
+
*
|
|
13
|
+
* dragon pentest|osint|memory|keep|wyrm|scale — per-product CLIs
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Dragon CLI — Unified management for Ghost Protocol products.
|
|
4
|
+
*
|
|
5
|
+
* dragon list — print the stack + live install / running state
|
|
6
|
+
* dragon up — clone + install every product (fresh-machine bootstrap)
|
|
7
|
+
* dragon install — install one product
|
|
8
|
+
* dragon update — git pull every product
|
|
9
|
+
* dragon serve — boot the dragon stack daemons
|
|
10
|
+
* dragon down — stop everything
|
|
11
|
+
* dragon doctor — quick health check across the stack
|
|
12
|
+
*
|
|
13
|
+
* dragon pentest|osint|memory|keep|wyrm|scale — per-product CLIs
|
|
14
|
+
*/
|
|
15
|
+
import { Command } from 'commander';
|
|
16
|
+
import chalk from 'chalk';
|
|
17
|
+
import { C } from './utils.js';
|
|
18
|
+
import { brandHeader } from './ui.js';
|
|
19
|
+
import { loadConfig } from './config.js';
|
|
20
|
+
import { registerScaleCommands } from './commands/scale.js';
|
|
21
|
+
import { registerWyrmCommands } from './commands/wyrm.js';
|
|
22
|
+
import { registerPentestCommands } from './commands/pentest.js';
|
|
23
|
+
import { registerKeepCommands } from './commands/keep.js';
|
|
24
|
+
import { registerOsintCommands } from './commands/osint.js';
|
|
25
|
+
import { registerMemoryCommands } from './commands/memory.js';
|
|
26
|
+
import { registerServeCommands } from './commands/serve.js';
|
|
27
|
+
import { registerAlertsCommands } from './commands/alerts.js';
|
|
28
|
+
import { registerAiCommands } from './commands/ai.js';
|
|
29
|
+
import { registerChatCommands } from './commands/chat.js';
|
|
30
|
+
import { registerLoginCommands } from './commands/login.js';
|
|
31
|
+
import { registerConfigCommands } from './commands/config.js';
|
|
32
|
+
import { registerDoctorCommands } from './commands/doctor.js';
|
|
33
|
+
import { registerMcpCommands } from './commands/mcp.js';
|
|
34
|
+
import { registerMaintenanceCommands } from './commands/maintenance.js';
|
|
35
|
+
import { registerTuiCommands } from './commands/tui.js';
|
|
36
|
+
import { registerBillingCommands } from './commands/billing.js';
|
|
37
|
+
import { registerLifecycleCommands } from './commands/lifecycle.js';
|
|
38
|
+
import { registerGlobalCommands } from './commands/global.js';
|
|
39
|
+
const VERSION = '4.2.1';
|
|
40
|
+
// Ops-console brand splash — framed panel + chrome wordmark (see src/ui.ts).
|
|
41
|
+
const HEADER = brandHeader(VERSION);
|
|
42
|
+
const program = new Command();
|
|
43
|
+
program
|
|
44
|
+
.name('dragon')
|
|
45
|
+
.description('Ghost Protocol — unified stack control')
|
|
46
|
+
.version(VERSION, '-v, --version')
|
|
47
|
+
.option('--debug', 'print full stack traces on error')
|
|
48
|
+
.addHelpText('beforeAll', `\n${HEADER}`)
|
|
49
|
+
.addHelpText('afterAll', `\n ${chalk.dim('Tip:')} run ${chalk.green('dragon list')} to see what's installed,` +
|
|
50
|
+
`\n ${chalk.green('dragon up')} to bootstrap a fresh machine.\n`);
|
|
51
|
+
const config = loadConfig();
|
|
52
|
+
registerScaleCommands(program, config);
|
|
53
|
+
registerWyrmCommands(program, config);
|
|
54
|
+
registerPentestCommands(program, config);
|
|
55
|
+
registerKeepCommands(program, config);
|
|
56
|
+
registerOsintCommands(program, config);
|
|
57
|
+
registerMemoryCommands(program, config);
|
|
58
|
+
registerServeCommands(program, config);
|
|
59
|
+
registerAlertsCommands(program, config);
|
|
60
|
+
registerAiCommands(program, config);
|
|
61
|
+
registerChatCommands(program, config);
|
|
62
|
+
registerLoginCommands(program, config);
|
|
63
|
+
registerConfigCommands(program, config);
|
|
64
|
+
registerDoctorCommands(program, config);
|
|
65
|
+
registerMcpCommands(program, config);
|
|
66
|
+
registerMaintenanceCommands(program, config);
|
|
67
|
+
registerTuiCommands(program, config);
|
|
68
|
+
registerBillingCommands(program, config);
|
|
69
|
+
registerLifecycleCommands(program, config);
|
|
70
|
+
registerGlobalCommands(program, config);
|
|
71
|
+
// ── Global error safety net — never dump a raw stack on the operator. ──
|
|
72
|
+
const DEBUG = process.argv.includes('--debug') || !!process.env.DRAGON_DEBUG;
|
|
73
|
+
function fatal(label, err) {
|
|
74
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
75
|
+
process.stderr.write(`\n ${C.critical('⚠')} ${label}: ${msg}\n`);
|
|
76
|
+
if (DEBUG && err instanceof Error && err.stack)
|
|
77
|
+
process.stderr.write(C.faint(err.stack) + '\n');
|
|
78
|
+
else
|
|
79
|
+
process.stderr.write(C.faint(' run with --debug (or DRAGON_DEBUG=1) for the full trace\n'));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
process.on('uncaughtException', (e) => fatal('unexpected error', e));
|
|
83
|
+
process.on('unhandledRejection', (e) => fatal('unhandled rejection', e));
|
|
84
|
+
program.parseAsync().catch((e) => fatal('command failed', e));
|
|
85
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dragon Stack manifest loader.
|
|
3
|
+
*
|
|
4
|
+
* Reads dragon-manifest.toml at install time so `dragon up`,
|
|
5
|
+
* `dragon update`, `dragon install`, `dragon stop --all` know the
|
|
6
|
+
* full product portfolio.
|
|
7
|
+
*
|
|
8
|
+
* Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
|
|
9
|
+
*/
|
|
10
|
+
export interface ManifestProduct {
|
|
11
|
+
key: string;
|
|
12
|
+
name: string;
|
|
13
|
+
repo: string;
|
|
14
|
+
dir: string;
|
|
15
|
+
stack: 'operator' | 'commercial' | 'daemon' | 'library';
|
|
16
|
+
kind: 'rust' | 'node' | 'python' | 'monorepo' | 'docker';
|
|
17
|
+
install: string[];
|
|
18
|
+
start?: string;
|
|
19
|
+
stop?: string;
|
|
20
|
+
port?: number;
|
|
21
|
+
requires?: string[];
|
|
22
|
+
}
|
|
23
|
+
export interface Manifest {
|
|
24
|
+
manifest_version: string;
|
|
25
|
+
default_root: string;
|
|
26
|
+
product: ManifestProduct[];
|
|
27
|
+
}
|
|
28
|
+
export declare function loadManifest(): Manifest;
|
|
29
|
+
export declare function resolveRoot(m: Manifest): string;
|
|
30
|
+
export declare function productPath(m: Manifest, p: ManifestProduct): string;
|
|
31
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,UAAU,GAAG,YAAY,GAAG,QAAQ,GAAG,SAAS,CAAA;IACvD,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAA;IACxD,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,gBAAgB,EAAE,MAAM,CAAA;IACxB,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,eAAe,EAAE,CAAA;CAC3B;AAoDD,wBAAgB,YAAY,IAAI,QAAQ,CASvC;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAG/C;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,eAAe,GAAG,MAAM,CAEnE"}
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dragon Stack manifest loader.
|
|
3
|
+
*
|
|
4
|
+
* Reads dragon-manifest.toml at install time so `dragon up`,
|
|
5
|
+
* `dragon update`, `dragon install`, `dragon stop --all` know the
|
|
6
|
+
* full product portfolio.
|
|
7
|
+
*
|
|
8
|
+
* Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
import { join, resolve } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
// Minimal TOML reader — handles the subset we use here so the CLI
|
|
15
|
+
// doesn't take on a TOML dep. Spec: `[[product]]` arrays, `key = "value"`
|
|
16
|
+
// strings, `key = ["a", "b"]` string arrays, `key = 123` numbers,
|
|
17
|
+
// comment lines starting with `#`.
|
|
18
|
+
function parseToml(text) {
|
|
19
|
+
const out = { product: [] };
|
|
20
|
+
let cur = null;
|
|
21
|
+
let inArray = false;
|
|
22
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
23
|
+
const line = rawLine.replace(/#.*$/, '').trim();
|
|
24
|
+
if (!line)
|
|
25
|
+
continue;
|
|
26
|
+
if (line === '[[product]]') {
|
|
27
|
+
inArray = true;
|
|
28
|
+
cur = {};
|
|
29
|
+
out.product.push(cur);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (line.startsWith('[') && line.endsWith(']')) {
|
|
33
|
+
inArray = false;
|
|
34
|
+
cur = null;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const eq = line.indexOf('=');
|
|
38
|
+
if (eq < 0)
|
|
39
|
+
continue;
|
|
40
|
+
const key = line.slice(0, eq).trim();
|
|
41
|
+
let val = line.slice(eq + 1).trim();
|
|
42
|
+
if (val.startsWith('[') && val.endsWith(']')) {
|
|
43
|
+
val = val
|
|
44
|
+
.slice(1, -1)
|
|
45
|
+
.split(',')
|
|
46
|
+
.map((s) => s.trim().replace(/^"|"$/g, ''))
|
|
47
|
+
.filter(Boolean);
|
|
48
|
+
}
|
|
49
|
+
else if (val.startsWith('"') && val.endsWith('"')) {
|
|
50
|
+
val = val.slice(1, -1);
|
|
51
|
+
}
|
|
52
|
+
else if (!isNaN(Number(val))) {
|
|
53
|
+
val = Number(val);
|
|
54
|
+
}
|
|
55
|
+
if (inArray && cur)
|
|
56
|
+
cur[key] = val;
|
|
57
|
+
else
|
|
58
|
+
out[key] = val;
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
const SELF_DIR = fileURLToPath(new URL('..', import.meta.url));
|
|
63
|
+
const CANDIDATES = [
|
|
64
|
+
join(homedir(), '.dragon', 'manifest.toml'),
|
|
65
|
+
resolve(SELF_DIR, '..', 'dragon-manifest.toml'),
|
|
66
|
+
resolve(homedir(), 'Git Projects', 'dragon-cli', 'dragon-manifest.toml'),
|
|
67
|
+
];
|
|
68
|
+
export function loadManifest() {
|
|
69
|
+
for (const p of CANDIDATES) {
|
|
70
|
+
if (existsSync(p)) {
|
|
71
|
+
return parseToml(readFileSync(p, 'utf-8'));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
throw new Error(`dragon-manifest.toml not found. Looked in:\n ${CANDIDATES.join('\n ')}`);
|
|
75
|
+
}
|
|
76
|
+
export function resolveRoot(m) {
|
|
77
|
+
const r = m.default_root.replace(/^~/, homedir());
|
|
78
|
+
return r;
|
|
79
|
+
}
|
|
80
|
+
export function productPath(m, p) {
|
|
81
|
+
return join(resolveRoot(m), p.dir);
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=manifest.js.map
|