@aitherium/shell-cli 1.1.0
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/dist/auth.d.ts +68 -0
- package/dist/auth.js +288 -0
- package/dist/client.d.ts +116 -0
- package/dist/client.js +528 -0
- package/dist/command-registry.d.ts +71 -0
- package/dist/command-registry.js +223 -0
- package/dist/commands.d.ts +14 -0
- package/dist/commands.js +6785 -0
- package/dist/completions.d.ts +27 -0
- package/dist/completions.js +351 -0
- package/dist/config.d.ts +23 -0
- package/dist/config.js +48 -0
- package/dist/gargbot.d.ts +11 -0
- package/dist/gargbot.js +230 -0
- package/dist/jobs.d.ts +65 -0
- package/dist/jobs.js +386 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +389 -0
- package/dist/notebooks.d.ts +19 -0
- package/dist/notebooks.js +685 -0
- package/dist/products.d.ts +12 -0
- package/dist/products.js +159 -0
- package/dist/renderer.d.ts +104 -0
- package/dist/renderer.js +1812 -0
- package/dist/repl.d.ts +16 -0
- package/dist/repl.js +1190 -0
- package/dist/session-store.d.ts +35 -0
- package/dist/session-store.js +153 -0
- package/dist/tunnel.d.ts +13 -0
- package/dist/tunnel.js +169 -0
- package/package.json +36 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommandRegistry — Score-based command matching and dynamic loading.
|
|
3
|
+
*
|
|
4
|
+
* Loads commands from:
|
|
5
|
+
* 1. commands.json (static definitions — immediate, offline fallback)
|
|
6
|
+
* 2. Genesis /shell/commands (aggregated catalog — all sources)
|
|
7
|
+
* 3. Genesis /shell/commands/mcp (MCP tools as callable commands)
|
|
8
|
+
* 4. Built-in handlers from commands.ts
|
|
9
|
+
*
|
|
10
|
+
* At REPL startup, loadDynamicCommands() fetches from Genesis and merges
|
|
11
|
+
* new commands into the registry. Static commands.json is never manually
|
|
12
|
+
* edited again — it serves as an offline fallback only.
|
|
13
|
+
*/
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
import { resolve, dirname } from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
// ── Tokenizer ────────────────────────────────────────────────────────
|
|
19
|
+
const STOP_WORDS = new Set([
|
|
20
|
+
'a', 'an', 'the', 'is', 'are', 'to', 'of', 'in', 'for', 'on', 'with',
|
|
21
|
+
'at', 'by', 'from', 'and', 'or', 'not', 'it', 'this', 'that', 'my',
|
|
22
|
+
]);
|
|
23
|
+
function tokenize(text) {
|
|
24
|
+
return text
|
|
25
|
+
.toLowerCase()
|
|
26
|
+
.split(/[\s_\-/.]+/)
|
|
27
|
+
.filter(w => w.length > 1 && !STOP_WORDS.has(w));
|
|
28
|
+
}
|
|
29
|
+
// ── Registry ─────────────────────────────────────────────────────────
|
|
30
|
+
export class CommandRegistry {
|
|
31
|
+
commands = new Map();
|
|
32
|
+
aliasMap = new Map();
|
|
33
|
+
dynamicLoaded = false;
|
|
34
|
+
/** MCP tools discovered from Genesis — callable via /tools/call. */
|
|
35
|
+
mcpTools = new Map();
|
|
36
|
+
constructor() {
|
|
37
|
+
this.loadFromJSON();
|
|
38
|
+
}
|
|
39
|
+
loadFromJSON() {
|
|
40
|
+
try {
|
|
41
|
+
const jsonPath = resolve(__dirname, '..', 'commands.json');
|
|
42
|
+
const raw = readFileSync(jsonPath, 'utf-8');
|
|
43
|
+
const data = JSON.parse(raw);
|
|
44
|
+
for (const cmd of data.commands) {
|
|
45
|
+
this.commands.set(cmd.name, {
|
|
46
|
+
name: cmd.name,
|
|
47
|
+
category: cmd.category,
|
|
48
|
+
description: cmd.description,
|
|
49
|
+
aliases: cmd.aliases || [],
|
|
50
|
+
subcommands: [],
|
|
51
|
+
source: 'static',
|
|
52
|
+
});
|
|
53
|
+
for (const alias of cmd.aliases || []) {
|
|
54
|
+
this.aliasMap.set(alias, cmd.name);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// commands.json not found — rely on dynamic loading + built-in handlers
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Fetch the unified command catalog from Genesis /shell/commands.
|
|
64
|
+
* Merges discovered commands into the registry without overwriting
|
|
65
|
+
* existing handlers. Call once at REPL startup.
|
|
66
|
+
*/
|
|
67
|
+
async loadDynamicCommands(client) {
|
|
68
|
+
let added = 0;
|
|
69
|
+
try {
|
|
70
|
+
const result = await client.get('/shell/commands');
|
|
71
|
+
const commands = result?.commands || [];
|
|
72
|
+
for (const cmd of commands) {
|
|
73
|
+
if (!cmd.name)
|
|
74
|
+
continue;
|
|
75
|
+
const existing = this.commands.get(cmd.name);
|
|
76
|
+
if (existing) {
|
|
77
|
+
// Merge subcommands and metadata from dynamic source
|
|
78
|
+
if (cmd.subcommands?.length && !existing.subcommands.length) {
|
|
79
|
+
existing.subcommands = cmd.subcommands;
|
|
80
|
+
}
|
|
81
|
+
if (cmd.genesis_endpoint && !existing.genesisEndpoint) {
|
|
82
|
+
existing.genesisEndpoint = cmd.genesis_endpoint;
|
|
83
|
+
}
|
|
84
|
+
if (cmd.source && !existing.source.includes(cmd.source)) {
|
|
85
|
+
existing.source = `${existing.source}+${cmd.source}`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// New command discovered from Genesis
|
|
90
|
+
this.commands.set(cmd.name, {
|
|
91
|
+
name: cmd.name,
|
|
92
|
+
category: cmd.category || 'custom',
|
|
93
|
+
description: cmd.description || '',
|
|
94
|
+
aliases: cmd.aliases || [],
|
|
95
|
+
subcommands: cmd.subcommands || [],
|
|
96
|
+
source: cmd.source || 'dynamic',
|
|
97
|
+
genesisEndpoint: cmd.genesis_endpoint,
|
|
98
|
+
});
|
|
99
|
+
for (const alias of cmd.aliases || []) {
|
|
100
|
+
if (!this.aliasMap.has(alias)) {
|
|
101
|
+
this.aliasMap.set(alias, cmd.name);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
added++;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
this.dynamicLoaded = true;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Genesis unreachable — use static commands only
|
|
111
|
+
}
|
|
112
|
+
// Also fetch MCP tools (async, non-blocking)
|
|
113
|
+
try {
|
|
114
|
+
const mcpResult = await client.get('/shell/commands/mcp');
|
|
115
|
+
const mcpCommands = mcpResult?.commands || [];
|
|
116
|
+
for (const tool of mcpCommands) {
|
|
117
|
+
if (tool.name) {
|
|
118
|
+
this.mcpTools.set(tool.name, tool);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// MCP discovery failed — non-fatal
|
|
124
|
+
}
|
|
125
|
+
return added;
|
|
126
|
+
}
|
|
127
|
+
/** Get all discovered MCP tools. */
|
|
128
|
+
getMcpTools() {
|
|
129
|
+
return [...this.mcpTools.values()];
|
|
130
|
+
}
|
|
131
|
+
/** Check if an MCP tool exists by name. */
|
|
132
|
+
getMcpTool(name) {
|
|
133
|
+
return this.mcpTools.get(name);
|
|
134
|
+
}
|
|
135
|
+
/** Register a built-in handler for a command. */
|
|
136
|
+
registerHandler(name, handler) {
|
|
137
|
+
const existing = this.commands.get(name);
|
|
138
|
+
if (existing) {
|
|
139
|
+
existing.handler = handler;
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
this.commands.set(name, {
|
|
143
|
+
name,
|
|
144
|
+
category: 'custom',
|
|
145
|
+
description: '',
|
|
146
|
+
aliases: [],
|
|
147
|
+
subcommands: [],
|
|
148
|
+
source: 'runtime',
|
|
149
|
+
handler,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** Resolve a command name (handles aliases). */
|
|
154
|
+
resolve(input) {
|
|
155
|
+
const lower = input.toLowerCase();
|
|
156
|
+
const name = this.aliasMap.get(lower) || lower;
|
|
157
|
+
return this.commands.get(name);
|
|
158
|
+
}
|
|
159
|
+
/** Get all command names (including aliases). */
|
|
160
|
+
allNames() {
|
|
161
|
+
const names = [...this.commands.keys()];
|
|
162
|
+
const aliases = [...this.aliasMap.keys()];
|
|
163
|
+
return [...new Set([...names, ...aliases])].sort();
|
|
164
|
+
}
|
|
165
|
+
/** Get all commands. */
|
|
166
|
+
allCommands() {
|
|
167
|
+
return [...this.commands.values()];
|
|
168
|
+
}
|
|
169
|
+
/** Whether dynamic commands have been loaded from Genesis. */
|
|
170
|
+
isDynamicLoaded() {
|
|
171
|
+
return this.dynamicLoaded;
|
|
172
|
+
}
|
|
173
|
+
/** Score-based fuzzy matching for tab completion. */
|
|
174
|
+
match(partial, limit = 5) {
|
|
175
|
+
const tokens = tokenize(partial);
|
|
176
|
+
if (!tokens.length)
|
|
177
|
+
return this.allNames().slice(0, limit);
|
|
178
|
+
const scored = [];
|
|
179
|
+
for (const [name, cmd] of this.commands) {
|
|
180
|
+
let score = 0;
|
|
181
|
+
// Exact prefix match on name
|
|
182
|
+
if (name.startsWith(partial.toLowerCase())) {
|
|
183
|
+
score += 10;
|
|
184
|
+
}
|
|
185
|
+
// Token overlap with name + description
|
|
186
|
+
const docTokens = tokenize(`${name} ${cmd.description} ${cmd.aliases.join(' ')}`);
|
|
187
|
+
for (const qt of tokens) {
|
|
188
|
+
for (const dt of docTokens) {
|
|
189
|
+
if (dt === qt)
|
|
190
|
+
score += 3;
|
|
191
|
+
else if (dt.startsWith(qt) || qt.startsWith(dt))
|
|
192
|
+
score += 1;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (score > 0) {
|
|
196
|
+
scored.push([name, score]);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
scored.sort((a, b) => b[1] - a[1]);
|
|
200
|
+
return scored.slice(0, limit).map(([name]) => name);
|
|
201
|
+
}
|
|
202
|
+
/** Get commands by category. */
|
|
203
|
+
byCategory() {
|
|
204
|
+
const cats = new Map();
|
|
205
|
+
for (const cmd of this.commands.values()) {
|
|
206
|
+
const list = cats.get(cmd.category) || [];
|
|
207
|
+
list.push(cmd);
|
|
208
|
+
cats.set(cmd.category, list);
|
|
209
|
+
}
|
|
210
|
+
return cats;
|
|
211
|
+
}
|
|
212
|
+
get size() {
|
|
213
|
+
return this.commands.size;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// ── Singleton ────────────────────────────────────────────────────────
|
|
217
|
+
let _instance = null;
|
|
218
|
+
export function getCommandRegistry() {
|
|
219
|
+
if (!_instance) {
|
|
220
|
+
_instance = new CommandRegistry();
|
|
221
|
+
}
|
|
222
|
+
return _instance;
|
|
223
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in slash commands for AitherShell CLI.
|
|
3
|
+
*/
|
|
4
|
+
import type { GenesisClient } from './client.js';
|
|
5
|
+
import type { ShellConfig } from './config.js';
|
|
6
|
+
export type CommandHandler = (client: GenesisClient, args: string, config: ShellConfig) => Promise<void>;
|
|
7
|
+
interface Command {
|
|
8
|
+
description: string;
|
|
9
|
+
usage?: string;
|
|
10
|
+
handler: CommandHandler;
|
|
11
|
+
}
|
|
12
|
+
export declare function getCommand(name: string): Command | undefined;
|
|
13
|
+
export declare function getCommandNames(): string[];
|
|
14
|
+
export {};
|