0agent 1.0.40 → 1.0.42
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/bin/0agent.js +67 -28
- package/bin/chat.js +88 -13
- package/dist/daemon.mjs +77 -45
- package/package.json +1 -1
package/bin/0agent.js
CHANGED
|
@@ -278,7 +278,16 @@ async function runInit() {
|
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
// ── Step 3:
|
|
281
|
+
// ── Step 3: Workspace folder ─────────────────────────────────────────────
|
|
282
|
+
const defaultWorkspace = resolve(homedir(), '0agent-workspace');
|
|
283
|
+
console.log(`\n \x1b[1mWorkspace folder\x1b[0m`);
|
|
284
|
+
console.log(` \x1b[2mAll files the agent creates will go here. You own it.\x1b[0m`);
|
|
285
|
+
const wsRaw = await ask(` Path [${defaultWorkspace}]: `);
|
|
286
|
+
const workspacePath = wsRaw.trim() || defaultWorkspace;
|
|
287
|
+
mkdirSync(workspacePath, { recursive: true });
|
|
288
|
+
console.log(` \x1b[32m✓\x1b[0m Workspace: \x1b[36m${workspacePath}\x1b[0m`);
|
|
289
|
+
|
|
290
|
+
// ── Step 4: Embedding ────────────────────────────────────────────────────
|
|
282
291
|
const embIdx = await arrowSelect('Embeddings (for semantic memory search)?', [
|
|
283
292
|
'Local via Ollama (nomic-embed-text) — free, private',
|
|
284
293
|
'OpenAI text-embedding-3-small — cloud',
|
|
@@ -286,12 +295,12 @@ async function runInit() {
|
|
|
286
295
|
], 0);
|
|
287
296
|
const embeddingProvider = ['nomic-ollama', 'openai', 'none'][embIdx];
|
|
288
297
|
|
|
289
|
-
// ── Step
|
|
298
|
+
// ── Step 5: Sandbox ──────────────────────────────────────────────────────
|
|
290
299
|
const sandboxes = detectSandboxes();
|
|
291
300
|
const sandboxChoice = sandboxes[0] ?? 'process';
|
|
292
301
|
console.log(`\n Sandbox: \x1b[32m${sandboxChoice}\x1b[0m detected`);
|
|
293
302
|
|
|
294
|
-
// ── Step
|
|
303
|
+
// ── Step 6: Seed graph ───────────────────────────────────────────────────
|
|
295
304
|
const seedIdx = await arrowSelect('Starting knowledge?', [
|
|
296
305
|
'software-engineering — sprint workflow + 15 skills ← recommended',
|
|
297
306
|
'Start from scratch',
|
|
@@ -300,11 +309,12 @@ async function runInit() {
|
|
|
300
309
|
|
|
301
310
|
// ── Summary ───────────────────────────────────────────────────────────────
|
|
302
311
|
console.log('\n \x1b[1mReady to launch\x1b[0m\n');
|
|
303
|
-
console.log(` LLM:
|
|
304
|
-
console.log(` API Key:
|
|
305
|
-
console.log(` Memory:
|
|
306
|
-
console.log(`
|
|
307
|
-
console.log(`
|
|
312
|
+
console.log(` LLM: \x1b[36m${providerKey}/${model}\x1b[0m`);
|
|
313
|
+
console.log(` API Key: ${apiKey ? '\x1b[32m✓ set\x1b[0m (' + apiKey.slice(0, 8) + '••••)' : '\x1b[33mnot set\x1b[0m'}`);
|
|
314
|
+
console.log(` Memory: ${ghToken ? `\x1b[32mgithub.com/${ghOwner}/0agent-memory\x1b[0m` : '\x1b[2mlocal only\x1b[0m'}`);
|
|
315
|
+
console.log(` Workspace: \x1b[36m${workspacePath}\x1b[0m`);
|
|
316
|
+
console.log(` Sandbox: \x1b[36m${sandboxChoice}\x1b[0m`);
|
|
317
|
+
console.log(` Seed: \x1b[36m${seedName ?? 'scratch'}\x1b[0m\n`);
|
|
308
318
|
|
|
309
319
|
// Write config
|
|
310
320
|
const dbPath = resolve(AGENT_DIR, 'graph.db');
|
|
@@ -329,6 +339,9 @@ embedding:
|
|
|
329
339
|
sandbox:
|
|
330
340
|
backend: ${sandboxChoice}
|
|
331
341
|
|
|
342
|
+
workspace:
|
|
343
|
+
path: "${workspacePath}"
|
|
344
|
+
|
|
332
345
|
mcp_servers: []
|
|
333
346
|
|
|
334
347
|
server:
|
|
@@ -356,11 +369,20 @@ ${ghToken && ghOwner ? `\ngithub_memory:\n enabled: true\n token: "${ghToken}"
|
|
|
356
369
|
console.log(' ✓ Built-in skills installed');
|
|
357
370
|
}
|
|
358
371
|
|
|
359
|
-
//
|
|
360
|
-
|
|
372
|
+
// Force-kill any running daemon so new config is loaded fresh
|
|
373
|
+
await forceStopDaemon();
|
|
361
374
|
|
|
362
|
-
|
|
375
|
+
// Start fresh daemon with new config
|
|
376
|
+
console.log('\n Starting...');
|
|
363
377
|
await startDaemon();
|
|
378
|
+
|
|
379
|
+
// Open chat TUI immediately — no manual steps needed
|
|
380
|
+
const chatSc = resolve(dirname(new URL(import.meta.url).pathname), 'chat.js');
|
|
381
|
+
if (existsSync(chatSc)) {
|
|
382
|
+
const { spawn: sp } = await import('node:child_process');
|
|
383
|
+
const p = sp(process.execPath, [chatSc], { stdio: 'inherit' });
|
|
384
|
+
await new Promise(r => p.on('close', r));
|
|
385
|
+
}
|
|
364
386
|
}
|
|
365
387
|
|
|
366
388
|
function detectSandboxes() {
|
|
@@ -378,9 +400,17 @@ function detectSandboxes() {
|
|
|
378
400
|
// ─── Daemon lifecycle ─────────────────────────────────────────────────────
|
|
379
401
|
|
|
380
402
|
async function startDaemon() {
|
|
403
|
+
// If still running (e.g. stop was called just before), wait a bit
|
|
381
404
|
if (await isDaemonRunning()) {
|
|
382
|
-
|
|
383
|
-
|
|
405
|
+
// Give it up to 3s to die before giving up
|
|
406
|
+
for (let i = 0; i < 6; i++) {
|
|
407
|
+
await sleep(500);
|
|
408
|
+
if (!await isDaemonRunning()) break;
|
|
409
|
+
}
|
|
410
|
+
if (await isDaemonRunning()) {
|
|
411
|
+
console.log(' Daemon already running on port 4200. Run `0agent status`.');
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
384
414
|
}
|
|
385
415
|
|
|
386
416
|
if (!existsSync(CONFIG_PATH)) {
|
|
@@ -410,33 +440,42 @@ async function startDaemon() {
|
|
|
410
440
|
child.unref();
|
|
411
441
|
|
|
412
442
|
// Wait for daemon to be ready (poll /api/health)
|
|
413
|
-
process.stdout.write(' Starting');
|
|
414
443
|
for (let i = 0; i < 30; i++) {
|
|
415
444
|
await sleep(500);
|
|
416
|
-
process.stdout.write('.');
|
|
417
445
|
if (await isDaemonRunning()) {
|
|
418
|
-
|
|
419
|
-
console.log(` Daemon running on http://localhost:4200`);
|
|
420
|
-
console.log(` Dashboard: http://localhost:4200`);
|
|
421
|
-
console.log(` Run: 0agent run "your task"\n`);
|
|
446
|
+
process.stdout.write(` \x1b[32m✓\x1b[0m Daemon ready\n`);
|
|
422
447
|
return;
|
|
423
448
|
}
|
|
424
449
|
}
|
|
425
|
-
console.log('
|
|
450
|
+
console.log(' Daemon did not start in time. Check: 0agent logs');
|
|
426
451
|
}
|
|
427
452
|
|
|
428
453
|
function stopDaemon() {
|
|
429
|
-
|
|
454
|
+
// Try PID file first
|
|
455
|
+
if (existsSync(PID_PATH)) {
|
|
456
|
+
const pid = parseInt(readFileSync(PID_PATH, 'utf8').trim(), 10);
|
|
457
|
+
try { process.kill(pid, 'SIGTERM'); } catch {}
|
|
458
|
+
} else {
|
|
430
459
|
console.log(' No daemon PID file found. Is it running?');
|
|
431
|
-
return;
|
|
432
460
|
}
|
|
433
|
-
|
|
434
|
-
try {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
461
|
+
// Also kill by process name as a fallback
|
|
462
|
+
try { execSync('pkill -f "daemon.mjs" 2>/dev/null; true', { stdio: 'ignore' }); } catch {}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Forcefully kill any running daemon regardless of PID file state.
|
|
466
|
+
// Used after init to ensure fresh config is picked up.
|
|
467
|
+
async function forceStopDaemon() {
|
|
468
|
+
// Kill by PID file if present
|
|
469
|
+
if (existsSync(PID_PATH)) {
|
|
470
|
+
const pid = parseInt(readFileSync(PID_PATH, 'utf8').trim(), 10);
|
|
471
|
+
try { process.kill(pid, 'SIGTERM'); } catch {}
|
|
439
472
|
}
|
|
473
|
+
// Kill by process name (catches daemons started by chat.js or other means)
|
|
474
|
+
try { execSync('pkill -f "daemon.mjs" 2>/dev/null; true', { stdio: 'ignore' }); } catch {}
|
|
475
|
+
// Kill by port 4200 (last resort)
|
|
476
|
+
try { execSync('lsof -ti:4200 | xargs kill -9 2>/dev/null; true', { stdio: 'ignore' }); } catch {}
|
|
477
|
+
// Wait for port to be free
|
|
478
|
+
await sleep(1200);
|
|
440
479
|
}
|
|
441
480
|
|
|
442
481
|
async function showStatus() {
|
package/bin/chat.js
CHANGED
|
@@ -879,6 +879,64 @@ async function _safeJsonFetch(url, opts) {
|
|
|
879
879
|
console.log(` ${fmt(C.yellow, '⚠')} LLM check failed: ${e.message}\n`);
|
|
880
880
|
}
|
|
881
881
|
|
|
882
|
+
// ── Step 3: Workspace folder check ───────────────────────────────────────
|
|
883
|
+
// If no workspace is configured, ask the user to set one now.
|
|
884
|
+
// Then export memory (graph nodes) to that folder.
|
|
885
|
+
await (async () => {
|
|
886
|
+
const wsPath = cfg?.workspace?.path;
|
|
887
|
+
if (wsPath) {
|
|
888
|
+
// Already configured — just ensure the folder exists
|
|
889
|
+
try { mkdirSync(wsPath, { recursive: true }); } catch {}
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// No workspace configured — ask inline
|
|
894
|
+
const { homedir: hd } = await import('node:os');
|
|
895
|
+
const defaultWs = resolve(hd(), '0agent-workspace');
|
|
896
|
+
|
|
897
|
+
process.stdout.write(`\n ${fmt(C.yellow, '⚠')} No workspace folder configured.\n`);
|
|
898
|
+
process.stdout.write(` ${fmt(C.dim, 'The agent needs a folder to create and store files.')}\n\n`);
|
|
899
|
+
|
|
900
|
+
// Temporarily close readline so we can read a raw line
|
|
901
|
+
const wsInput = await new Promise((res) => {
|
|
902
|
+
process.stdout.write(` ${fmt(C.bold, 'Workspace path')} ${fmt(C.dim, `[${defaultWs}]`)}: `);
|
|
903
|
+
rl.once('line', (line) => res(line.trim() || defaultWs));
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
const chosenPath = resolve(wsInput.replace(/^~/, hd()));
|
|
907
|
+
try {
|
|
908
|
+
mkdirSync(chosenPath, { recursive: true });
|
|
909
|
+
process.stdout.write(` ${fmt(C.green, '✓')} Created: ${fmt(C.cyan, chosenPath)}\n`);
|
|
910
|
+
} catch (e) {
|
|
911
|
+
process.stdout.write(` ${fmt(C.red, '✗')} Could not create folder: ${e.message}\n`);
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// Save workspace path to config
|
|
916
|
+
if (!cfg) cfg = {};
|
|
917
|
+
cfg.workspace = { path: chosenPath };
|
|
918
|
+
saveConfig(cfg);
|
|
919
|
+
process.stdout.write(` ${fmt(C.green, '✓')} Workspace saved to config\n`);
|
|
920
|
+
|
|
921
|
+
// Export memory (graph nodes) to workspace as a JSON snapshot
|
|
922
|
+
try {
|
|
923
|
+
const nodesRes = await fetch(`${BASE_URL}/api/graph/nodes?limit=9999`, {
|
|
924
|
+
signal: AbortSignal.timeout(5000),
|
|
925
|
+
});
|
|
926
|
+
if (nodesRes.ok) {
|
|
927
|
+
const nodes = await nodesRes.json();
|
|
928
|
+
const count = Array.isArray(nodes) ? nodes.length : 0;
|
|
929
|
+
if (count > 0) {
|
|
930
|
+
const exportPath = resolve(chosenPath, '.0agent-memory.json');
|
|
931
|
+
writeFileSync(exportPath, JSON.stringify({ exported_at: new Date().toISOString(), nodes }, null, 2), 'utf8');
|
|
932
|
+
process.stdout.write(` ${fmt(C.green, '✓')} Memory exported: ${fmt(C.dim, `${count} nodes → .0agent-memory.json`)}\n`);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
} catch {}
|
|
936
|
+
|
|
937
|
+
process.stdout.write('\n');
|
|
938
|
+
})();
|
|
939
|
+
|
|
882
940
|
// ── Auto-update: check npm, update silently, restart ─────────────────────
|
|
883
941
|
// Runs in background after prompt — never blocks startup.
|
|
884
942
|
// If update found: counts down 3s (press any key to skip), then auto-installs.
|
|
@@ -935,8 +993,8 @@ async function _safeJsonFetch(url, opts) {
|
|
|
935
993
|
})();
|
|
936
994
|
|
|
937
995
|
// Restart using the GLOBAL install, not the npx cache that's currently running.
|
|
938
|
-
// After `npm install -g 0agent@latest`,
|
|
939
|
-
//
|
|
996
|
+
// After `npm install -g 0agent@latest`, use `npm root -g` to find the module root.
|
|
997
|
+
// `npm bin -g` is deprecated and unreliable — use the module root instead.
|
|
940
998
|
async function restartWithLatest() {
|
|
941
999
|
try {
|
|
942
1000
|
const { execSync: ex } = await import('node:child_process');
|
|
@@ -944,22 +1002,39 @@ async function restartWithLatest() {
|
|
|
944
1002
|
const { existsSync: ef } = await import('node:fs');
|
|
945
1003
|
const { spawn: sp } = await import('node:child_process');
|
|
946
1004
|
|
|
947
|
-
//
|
|
948
|
-
|
|
949
|
-
|
|
1005
|
+
// npm root -g → e.g. /Users/sahil/.nvm/versions/node/v20/lib/node_modules
|
|
1006
|
+
// The 0agent entry is at {root}/0agent/bin/0agent.js
|
|
1007
|
+
let newBin = null;
|
|
1008
|
+
try {
|
|
1009
|
+
const npmRoot = ex('npm root -g 2>/dev/null', { encoding: 'utf8' }).trim().split('\n')[0];
|
|
1010
|
+
if (npmRoot) {
|
|
1011
|
+
const candidate = res(npmRoot, '0agent', 'bin', '0agent.js');
|
|
1012
|
+
if (ef(candidate)) newBin = candidate;
|
|
1013
|
+
}
|
|
1014
|
+
} catch {}
|
|
1015
|
+
|
|
1016
|
+
if (!newBin) {
|
|
1017
|
+
// Fallback: npm prefix -g gives the prefix, bin is {prefix}/bin/0agent
|
|
1018
|
+
try {
|
|
1019
|
+
const prefix = ex('npm prefix -g 2>/dev/null', { encoding: 'utf8' }).trim().split('\n')[0];
|
|
1020
|
+
// The script wrapper lives in {prefix}/bin; the actual JS is in lib/node_modules
|
|
1021
|
+
const candidate = prefix ? res(prefix, 'lib', 'node_modules', '0agent', 'bin', '0agent.js') : null;
|
|
1022
|
+
if (candidate && ef(candidate)) newBin = candidate;
|
|
1023
|
+
} catch {}
|
|
1024
|
+
}
|
|
950
1025
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
// Fallback: let the shell find 0agent in PATH
|
|
957
|
-
child = sp('0agent', [], { stdio: 'inherit', shell: true });
|
|
1026
|
+
if (!newBin) {
|
|
1027
|
+
// Cannot locate the new binary — exit so user restarts manually
|
|
1028
|
+
process.stdout.write(` ${fmt(C.dim, 'Restart manually: 0agent')}\n`);
|
|
1029
|
+
process.exit(0);
|
|
1030
|
+
return;
|
|
958
1031
|
}
|
|
1032
|
+
|
|
1033
|
+
const child = sp(process.execPath, [newBin], { stdio: 'inherit' });
|
|
959
1034
|
child.on('close', (code) => process.exit(code ?? 0));
|
|
960
1035
|
process.stdin.pause();
|
|
961
1036
|
} catch {
|
|
962
|
-
process.exit(0);
|
|
1037
|
+
process.exit(0);
|
|
963
1038
|
}
|
|
964
1039
|
}
|
|
965
1040
|
|
package/dist/daemon.mjs
CHANGED
|
@@ -2111,22 +2111,39 @@ var init_WebSearchCapability = __esm({
|
|
|
2111
2111
|
});
|
|
2112
2112
|
|
|
2113
2113
|
// packages/daemon/src/capabilities/BrowserCapability.ts
|
|
2114
|
-
import { spawnSync as spawnSync2, execSync as execSync2 } from "node:child_process";
|
|
2114
|
+
import { spawnSync as spawnSync2, execSync as execSync2, spawn } from "node:child_process";
|
|
2115
|
+
import { platform } from "node:os";
|
|
2116
|
+
function findSystemChrome() {
|
|
2117
|
+
const candidates = platform() === "darwin" ? [
|
|
2118
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
2119
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
|
2120
|
+
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
|
2121
|
+
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
|
|
2122
|
+
] : platform() === "linux" ? ["google-chrome", "chromium-browser", "chromium", "microsoft-edge", "brave-browser"] : ["C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"];
|
|
2123
|
+
for (const c of candidates) {
|
|
2124
|
+
try {
|
|
2125
|
+
execSync2(`test -f "${c}" 2>/dev/null || which "${c}" 2>/dev/null`, { stdio: "pipe" });
|
|
2126
|
+
return c;
|
|
2127
|
+
} catch {
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
return null;
|
|
2131
|
+
}
|
|
2115
2132
|
var BrowserCapability;
|
|
2116
2133
|
var init_BrowserCapability = __esm({
|
|
2117
2134
|
"packages/daemon/src/capabilities/BrowserCapability.ts"() {
|
|
2118
2135
|
"use strict";
|
|
2119
2136
|
BrowserCapability = class {
|
|
2120
2137
|
name = "browser_open";
|
|
2121
|
-
description = "Open a URL in
|
|
2138
|
+
description = "Open a URL in the system browser or extract page content. Use when scrape_url fails on JS-heavy pages.";
|
|
2122
2139
|
toolDefinition = {
|
|
2123
2140
|
name: "browser_open",
|
|
2124
|
-
description:
|
|
2141
|
+
description: `Open or read a URL using the system browser. Use action="open" to launch the URL visibly in the user's default browser. Use action="read" (default) to extract page content headlessly. Use when scrape_url fails on JS-heavy pages.`,
|
|
2125
2142
|
input_schema: {
|
|
2126
2143
|
type: "object",
|
|
2127
2144
|
properties: {
|
|
2128
2145
|
url: { type: "string", description: "URL to open" },
|
|
2129
|
-
action: { type: "string", description: '
|
|
2146
|
+
action: { type: "string", description: '"open" \u2014 launch in system browser (visible); "read" \u2014 extract text content (default); "screenshot" \u2014 take screenshot' },
|
|
2130
2147
|
wait_for: { type: "string", description: "CSS selector to wait for before extracting content" },
|
|
2131
2148
|
extract: { type: "string", description: "CSS selector to extract specific element text" }
|
|
2132
2149
|
},
|
|
@@ -2142,23 +2159,27 @@ var init_BrowserCapability = __esm({
|
|
|
2142
2159
|
if (!url.startsWith("http")) {
|
|
2143
2160
|
return { success: false, output: "URL must start with http:// or https://", duration_ms: 0 };
|
|
2144
2161
|
}
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2162
|
+
if (action === "open") {
|
|
2163
|
+
return this.openInSystemBrowser(url, start);
|
|
2164
|
+
}
|
|
2165
|
+
const sysChromeExe = findSystemChrome();
|
|
2166
|
+
if (sysChromeExe) {
|
|
2167
|
+
try {
|
|
2168
|
+
const output = await this.chromeFetch(sysChromeExe, url);
|
|
2169
|
+
return { success: true, output, duration_ms: Date.now() - start };
|
|
2170
|
+
} catch {
|
|
2171
|
+
}
|
|
2149
2172
|
}
|
|
2150
2173
|
try {
|
|
2151
|
-
const output = await this.
|
|
2152
|
-
return { success: true, output, fallback_used: "
|
|
2174
|
+
const output = await this.playwrightFetch(url, action, waitFor, extract);
|
|
2175
|
+
return { success: true, output, fallback_used: "playwright", duration_ms: Date.now() - start };
|
|
2153
2176
|
} catch {
|
|
2154
2177
|
}
|
|
2155
2178
|
try {
|
|
2156
2179
|
const res = await fetch(url, {
|
|
2157
2180
|
headers: {
|
|
2158
2181
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/120.0 Safari/537.36",
|
|
2159
|
-
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
|
|
2160
|
-
"Accept-Language": "en-US,en;q=0.9",
|
|
2161
|
-
"Accept-Encoding": "gzip, deflate, br"
|
|
2182
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
|
|
2162
2183
|
},
|
|
2163
2184
|
signal: AbortSignal.timeout(15e3)
|
|
2164
2185
|
});
|
|
@@ -2168,19 +2189,33 @@ var init_BrowserCapability = __esm({
|
|
|
2168
2189
|
} catch (err) {
|
|
2169
2190
|
return {
|
|
2170
2191
|
success: false,
|
|
2171
|
-
output: `Browser unavailable.
|
|
2192
|
+
output: `Browser unavailable. No system Chrome/Chromium found and Playwright not installed.`,
|
|
2172
2193
|
error: err instanceof Error ? err.message : String(err),
|
|
2173
2194
|
duration_ms: Date.now() - start
|
|
2174
2195
|
};
|
|
2175
2196
|
}
|
|
2176
2197
|
}
|
|
2198
|
+
openInSystemBrowser(url, start) {
|
|
2199
|
+
try {
|
|
2200
|
+
const cmd = platform() === "darwin" ? "open" : platform() === "win32" ? "start" : "xdg-open";
|
|
2201
|
+
spawn(cmd, [url], { detached: true, stdio: "ignore" }).unref();
|
|
2202
|
+
return { success: true, output: `Opened in default browser: ${url}`, duration_ms: Date.now() - start };
|
|
2203
|
+
} catch (err) {
|
|
2204
|
+
return { success: false, output: `Failed to open browser: ${err instanceof Error ? err.message : String(err)}`, duration_ms: Date.now() - start };
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2177
2207
|
async playwrightFetch(url, action, waitFor, extract) {
|
|
2178
2208
|
const waitLine = waitFor ? `await p.waitForSelector('${waitFor}', { timeout: 8000 }).catch(() => {});` : "";
|
|
2179
2209
|
const extractLine = extract ? `const text = await p.$eval('${extract}', el => el.innerText).catch(() => page_text);` : `const text = page_text;`;
|
|
2180
2210
|
const script = `
|
|
2181
2211
|
const { chromium } = require('playwright');
|
|
2182
2212
|
(async () => {
|
|
2183
|
-
|
|
2213
|
+
let b;
|
|
2214
|
+
try {
|
|
2215
|
+
b = await chromium.launch({ channel: 'chrome', headless: true, args: ['--no-sandbox'] });
|
|
2216
|
+
} catch {
|
|
2217
|
+
b = await chromium.launch({ headless: true, args: ['--no-sandbox'] });
|
|
2218
|
+
}
|
|
2184
2219
|
const p = await b.newPage();
|
|
2185
2220
|
await p.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36');
|
|
2186
2221
|
await p.goto('${url}', { waitUntil: 'domcontentloaded', timeout: 20000 });
|
|
@@ -2195,27 +2230,17 @@ var init_BrowserCapability = __esm({
|
|
|
2195
2230
|
if (result.status !== 0) throw new Error(result.stderr || "Playwright failed");
|
|
2196
2231
|
return result.stdout.trim();
|
|
2197
2232
|
}
|
|
2198
|
-
async chromeFetch(url) {
|
|
2199
|
-
const
|
|
2200
|
-
"
|
|
2201
|
-
"
|
|
2202
|
-
"
|
|
2203
|
-
"
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
timeout: 15e3,
|
|
2210
|
-
encoding: "utf8"
|
|
2211
|
-
});
|
|
2212
|
-
if (result.status === 0 && result.stdout) {
|
|
2213
|
-
return result.stdout.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, 5e3);
|
|
2214
|
-
}
|
|
2215
|
-
} catch {
|
|
2216
|
-
}
|
|
2217
|
-
}
|
|
2218
|
-
throw new Error("No system Chrome found");
|
|
2233
|
+
async chromeFetch(executablePath, url) {
|
|
2234
|
+
const result = spawnSync2(executablePath, [
|
|
2235
|
+
"--headless=new",
|
|
2236
|
+
"--no-sandbox",
|
|
2237
|
+
"--disable-gpu",
|
|
2238
|
+
"--disable-dev-shm-usage",
|
|
2239
|
+
"--dump-dom",
|
|
2240
|
+
url
|
|
2241
|
+
], { timeout: 15e3, encoding: "utf8" });
|
|
2242
|
+
if (result.status !== 0 || !result.stdout) throw new Error("Chrome headless failed");
|
|
2243
|
+
return result.stdout.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, 5e3);
|
|
2219
2244
|
}
|
|
2220
2245
|
};
|
|
2221
2246
|
}
|
|
@@ -2318,7 +2343,7 @@ else:
|
|
|
2318
2343
|
});
|
|
2319
2344
|
|
|
2320
2345
|
// packages/daemon/src/capabilities/ShellCapability.ts
|
|
2321
|
-
import { spawn } from "node:child_process";
|
|
2346
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
2322
2347
|
var ShellCapability;
|
|
2323
2348
|
var init_ShellCapability = __esm({
|
|
2324
2349
|
"packages/daemon/src/capabilities/ShellCapability.ts"() {
|
|
@@ -2362,7 +2387,7 @@ var init_ShellCapability = __esm({
|
|
|
2362
2387
|
...!success && { error: `exit ${code ?? signal}` }
|
|
2363
2388
|
});
|
|
2364
2389
|
};
|
|
2365
|
-
const proc =
|
|
2390
|
+
const proc = spawn2("bash", ["-c", command], {
|
|
2366
2391
|
cwd,
|
|
2367
2392
|
env: { ...process.env, TERM: "dumb" }
|
|
2368
2393
|
// DO NOT set `timeout` here — we manage it manually via timer
|
|
@@ -2690,7 +2715,7 @@ var init_capabilities = __esm({
|
|
|
2690
2715
|
});
|
|
2691
2716
|
|
|
2692
2717
|
// packages/daemon/src/AgentExecutor.ts
|
|
2693
|
-
import { spawn as
|
|
2718
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
2694
2719
|
import { writeFileSync as writeFileSync2, readFileSync as readFileSync3, readdirSync as readdirSync2, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "node:fs";
|
|
2695
2720
|
import { resolve as resolve3, dirname as dirname2, relative } from "node:path";
|
|
2696
2721
|
var SELF_MOD_PATTERN, AgentExecutor;
|
|
@@ -2847,7 +2872,7 @@ var init_AgentExecutor = __esm({
|
|
|
2847
2872
|
shellExec(command, timeoutMs) {
|
|
2848
2873
|
return new Promise((resolve15) => {
|
|
2849
2874
|
const chunks = [];
|
|
2850
|
-
const proc =
|
|
2875
|
+
const proc = spawn3("bash", ["-c", command], {
|
|
2851
2876
|
cwd: this.cwd,
|
|
2852
2877
|
env: { ...process.env, TERM: "dumb" },
|
|
2853
2878
|
timeout: timeoutMs
|
|
@@ -3139,7 +3164,7 @@ var init_ExecutionVerifier = __esm({
|
|
|
3139
3164
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync6 } from "node:fs";
|
|
3140
3165
|
import { resolve as resolve5, dirname as dirname3 } from "node:path";
|
|
3141
3166
|
import { fileURLToPath } from "node:url";
|
|
3142
|
-
import { execSync as execSync4, spawn as
|
|
3167
|
+
import { execSync as execSync4, spawn as spawn4 } from "node:child_process";
|
|
3143
3168
|
function isRuntimeBug(error) {
|
|
3144
3169
|
if (TASK_FAILURE_PATTERNS.some((p) => p.test(error))) return false;
|
|
3145
3170
|
return RUNTIME_BUG_PATTERNS.some((p) => p.test(error));
|
|
@@ -3374,7 +3399,7 @@ Rules:
|
|
|
3374
3399
|
restartDaemon() {
|
|
3375
3400
|
const bundlePath = resolve5(this.projectRoot, "dist", "daemon.mjs");
|
|
3376
3401
|
if (existsSync6(bundlePath)) {
|
|
3377
|
-
const child =
|
|
3402
|
+
const child = spawn4(process.execPath, [bundlePath], {
|
|
3378
3403
|
detached: true,
|
|
3379
3404
|
stdio: "ignore",
|
|
3380
3405
|
env: process.env
|
|
@@ -6721,7 +6746,7 @@ git checkout <commit> graph/ # restore graph files
|
|
|
6721
6746
|
};
|
|
6722
6747
|
|
|
6723
6748
|
// packages/daemon/src/CodespaceManager.ts
|
|
6724
|
-
import { execSync as execSync5, spawn as
|
|
6749
|
+
import { execSync as execSync5, spawn as spawn5 } from "node:child_process";
|
|
6725
6750
|
var BROWSER_PORT_REMOTE = 3e3;
|
|
6726
6751
|
var BROWSER_PORT_LOCAL = 3001;
|
|
6727
6752
|
var DISPLAY_NAME = "0agent-browser";
|
|
@@ -6816,7 +6841,7 @@ var CodespaceManager = class {
|
|
|
6816
6841
|
async openTunnel(name) {
|
|
6817
6842
|
this.closeTunnel();
|
|
6818
6843
|
console.log(`[Codespace] Opening tunnel port ${BROWSER_PORT_REMOTE} \u2192 localhost:${BROWSER_PORT_LOCAL}...`);
|
|
6819
|
-
this.forwardProcess =
|
|
6844
|
+
this.forwardProcess = spawn5(
|
|
6820
6845
|
"gh",
|
|
6821
6846
|
["codespace", "ports", "forward", `${BROWSER_PORT_REMOTE}:${BROWSER_PORT_LOCAL}`, "--codespace", name],
|
|
6822
6847
|
{ stdio: ["ignore", "ignore", "ignore"] }
|
|
@@ -6976,7 +7001,14 @@ var ZeroAgentDaemon = class {
|
|
|
6976
7001
|
}).catch(() => {
|
|
6977
7002
|
});
|
|
6978
7003
|
}
|
|
6979
|
-
const
|
|
7004
|
+
const workspaceCfg = this.config["workspace"];
|
|
7005
|
+
const configuredWorkspace = workspaceCfg?.path;
|
|
7006
|
+
const cwd = process.env["ZEROAGENT_CWD"] ?? configuredWorkspace ?? process.cwd();
|
|
7007
|
+
if (configuredWorkspace) {
|
|
7008
|
+
const { mkdirSync: mks } = await import("node:fs");
|
|
7009
|
+
mks(configuredWorkspace, { recursive: true });
|
|
7010
|
+
console.log(`[0agent] Workspace: ${configuredWorkspace}`);
|
|
7011
|
+
}
|
|
6980
7012
|
const identityManager = new IdentityManager(this.graph);
|
|
6981
7013
|
const identity = await identityManager.init().catch(() => null);
|
|
6982
7014
|
if (identity) {
|