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,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dragon lifecycle commands — bootstrap + update + install + stop
|
|
3
|
+
*
|
|
4
|
+
* dragon up — clone every missing repo, install everything
|
|
5
|
+
* dragon update — git pull every product in the manifest
|
|
6
|
+
* dragon install <key> — install one product
|
|
7
|
+
* dragon uninstall <key> — remove a product directory (asks first)
|
|
8
|
+
* dragon list — print the manifest
|
|
9
|
+
* dragon stop --all — kill every running service (uses ~/.dragon/pids/)
|
|
10
|
+
*
|
|
11
|
+
* Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
|
|
12
|
+
* Author: Ryan Sebastian <ryan@ghosts.lk>
|
|
13
|
+
*/
|
|
14
|
+
import { loadManifest, resolveRoot, productPath } from '../manifest.js';
|
|
15
|
+
import { run, label, eyebrow, success, error, info, warn, isInstalled, isRunning, readVersion, C } from '../utils.js';
|
|
16
|
+
import chalk from 'chalk';
|
|
17
|
+
import { existsSync, mkdirSync, rmSync } from 'node:fs';
|
|
18
|
+
import { join } from 'node:path';
|
|
19
|
+
import { execSync } from 'node:child_process';
|
|
20
|
+
function clone(p, root) {
|
|
21
|
+
const target = join(root, p.dir);
|
|
22
|
+
if (existsSync(join(target, '.git'))) {
|
|
23
|
+
info(`${p.name.padEnd(28)} already cloned`);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
if (existsSync(target)) {
|
|
27
|
+
warn(`${p.name.padEnd(28)} dir exists but no .git — skipping clone`);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
info(`${p.name.padEnd(28)} cloning ghosts-lk-style…`);
|
|
31
|
+
try {
|
|
32
|
+
execSync(`git clone https://github.com/${p.repo}.git "${target}"`, { stdio: 'inherit' });
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
error(`${p.name}: clone failed (${e})`);
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function pull(p, root) {
|
|
41
|
+
const target = join(root, p.dir);
|
|
42
|
+
if (!existsSync(join(target, '.git'))) {
|
|
43
|
+
warn(`${p.name.padEnd(28)} not cloned — skipping update`);
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
execSync(`git -C "${target}" pull --ff-only`, { stdio: 'inherit' });
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
error(`${p.name}: update failed (${e})`);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function install(p, root) {
|
|
56
|
+
const target = join(root, p.dir);
|
|
57
|
+
if (!existsSync(target)) {
|
|
58
|
+
error(`${p.name}: dir missing at ${target}`);
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
for (const step of p.install || []) {
|
|
62
|
+
info(`${p.name.padEnd(28)} → ${chalk.dim(step)}`);
|
|
63
|
+
const parts = step.split(' ').filter(Boolean);
|
|
64
|
+
const cmd = parts.shift() ?? '';
|
|
65
|
+
const exit = await run(cmd, parts, target);
|
|
66
|
+
if (exit !== 0) {
|
|
67
|
+
error(`${p.name}: install step "${step}" failed (exit ${exit})`);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
function topoSort(products) {
|
|
74
|
+
// Stable topo sort by `requires` so dependencies install first.
|
|
75
|
+
const out = [];
|
|
76
|
+
const visited = new Set();
|
|
77
|
+
const visiting = new Set();
|
|
78
|
+
const byKey = new Map(products.map((p) => [p.key, p]));
|
|
79
|
+
function dfs(p) {
|
|
80
|
+
if (visited.has(p.key))
|
|
81
|
+
return;
|
|
82
|
+
if (visiting.has(p.key))
|
|
83
|
+
return;
|
|
84
|
+
visiting.add(p.key);
|
|
85
|
+
for (const req of p.requires ?? []) {
|
|
86
|
+
const dep = byKey.get(req);
|
|
87
|
+
if (dep)
|
|
88
|
+
dfs(dep);
|
|
89
|
+
}
|
|
90
|
+
visiting.delete(p.key);
|
|
91
|
+
visited.add(p.key);
|
|
92
|
+
out.push(p);
|
|
93
|
+
}
|
|
94
|
+
for (const p of products)
|
|
95
|
+
dfs(p);
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
export function registerLifecycleCommands(program, _config) {
|
|
99
|
+
program.command('list')
|
|
100
|
+
.description('Print the dragon stack with live install / running state')
|
|
101
|
+
.option('--stack <stack>', 'Filter by stack (operator|commercial|daemon|library)')
|
|
102
|
+
.option('--verbose', 'Include repo path + version per row')
|
|
103
|
+
.option('--json', 'Machine-readable output (skips pretty print)')
|
|
104
|
+
.action(async (opts) => {
|
|
105
|
+
const m = loadManifest();
|
|
106
|
+
const root = resolveRoot(m);
|
|
107
|
+
const items = opts.stack ? m.product.filter((p) => p.stack === opts.stack) : m.product;
|
|
108
|
+
const rows = await Promise.all(items.map(async (p) => {
|
|
109
|
+
const dir = productPath(m, p);
|
|
110
|
+
const installed = isInstalled(dir);
|
|
111
|
+
const running = installed && p.port ? await isRunning(p.port) : false;
|
|
112
|
+
const version = installed ? readVersion(dir) : null;
|
|
113
|
+
return { p, installed, running, version };
|
|
114
|
+
}));
|
|
115
|
+
if (opts.json) {
|
|
116
|
+
console.log(JSON.stringify({
|
|
117
|
+
manifest_version: m.manifest_version, root,
|
|
118
|
+
rows: rows.map((r) => ({
|
|
119
|
+
key: r.p.key, name: r.p.name, stack: r.p.stack, kind: r.p.kind,
|
|
120
|
+
repo: r.p.repo, port: r.p.port,
|
|
121
|
+
installed: r.installed, running: r.running, version: r.version,
|
|
122
|
+
})),
|
|
123
|
+
}, null, 2));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
console.log();
|
|
127
|
+
console.log(` ${label('Dragon')} ${C.info(`manifest v${m.manifest_version} · root ${root}`)}`);
|
|
128
|
+
console.log();
|
|
129
|
+
// Stack labels + per-stack accent glyph mirror the rail dividers
|
|
130
|
+
// in the Dragon Console. Glyphs picked to evoke each stack's
|
|
131
|
+
// shape language (chevron / diamond / graph-node / stacked rows).
|
|
132
|
+
const STACK_LABELS = {
|
|
133
|
+
operator: 'OPERATOR · internal tools',
|
|
134
|
+
commercial: 'COMMERCIAL · customer-facing',
|
|
135
|
+
daemon: 'DAEMON · background services',
|
|
136
|
+
library: 'LIBRARY · reusable modules',
|
|
137
|
+
};
|
|
138
|
+
const STACK_GLYPH = {
|
|
139
|
+
operator: '▲',
|
|
140
|
+
commercial: '◆',
|
|
141
|
+
daemon: '◇',
|
|
142
|
+
library: '▤',
|
|
143
|
+
};
|
|
144
|
+
let section = 0;
|
|
145
|
+
for (const stack of ['operator', 'commercial', 'daemon', 'library']) {
|
|
146
|
+
const sub = rows.filter((r) => r.p.stack === stack);
|
|
147
|
+
if (sub.length === 0)
|
|
148
|
+
continue;
|
|
149
|
+
section += 1;
|
|
150
|
+
const glyph = C.accent(STACK_GLYPH[stack]);
|
|
151
|
+
console.log(` ${glyph} ${eyebrow(section, STACK_LABELS[stack])}`);
|
|
152
|
+
for (const { p, installed, running, version } of sub) {
|
|
153
|
+
// Three-state mark — matches the web UI status dots:
|
|
154
|
+
// ● hot-teal (running) · ✓ teal (installed) · ○ grey (available)
|
|
155
|
+
const mark = running ? C.hot.bold('●') :
|
|
156
|
+
installed ? C.accent('✓') :
|
|
157
|
+
C.info('○');
|
|
158
|
+
const nameRaw = p.name.padEnd(28);
|
|
159
|
+
const name = installed ? chalk.bold(nameRaw) : C.info(nameRaw);
|
|
160
|
+
const port = p.port ? C.info(`:${p.port}`.padStart(6)) : ' ';
|
|
161
|
+
const ver = version ? C.faint(` v${version}`.padEnd(11)) : C.faint(' —'.padEnd(11));
|
|
162
|
+
const repo = opts.verbose ? ` ${C.faint(p.repo)}` : '';
|
|
163
|
+
console.log(` ${mark} ${name}${port}${ver}${repo}`);
|
|
164
|
+
}
|
|
165
|
+
console.log();
|
|
166
|
+
}
|
|
167
|
+
const installed = rows.filter((r) => r.installed).length;
|
|
168
|
+
const running = rows.filter((r) => r.running).length;
|
|
169
|
+
console.log(` ${C.faint('—')}`);
|
|
170
|
+
console.log(` ${C.info(`${rows.length} total`)}` +
|
|
171
|
+
` ${C.accent(`✓ ${installed} installed`)}` +
|
|
172
|
+
` ${C.hot.bold(`● ${running} running`)}` +
|
|
173
|
+
` ${C.info(`○ ${rows.length - installed} available`)}`);
|
|
174
|
+
console.log();
|
|
175
|
+
console.log(` ${C.faint('next:')} ${C.accent('dragon install <key>')} · ${C.accent('dragon up')} (everything) · ${C.accent('dragon serve')} (start daemons)`);
|
|
176
|
+
console.log();
|
|
177
|
+
});
|
|
178
|
+
program.command('up')
|
|
179
|
+
.description('Clone every missing repo + install — full fresh-machine bootstrap')
|
|
180
|
+
.option('--stack <stack>', 'Limit to one stack')
|
|
181
|
+
.option('--dry-run', 'Show what would happen, take no action')
|
|
182
|
+
.action(async (opts) => {
|
|
183
|
+
const m = loadManifest();
|
|
184
|
+
const root = resolveRoot(m);
|
|
185
|
+
mkdirSync(root, { recursive: true });
|
|
186
|
+
const items = topoSort(opts.stack ? m.product.filter((p) => p.stack === opts.stack) : m.product);
|
|
187
|
+
console.log(label('Dragon'), `Bootstrapping ${items.length} product(s) into ${root}\n`);
|
|
188
|
+
let ok = 0;
|
|
189
|
+
for (const p of items) {
|
|
190
|
+
if (opts.dryRun) {
|
|
191
|
+
info(`would clone + install ${p.name}`);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (!clone(p, root))
|
|
195
|
+
continue;
|
|
196
|
+
if (await install(p, root)) {
|
|
197
|
+
ok++;
|
|
198
|
+
success(`${p.name} ready`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
console.log();
|
|
202
|
+
success(`${ok} of ${items.length} ready · run \`dragon serve\` to start daemons`);
|
|
203
|
+
});
|
|
204
|
+
program.command('update')
|
|
205
|
+
.description('git pull every product in the manifest')
|
|
206
|
+
.option('--stack <stack>', 'Limit to one stack')
|
|
207
|
+
.action((opts) => {
|
|
208
|
+
const m = loadManifest();
|
|
209
|
+
const root = resolveRoot(m);
|
|
210
|
+
const items = opts.stack ? m.product.filter((p) => p.stack === opts.stack) : m.product;
|
|
211
|
+
console.log(label('Dragon'), `Pulling latest for ${items.length} product(s)\n`);
|
|
212
|
+
let ok = 0;
|
|
213
|
+
for (const p of items) {
|
|
214
|
+
if (pull(p, root))
|
|
215
|
+
ok++;
|
|
216
|
+
}
|
|
217
|
+
console.log();
|
|
218
|
+
success(`${ok} of ${items.length} updated`);
|
|
219
|
+
});
|
|
220
|
+
program.command('install <key>')
|
|
221
|
+
.description('Install one product (clone + install) from the manifest')
|
|
222
|
+
.action(async (key) => {
|
|
223
|
+
const m = loadManifest();
|
|
224
|
+
const root = resolveRoot(m);
|
|
225
|
+
const p = m.product.find((q) => q.key === key);
|
|
226
|
+
if (!p) {
|
|
227
|
+
error(`Unknown product key '${key}'`);
|
|
228
|
+
info(`Run \`dragon list\` to see all keys.`);
|
|
229
|
+
process.exit(2);
|
|
230
|
+
}
|
|
231
|
+
// Install requires first
|
|
232
|
+
const order = topoSort([p]);
|
|
233
|
+
for (const dep of order) {
|
|
234
|
+
if (!existsSync(productPath(m, dep)))
|
|
235
|
+
clone(dep, root);
|
|
236
|
+
if (!await install(dep, root)) {
|
|
237
|
+
error(`${dep.name} install failed`);
|
|
238
|
+
process.exit(3);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
success(`${p.name} ready`);
|
|
242
|
+
});
|
|
243
|
+
program.command('uninstall <key>')
|
|
244
|
+
.description('Remove a product directory (asks first)')
|
|
245
|
+
.option('-y, --yes', 'Skip confirmation prompt')
|
|
246
|
+
.action(async (key, opts) => {
|
|
247
|
+
const m = loadManifest();
|
|
248
|
+
const p = m.product.find((q) => q.key === key);
|
|
249
|
+
if (!p) {
|
|
250
|
+
error(`Unknown product '${key}'`);
|
|
251
|
+
process.exit(2);
|
|
252
|
+
}
|
|
253
|
+
const target = productPath(m, p);
|
|
254
|
+
if (!existsSync(target)) {
|
|
255
|
+
info(`Not installed at ${target}`);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (!opts.yes) {
|
|
259
|
+
warn(`This will rm -rf ${target}`);
|
|
260
|
+
info(`Re-run with -y to confirm.`);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
rmSync(target, { recursive: true, force: true });
|
|
264
|
+
success(`Removed ${target}`);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dragon login / logout / whoami — authenticate the CLI with the Dragon assistant
|
|
3
|
+
* (account.ghosts.lk).
|
|
4
|
+
*
|
|
5
|
+
* Default is a browser DEVICE-CODE flow (like gh/Claude Code): `dragon login` opens
|
|
6
|
+
* the approve page, you confirm while signed in, and the CLI receives a 90-day bearer
|
|
7
|
+
* token — no cookie pasting. Fallbacks: `--session <gp_session>` (works even before the
|
|
8
|
+
* device-flow backend is deployed) and `--token <pat>`. Credentials live in
|
|
9
|
+
* ~/.dragon/config.json (locked to 0600).
|
|
10
|
+
*
|
|
11
|
+
* Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
|
|
12
|
+
*/
|
|
13
|
+
import type { Command } from 'commander';
|
|
14
|
+
import type { DragonConfig } from '../config.js';
|
|
15
|
+
export declare function registerLoginCommands(program: Command, _config: DragonConfig): void;
|
|
16
|
+
//# sourceMappingURL=login.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAmHhD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,QAsD5E"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dragon login / logout / whoami — authenticate the CLI with the Dragon assistant
|
|
3
|
+
* (account.ghosts.lk).
|
|
4
|
+
*
|
|
5
|
+
* Default is a browser DEVICE-CODE flow (like gh/Claude Code): `dragon login` opens
|
|
6
|
+
* the approve page, you confirm while signed in, and the CLI receives a 90-day bearer
|
|
7
|
+
* token — no cookie pasting. Fallbacks: `--session <gp_session>` (works even before the
|
|
8
|
+
* device-flow backend is deployed) and `--token <pat>`. Credentials live in
|
|
9
|
+
* ~/.dragon/config.json (locked to 0600).
|
|
10
|
+
*
|
|
11
|
+
* Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
|
|
12
|
+
*/
|
|
13
|
+
import { resolveAuth, saveAuth, clearAuth, whoami, isBrowsableHttpUrl } from '../auth.js';
|
|
14
|
+
import { C, success, error, info } from '../utils.js';
|
|
15
|
+
import { createInterface } from 'node:readline/promises';
|
|
16
|
+
import { stdin, stdout } from 'node:process';
|
|
17
|
+
import { spawn } from 'node:child_process';
|
|
18
|
+
import { chmodSync } from 'node:fs';
|
|
19
|
+
import { homedir } from 'node:os';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
22
|
+
/** Open a URL in the browser. Returns false (without spawning) if the URL isn't
|
|
23
|
+
* something a browser will accept — so we never trigger "this address is
|
|
24
|
+
* restricted"; the caller has already printed the URL for manual use. */
|
|
25
|
+
function openBrowser(url) {
|
|
26
|
+
if (!isBrowsableHttpUrl(url))
|
|
27
|
+
return false;
|
|
28
|
+
const isMac = process.platform === 'darwin';
|
|
29
|
+
const isWin = process.platform === 'win32';
|
|
30
|
+
const cmd = isMac ? 'open' : isWin ? 'cmd' : 'xdg-open';
|
|
31
|
+
const args = isWin ? ['/c', 'start', '', url] : [url];
|
|
32
|
+
try {
|
|
33
|
+
const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
|
|
34
|
+
child.on('error', () => { });
|
|
35
|
+
child.unref();
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function lockConfig() {
|
|
43
|
+
try {
|
|
44
|
+
chmodSync(join(homedir(), '.dragon', 'config.json'), 0o600);
|
|
45
|
+
}
|
|
46
|
+
catch { /* best-effort */ }
|
|
47
|
+
}
|
|
48
|
+
async function verifyAndReport() {
|
|
49
|
+
const me = await whoami();
|
|
50
|
+
if (me.ok) {
|
|
51
|
+
if (me.email)
|
|
52
|
+
saveAuth({ email: me.email });
|
|
53
|
+
lockConfig();
|
|
54
|
+
success(`signed in${me.email ? ` as ${C.accent(me.email)}` : ''}`);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if (me.status === 401)
|
|
58
|
+
error('credential rejected — sign in again.');
|
|
59
|
+
else
|
|
60
|
+
error(`could not verify (${me.error ?? me.status}).`);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
/** Returns 'ok' | 'failed' | 'unavailable' (unavailable → caller falls back to paste). */
|
|
64
|
+
async function deviceFlow(apiBase, open) {
|
|
65
|
+
let start;
|
|
66
|
+
try {
|
|
67
|
+
const r = await fetch(`${apiBase}/api/v1/cli/auth/start`, { method: 'POST' });
|
|
68
|
+
if (r.status === 404)
|
|
69
|
+
return 'unavailable'; // route not deployed → caller offers paste
|
|
70
|
+
if (!r.ok) {
|
|
71
|
+
error(`login couldn't start: HTTP ${r.status} from ${apiBase}.`);
|
|
72
|
+
return 'failed';
|
|
73
|
+
}
|
|
74
|
+
start = (await r.json());
|
|
75
|
+
if (!start?.verification_uri_complete || !start?.device_code) {
|
|
76
|
+
error('the server returned an unexpected response.');
|
|
77
|
+
return 'failed';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
error(`couldn't reach ${apiBase} — ${e instanceof Error ? e.message : String(e)}.`);
|
|
82
|
+
info('check your connection, or pass a valid endpoint with `--api`.');
|
|
83
|
+
return 'failed';
|
|
84
|
+
}
|
|
85
|
+
console.log();
|
|
86
|
+
console.log(` ${C.accent.bold('Dragon login')} ${C.faint('· ' + apiBase)}`);
|
|
87
|
+
console.log(` Approve this device in your browser:`);
|
|
88
|
+
console.log(` ${C.info(start.verification_uri_complete)}`);
|
|
89
|
+
console.log(` Verification code: ${C.accent.bold(start.user_code)}`);
|
|
90
|
+
console.log();
|
|
91
|
+
if (open && !openBrowser(start.verification_uri_complete))
|
|
92
|
+
info('open the link above manually to approve.');
|
|
93
|
+
const deadline = Date.now() + start.expires_in * 1000;
|
|
94
|
+
const interval = Math.max(2, start.interval || 3) * 1000;
|
|
95
|
+
process.stdout.write(` ${C.faint('waiting for approval')} `);
|
|
96
|
+
while (Date.now() < deadline) {
|
|
97
|
+
await sleep(interval);
|
|
98
|
+
process.stdout.write(C.faint('·'));
|
|
99
|
+
let poll;
|
|
100
|
+
try {
|
|
101
|
+
poll = (await fetch(`${apiBase}/api/v1/cli/auth/poll`, {
|
|
102
|
+
method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ device_code: start.device_code }),
|
|
103
|
+
}).then((r) => r.json()));
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (poll.status === 'approved' && poll.token) {
|
|
109
|
+
process.stdout.write('\n');
|
|
110
|
+
saveAuth({ token: poll.token, session: undefined }); // token wins; drop any stale session
|
|
111
|
+
return (await verifyAndReport()) ? 'ok' : 'failed';
|
|
112
|
+
}
|
|
113
|
+
if (poll.status === 'denied') {
|
|
114
|
+
process.stdout.write('\n');
|
|
115
|
+
error('approval was denied in the browser.');
|
|
116
|
+
return 'failed';
|
|
117
|
+
}
|
|
118
|
+
if (poll.status === 'expired' || poll.status === 'not_found') {
|
|
119
|
+
process.stdout.write('\n');
|
|
120
|
+
error('the code expired — run `dragon login` again.');
|
|
121
|
+
return 'failed';
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
process.stdout.write('\n');
|
|
125
|
+
error('timed out waiting for approval.');
|
|
126
|
+
return 'failed';
|
|
127
|
+
}
|
|
128
|
+
async function pasteFlow(apiBase, open) {
|
|
129
|
+
console.log();
|
|
130
|
+
console.log(` ${C.accent.bold('Dragon login')} ${C.faint('· ' + apiBase)} ${C.faint('(session paste)')}`);
|
|
131
|
+
console.log(` ${C.faint('1.')} Sign in at ${C.info(apiBase + '/login')}`);
|
|
132
|
+
console.log(` ${C.faint('2.')} DevTools → Application → Cookies → ${C.info(new URL(apiBase).host)}`);
|
|
133
|
+
console.log(` ${C.faint('3.')} Copy the ${C.accent('gp_session')} value and paste it below.`);
|
|
134
|
+
console.log();
|
|
135
|
+
if (open && !openBrowser(`${apiBase}/login`))
|
|
136
|
+
info('open the sign-in link above manually.');
|
|
137
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
138
|
+
let value = '';
|
|
139
|
+
try {
|
|
140
|
+
value = (await rl.question(` ${C.accent('gp_session')} ▸ `)).trim();
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
rl.close();
|
|
144
|
+
}
|
|
145
|
+
if (!value) {
|
|
146
|
+
error('no value entered — aborted.');
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
saveAuth({ session: value, token: undefined });
|
|
150
|
+
return verifyAndReport();
|
|
151
|
+
}
|
|
152
|
+
export function registerLoginCommands(program, _config) {
|
|
153
|
+
program
|
|
154
|
+
.command('login')
|
|
155
|
+
.description('Authenticate the CLI (browser device-code flow by default)')
|
|
156
|
+
.option('--token <token>', 'use a personal-access-token directly')
|
|
157
|
+
.option('--session <value>', 'use a gp_session cookie value (fallback)')
|
|
158
|
+
.option('--paste', 'force the gp_session paste flow instead of the browser device flow')
|
|
159
|
+
.option('--api <url>', 'point at a non-default origin (e.g. http://localhost:8799)')
|
|
160
|
+
.option('--no-open', "don't auto-open the browser")
|
|
161
|
+
.action(async (opts) => {
|
|
162
|
+
try {
|
|
163
|
+
if (opts.api) {
|
|
164
|
+
const base = opts.api.replace(/\/+$/, '');
|
|
165
|
+
if (!isBrowsableHttpUrl(base)) {
|
|
166
|
+
error(`--api "${opts.api}" isn't a usable http(s) URL (or it uses a restricted port).`);
|
|
167
|
+
process.exitCode = 1;
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
saveAuth({ apiBase: base });
|
|
171
|
+
}
|
|
172
|
+
const { apiBase } = resolveAuth(); // sanitized — a bad stored value self-heals to the default
|
|
173
|
+
if (opts.token) {
|
|
174
|
+
saveAuth({ token: opts.token, session: undefined });
|
|
175
|
+
if (!(await verifyAndReport()))
|
|
176
|
+
process.exitCode = 1;
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (opts.session) {
|
|
180
|
+
saveAuth({ session: opts.session.trim(), token: undefined });
|
|
181
|
+
if (!(await verifyAndReport()))
|
|
182
|
+
process.exitCode = 1;
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (opts.paste) {
|
|
186
|
+
if (!(await pasteFlow(apiBase, opts.open !== false)))
|
|
187
|
+
process.exitCode = 1;
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const result = await deviceFlow(apiBase, opts.open !== false);
|
|
191
|
+
if (result === 'unavailable') {
|
|
192
|
+
info('device-code flow not available on this server yet — falling back to session paste.');
|
|
193
|
+
if (!(await pasteFlow(apiBase, opts.open !== false)))
|
|
194
|
+
process.exitCode = 1;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (result === 'ok')
|
|
198
|
+
info('run `dragon chat` to start, or `dragon ask "…"` for one-shot.');
|
|
199
|
+
else
|
|
200
|
+
process.exitCode = 1;
|
|
201
|
+
}
|
|
202
|
+
catch (e) {
|
|
203
|
+
error(`login failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
204
|
+
process.exitCode = 1;
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
program
|
|
208
|
+
.command('logout')
|
|
209
|
+
.description('Forget the stored Dragon assistant credentials')
|
|
210
|
+
.action(() => { clearAuth(); success('signed out — credentials cleared from ~/.dragon/config.json'); });
|
|
211
|
+
program
|
|
212
|
+
.command('whoami')
|
|
213
|
+
.description('Show the current Dragon assistant identity + endpoint')
|
|
214
|
+
.action(async () => {
|
|
215
|
+
const { apiBase, mode, email } = resolveAuth();
|
|
216
|
+
console.log(` ${C.faint('endpoint:')} ${C.info(apiBase)}`);
|
|
217
|
+
console.log(` ${C.faint('auth:')} ${mode === 'none' ? C.faint('none') : C.accent(mode)}`);
|
|
218
|
+
if (mode === 'none') {
|
|
219
|
+
info('not signed in — run `dragon login`.');
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const me = await whoami();
|
|
223
|
+
if (me.ok)
|
|
224
|
+
success(`verified${me.email ? ` as ${C.accent(me.email)}` : ''}`);
|
|
225
|
+
else if (me.status === 401)
|
|
226
|
+
error('stored credential is no longer valid — run `dragon login`.');
|
|
227
|
+
else {
|
|
228
|
+
error(`could not verify (${me.error ?? me.status}).`);
|
|
229
|
+
if (email)
|
|
230
|
+
info(`last known: ${email}`);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dragon completions / upgrade — quality-of-life maintenance.
|
|
3
|
+
*
|
|
4
|
+
* dragon completions [bash|zsh|fish] # shell tab-completion script
|
|
5
|
+
* dragon upgrade # git pull + build (from-source installs)
|
|
6
|
+
*
|
|
7
|
+
* Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
|
|
8
|
+
*/
|
|
9
|
+
import type { Command } from 'commander';
|
|
10
|
+
import type { DragonConfig } from '../config.js';
|
|
11
|
+
export declare function registerMaintenanceCommands(program: Command, _config: DragonConfig): void;
|
|
12
|
+
//# sourceMappingURL=maintenance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maintenance.d.ts","sourceRoot":"","sources":["../../src/commands/maintenance.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAkBhD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,QAyBlF"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dragon completions / upgrade — quality-of-life maintenance.
|
|
3
|
+
*
|
|
4
|
+
* dragon completions [bash|zsh|fish] # shell tab-completion script
|
|
5
|
+
* dragon upgrade # git pull + build (from-source installs)
|
|
6
|
+
*
|
|
7
|
+
* Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
|
|
8
|
+
*/
|
|
9
|
+
import { C, success, error, info } from '../utils.js';
|
|
10
|
+
import { execFile } from 'node:child_process';
|
|
11
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
function installRoot() {
|
|
14
|
+
const entry = process.argv[1];
|
|
15
|
+
if (!entry)
|
|
16
|
+
return null;
|
|
17
|
+
try {
|
|
18
|
+
return dirname(dirname(realpathSync(entry)));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return dirname(dirname(entry));
|
|
22
|
+
} // dist/index.js → root
|
|
23
|
+
}
|
|
24
|
+
function run(cmd, args, cwd) {
|
|
25
|
+
return new Promise((res) => {
|
|
26
|
+
execFile(cmd, args, { cwd }, (err, stdout, stderr) => { process.stdout.write((stdout || '') + (stderr || '')); res(err ? 1 : 0); });
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export function registerMaintenanceCommands(program, _config) {
|
|
30
|
+
program
|
|
31
|
+
.command('completions [shell]')
|
|
32
|
+
.description('Print a shell completion script (bash | zsh | fish)')
|
|
33
|
+
.action((shell = 'bash') => {
|
|
34
|
+
const cmds = program.commands.map((c) => c.name()).filter((n) => n && n !== 'completions').sort().join(' ');
|
|
35
|
+
if (shell === 'fish')
|
|
36
|
+
console.log(`complete -c dragon -f -a "${cmds}"`);
|
|
37
|
+
else if (shell === 'zsh')
|
|
38
|
+
console.log(`#compdef dragon\n_dragon(){ _arguments '1:command:(${cmds})' '*::arg:_files' }\ncompdef _dragon dragon`);
|
|
39
|
+
else
|
|
40
|
+
console.log(`_dragon(){ COMPREPLY=( $(compgen -W "${cmds}" -- "\${COMP_WORDS[1]}") ); }\ncomplete -F _dragon dragon`);
|
|
41
|
+
process.stderr.write(C.faint(`# install: dragon completions ${shell} >> ~/.${shell === 'fish' ? 'config/fish/completions/dragon.fish' : shell + 'rc'}\n`));
|
|
42
|
+
});
|
|
43
|
+
program
|
|
44
|
+
.command('upgrade')
|
|
45
|
+
.description('Update the dragon CLI from source (git pull + build)')
|
|
46
|
+
.action(async () => {
|
|
47
|
+
const root = installRoot();
|
|
48
|
+
if (!root) {
|
|
49
|
+
error('could not locate the install directory');
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!existsSync(join(root, '.git'))) {
|
|
54
|
+
info(`installed at ${C.info(root)} — not a git checkout, update it however you installed it.`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
info(`upgrading dragon at ${C.info(root)} …`);
|
|
58
|
+
if (await run('git', ['pull', '--ff-only'], root)) {
|
|
59
|
+
error('git pull failed (commit/stash local changes first?)');
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (await run('npm', ['install'], root)) {
|
|
64
|
+
error('npm install failed');
|
|
65
|
+
process.exitCode = 1;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (await run('npm', ['run', 'build'], root)) {
|
|
69
|
+
error('build failed');
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
success('dragon upgraded.');
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=maintenance.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
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 type { Command } from 'commander';
|
|
14
|
+
import { type DragonConfig } from '../config.js';
|
|
15
|
+
export declare function registerMcpCommands(program: Command, _config: DragonConfig): void;
|
|
16
|
+
//# sourceMappingURL=mcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAA0B,KAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAIxE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,QAsC1E"}
|