overarch-linux-arm64 0.8.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/LICENSE +21 -0
- package/README.md +214 -0
- package/dist/bin/overarch.d.ts +2 -0
- package/dist/bin/overarch.js +178 -0
- package/dist/src/agent/context.d.ts +14 -0
- package/dist/src/agent/context.js +200 -0
- package/dist/src/agent/context_compressor.d.ts +21 -0
- package/dist/src/agent/context_compressor.js +90 -0
- package/dist/src/agent/manifest.d.ts +22 -0
- package/dist/src/agent/manifest.js +131 -0
- package/dist/src/agent/planner.d.ts +38 -0
- package/dist/src/agent/planner.js +118 -0
- package/dist/src/agent/runtime.d.ts +59 -0
- package/dist/src/agent/runtime.js +400 -0
- package/dist/src/cli/approval.d.ts +13 -0
- package/dist/src/cli/approval.js +71 -0
- package/dist/src/cli/args.d.ts +15 -0
- package/dist/src/cli/args.js +47 -0
- package/dist/src/cli/commands/audit.d.ts +10 -0
- package/dist/src/cli/commands/audit.js +79 -0
- package/dist/src/cli/commands/companion.d.ts +1 -0
- package/dist/src/cli/commands/companion.js +143 -0
- package/dist/src/cli/commands/config.d.ts +1 -0
- package/dist/src/cli/commands/config.js +135 -0
- package/dist/src/cli/commands/doctor.d.ts +1 -0
- package/dist/src/cli/commands/doctor.js +217 -0
- package/dist/src/cli/commands/init.d.ts +1 -0
- package/dist/src/cli/commands/init.js +134 -0
- package/dist/src/cli/commands/memory.d.ts +2 -0
- package/dist/src/cli/commands/memory.js +98 -0
- package/dist/src/cli/commands/onboarding.d.ts +3 -0
- package/dist/src/cli/commands/onboarding.js +223 -0
- package/dist/src/cli/commands/sessions.d.ts +1 -0
- package/dist/src/cli/commands/sessions.js +22 -0
- package/dist/src/cli/commands/status.d.ts +1 -0
- package/dist/src/cli/commands/status.js +37 -0
- package/dist/src/cli/commands/update.d.ts +9 -0
- package/dist/src/cli/commands/update.js +105 -0
- package/dist/src/cli/repl.d.ts +3 -0
- package/dist/src/cli/repl.js +821 -0
- package/dist/src/cli/ui.d.ts +53 -0
- package/dist/src/cli/ui.js +215 -0
- package/dist/src/companion/petdex_adapter.d.ts +31 -0
- package/dist/src/companion/petdex_adapter.js +171 -0
- package/dist/src/companion/petdex_bridge.d.ts +65 -0
- package/dist/src/companion/petdex_bridge.js +356 -0
- package/dist/src/companion/types.d.ts +57 -0
- package/dist/src/companion/types.js +11 -0
- package/dist/src/config/index.d.ts +24 -0
- package/dist/src/config/index.js +237 -0
- package/dist/src/config/types.d.ts +58 -0
- package/dist/src/config/types.js +7 -0
- package/dist/src/core/diagnostics.d.ts +48 -0
- package/dist/src/core/diagnostics.js +115 -0
- package/dist/src/core/index.d.ts +23 -0
- package/dist/src/core/index.js +23 -0
- package/dist/src/core/mission.d.ts +55 -0
- package/dist/src/core/mission.js +173 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/learning/memory_manager.d.ts +26 -0
- package/dist/src/learning/memory_manager.js +211 -0
- package/dist/src/learning/types.d.ts +54 -0
- package/dist/src/learning/types.js +1 -0
- package/dist/src/mcp/registry.d.ts +17 -0
- package/dist/src/mcp/registry.js +97 -0
- package/dist/src/mcp/types.d.ts +13 -0
- package/dist/src/mcp/types.js +1 -0
- package/dist/src/providers/anthropic.d.ts +10 -0
- package/dist/src/providers/anthropic.js +95 -0
- package/dist/src/providers/base.d.ts +15 -0
- package/dist/src/providers/base.js +108 -0
- package/dist/src/providers/factory.d.ts +28 -0
- package/dist/src/providers/factory.js +142 -0
- package/dist/src/providers/gemini.d.ts +10 -0
- package/dist/src/providers/gemini.js +97 -0
- package/dist/src/providers/openai.d.ts +9 -0
- package/dist/src/providers/openai.js +85 -0
- package/dist/src/providers/openrouter.d.ts +5 -0
- package/dist/src/providers/openrouter.js +14 -0
- package/dist/src/providers/registry.d.ts +19 -0
- package/dist/src/providers/registry.js +209 -0
- package/dist/src/providers/types.d.ts +59 -0
- package/dist/src/providers/types.js +1 -0
- package/dist/src/providers/validator.d.ts +24 -0
- package/dist/src/providers/validator.js +161 -0
- package/dist/src/security/audit.d.ts +56 -0
- package/dist/src/security/audit.js +154 -0
- package/dist/src/security/guardrails.d.ts +9 -0
- package/dist/src/security/guardrails.js +32 -0
- package/dist/src/security/permissions.d.ts +12 -0
- package/dist/src/security/permissions.js +68 -0
- package/dist/src/security/policy.d.ts +220 -0
- package/dist/src/security/policy.js +720 -0
- package/dist/src/session/manager.d.ts +22 -0
- package/dist/src/session/manager.js +172 -0
- package/dist/src/session/types.d.ts +36 -0
- package/dist/src/session/types.js +1 -0
- package/dist/src/tools/checkpoint.d.ts +26 -0
- package/dist/src/tools/checkpoint.js +160 -0
- package/dist/src/tools/diagnostics.d.ts +2 -0
- package/dist/src/tools/diagnostics.js +125 -0
- package/dist/src/tools/diff.d.ts +14 -0
- package/dist/src/tools/diff.js +124 -0
- package/dist/src/tools/filesystem.d.ts +2 -0
- package/dist/src/tools/filesystem.js +224 -0
- package/dist/src/tools/git.d.ts +2 -0
- package/dist/src/tools/git.js +138 -0
- package/dist/src/tools/markdown.d.ts +33 -0
- package/dist/src/tools/markdown.js +190 -0
- package/dist/src/tools/project.d.ts +2 -0
- package/dist/src/tools/project.js +63 -0
- package/dist/src/tools/registry.d.ts +14 -0
- package/dist/src/tools/registry.js +230 -0
- package/dist/src/tools/search.d.ts +2 -0
- package/dist/src/tools/search.js +136 -0
- package/dist/src/tools/shell.d.ts +2 -0
- package/dist/src/tools/shell.js +74 -0
- package/dist/src/tools/symbols.d.ts +30 -0
- package/dist/src/tools/symbols.js +155 -0
- package/dist/src/tools/types.d.ts +28 -0
- package/dist/src/tools/types.js +1 -0
- package/dist/src/tools/web_search.d.ts +14 -0
- package/dist/src/tools/web_search.js +154 -0
- package/install.sh +29 -0
- package/overarch +15 -0
- package/package.json +28 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import * as http from 'node:http';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as fs from 'node:fs/promises';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import { spawn } from 'node:child_process';
|
|
6
|
+
import { petdexAdapter } from './petdex_adapter.js';
|
|
7
|
+
export const STATE_TO_PETDEX_TRIGGER = {
|
|
8
|
+
idle: 'idle',
|
|
9
|
+
thinking: 'focus',
|
|
10
|
+
reading: 'reading',
|
|
11
|
+
searching: 'searching',
|
|
12
|
+
editing: 'typing',
|
|
13
|
+
executing: 'running',
|
|
14
|
+
testing: 'testing',
|
|
15
|
+
waiting: 'waiting',
|
|
16
|
+
awaiting_approval: 'waiting',
|
|
17
|
+
success: 'celebrate',
|
|
18
|
+
warning: 'alert',
|
|
19
|
+
error: 'alert',
|
|
20
|
+
paused: 'sleep',
|
|
21
|
+
stopped: 'sleep',
|
|
22
|
+
offline: 'sleep',
|
|
23
|
+
};
|
|
24
|
+
export class PetdexBridge {
|
|
25
|
+
server = null;
|
|
26
|
+
sseClients = new Set();
|
|
27
|
+
currentState = 'idle';
|
|
28
|
+
currentMessage = '';
|
|
29
|
+
activePet = 'savage-codex-hacker';
|
|
30
|
+
port = 45731;
|
|
31
|
+
externalHookPort = 7777;
|
|
32
|
+
isRunning = false;
|
|
33
|
+
companionProcess = null;
|
|
34
|
+
constructor(options) {
|
|
35
|
+
if (options?.port)
|
|
36
|
+
this.port = options.port;
|
|
37
|
+
if (options?.externalHookPort)
|
|
38
|
+
this.externalHookPort = options.externalHookPort;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Starts local SSE / HTTP bridge server (unref'd so it does not block Node process exit)
|
|
42
|
+
*/
|
|
43
|
+
start() {
|
|
44
|
+
if (this.isRunning && this.server) {
|
|
45
|
+
return Promise.resolve();
|
|
46
|
+
}
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
try {
|
|
49
|
+
this.server = http.createServer((req, res) => {
|
|
50
|
+
this.handleRequest(req, res);
|
|
51
|
+
});
|
|
52
|
+
this.server.on('error', (_err) => {
|
|
53
|
+
this.isRunning = false;
|
|
54
|
+
resolve();
|
|
55
|
+
});
|
|
56
|
+
this.server.listen(this.port, '127.0.0.1', () => {
|
|
57
|
+
this.isRunning = true;
|
|
58
|
+
this.server?.unref();
|
|
59
|
+
resolve();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
resolve();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
handleRequest(req, res) {
|
|
68
|
+
const url = new URL(req.url || '/', `http://${req.headers.host || '127.0.0.1'}`);
|
|
69
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
70
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
71
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
72
|
+
if (req.method === 'OPTIONS') {
|
|
73
|
+
res.writeHead(204);
|
|
74
|
+
res.end();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// SSE endpoint: /events
|
|
78
|
+
if (url.pathname === '/events' && req.method === 'GET') {
|
|
79
|
+
res.writeHead(200, {
|
|
80
|
+
'Content-Type': 'text/event-stream',
|
|
81
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
82
|
+
Connection: 'keep-alive',
|
|
83
|
+
});
|
|
84
|
+
res.write(`: connected to Overarch Petdex Companion Bridge\n\n`);
|
|
85
|
+
this.sseClients.add(res);
|
|
86
|
+
const rowMapping = petdexAdapter.mapStateToAnimationRow(this.currentState);
|
|
87
|
+
const initialPayload = {
|
|
88
|
+
event: 'state_change',
|
|
89
|
+
data: {
|
|
90
|
+
state: this.currentState,
|
|
91
|
+
petdexTrigger: STATE_TO_PETDEX_TRIGGER[this.currentState] || 'idle',
|
|
92
|
+
animationRow: rowMapping.row,
|
|
93
|
+
message: this.currentMessage,
|
|
94
|
+
pet: this.activePet,
|
|
95
|
+
timestamp: new Date().toISOString(),
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
res.write(`data: ${JSON.stringify(initialPayload)}\n\n`);
|
|
99
|
+
req.on('close', () => {
|
|
100
|
+
this.sseClients.delete(res);
|
|
101
|
+
});
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// JSON status endpoint: /state or /companion/status
|
|
105
|
+
if ((url.pathname === '/state' || url.pathname === '/companion/status') && req.method === 'GET') {
|
|
106
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
107
|
+
res.end(JSON.stringify({
|
|
108
|
+
active: true,
|
|
109
|
+
state: this.currentState,
|
|
110
|
+
petdexTrigger: STATE_TO_PETDEX_TRIGGER[this.currentState] || 'idle',
|
|
111
|
+
animationRow: petdexAdapter.mapStateToAnimationRow(this.currentState).row,
|
|
112
|
+
message: this.currentMessage,
|
|
113
|
+
pet: this.activePet,
|
|
114
|
+
activePet: this.activePet,
|
|
115
|
+
clientsConnected: this.sseClients.size,
|
|
116
|
+
port: this.port,
|
|
117
|
+
}));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// Discovered pets endpoint: /pets or /companion/pets
|
|
121
|
+
if ((url.pathname === '/pets' || url.pathname === '/companion/pets') && req.method === 'GET') {
|
|
122
|
+
petdexAdapter.listPets().then((pets) => {
|
|
123
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
124
|
+
res.end(JSON.stringify({ activePet: this.activePet, pets }));
|
|
125
|
+
});
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// Select active companion pet: POST /select or /companion/select
|
|
129
|
+
if ((url.pathname === '/select' || url.pathname === '/companion/select') && req.method === 'POST') {
|
|
130
|
+
let body = '';
|
|
131
|
+
req.on('data', (chunk) => (body += chunk));
|
|
132
|
+
req.on('end', () => {
|
|
133
|
+
try {
|
|
134
|
+
const parsed = JSON.parse(body || '{}');
|
|
135
|
+
if (parsed.pet) {
|
|
136
|
+
this.activePet = String(parsed.pet);
|
|
137
|
+
const rowMapping = petdexAdapter.mapStateToAnimationRow(this.currentState);
|
|
138
|
+
this.broadcast('pet_selected', {
|
|
139
|
+
state: this.currentState,
|
|
140
|
+
petdexTrigger: STATE_TO_PETDEX_TRIGGER[this.currentState] || 'idle',
|
|
141
|
+
animationRow: rowMapping.row,
|
|
142
|
+
pet: this.activePet,
|
|
143
|
+
timestamp: new Date().toISOString(),
|
|
144
|
+
});
|
|
145
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
146
|
+
res.end(JSON.stringify({ success: true, activePet: this.activePet }));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch { }
|
|
151
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
152
|
+
res.end(JSON.stringify({ error: 'Missing or invalid pet name in payload' }));
|
|
153
|
+
});
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// Install Petdex pet endpoint: POST /companion/install
|
|
157
|
+
if (url.pathname === '/companion/install' && req.method === 'POST') {
|
|
158
|
+
let body = '';
|
|
159
|
+
req.on('data', (chunk) => (body += chunk));
|
|
160
|
+
req.on('end', async () => {
|
|
161
|
+
try {
|
|
162
|
+
const parsed = JSON.parse(body || '{}');
|
|
163
|
+
if (parsed.slug) {
|
|
164
|
+
const installRes = await petdexAdapter.installPet(parsed.slug);
|
|
165
|
+
res.writeHead(installRes.success ? 200 : 500, { 'Content-Type': 'application/json' });
|
|
166
|
+
res.end(JSON.stringify(installRes));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch { }
|
|
171
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
172
|
+
res.end(JSON.stringify({ error: 'Missing slug' }));
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
177
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Broadcast state change to SSE subscribers and notify external hook port if present
|
|
181
|
+
*/
|
|
182
|
+
broadcastState(state, message) {
|
|
183
|
+
this.currentState = state;
|
|
184
|
+
if (message !== undefined) {
|
|
185
|
+
this.currentMessage = message;
|
|
186
|
+
}
|
|
187
|
+
const trigger = STATE_TO_PETDEX_TRIGGER[state] || 'idle';
|
|
188
|
+
const rowMapping = petdexAdapter.mapStateToAnimationRow(state);
|
|
189
|
+
const payload = {
|
|
190
|
+
event: 'state_change',
|
|
191
|
+
data: {
|
|
192
|
+
state,
|
|
193
|
+
petdexTrigger: trigger,
|
|
194
|
+
animationRow: rowMapping.row,
|
|
195
|
+
message: this.currentMessage,
|
|
196
|
+
pet: this.activePet,
|
|
197
|
+
timestamp: new Date().toISOString(),
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
const dataStr = `data: ${JSON.stringify(payload)}\n\n`;
|
|
201
|
+
for (const client of this.sseClients) {
|
|
202
|
+
try {
|
|
203
|
+
client.write(dataStr);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
this.sseClients.delete(client);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
this.notifyExternalPetdexHook(payload);
|
|
210
|
+
}
|
|
211
|
+
broadcast(event, data) {
|
|
212
|
+
const payload = { event, data };
|
|
213
|
+
const dataStr = `data: ${JSON.stringify(payload)}\n\n`;
|
|
214
|
+
for (const client of this.sseClients) {
|
|
215
|
+
try {
|
|
216
|
+
client.write(dataStr);
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
this.sseClients.delete(client);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
this.notifyExternalPetdexHook(payload);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Non-blocking, error-tolerant notification to standard Petdex hook port 7777
|
|
226
|
+
*/
|
|
227
|
+
notifyExternalPetdexHook(payload) {
|
|
228
|
+
const postData = JSON.stringify(payload);
|
|
229
|
+
const req = http.request({
|
|
230
|
+
hostname: '127.0.0.1',
|
|
231
|
+
port: this.externalHookPort,
|
|
232
|
+
path: '/events',
|
|
233
|
+
method: 'POST',
|
|
234
|
+
headers: {
|
|
235
|
+
'Content-Type': 'application/json',
|
|
236
|
+
'Content-Length': Buffer.byteLength(postData),
|
|
237
|
+
},
|
|
238
|
+
timeout: 60,
|
|
239
|
+
}, (res) => {
|
|
240
|
+
res.resume();
|
|
241
|
+
});
|
|
242
|
+
req.on('error', () => {
|
|
243
|
+
// Gracefully silent when external hook server is not running
|
|
244
|
+
});
|
|
245
|
+
req.on('timeout', () => {
|
|
246
|
+
req.destroy();
|
|
247
|
+
});
|
|
248
|
+
req.write(postData);
|
|
249
|
+
req.end();
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Discovers Petdex-compatible pets
|
|
253
|
+
*/
|
|
254
|
+
async discoverPets() {
|
|
255
|
+
return petdexAdapter.listPets();
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Launches native Overarch desktop companion (or external Petdex app if present)
|
|
259
|
+
*/
|
|
260
|
+
async launchCompanion(cwd = process.cwd()) {
|
|
261
|
+
// Check candidate paths for OverarchCompanion.exe
|
|
262
|
+
const companionCandidates = [
|
|
263
|
+
path.join(cwd, 'dist-desktop', 'OverarchCompanion.exe'),
|
|
264
|
+
path.join(path.dirname(process.execPath), 'OverarchCompanion.exe'),
|
|
265
|
+
path.join(os.homedir(), '.overarch', 'bin', 'OverarchCompanion.exe'),
|
|
266
|
+
];
|
|
267
|
+
let binaryPath = null;
|
|
268
|
+
for (const candidate of companionCandidates) {
|
|
269
|
+
try {
|
|
270
|
+
await fs.access(candidate);
|
|
271
|
+
binaryPath = candidate;
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
catch { }
|
|
275
|
+
}
|
|
276
|
+
if (binaryPath) {
|
|
277
|
+
try {
|
|
278
|
+
const proc = spawn(binaryPath, [`--bridge=http://127.0.0.1:${this.port}`, `--pet=${this.activePet}`], {
|
|
279
|
+
detached: true,
|
|
280
|
+
stdio: 'ignore',
|
|
281
|
+
});
|
|
282
|
+
proc.unref();
|
|
283
|
+
this.companionProcess = proc;
|
|
284
|
+
return {
|
|
285
|
+
success: true,
|
|
286
|
+
message: `Launched Overarch Desktop Companion (${this.activePet}) connected to bridge port ${this.port}.`,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
return { success: false, message: `Failed to launch ${binaryPath}: ${err.message}` };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// Fallback: try petdex CLI or desktop
|
|
294
|
+
return new Promise((resolve) => {
|
|
295
|
+
const proc = spawn(`petdex start --bridge http://127.0.0.1:${this.port}`, {
|
|
296
|
+
detached: true,
|
|
297
|
+
stdio: 'ignore',
|
|
298
|
+
shell: true,
|
|
299
|
+
});
|
|
300
|
+
proc.on('error', () => {
|
|
301
|
+
resolve({
|
|
302
|
+
success: false,
|
|
303
|
+
message: 'OverarchCompanion.exe not found in dist-desktop/ and external petdex not in PATH.',
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
proc.unref();
|
|
307
|
+
setTimeout(() => {
|
|
308
|
+
resolve({
|
|
309
|
+
success: true,
|
|
310
|
+
message: `Dispatched launch to companion bridge (listening on http://127.0.0.1:${this.port}/events).`,
|
|
311
|
+
});
|
|
312
|
+
}, 120);
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
getActivePet() {
|
|
316
|
+
return this.activePet;
|
|
317
|
+
}
|
|
318
|
+
setActivePet(pet) {
|
|
319
|
+
this.activePet = pet;
|
|
320
|
+
const rowMapping = petdexAdapter.mapStateToAnimationRow(this.currentState);
|
|
321
|
+
this.broadcast('pet_selected', {
|
|
322
|
+
state: this.currentState,
|
|
323
|
+
petdexTrigger: STATE_TO_PETDEX_TRIGGER[this.currentState] || 'idle',
|
|
324
|
+
animationRow: rowMapping.row,
|
|
325
|
+
pet: this.activePet,
|
|
326
|
+
timestamp: new Date().toISOString(),
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
getStatus() {
|
|
330
|
+
return {
|
|
331
|
+
running: this.isRunning,
|
|
332
|
+
port: this.port,
|
|
333
|
+
connectedClients: this.sseClients.size,
|
|
334
|
+
activePet: this.activePet,
|
|
335
|
+
state: this.currentState,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
stop() {
|
|
339
|
+
for (const client of this.sseClients) {
|
|
340
|
+
try {
|
|
341
|
+
client.end();
|
|
342
|
+
}
|
|
343
|
+
catch { }
|
|
344
|
+
}
|
|
345
|
+
this.sseClients.clear();
|
|
346
|
+
if (this.server) {
|
|
347
|
+
try {
|
|
348
|
+
this.server.close();
|
|
349
|
+
}
|
|
350
|
+
catch { }
|
|
351
|
+
this.server = null;
|
|
352
|
+
}
|
|
353
|
+
this.isRunning = false;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
export const petdexBridge = new PetdexBridge();
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export type AgentState = 'idle' | 'thinking' | 'reading' | 'searching' | 'editing' | 'executing' | 'testing' | 'waiting' | 'awaiting_approval' | 'success' | 'warning' | 'error' | 'paused' | 'stopped' | 'offline';
|
|
2
|
+
export type MissionState = AgentState;
|
|
3
|
+
export interface PetdexManifest {
|
|
4
|
+
id: string;
|
|
5
|
+
displayName: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
spritesheetPath?: string;
|
|
8
|
+
spriteVersionNumber?: 1 | 2;
|
|
9
|
+
author?: string;
|
|
10
|
+
version?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface CompanionPetInfo {
|
|
13
|
+
id: string;
|
|
14
|
+
displayName: string;
|
|
15
|
+
description: string;
|
|
16
|
+
source: 'petdex-installed' | 'petdex-gallery' | 'local';
|
|
17
|
+
spritesheetPath?: string;
|
|
18
|
+
spritesheetFormat: 'webp' | 'png';
|
|
19
|
+
spriteVersionNumber: 1 | 2;
|
|
20
|
+
columns: number;
|
|
21
|
+
rows: number;
|
|
22
|
+
frameWidth?: number;
|
|
23
|
+
frameHeight?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface PetdexAnimationRowMapping {
|
|
26
|
+
idle: number;
|
|
27
|
+
runningRight: number;
|
|
28
|
+
runningLeft: number;
|
|
29
|
+
waving: number;
|
|
30
|
+
jumping: number;
|
|
31
|
+
failed: number;
|
|
32
|
+
waiting: number;
|
|
33
|
+
running: number;
|
|
34
|
+
review: number;
|
|
35
|
+
}
|
|
36
|
+
export declare const PETDEX_DEFAULT_ROW_MAPPING: PetdexAnimationRowMapping;
|
|
37
|
+
export interface CompanionConfig {
|
|
38
|
+
enabled: boolean;
|
|
39
|
+
activePet: string;
|
|
40
|
+
position?: {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
};
|
|
44
|
+
alwaysOnTop: boolean;
|
|
45
|
+
opacity: number;
|
|
46
|
+
scale: number;
|
|
47
|
+
}
|
|
48
|
+
export interface PetProvider {
|
|
49
|
+
id: string;
|
|
50
|
+
name: string;
|
|
51
|
+
listPets(): Promise<CompanionPetInfo[]>;
|
|
52
|
+
getPet(id: string): Promise<CompanionPetInfo | null>;
|
|
53
|
+
installPet?(slug: string): Promise<{
|
|
54
|
+
success: boolean;
|
|
55
|
+
message: string;
|
|
56
|
+
}>;
|
|
57
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { OverarchConfig, ResolvedConfig } from './types.js';
|
|
2
|
+
export declare const DEFAULT_CONFIG: OverarchConfig;
|
|
3
|
+
export declare function getConfigDir(): string;
|
|
4
|
+
/**
|
|
5
|
+
* Load environment variables from .env file without external dependencies
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadDotenv(cwd?: string): Promise<Record<string, string>>;
|
|
8
|
+
/**
|
|
9
|
+
* Safely writes or updates an API key in the project's .env file.
|
|
10
|
+
* Automatically verifies and adds .env to .gitignore to prevent secret leaks.
|
|
11
|
+
*/
|
|
12
|
+
export declare function writeDotenvKey(cwd: string, keyName: string, keyValue: string): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Ensures an entry is present in the workspace .gitignore
|
|
15
|
+
*/
|
|
16
|
+
export declare function ensureGitignoreEntry(cwd: string, entry: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Auto-generates or verifies the .overarch project subsystem structure
|
|
19
|
+
*/
|
|
20
|
+
export declare function autoEnsureOverarch(cwd?: string): Promise<string>;
|
|
21
|
+
export declare function loadConfig(): Promise<ResolvedConfig>;
|
|
22
|
+
export declare function saveConfig(config: OverarchConfig, targetPath: string): Promise<void>;
|
|
23
|
+
export declare function saveGlobalConfig(updates: Partial<OverarchConfig>): Promise<void>;
|
|
24
|
+
export declare function getApiKey(provider: string, cwd?: string): string | undefined;
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import { PermissionTier } from './types.js';
|
|
5
|
+
export const DEFAULT_CONFIG = {
|
|
6
|
+
providers: {},
|
|
7
|
+
defaultModel: '',
|
|
8
|
+
mode: 'online',
|
|
9
|
+
agentMode: 'approval',
|
|
10
|
+
ponytail: 'full',
|
|
11
|
+
ccss: 'auto',
|
|
12
|
+
permissions: {
|
|
13
|
+
autoApprove: [PermissionTier.ReadOnly, PermissionTier.SafeMutation],
|
|
14
|
+
requireApproval: [PermissionTier.StateMutating, PermissionTier.Destructive],
|
|
15
|
+
deny: []
|
|
16
|
+
},
|
|
17
|
+
context: {
|
|
18
|
+
maxTokens: 100000,
|
|
19
|
+
maxFileSize: 1024 * 1024
|
|
20
|
+
},
|
|
21
|
+
project: {
|
|
22
|
+
include: [],
|
|
23
|
+
exclude: [],
|
|
24
|
+
instructions: ''
|
|
25
|
+
},
|
|
26
|
+
ui: {
|
|
27
|
+
accentColor: 'cyan',
|
|
28
|
+
avatar: '◆',
|
|
29
|
+
verbosity: 'normal',
|
|
30
|
+
},
|
|
31
|
+
onboarding: {
|
|
32
|
+
completed: false,
|
|
33
|
+
version: '0.3.0',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
export function getConfigDir() {
|
|
37
|
+
return path.join(os.homedir(), '.overarch');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Load environment variables from .env file without external dependencies
|
|
41
|
+
*/
|
|
42
|
+
export async function loadDotenv(cwd = process.cwd()) {
|
|
43
|
+
const envVars = {};
|
|
44
|
+
const pathsToCheck = [
|
|
45
|
+
path.join(cwd, '.env'),
|
|
46
|
+
path.join(getConfigDir(), '.env')
|
|
47
|
+
];
|
|
48
|
+
for (const p of pathsToCheck) {
|
|
49
|
+
try {
|
|
50
|
+
const content = await fs.readFile(p, 'utf8');
|
|
51
|
+
const lines = content.replace(/\r\n/g, '\n').split('\n');
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
const trimmed = line.trim();
|
|
54
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
55
|
+
continue;
|
|
56
|
+
const eqIdx = trimmed.indexOf('=');
|
|
57
|
+
if (eqIdx > 0) {
|
|
58
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
59
|
+
let val = trimmed.slice(eqIdx + 1).trim();
|
|
60
|
+
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
|
|
61
|
+
val = val.slice(1, -1);
|
|
62
|
+
}
|
|
63
|
+
envVars[key] = val;
|
|
64
|
+
if (!process.env[key]) {
|
|
65
|
+
process.env[key] = val;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch { }
|
|
71
|
+
}
|
|
72
|
+
return envVars;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Safely writes or updates an API key in the project's .env file.
|
|
76
|
+
* Automatically verifies and adds .env to .gitignore to prevent secret leaks.
|
|
77
|
+
*/
|
|
78
|
+
export async function writeDotenvKey(cwd, keyName, keyValue) {
|
|
79
|
+
const envPath = path.join(cwd, '.env');
|
|
80
|
+
let lines = [];
|
|
81
|
+
try {
|
|
82
|
+
const existing = await fs.readFile(envPath, 'utf8');
|
|
83
|
+
lines = existing.replace(/\r\n/g, '\n').split('\n');
|
|
84
|
+
}
|
|
85
|
+
catch { }
|
|
86
|
+
let found = false;
|
|
87
|
+
const updatedLines = lines.map(line => {
|
|
88
|
+
const trimmed = line.trim();
|
|
89
|
+
if (trimmed.startsWith(`${keyName}=`)) {
|
|
90
|
+
found = true;
|
|
91
|
+
return `${keyName}="${keyValue}"`;
|
|
92
|
+
}
|
|
93
|
+
return line;
|
|
94
|
+
});
|
|
95
|
+
if (!found) {
|
|
96
|
+
if (updatedLines.length > 0 && updatedLines[updatedLines.length - 1] !== '') {
|
|
97
|
+
updatedLines.push('');
|
|
98
|
+
}
|
|
99
|
+
updatedLines.push(`${keyName}="${keyValue}"`);
|
|
100
|
+
}
|
|
101
|
+
await fs.writeFile(envPath, updatedLines.join('\n') + '\n', 'utf8');
|
|
102
|
+
process.env[keyName] = keyValue;
|
|
103
|
+
// Ensure .env is in .gitignore
|
|
104
|
+
await ensureGitignoreEntry(cwd, '.env');
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Ensures an entry is present in the workspace .gitignore
|
|
108
|
+
*/
|
|
109
|
+
export async function ensureGitignoreEntry(cwd, entry) {
|
|
110
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
111
|
+
try {
|
|
112
|
+
let content = '';
|
|
113
|
+
try {
|
|
114
|
+
content = await fs.readFile(gitignorePath, 'utf8');
|
|
115
|
+
}
|
|
116
|
+
catch { }
|
|
117
|
+
const lines = content.replace(/\r\n/g, '\n').split('\n').map(l => l.trim());
|
|
118
|
+
if (!lines.includes(entry.trim())) {
|
|
119
|
+
const addition = (content && !content.endsWith('\n') ? '\n' : '') + `${entry}\n`;
|
|
120
|
+
await fs.appendFile(gitignorePath, addition, 'utf8');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch { }
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Auto-generates or verifies the .overarch project subsystem structure
|
|
127
|
+
*/
|
|
128
|
+
export async function autoEnsureOverarch(cwd = process.cwd()) {
|
|
129
|
+
const overarchDir = path.join(cwd, '.overarch');
|
|
130
|
+
await fs.mkdir(overarchDir, { recursive: true });
|
|
131
|
+
await fs.mkdir(path.join(overarchDir, 'sessions'), { recursive: true });
|
|
132
|
+
await fs.mkdir(path.join(overarchDir, 'cache'), { recursive: true });
|
|
133
|
+
const filesToEnsure = {
|
|
134
|
+
'config.json': JSON.stringify({ project: DEFAULT_CONFIG.project }, null, 2),
|
|
135
|
+
'instructions.md': '# Overarch Project Instructions\n\nAdd repository-specific guidelines, architecture rules, and patterns here.\n',
|
|
136
|
+
'memory.md': '# Project Memory\n\nPersistent decisions, patterns, and notes discovered during sessions.\n',
|
|
137
|
+
'.gitignore': 'sessions/\ncache/\n*.log\n*.env\nstate.json\n'
|
|
138
|
+
};
|
|
139
|
+
for (const [filename, defaultContent] of Object.entries(filesToEnsure)) {
|
|
140
|
+
const filePath = path.join(overarchDir, filename);
|
|
141
|
+
try {
|
|
142
|
+
await fs.access(filePath);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
await fs.writeFile(filePath, defaultContent, 'utf8');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return overarchDir;
|
|
149
|
+
}
|
|
150
|
+
export async function loadConfig() {
|
|
151
|
+
await loadDotenv(process.cwd());
|
|
152
|
+
let globalConfig = {};
|
|
153
|
+
let projectConfig = {};
|
|
154
|
+
const globalConfigPath = path.join(getConfigDir(), 'config.json');
|
|
155
|
+
try {
|
|
156
|
+
const globalConfigData = await fs.readFile(globalConfigPath, 'utf8');
|
|
157
|
+
globalConfig = JSON.parse(globalConfigData);
|
|
158
|
+
}
|
|
159
|
+
catch (e) {
|
|
160
|
+
// Ignore error
|
|
161
|
+
}
|
|
162
|
+
const projectConfigPath = path.join(process.cwd(), '.overarch', 'config.json');
|
|
163
|
+
try {
|
|
164
|
+
const projectConfigData = await fs.readFile(projectConfigPath, 'utf8');
|
|
165
|
+
projectConfig = JSON.parse(projectConfigData);
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
// Ignore error
|
|
169
|
+
}
|
|
170
|
+
const mergedConfig = {
|
|
171
|
+
...DEFAULT_CONFIG,
|
|
172
|
+
...globalConfig,
|
|
173
|
+
...projectConfig,
|
|
174
|
+
providers: { ...DEFAULT_CONFIG.providers, ...globalConfig.providers, ...projectConfig.providers },
|
|
175
|
+
permissions: { ...DEFAULT_CONFIG.permissions, ...globalConfig.permissions, ...projectConfig.permissions },
|
|
176
|
+
context: { ...DEFAULT_CONFIG.context, ...globalConfig.context, ...projectConfig.context },
|
|
177
|
+
project: { ...DEFAULT_CONFIG.project, ...globalConfig.project, ...projectConfig.project },
|
|
178
|
+
ui: { ...DEFAULT_CONFIG.ui, ...globalConfig.ui, ...projectConfig.ui },
|
|
179
|
+
onboarding: { ...DEFAULT_CONFIG.onboarding, ...globalConfig.onboarding, ...projectConfig.onboarding },
|
|
180
|
+
};
|
|
181
|
+
return {
|
|
182
|
+
...mergedConfig,
|
|
183
|
+
configPath: Object.keys(projectConfig).length > 0 ? projectConfigPath : (Object.keys(globalConfig).length > 0 ? globalConfigPath : ''),
|
|
184
|
+
isProjectConfig: Object.keys(projectConfig).length > 0
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
export async function saveConfig(config, targetPath) {
|
|
188
|
+
const dir = path.dirname(targetPath);
|
|
189
|
+
await fs.mkdir(dir, { recursive: true });
|
|
190
|
+
await fs.writeFile(targetPath, JSON.stringify(config, null, 2), 'utf8');
|
|
191
|
+
}
|
|
192
|
+
export async function saveGlobalConfig(updates) {
|
|
193
|
+
const globalConfigPath = path.join(getConfigDir(), 'config.json');
|
|
194
|
+
let currentConfig = {};
|
|
195
|
+
try {
|
|
196
|
+
const data = await fs.readFile(globalConfigPath, 'utf8');
|
|
197
|
+
currentConfig = JSON.parse(data);
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
// ignore
|
|
201
|
+
}
|
|
202
|
+
const newConfig = {
|
|
203
|
+
...currentConfig,
|
|
204
|
+
...updates,
|
|
205
|
+
providers: { ...currentConfig.providers, ...updates.providers },
|
|
206
|
+
permissions: { ...currentConfig.permissions, ...updates.permissions },
|
|
207
|
+
context: { ...currentConfig.context, ...updates.context },
|
|
208
|
+
project: { ...currentConfig.project, ...updates.project },
|
|
209
|
+
ui: { ...currentConfig.ui, ...updates.ui },
|
|
210
|
+
onboarding: { ...currentConfig.onboarding, ...updates.onboarding },
|
|
211
|
+
};
|
|
212
|
+
// Strip raw API keys before persisting to disk
|
|
213
|
+
const configToSave = JSON.parse(JSON.stringify(newConfig));
|
|
214
|
+
if (configToSave.providers) {
|
|
215
|
+
for (const p of Object.values(configToSave.providers)) {
|
|
216
|
+
if (p && 'apiKey' in p) {
|
|
217
|
+
delete p.apiKey;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
await fs.mkdir(getConfigDir(), { recursive: true });
|
|
222
|
+
await fs.writeFile(globalConfigPath, JSON.stringify(configToSave, null, 2), 'utf8');
|
|
223
|
+
}
|
|
224
|
+
export function getApiKey(provider, cwd = process.cwd()) {
|
|
225
|
+
const envMap = {
|
|
226
|
+
openai: 'OPENAI_API_KEY',
|
|
227
|
+
anthropic: 'ANTHROPIC_API_KEY',
|
|
228
|
+
gemini: 'GEMINI_API_KEY',
|
|
229
|
+
google: 'GEMINI_API_KEY',
|
|
230
|
+
openrouter: 'OPENROUTER_API_KEY'
|
|
231
|
+
};
|
|
232
|
+
const envVar = envMap[provider.toLowerCase()];
|
|
233
|
+
if (envVar && process.env[envVar]) {
|
|
234
|
+
return process.env[envVar];
|
|
235
|
+
}
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|