spidersan 0.2.2
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/CHANGELOG.md +91 -0
- package/LICENSE +21 -0
- package/LICENSE.md +85 -0
- package/README.md +217 -0
- package/dist/bin/spidersan.d.ts +9 -0
- package/dist/bin/spidersan.d.ts.map +1 -0
- package/dist/bin/spidersan.js +152 -0
- package/dist/bin/spidersan.js.map +1 -0
- package/dist/commands/abandon.d.ts +8 -0
- package/dist/commands/abandon.d.ts.map +1 -0
- package/dist/commands/abandon.js +49 -0
- package/dist/commands/abandon.js.map +1 -0
- package/dist/commands/activate.d.ts +9 -0
- package/dist/commands/activate.d.ts.map +1 -0
- package/dist/commands/activate.js +45 -0
- package/dist/commands/activate.js.map +1 -0
- package/dist/commands/active-windows.d.ts +16 -0
- package/dist/commands/active-windows.d.ts.map +1 -0
- package/dist/commands/active-windows.js +373 -0
- package/dist/commands/active-windows.js.map +1 -0
- package/dist/commands/audit-mark.d.ts +12 -0
- package/dist/commands/audit-mark.d.ts.map +1 -0
- package/dist/commands/audit-mark.js +204 -0
- package/dist/commands/audit-mark.js.map +1 -0
- package/dist/commands/cleanup.d.ts +8 -0
- package/dist/commands/cleanup.d.ts.map +1 -0
- package/dist/commands/cleanup.js +41 -0
- package/dist/commands/cleanup.js.map +1 -0
- package/dist/commands/close.d.ts +9 -0
- package/dist/commands/close.d.ts.map +1 -0
- package/dist/commands/close.js +129 -0
- package/dist/commands/close.js.map +1 -0
- package/dist/commands/collab-sync.d.ts +14 -0
- package/dist/commands/collab-sync.d.ts.map +1 -0
- package/dist/commands/collab-sync.js +240 -0
- package/dist/commands/collab-sync.js.map +1 -0
- package/dist/commands/collab.d.ts +6 -0
- package/dist/commands/collab.d.ts.map +1 -0
- package/dist/commands/collab.js +103 -0
- package/dist/commands/collab.js.map +1 -0
- package/dist/commands/completions.d.ts +8 -0
- package/dist/commands/completions.d.ts.map +1 -0
- package/dist/commands/completions.js +83 -0
- package/dist/commands/completions.js.map +1 -0
- package/dist/commands/conflicts.d.ts +12 -0
- package/dist/commands/conflicts.d.ts.map +1 -0
- package/dist/commands/conflicts.js +426 -0
- package/dist/commands/conflicts.js.map +1 -0
- package/dist/commands/demo.d.ts +3 -0
- package/dist/commands/demo.d.ts.map +1 -0
- package/dist/commands/demo.js +113 -0
- package/dist/commands/demo.js.map +1 -0
- package/dist/commands/depends.d.ts +8 -0
- package/dist/commands/depends.d.ts.map +1 -0
- package/dist/commands/depends.js +44 -0
- package/dist/commands/depends.js.map +1 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +249 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/inbox.d.ts +3 -0
- package/dist/commands/inbox.d.ts.map +1 -0
- package/dist/commands/inbox.js +66 -0
- package/dist/commands/inbox.js.map +1 -0
- package/dist/commands/index.d.ts +43 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +61 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +23 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/intent-scan.d.ts +17 -0
- package/dist/commands/intent-scan.d.ts.map +1 -0
- package/dist/commands/intent-scan.js +336 -0
- package/dist/commands/intent-scan.js.map +1 -0
- package/dist/commands/key-import.d.ts +3 -0
- package/dist/commands/key-import.d.ts.map +1 -0
- package/dist/commands/key-import.js +54 -0
- package/dist/commands/key-import.js.map +1 -0
- package/dist/commands/keygen.d.ts +3 -0
- package/dist/commands/keygen.d.ts.map +1 -0
- package/dist/commands/keygen.js +53 -0
- package/dist/commands/keygen.js.map +1 -0
- package/dist/commands/keys.d.ts +3 -0
- package/dist/commands/keys.d.ts.map +1 -0
- package/dist/commands/keys.js +56 -0
- package/dist/commands/keys.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +45 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/lock.d.ts +11 -0
- package/dist/commands/lock.d.ts.map +1 -0
- package/dist/commands/lock.js +220 -0
- package/dist/commands/lock.js.map +1 -0
- package/dist/commands/log.d.ts +8 -0
- package/dist/commands/log.d.ts.map +1 -0
- package/dist/commands/log.js +95 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/mcp-health.d.ts +3 -0
- package/dist/commands/mcp-health.d.ts.map +1 -0
- package/dist/commands/mcp-health.js +305 -0
- package/dist/commands/mcp-health.js.map +1 -0
- package/dist/commands/mcp-restart.d.ts +3 -0
- package/dist/commands/mcp-restart.d.ts.map +1 -0
- package/dist/commands/mcp-restart.js +176 -0
- package/dist/commands/mcp-restart.js.map +1 -0
- package/dist/commands/merge-order.d.ts +9 -0
- package/dist/commands/merge-order.d.ts.map +1 -0
- package/dist/commands/merge-order.js +138 -0
- package/dist/commands/merge-order.js.map +1 -0
- package/dist/commands/merged.d.ts +8 -0
- package/dist/commands/merged.d.ts.map +1 -0
- package/dist/commands/merged.js +39 -0
- package/dist/commands/merged.js.map +1 -0
- package/dist/commands/msg-read.d.ts +3 -0
- package/dist/commands/msg-read.d.ts.map +1 -0
- package/dist/commands/msg-read.js +70 -0
- package/dist/commands/msg-read.js.map +1 -0
- package/dist/commands/pulse.d.ts +9 -0
- package/dist/commands/pulse.d.ts.map +1 -0
- package/dist/commands/pulse.js +232 -0
- package/dist/commands/pulse.js.map +1 -0
- package/dist/commands/radar.d.ts +3 -0
- package/dist/commands/radar.d.ts.map +1 -0
- package/dist/commands/radar.js +147 -0
- package/dist/commands/radar.js.map +1 -0
- package/dist/commands/ready-check.d.ts +9 -0
- package/dist/commands/ready-check.d.ts.map +1 -0
- package/dist/commands/ready-check.js +156 -0
- package/dist/commands/ready-check.js.map +1 -0
- package/dist/commands/register.d.ts +3 -0
- package/dist/commands/register.d.ts.map +1 -0
- package/dist/commands/register.js +124 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/commands/send.d.ts +3 -0
- package/dist/commands/send.d.ts.map +1 -0
- package/dist/commands/send.js +134 -0
- package/dist/commands/send.js.map +1 -0
- package/dist/commands/stale.d.ts +8 -0
- package/dist/commands/stale.d.ts.map +1 -0
- package/dist/commands/stale.js +38 -0
- package/dist/commands/stale.js.map +1 -0
- package/dist/commands/status.d.ts +9 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +41 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync-all.d.ts +9 -0
- package/dist/commands/sync-all.d.ts.map +1 -0
- package/dist/commands/sync-all.js +180 -0
- package/dist/commands/sync-all.js.map +1 -0
- package/dist/commands/sync.d.ts +8 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +49 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/tension.d.ts +12 -0
- package/dist/commands/tension.d.ts.map +1 -0
- package/dist/commands/tension.js +242 -0
- package/dist/commands/tension.js.map +1 -0
- package/dist/commands/torrent.d.ts +9 -0
- package/dist/commands/torrent.d.ts.map +1 -0
- package/dist/commands/torrent.js +431 -0
- package/dist/commands/torrent.js.map +1 -0
- package/dist/commands/tui.d.ts +8 -0
- package/dist/commands/tui.d.ts.map +1 -0
- package/dist/commands/tui.js +15 -0
- package/dist/commands/tui.js.map +1 -0
- package/dist/commands/wake.d.ts +9 -0
- package/dist/commands/wake.d.ts.map +1 -0
- package/dist/commands/wake.js +188 -0
- package/dist/commands/wake.js.map +1 -0
- package/dist/commands/watch.d.ts +3 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +245 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/commands/who-touched.d.ts +9 -0
- package/dist/commands/who-touched.d.ts.map +1 -0
- package/dist/commands/who-touched.js +186 -0
- package/dist/commands/who-touched.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/agent-errors.d.ts +41 -0
- package/dist/lib/agent-errors.d.ts.map +1 -0
- package/dist/lib/agent-errors.js +143 -0
- package/dist/lib/agent-errors.js.map +1 -0
- package/dist/lib/ast.d.ts +56 -0
- package/dist/lib/ast.d.ts.map +1 -0
- package/dist/lib/ast.js +134 -0
- package/dist/lib/ast.js.map +1 -0
- package/dist/lib/config.d.ts +33 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +127 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/crdt.d.ts +83 -0
- package/dist/lib/crdt.d.ts.map +1 -0
- package/dist/lib/crdt.js +160 -0
- package/dist/lib/crdt.js.map +1 -0
- package/dist/lib/crypto.d.ts +71 -0
- package/dist/lib/crypto.d.ts.map +1 -0
- package/dist/lib/crypto.js +172 -0
- package/dist/lib/crypto.js.map +1 -0
- package/dist/lib/errors.d.ts +35 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +71 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/license.d.ts +61 -0
- package/dist/lib/license.d.ts.map +1 -0
- package/dist/lib/license.js +171 -0
- package/dist/lib/license.js.map +1 -0
- package/dist/lib/security.d.ts +53 -0
- package/dist/lib/security.d.ts.map +1 -0
- package/dist/lib/security.js +122 -0
- package/dist/lib/security.js.map +1 -0
- package/dist/lib/update-check.d.ts +11 -0
- package/dist/lib/update-check.d.ts.map +1 -0
- package/dist/lib/update-check.js +113 -0
- package/dist/lib/update-check.js.map +1 -0
- package/dist/storage/adapter.d.ts +58 -0
- package/dist/storage/adapter.d.ts.map +1 -0
- package/dist/storage/adapter.js +8 -0
- package/dist/storage/adapter.js.map +1 -0
- package/dist/storage/factory.d.ts +6 -0
- package/dist/storage/factory.d.ts.map +1 -0
- package/dist/storage/factory.js +22 -0
- package/dist/storage/factory.js.map +1 -0
- package/dist/storage/index.d.ts +5 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +5 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/local.d.ts +24 -0
- package/dist/storage/local.d.ts.map +1 -0
- package/dist/storage/local.js +101 -0
- package/dist/storage/local.js.map +1 -0
- package/dist/storage/supabase.d.ts +52 -0
- package/dist/storage/supabase.d.ts.map +1 -0
- package/dist/storage/supabase.js +221 -0
- package/dist/storage/supabase.js.map +1 -0
- package/dist/tui/screen.d.ts +14 -0
- package/dist/tui/screen.d.ts.map +1 -0
- package/dist/tui/screen.js +136 -0
- package/dist/tui/screen.js.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spidersan lock
|
|
3
|
+
*
|
|
4
|
+
* Claim or release locks on symbols (functions, classes, etc.)
|
|
5
|
+
* Uses CRDT for decentralized coordination, syncs via Hub.
|
|
6
|
+
*
|
|
7
|
+
* Part of Codex 5.2 implementation.
|
|
8
|
+
*/
|
|
9
|
+
import { Command } from 'commander';
|
|
10
|
+
import { readFileSync, existsSync } from 'fs';
|
|
11
|
+
import { ASTParser } from '../lib/ast.js';
|
|
12
|
+
import { createSwarmState } from '../lib/crdt.js';
|
|
13
|
+
const HUB_URL = process.env.HUB_URL || 'https://hub.treebird.uk';
|
|
14
|
+
const AGENT_ID = process.env.SPIDERSAN_AGENT || process.env.MYCELIUMAIL_AGENT_ID || 'unknown';
|
|
15
|
+
// Singleton swarm state (persists across commands in same session)
|
|
16
|
+
let swarmState = null;
|
|
17
|
+
function getSwarmState() {
|
|
18
|
+
if (!swarmState) {
|
|
19
|
+
swarmState = createSwarmState(AGENT_ID, async (update) => {
|
|
20
|
+
// Sync updates to Hub
|
|
21
|
+
try {
|
|
22
|
+
await syncToHub(update);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// Hub offline - silent fail
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return swarmState;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Sync CRDT update to Hub for other agents
|
|
33
|
+
*/
|
|
34
|
+
async function syncToHub(update) {
|
|
35
|
+
const base64Update = Buffer.from(update).toString('base64');
|
|
36
|
+
await fetch(`${HUB_URL}/api/spidersan/crdt-sync`, {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
headers: { 'Content-Type': 'application/json' },
|
|
39
|
+
body: JSON.stringify({
|
|
40
|
+
agent: AGENT_ID,
|
|
41
|
+
update: base64Update,
|
|
42
|
+
timestamp: Date.now(),
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Pull latest state from Hub
|
|
48
|
+
*/
|
|
49
|
+
async function pullFromHub() {
|
|
50
|
+
try {
|
|
51
|
+
const response = await fetch(`${HUB_URL}/api/spidersan/crdt-state`);
|
|
52
|
+
if (response.ok) {
|
|
53
|
+
const data = await response.json();
|
|
54
|
+
const state = getSwarmState();
|
|
55
|
+
for (const base64Update of data.updates || []) {
|
|
56
|
+
const update = Buffer.from(base64Update, 'base64');
|
|
57
|
+
state.applyUpdate(new Uint8Array(update));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Hub offline - continue with local state
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Extract symbol ID from file:symbol format
|
|
67
|
+
*/
|
|
68
|
+
function parseSymbolId(symbolId) {
|
|
69
|
+
const parts = symbolId.split(':');
|
|
70
|
+
if (parts.length === 2) {
|
|
71
|
+
return { file: parts[0], symbol: parts[1] };
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Generate symbol ID from file path and symbol name
|
|
77
|
+
*/
|
|
78
|
+
function makeSymbolId(file, symbol) {
|
|
79
|
+
return `${file}:${symbol}`;
|
|
80
|
+
}
|
|
81
|
+
export const lockCommand = new Command('lock')
|
|
82
|
+
.description('Claim or release locks on symbols for coordination')
|
|
83
|
+
.argument('[symbolId]', 'Symbol to lock (format: file.ts:functionName)')
|
|
84
|
+
.option('--release', 'Release a lock instead of acquiring')
|
|
85
|
+
.option('--release-all', 'Release all locks held by this agent')
|
|
86
|
+
.option('--list', 'List all active locks')
|
|
87
|
+
.option('--file <path>', 'Lock all symbols in a file')
|
|
88
|
+
.option('--intent <description>', 'Describe what you intend to do with the symbol')
|
|
89
|
+
.option('--sync', 'Force sync with Hub before operation')
|
|
90
|
+
.action(async (symbolId, options) => {
|
|
91
|
+
const state = getSwarmState();
|
|
92
|
+
// Sync with Hub first if requested
|
|
93
|
+
if (options.sync) {
|
|
94
|
+
console.log('๐ Syncing with Hub...');
|
|
95
|
+
await pullFromHub();
|
|
96
|
+
}
|
|
97
|
+
// List all locks
|
|
98
|
+
if (options.list) {
|
|
99
|
+
const locks = state.getAllLocks();
|
|
100
|
+
if (locks.length === 0) {
|
|
101
|
+
console.log('๐ท๏ธ No active locks');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
console.log(`\n๐ท๏ธ ACTIVE LOCKS (${locks.length}):`);
|
|
105
|
+
console.log('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n');
|
|
106
|
+
// Group by agent
|
|
107
|
+
const byAgent = new Map();
|
|
108
|
+
for (const lock of locks) {
|
|
109
|
+
const existing = byAgent.get(lock.agentId) || [];
|
|
110
|
+
existing.push(lock);
|
|
111
|
+
byAgent.set(lock.agentId, existing);
|
|
112
|
+
}
|
|
113
|
+
for (const [agentId, agentLocks] of byAgent) {
|
|
114
|
+
const isMe = agentId === AGENT_ID;
|
|
115
|
+
console.log(`${isMe ? '๐ค' : '๐ฅ'} ${agentId}${isMe ? ' (you)' : ''}:`);
|
|
116
|
+
for (const lock of agentLocks) {
|
|
117
|
+
const age = Math.floor((Date.now() - lock.timestamp) / 1000 / 60);
|
|
118
|
+
console.log(` ๐ ${lock.symbolId} (${age}m ago)`);
|
|
119
|
+
if (lock.description) {
|
|
120
|
+
console.log(` โโ ${lock.description}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
console.log('');
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// Release all locks
|
|
128
|
+
if (options.releaseAll) {
|
|
129
|
+
const released = state.releaseAllLocks();
|
|
130
|
+
console.log(`๐ Released ${released} lock(s)`);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// Lock all symbols in a file
|
|
134
|
+
if (options.file) {
|
|
135
|
+
const filePath = options.file;
|
|
136
|
+
if (!existsSync(filePath)) {
|
|
137
|
+
console.error(`โ File not found: ${filePath}`);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
const code = readFileSync(filePath, 'utf-8');
|
|
141
|
+
const parser = new ASTParser();
|
|
142
|
+
const fileSymbols = parser.parseCode(code, filePath);
|
|
143
|
+
console.log(`\n๐ท๏ธ LOCKING SYMBOLS IN ${filePath}:`);
|
|
144
|
+
console.log('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n');
|
|
145
|
+
let acquired = 0;
|
|
146
|
+
let blocked = 0;
|
|
147
|
+
for (const symbol of fileSymbols.symbols) {
|
|
148
|
+
const id = makeSymbolId(filePath, symbol.name);
|
|
149
|
+
const success = state.acquireLock(id, symbol.hash, options.intent);
|
|
150
|
+
if (success) {
|
|
151
|
+
console.log(` ๐ ${symbol.type} '${symbol.name}' โ locked`);
|
|
152
|
+
acquired++;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
const existingLock = state.getLock(id);
|
|
156
|
+
console.log(` โ ${symbol.type} '${symbol.name}' โ locked by ${existingLock?.agentId}`);
|
|
157
|
+
blocked++;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
console.log(`\nโ
Acquired: ${acquired} | โ Blocked: ${blocked}`);
|
|
161
|
+
if (options.intent) {
|
|
162
|
+
state.setIntent(options.intent);
|
|
163
|
+
console.log(`\n๐ Intent set: "${options.intent}"`);
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
// Single symbol lock/release
|
|
168
|
+
if (!symbolId) {
|
|
169
|
+
console.error('โ Symbol ID required (format: file.ts:functionName)');
|
|
170
|
+
console.error(' Or use: --file <path> to lock all symbols in a file');
|
|
171
|
+
console.error(' Or use: --list to see active locks');
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
if (options.release) {
|
|
175
|
+
const success = state.releaseLock(symbolId);
|
|
176
|
+
if (success) {
|
|
177
|
+
console.log(`๐ Released lock on '${symbolId}'`);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
console.log(`โ ๏ธ Could not release '${symbolId}' (not locked by you)`);
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
// Acquire lock
|
|
185
|
+
const parsed = parseSymbolId(symbolId);
|
|
186
|
+
let hash = 'manual';
|
|
187
|
+
// If it's a file:symbol format, try to compute actual hash
|
|
188
|
+
if (parsed && existsSync(parsed.file)) {
|
|
189
|
+
try {
|
|
190
|
+
const code = readFileSync(parsed.file, 'utf-8');
|
|
191
|
+
const parser = new ASTParser();
|
|
192
|
+
const fileSymbols = parser.parseCode(code, parsed.file);
|
|
193
|
+
const symbol = fileSymbols.symbols.find(s => s.name === parsed.symbol);
|
|
194
|
+
if (symbol) {
|
|
195
|
+
hash = symbol.hash;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
// Use manual hash
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const success = state.acquireLock(symbolId, hash, options.intent);
|
|
203
|
+
if (success) {
|
|
204
|
+
console.log(`๐ Acquired lock on '${symbolId}'`);
|
|
205
|
+
if (options.intent) {
|
|
206
|
+
state.setIntent(options.intent);
|
|
207
|
+
console.log(`๐ Intent: "${options.intent}"`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
const existingLock = state.getLock(symbolId);
|
|
212
|
+
console.log(`โ Could not lock '${symbolId}'`);
|
|
213
|
+
console.log(` Already locked by: ${existingLock?.agentId}`);
|
|
214
|
+
if (existingLock?.description) {
|
|
215
|
+
console.log(` Their intent: "${existingLock.description}"`);
|
|
216
|
+
}
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/commands/lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/commands/log.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsCpC,eAAO,MAAM,UAAU,SAsEjB,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spidersan log
|
|
3
|
+
*
|
|
4
|
+
* Record successful multi-agent collaboration sessions for future reference.
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import * as os from 'os';
|
|
10
|
+
const LOG_FILE = path.join(os.homedir(), '.spidersan', 'sessions.json');
|
|
11
|
+
function ensureLogFile() {
|
|
12
|
+
const dir = path.dirname(LOG_FILE);
|
|
13
|
+
if (!fs.existsSync(dir)) {
|
|
14
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
if (!fs.existsSync(LOG_FILE)) {
|
|
17
|
+
fs.writeFileSync(LOG_FILE, '[]', 'utf-8');
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(fs.readFileSync(LOG_FILE, 'utf-8'));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function saveLog(logs) {
|
|
28
|
+
fs.writeFileSync(LOG_FILE, JSON.stringify(logs, null, 2), 'utf-8');
|
|
29
|
+
}
|
|
30
|
+
function generateId() {
|
|
31
|
+
return `sess_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
32
|
+
}
|
|
33
|
+
export const logCommand = new Command('log')
|
|
34
|
+
.description('Record multi-agent collaboration sessions')
|
|
35
|
+
.argument('[message]', 'Session description to log')
|
|
36
|
+
.option('--list', 'List all logged sessions')
|
|
37
|
+
.option('--last <n>', 'Show last N sessions', '10')
|
|
38
|
+
.option('--tags <tags>', 'Comma-separated tags for the session')
|
|
39
|
+
.option('--json', 'Output as JSON')
|
|
40
|
+
.option('--clear', 'Clear all session logs')
|
|
41
|
+
.action(async (message, options) => {
|
|
42
|
+
const logs = ensureLogFile();
|
|
43
|
+
// Clear logs
|
|
44
|
+
if (options.clear) {
|
|
45
|
+
saveLog([]);
|
|
46
|
+
console.log('๐ท๏ธ Session logs cleared.');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// List mode
|
|
50
|
+
if (options.list || !message) {
|
|
51
|
+
if (logs.length === 0) {
|
|
52
|
+
console.log('๐ท๏ธ No sessions logged yet.');
|
|
53
|
+
console.log(' Run: spidersan log "Your session description"');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const limit = parseInt(options.last, 10) || 10;
|
|
57
|
+
const recent = logs.slice(-limit).reverse();
|
|
58
|
+
if (options.json) {
|
|
59
|
+
console.log(JSON.stringify(recent, null, 2));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
console.log('๐ท๏ธ Session History:\n');
|
|
63
|
+
for (const session of recent) {
|
|
64
|
+
const date = new Date(session.timestamp).toLocaleString();
|
|
65
|
+
const tags = session.tags?.length ? ` [${session.tags.join(', ')}]` : '';
|
|
66
|
+
console.log(` ๐ ${date}${tags}`);
|
|
67
|
+
console.log(` ${session.message}`);
|
|
68
|
+
console.log(` ID: ${session.id}\n`);
|
|
69
|
+
}
|
|
70
|
+
console.log(`Showing ${recent.length} of ${logs.length} sessions.`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Add new log
|
|
74
|
+
const tags = options.tags ? options.tags.split(',').map((t) => t.trim()) : [];
|
|
75
|
+
const newLog = {
|
|
76
|
+
id: generateId(),
|
|
77
|
+
timestamp: new Date().toISOString(),
|
|
78
|
+
message,
|
|
79
|
+
tags: tags.length > 0 ? tags : undefined
|
|
80
|
+
};
|
|
81
|
+
logs.push(newLog);
|
|
82
|
+
saveLog(logs);
|
|
83
|
+
if (options.json) {
|
|
84
|
+
console.log(JSON.stringify(newLog, null, 2));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
console.log('๐ท๏ธ Session logged!');
|
|
88
|
+
console.log(` ID: ${newLog.id}`);
|
|
89
|
+
console.log(` Message: ${message}`);
|
|
90
|
+
if (tags.length > 0) {
|
|
91
|
+
console.log(` Tags: ${tags.join(', ')}`);
|
|
92
|
+
}
|
|
93
|
+
console.log(`\n Total sessions: ${logs.length}`);
|
|
94
|
+
});
|
|
95
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/commands/log.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-health.d.ts","sourceRoot":"","sources":["../../src/commands/mcp-health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsLpC,eAAO,MAAM,gBAAgB,SAkKvB,CAAC"}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { execSync, spawn } from 'child_process';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import * as os from 'os';
|
|
6
|
+
// Config
|
|
7
|
+
const HUB_URL = process.env.HUB_URL || 'https://hub.treebird.uk';
|
|
8
|
+
const MCP_CONFIG_PATH = path.join(os.homedir(), '.gemini', 'antigravity', 'mcp_config.json');
|
|
9
|
+
/**
|
|
10
|
+
* Read MCP config from Antigravity config file
|
|
11
|
+
*/
|
|
12
|
+
function readMcpConfig() {
|
|
13
|
+
try {
|
|
14
|
+
if (!fs.existsSync(MCP_CONFIG_PATH)) {
|
|
15
|
+
console.log(`โ ๏ธ MCP config not found: ${MCP_CONFIG_PATH}`);
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const content = fs.readFileSync(MCP_CONFIG_PATH, 'utf-8');
|
|
19
|
+
return JSON.parse(content);
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
console.log(`โ ๏ธ Failed to read MCP config: ${err}`);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Start an MCP server from config
|
|
28
|
+
*/
|
|
29
|
+
function startMcpServer(name, config) {
|
|
30
|
+
try {
|
|
31
|
+
const env = { ...process.env, ...config.env };
|
|
32
|
+
const child = spawn(config.command, config.args, {
|
|
33
|
+
detached: true,
|
|
34
|
+
stdio: 'ignore',
|
|
35
|
+
env
|
|
36
|
+
});
|
|
37
|
+
child.unref();
|
|
38
|
+
console.log(`๐ Started ${name} (PID: ${child.pid})`);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
console.log(`โ Failed to start ${name}: ${err}`);
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Known MCP servers in the Treebird ecosystem
|
|
47
|
+
const KNOWN_MCP_SERVERS = [
|
|
48
|
+
{ name: 'watsan-mcp', pattern: 'watsan.*mcp', required: true },
|
|
49
|
+
{ name: 'myceliumail-mcp', pattern: 'myceliumail.*mcp', required: true },
|
|
50
|
+
{ name: 'spidersan-mcp', pattern: 'spidersan.*mcp', required: false },
|
|
51
|
+
{ name: 'supabase-mcp', pattern: 'supabase.*mcp', required: false },
|
|
52
|
+
{ name: 'perplexity-mcp', pattern: 'perplexity', required: false },
|
|
53
|
+
];
|
|
54
|
+
function findMcpProcesses() {
|
|
55
|
+
const results = new Map();
|
|
56
|
+
try {
|
|
57
|
+
// Get all MCP-related processes
|
|
58
|
+
const output = execSync('ps aux | grep -E "mcp|MCP" | grep -v grep', {
|
|
59
|
+
encoding: 'utf-8',
|
|
60
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
61
|
+
}).trim();
|
|
62
|
+
if (!output)
|
|
63
|
+
return results;
|
|
64
|
+
const lines = output.split('\n');
|
|
65
|
+
for (const line of lines) {
|
|
66
|
+
const parts = line.split(/\s+/);
|
|
67
|
+
if (parts.length < 11)
|
|
68
|
+
continue;
|
|
69
|
+
const pid = parseInt(parts[1], 10);
|
|
70
|
+
const startTime = parts[8]; // TIME column
|
|
71
|
+
const command = parts.slice(10).join(' ');
|
|
72
|
+
// Match against known servers
|
|
73
|
+
for (const server of KNOWN_MCP_SERVERS) {
|
|
74
|
+
const regex = new RegExp(server.pattern, 'i');
|
|
75
|
+
if (regex.test(command)) {
|
|
76
|
+
if (!results.has(server.name)) {
|
|
77
|
+
results.set(server.name, []);
|
|
78
|
+
}
|
|
79
|
+
results.get(server.name).push({
|
|
80
|
+
pid,
|
|
81
|
+
command: command.substring(0, 60) + (command.length > 60 ? '...' : ''),
|
|
82
|
+
uptime: startTime
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Check for unknown MCP servers
|
|
87
|
+
if (!Array.from(results.values()).some(procs => procs.some(p => p.pid === pid))) {
|
|
88
|
+
if (/mcp|MCP/.test(command)) {
|
|
89
|
+
const unknownKey = 'unknown-mcp';
|
|
90
|
+
if (!results.has(unknownKey)) {
|
|
91
|
+
results.set(unknownKey, []);
|
|
92
|
+
}
|
|
93
|
+
results.get(unknownKey).push({
|
|
94
|
+
pid,
|
|
95
|
+
command: command.substring(0, 60) + (command.length > 60 ? '...' : ''),
|
|
96
|
+
uptime: startTime
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
// No processes found - that's okay
|
|
104
|
+
}
|
|
105
|
+
return results;
|
|
106
|
+
}
|
|
107
|
+
function getProcessUptime(pid) {
|
|
108
|
+
try {
|
|
109
|
+
const output = execSync(`ps -p ${pid} -o etime=`, { encoding: 'utf-8' }).trim();
|
|
110
|
+
return output || 'unknown';
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return 'unknown';
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Post MCP health summary to Hub chat
|
|
118
|
+
*/
|
|
119
|
+
async function postToHubChat(healthReport, hasIssues) {
|
|
120
|
+
const running = Object.entries(healthReport).filter(([_, v]) => v.status === 'running').length;
|
|
121
|
+
const stopped = Object.entries(healthReport).filter(([_, v]) => v.status === 'stopped').length;
|
|
122
|
+
const zombies = Object.entries(healthReport).filter(([_, v]) => v.status === 'zombie').length;
|
|
123
|
+
const statusIcon = hasIssues ? 'โ ๏ธ' : 'โ
';
|
|
124
|
+
const statusText = hasIssues ? 'Issues detected' : 'All healthy';
|
|
125
|
+
const serverLines = Object.entries(healthReport)
|
|
126
|
+
.map(([name, info]) => {
|
|
127
|
+
const icon = info.status === 'running' ? 'โ
' : info.status === 'zombie' ? 'โ ๏ธ' : 'โ';
|
|
128
|
+
return `${icon} ${name}: ${info.status}`;
|
|
129
|
+
})
|
|
130
|
+
.join('\n');
|
|
131
|
+
const message = `๐ฉบ **MCP Health Report** ${statusIcon}\n\n${serverLines}\n\n**Summary:** ${running} running, ${stopped} stopped, ${zombies} zombies`;
|
|
132
|
+
try {
|
|
133
|
+
const response = await fetch(`${HUB_URL}/api/chat`, {
|
|
134
|
+
method: 'POST',
|
|
135
|
+
headers: { 'Content-Type': 'application/json' },
|
|
136
|
+
body: JSON.stringify({
|
|
137
|
+
agent: 'ssan',
|
|
138
|
+
name: 'Spidersan',
|
|
139
|
+
message,
|
|
140
|
+
glyph: '๐ท๏ธ'
|
|
141
|
+
})
|
|
142
|
+
});
|
|
143
|
+
if (response.ok) {
|
|
144
|
+
console.log('๐ค Posted health report to Hub chat');
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
console.log(`โ ๏ธ Failed to post to Hub: ${response.status}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
console.log('โ ๏ธ Could not connect to Hub');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export const mcpHealthCommand = new Command('mcp-health')
|
|
155
|
+
.description('๐ฉบ Check health of MCP servers in the ecosystem')
|
|
156
|
+
.option('--json', 'Output as JSON for Hub events')
|
|
157
|
+
.option('--hub', 'Post health report to Hub chat')
|
|
158
|
+
.option('--kill-zombies', 'Kill duplicate MCP processes (keep only newest)')
|
|
159
|
+
.option('--auto-restart', 'Automatically restart stopped required MCP servers')
|
|
160
|
+
.action(async (options) => {
|
|
161
|
+
console.log(`
|
|
162
|
+
๐ฉบ MCP Health Check
|
|
163
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
164
|
+
`);
|
|
165
|
+
const processes = findMcpProcesses();
|
|
166
|
+
const healthReport = {};
|
|
167
|
+
let hasIssues = false;
|
|
168
|
+
let zombiesKilled = 0;
|
|
169
|
+
let serversRestarted = 0;
|
|
170
|
+
const stoppedServers = [];
|
|
171
|
+
// Load MCP config for potential restart
|
|
172
|
+
const mcpConfig = options.autoRestart ? readMcpConfig() : null;
|
|
173
|
+
// Check each known server
|
|
174
|
+
for (const server of KNOWN_MCP_SERVERS) {
|
|
175
|
+
const procs = processes.get(server.name) || [];
|
|
176
|
+
const count = procs.length;
|
|
177
|
+
let status;
|
|
178
|
+
let icon;
|
|
179
|
+
if (count === 0) {
|
|
180
|
+
if (server.required) {
|
|
181
|
+
stoppedServers.push(server.name);
|
|
182
|
+
// Try to auto-restart if flag is set
|
|
183
|
+
if (options.autoRestart && mcpConfig?.mcpServers[server.name]) {
|
|
184
|
+
const serverConfig = mcpConfig.mcpServers[server.name];
|
|
185
|
+
if (startMcpServer(server.name, serverConfig)) {
|
|
186
|
+
serversRestarted++;
|
|
187
|
+
status = 'RESTARTED';
|
|
188
|
+
icon = '๐';
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
status = 'RESTART FAILED';
|
|
192
|
+
icon = 'โ';
|
|
193
|
+
hasIssues = true;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
status = 'NOT RUNNING';
|
|
198
|
+
icon = 'โ';
|
|
199
|
+
hasIssues = true;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
status = 'not running (optional)';
|
|
204
|
+
icon = 'โช';
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else if (count === 1) {
|
|
208
|
+
const uptime = getProcessUptime(procs[0].pid);
|
|
209
|
+
status = `running (PID: ${procs[0].pid}, uptime: ${uptime})`;
|
|
210
|
+
icon = 'โ
';
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
status = `${count} ZOMBIES FOUND!`;
|
|
214
|
+
icon = 'โ ๏ธ';
|
|
215
|
+
hasIssues = true;
|
|
216
|
+
if (options.killZombies) {
|
|
217
|
+
// Kill all but the newest (highest PID)
|
|
218
|
+
// IMPORTANT: Exclude current process and parent to avoid self-termination
|
|
219
|
+
const currentPid = process.pid;
|
|
220
|
+
const parentPid = process.ppid;
|
|
221
|
+
const protectedPids = new Set([currentPid, parentPid]);
|
|
222
|
+
const sortedProcs = [...procs].sort((a, b) => b.pid - a.pid);
|
|
223
|
+
const toKill = sortedProcs.slice(1).filter(p => !protectedPids.has(p.pid));
|
|
224
|
+
for (const proc of toKill) {
|
|
225
|
+
try {
|
|
226
|
+
execSync(`kill ${proc.pid}`, { stdio: 'ignore' });
|
|
227
|
+
zombiesKilled++;
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
// Process might already be dead
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
status = `${count} found, ${toKill.length} killed (kept PID: ${sortedProcs[0].pid})`;
|
|
234
|
+
icon = '๐งน';
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
healthReport[server.name] = {
|
|
238
|
+
status: count === 0 ? 'stopped' : count === 1 ? 'running' : 'zombie',
|
|
239
|
+
pids: procs.map(p => p.pid),
|
|
240
|
+
zombieCount: count > 1 ? count - 1 : 0
|
|
241
|
+
};
|
|
242
|
+
if (!options.json) {
|
|
243
|
+
console.log(`${icon} ${server.name}: ${status}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// Check for unknown MCP processes
|
|
247
|
+
const unknownProcs = processes.get('unknown-mcp') || [];
|
|
248
|
+
if (unknownProcs.length > 0) {
|
|
249
|
+
if (!options.json) {
|
|
250
|
+
console.log(`\nโ ๏ธ Unknown MCP processes found:`);
|
|
251
|
+
for (const proc of unknownProcs) {
|
|
252
|
+
console.log(` PID ${proc.pid}: ${proc.command}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
healthReport['unknown'] = {
|
|
256
|
+
status: 'unknown',
|
|
257
|
+
pids: unknownProcs.map(p => p.pid),
|
|
258
|
+
zombieCount: unknownProcs.length
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
if (!options.json) {
|
|
262
|
+
console.log(`
|
|
263
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ`);
|
|
264
|
+
if (options.killZombies && zombiesKilled > 0) {
|
|
265
|
+
console.log(`๐งน Killed ${zombiesKilled} zombie processes`);
|
|
266
|
+
}
|
|
267
|
+
if (serversRestarted > 0) {
|
|
268
|
+
console.log(`๐ Restarted ${serversRestarted} MCP server(s)`);
|
|
269
|
+
}
|
|
270
|
+
if (hasIssues) {
|
|
271
|
+
const hints = [];
|
|
272
|
+
if (stoppedServers.length > 0 && !options.autoRestart) {
|
|
273
|
+
hints.push('spidersan mcp-health --auto-restart');
|
|
274
|
+
}
|
|
275
|
+
hints.push('spidersan mcp-health --kill-zombies');
|
|
276
|
+
console.log(`
|
|
277
|
+
โ ๏ธ Issues found! Try:
|
|
278
|
+
${hints.join('\n ')}
|
|
279
|
+
`);
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
console.log(`
|
|
283
|
+
โ
All MCP servers healthy!
|
|
284
|
+
`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (options.json) {
|
|
288
|
+
const event = {
|
|
289
|
+
event: 'mcp:health',
|
|
290
|
+
payload: {
|
|
291
|
+
servers: healthReport,
|
|
292
|
+
hasIssues,
|
|
293
|
+
zombiesKilled,
|
|
294
|
+
timestamp: new Date().toISOString()
|
|
295
|
+
},
|
|
296
|
+
emitter: 'spidersan'
|
|
297
|
+
};
|
|
298
|
+
console.log(JSON.stringify(event, null, 2));
|
|
299
|
+
}
|
|
300
|
+
// Post to Hub if --hub flag is set
|
|
301
|
+
if (options.hub) {
|
|
302
|
+
await postToHubChat(healthReport, hasIssues);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
//# sourceMappingURL=mcp-health.js.map
|