@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,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionStore — Save/resume CLI conversations.
|
|
3
|
+
*
|
|
4
|
+
* Sessions stored in ~/.aither/sessions/ as JSON.
|
|
5
|
+
*/
|
|
6
|
+
export interface SessionEntry {
|
|
7
|
+
sessionId: string;
|
|
8
|
+
agent: string;
|
|
9
|
+
messages: Array<{
|
|
10
|
+
role: string;
|
|
11
|
+
content: string;
|
|
12
|
+
timestamp?: string;
|
|
13
|
+
}>;
|
|
14
|
+
createdAt: string;
|
|
15
|
+
updatedAt: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function saveSession(session: SessionEntry): void;
|
|
18
|
+
export declare function loadSession(id: string): SessionEntry | null;
|
|
19
|
+
export declare function listSessions(limit?: number): Array<{
|
|
20
|
+
sessionId: string;
|
|
21
|
+
agent: string;
|
|
22
|
+
messageCount: number;
|
|
23
|
+
updatedAt: string;
|
|
24
|
+
}>;
|
|
25
|
+
export declare function deleteSession(id: string): boolean;
|
|
26
|
+
export declare function configureRemoteSync(genesisUrl: string, authToken: string | null): void;
|
|
27
|
+
export declare function syncSessionToRemote(session: SessionEntry): Promise<void>;
|
|
28
|
+
export declare function loadRemoteSession(sessionId: string): Promise<SessionEntry | null>;
|
|
29
|
+
export declare function listRemoteSessions(limit?: number): Promise<Array<{
|
|
30
|
+
sessionId: string;
|
|
31
|
+
agent: string;
|
|
32
|
+
messageCount: number;
|
|
33
|
+
updatedAt: string;
|
|
34
|
+
clientType: string;
|
|
35
|
+
}>>;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionStore — Save/resume CLI conversations.
|
|
3
|
+
*
|
|
4
|
+
* Sessions stored in ~/.aither/sessions/ as JSON.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, unlinkSync, statSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
const SESSION_DIR = join(homedir(), '.aither', 'sessions');
|
|
10
|
+
function ensureDir() {
|
|
11
|
+
if (!existsSync(SESSION_DIR)) {
|
|
12
|
+
mkdirSync(SESSION_DIR, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function sessionPath(id) {
|
|
16
|
+
return join(SESSION_DIR, `${id.replace(/[^a-zA-Z0-9_-]/g, '_')}.json`);
|
|
17
|
+
}
|
|
18
|
+
export function saveSession(session) {
|
|
19
|
+
ensureDir();
|
|
20
|
+
session.updatedAt = new Date().toISOString();
|
|
21
|
+
writeFileSync(sessionPath(session.sessionId), JSON.stringify(session, null, 2));
|
|
22
|
+
}
|
|
23
|
+
export function loadSession(id) {
|
|
24
|
+
const path = sessionPath(id);
|
|
25
|
+
if (!existsSync(path))
|
|
26
|
+
return null;
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function listSessions(limit = 20) {
|
|
35
|
+
ensureDir();
|
|
36
|
+
const files = readdirSync(SESSION_DIR)
|
|
37
|
+
.filter(f => f.endsWith('.json'))
|
|
38
|
+
.map(f => ({ name: f, mtime: statSync(join(SESSION_DIR, f)).mtimeMs }))
|
|
39
|
+
.sort((a, b) => b.mtime - a.mtime)
|
|
40
|
+
.slice(0, limit);
|
|
41
|
+
return files.map(f => {
|
|
42
|
+
try {
|
|
43
|
+
const data = JSON.parse(readFileSync(join(SESSION_DIR, f.name), 'utf-8'));
|
|
44
|
+
return {
|
|
45
|
+
sessionId: data.sessionId,
|
|
46
|
+
agent: data.agent || 'aither',
|
|
47
|
+
messageCount: data.messages?.length || 0,
|
|
48
|
+
updatedAt: data.updatedAt || new Date(f.mtime).toISOString(),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return {
|
|
53
|
+
sessionId: f.name.replace('.json', ''),
|
|
54
|
+
agent: 'unknown',
|
|
55
|
+
messageCount: 0,
|
|
56
|
+
updatedAt: new Date(f.mtime).toISOString(),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
export function deleteSession(id) {
|
|
62
|
+
const path = sessionPath(id);
|
|
63
|
+
if (existsSync(path)) {
|
|
64
|
+
unlinkSync(path);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
// ── Remote Sync — push/pull sessions via Genesis /session/sync/* ─────
|
|
70
|
+
let _genesisUrl = null;
|
|
71
|
+
let _authToken = null;
|
|
72
|
+
export function configureRemoteSync(genesisUrl, authToken) {
|
|
73
|
+
_genesisUrl = genesisUrl;
|
|
74
|
+
_authToken = authToken;
|
|
75
|
+
}
|
|
76
|
+
export async function syncSessionToRemote(session) {
|
|
77
|
+
if (!_genesisUrl)
|
|
78
|
+
return;
|
|
79
|
+
try {
|
|
80
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
81
|
+
if (_authToken)
|
|
82
|
+
headers['Authorization'] = `Bearer ${_authToken}`;
|
|
83
|
+
await fetch(`${_genesisUrl}/session/sync/save`, {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers,
|
|
86
|
+
body: JSON.stringify({
|
|
87
|
+
session_id: session.sessionId,
|
|
88
|
+
agent: session.agent,
|
|
89
|
+
messages: session.messages,
|
|
90
|
+
client_type: 'cli',
|
|
91
|
+
}),
|
|
92
|
+
signal: AbortSignal.timeout(5000),
|
|
93
|
+
}).catch(() => { }); // fire-and-forget, don't block shell
|
|
94
|
+
}
|
|
95
|
+
catch { /* silent */ }
|
|
96
|
+
}
|
|
97
|
+
export async function loadRemoteSession(sessionId) {
|
|
98
|
+
if (!_genesisUrl)
|
|
99
|
+
return null;
|
|
100
|
+
try {
|
|
101
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
102
|
+
if (_authToken)
|
|
103
|
+
headers['Authorization'] = `Bearer ${_authToken}`;
|
|
104
|
+
const res = await fetch(`${_genesisUrl}/session/sync/load`, {
|
|
105
|
+
method: 'POST',
|
|
106
|
+
headers,
|
|
107
|
+
body: JSON.stringify({ session_id: sessionId }),
|
|
108
|
+
signal: AbortSignal.timeout(5000),
|
|
109
|
+
});
|
|
110
|
+
if (!res.ok)
|
|
111
|
+
return null;
|
|
112
|
+
const data = await res.json();
|
|
113
|
+
const s = data.session;
|
|
114
|
+
if (!s)
|
|
115
|
+
return null;
|
|
116
|
+
return {
|
|
117
|
+
sessionId: s.session_id,
|
|
118
|
+
agent: s.agent || 'aither',
|
|
119
|
+
messages: s.messages || [],
|
|
120
|
+
createdAt: s.created_at || '',
|
|
121
|
+
updatedAt: s.updated_at || '',
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export async function listRemoteSessions(limit = 20) {
|
|
129
|
+
if (!_genesisUrl)
|
|
130
|
+
return [];
|
|
131
|
+
try {
|
|
132
|
+
const headers = {};
|
|
133
|
+
if (_authToken)
|
|
134
|
+
headers['Authorization'] = `Bearer ${_authToken}`;
|
|
135
|
+
const res = await fetch(`${_genesisUrl}/session/sync/list?limit=${limit}`, {
|
|
136
|
+
headers,
|
|
137
|
+
signal: AbortSignal.timeout(5000),
|
|
138
|
+
});
|
|
139
|
+
if (!res.ok)
|
|
140
|
+
return [];
|
|
141
|
+
const data = await res.json();
|
|
142
|
+
return (data.sessions || []).map((s) => ({
|
|
143
|
+
sessionId: s.session_id,
|
|
144
|
+
agent: s.agent || 'aither',
|
|
145
|
+
messageCount: s.message_count || 0,
|
|
146
|
+
updatedAt: s.updated_at || '',
|
|
147
|
+
clientType: s.client_type || 'unknown',
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
}
|
package/dist/tunnel.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Tunnel + DNS CLI commands.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* aither tunnel list List all ingress routes
|
|
6
|
+
* aither tunnel add <subdomain> [service] Add route (default: veil-lb:3000)
|
|
7
|
+
* aither tunnel remove <subdomain> Remove route
|
|
8
|
+
* aither tunnel status Show tunnel health
|
|
9
|
+
* aither dns add <subdomain> Create CNAME
|
|
10
|
+
* aither dns remove <subdomain> Delete CNAME
|
|
11
|
+
*/
|
|
12
|
+
export declare function handleTunnelCommand(args: string[]): Promise<void>;
|
|
13
|
+
export declare function handleDnsCommand(args: string[]): Promise<void>;
|
package/dist/tunnel.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Tunnel + DNS CLI commands.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* aither tunnel list List all ingress routes
|
|
6
|
+
* aither tunnel add <subdomain> [service] Add route (default: veil-lb:3000)
|
|
7
|
+
* aither tunnel remove <subdomain> Remove route
|
|
8
|
+
* aither tunnel status Show tunnel health
|
|
9
|
+
* aither dns add <subdomain> Create CNAME
|
|
10
|
+
* aither dns remove <subdomain> Delete CNAME
|
|
11
|
+
*/
|
|
12
|
+
function getGenesisUrl() {
|
|
13
|
+
return process.env.GENESIS_URL || 'http://localhost:8001';
|
|
14
|
+
}
|
|
15
|
+
async function genesisGet(path) {
|
|
16
|
+
const res = await fetch(`${getGenesisUrl()}${path}`, { headers: { Accept: 'application/json' } });
|
|
17
|
+
if (!res.ok)
|
|
18
|
+
throw new Error(`${res.status} ${res.statusText}`);
|
|
19
|
+
return res.json();
|
|
20
|
+
}
|
|
21
|
+
async function genesisPost(path, body = {}) {
|
|
22
|
+
const res = await fetch(`${getGenesisUrl()}${path}`, {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
25
|
+
body: JSON.stringify(body),
|
|
26
|
+
});
|
|
27
|
+
if (!res.ok)
|
|
28
|
+
throw new Error(`${res.status} ${res.statusText}`);
|
|
29
|
+
return res.json();
|
|
30
|
+
}
|
|
31
|
+
async function genesisDelete(path) {
|
|
32
|
+
const res = await fetch(`${getGenesisUrl()}${path}`, { method: 'DELETE', headers: { Accept: 'application/json' } });
|
|
33
|
+
if (!res.ok)
|
|
34
|
+
throw new Error(`${res.status} ${res.statusText}`);
|
|
35
|
+
return res.json();
|
|
36
|
+
}
|
|
37
|
+
// ── Tunnel commands ──────────────────────────────────────────────────────
|
|
38
|
+
async function tunnelList() {
|
|
39
|
+
try {
|
|
40
|
+
const data = await genesisGet('/cloudflare/tunnel/routes');
|
|
41
|
+
console.log(`\x1b[36mTunnel Ingress Routes\x1b[0m (${data.count || data.routes?.length || 0})\n`);
|
|
42
|
+
for (const r of data.routes || []) {
|
|
43
|
+
const host = r.hostname || '* (catch-all)';
|
|
44
|
+
console.log(` ${host.padEnd(35)} → ${r.service}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
console.error(`\x1b[31mError:\x1b[0m ${e.message}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function tunnelAdd(args) {
|
|
52
|
+
const subdomain = args[0];
|
|
53
|
+
if (!subdomain) {
|
|
54
|
+
console.log('\x1b[33mUsage:\x1b[0m aither tunnel add <subdomain> [service]');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const hostname = subdomain.includes('.') ? subdomain : `${subdomain}.aitherium.com`;
|
|
58
|
+
const service = args[1] || 'http://aitheros-veil-lb:3000';
|
|
59
|
+
console.log(`\x1b[2mAdding route: ${hostname} → ${service}...\x1b[0m`);
|
|
60
|
+
try {
|
|
61
|
+
const data = await genesisPost('/cloudflare/tunnel/add', { hostname, service });
|
|
62
|
+
if (data.action === 'exists') {
|
|
63
|
+
console.log(`\x1b[33m!\x1b[0m Route already exists: ${hostname} → ${data.service}`);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
console.log(`\x1b[32m✓\x1b[0m Route added: ${hostname} → ${service} (${data.total_rules} total)`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
console.error(`\x1b[31mError:\x1b[0m ${e.message}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function tunnelRemove(args) {
|
|
74
|
+
const subdomain = args[0];
|
|
75
|
+
if (!subdomain) {
|
|
76
|
+
console.log('\x1b[33mUsage:\x1b[0m aither tunnel remove <subdomain>');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const hostname = subdomain.includes('.') ? subdomain : `${subdomain}.aitherium.com`;
|
|
80
|
+
try {
|
|
81
|
+
const data = await genesisDelete(`/cloudflare/tunnel/${hostname}`);
|
|
82
|
+
if (data.action === 'not_found') {
|
|
83
|
+
console.log(`\x1b[33m!\x1b[0m No route found for ${hostname}`);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
console.log(`\x1b[32m✓\x1b[0m Removed: ${hostname}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
console.error(`\x1b[31mError:\x1b[0m ${e.message}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function tunnelStatus() {
|
|
94
|
+
try {
|
|
95
|
+
const data = await genesisGet('/cloudflare/tunnel/status');
|
|
96
|
+
console.log(`\x1b[36mTunnel Status\x1b[0m`);
|
|
97
|
+
console.log(` Name: ${data.name}`);
|
|
98
|
+
console.log(` Status: ${data.status}`);
|
|
99
|
+
console.log(` ID: ${data.id}`);
|
|
100
|
+
if (data.connections?.length) {
|
|
101
|
+
console.log(` Connections:`);
|
|
102
|
+
for (const c of data.connections) {
|
|
103
|
+
console.log(` ${c.colo_name || c.id} (${c.is_pending_reconnect ? 'reconnecting' : 'active'})`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
console.error(`\x1b[31mError:\x1b[0m ${e.message}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export async function handleTunnelCommand(args) {
|
|
112
|
+
const sub = args[0] || 'help';
|
|
113
|
+
switch (sub) {
|
|
114
|
+
case 'list':
|
|
115
|
+
case 'ls': return tunnelList();
|
|
116
|
+
case 'add': return tunnelAdd(args.slice(1));
|
|
117
|
+
case 'remove':
|
|
118
|
+
case 'rm': return tunnelRemove(args.slice(1));
|
|
119
|
+
case 'status': return tunnelStatus();
|
|
120
|
+
default:
|
|
121
|
+
console.log(`\x1b[36mTunnel Management\x1b[0m\n`);
|
|
122
|
+
console.log(' aither tunnel list List ingress routes');
|
|
123
|
+
console.log(' aither tunnel add <sub> [service] Add route');
|
|
124
|
+
console.log(' aither tunnel remove <sub> Remove route');
|
|
125
|
+
console.log(' aither tunnel status Show health');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// ── DNS commands ─────────────────────────────────────────────────────────
|
|
129
|
+
async function dnsAdd(args) {
|
|
130
|
+
const subdomain = args[0];
|
|
131
|
+
if (!subdomain) {
|
|
132
|
+
console.log('\x1b[33mUsage:\x1b[0m aither dns add <subdomain>');
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
console.log(`\x1b[2mCreating CNAME: ${subdomain}.aitherium.com...\x1b[0m`);
|
|
136
|
+
try {
|
|
137
|
+
const data = await genesisPost('/cloudflare/dns/add', { subdomain });
|
|
138
|
+
console.log(`\x1b[32m✓\x1b[0m DNS ${data.action}: ${subdomain}.aitherium.com`);
|
|
139
|
+
}
|
|
140
|
+
catch (e) {
|
|
141
|
+
console.error(`\x1b[31mError:\x1b[0m ${e.message}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function dnsRemove(args) {
|
|
145
|
+
const subdomain = args[0];
|
|
146
|
+
if (!subdomain) {
|
|
147
|
+
console.log('\x1b[33mUsage:\x1b[0m aither dns remove <subdomain>');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
const data = await genesisDelete(`/cloudflare/dns/${subdomain}`);
|
|
152
|
+
console.log(`\x1b[32m✓\x1b[0m DNS ${data.action}: ${subdomain}.aitherium.com`);
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
console.error(`\x1b[31mError:\x1b[0m ${e.message}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
export async function handleDnsCommand(args) {
|
|
159
|
+
const sub = args[0] || 'help';
|
|
160
|
+
switch (sub) {
|
|
161
|
+
case 'add': return dnsAdd(args.slice(1));
|
|
162
|
+
case 'remove':
|
|
163
|
+
case 'rm': return dnsRemove(args.slice(1));
|
|
164
|
+
default:
|
|
165
|
+
console.log(`\x1b[36mDNS Management\x1b[0m\n`);
|
|
166
|
+
console.log(' aither dns add <subdomain> Create CNAME');
|
|
167
|
+
console.log(' aither dns remove <subdomain> Delete CNAME');
|
|
168
|
+
}
|
|
169
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aitherium/shell-cli",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Interactive terminal for AitherOS and ADK agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"aither": "./dist/main.js",
|
|
8
|
+
"aither-shell": "./dist/main.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/",
|
|
12
|
+
"package.json",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"dev": "tsx src/main.ts",
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"build:standalone": "bun build --compile src/main.ts --outfile dist/aither-shell",
|
|
19
|
+
"start": "node dist/main.js"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@inquirer/prompts": "^8.3.2",
|
|
23
|
+
"chalk": "^5.4.0",
|
|
24
|
+
"marked": "^15.0.0",
|
|
25
|
+
"marked-terminal": "^7.3.0",
|
|
26
|
+
"ora": "^8.2.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^22.0.0",
|
|
30
|
+
"tsx": "^4.19.0",
|
|
31
|
+
"typescript": "^5.8.0"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
}
|
|
36
|
+
}
|