0agent 1.0.50 → 1.0.55

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 CHANGED
@@ -19,7 +19,7 @@
19
19
  * 0agent improve # self-improvement analysis
20
20
  */
21
21
 
22
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
22
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, openSync } from 'node:fs';
23
23
  import { resolve, dirname } from 'node:path';
24
24
  import { homedir, platform } from 'node:os';
25
25
  import { spawn, execSync } from 'node:child_process';
@@ -431,10 +431,10 @@ async function startDaemon() {
431
431
 
432
432
  mkdirSync(resolve(AGENT_DIR, 'logs'), { recursive: true });
433
433
 
434
- const logFile = writeFileSync(LOG_PATH, '', 'utf8');
434
+ const logFd = openSync(LOG_PATH, 'w');
435
435
  const child = spawn(process.execPath, [startScript], {
436
436
  detached: true,
437
- stdio: ['ignore', 'ignore', 'ignore'],
437
+ stdio: ['ignore', logFd, logFd],
438
438
  env: { ...process.env, ZEROAGENT_CONFIG: CONFIG_PATH },
439
439
  });
440
440
  child.unref();
package/bin/chat.js CHANGED
@@ -1192,7 +1192,14 @@ process.stdin.on('keypress', (_char, key) => {
1192
1192
  sessionId = null;
1193
1193
  streaming = false;
1194
1194
  streamLineCount = 0;
1195
- messageQueue.length = 0; // also clear queue — fresh start
1195
+ messageQueue.length = 0;
1196
+
1197
+ // Kill any OS-level processes spawned by GUI/shell capabilities
1198
+ // (python3 GUI scripts, bash subprocesses) so nothing keeps running
1199
+ import('node:child_process').then(({ execSync: _exec }) => {
1200
+ try { _exec('pkill -f "0agent_gui_" 2>/dev/null; pkill -f "0agent-bg-" 2>/dev/null; true', { stdio: 'ignore' }); } catch {}
1201
+ }).catch(() => {});
1202
+
1196
1203
  res();
1197
1204
  rl.prompt();
1198
1205
  }
@@ -1206,24 +1213,36 @@ connectWS();
1206
1213
 
1207
1214
  // ── Startup: ensure fresh daemon + verify LLM ────────────────────────────────
1208
1215
  async function _spawnDaemon() {
1209
- const pkgRoot = resolve(new URL(import.meta.url).pathname, '..', '..');
1216
+ const pkgRoot = resolve(new URL(import.meta.url).pathname, '..', '..');
1210
1217
  const bundled = resolve(pkgRoot, 'dist', 'daemon.mjs');
1211
- if (!existsSync(bundled) || !existsSync(CONFIG_PATH)) return false;
1218
+ const devPath = resolve(pkgRoot, 'packages', 'daemon', 'dist', 'start.js');
1219
+ const daemonScript = existsSync(bundled) ? bundled : existsSync(devPath) ? devPath : null;
1220
+
1221
+ if (!daemonScript) return 'no-bundle';
1222
+ if (!existsSync(CONFIG_PATH)) return 'no-config';
1223
+
1212
1224
  const { spawn } = await import('node:child_process');
1213
- const child = spawn(process.execPath, [bundled], {
1214
- detached: true, stdio: 'ignore',
1225
+ const { openSync: fsOpen, mkdirSync: fsMkdir } = await import('node:fs');
1226
+ const logDir = resolve(AGENT_DIR, 'logs');
1227
+ fsMkdir(logDir, { recursive: true });
1228
+ const logFd = fsOpen(resolve(logDir, 'daemon.log'), 'w');
1229
+
1230
+ const child = spawn(process.execPath, [daemonScript], {
1231
+ detached: true,
1232
+ stdio: ['ignore', logFd, logFd],
1215
1233
  env: { ...process.env, ZEROAGENT_CONFIG: CONFIG_PATH },
1216
1234
  });
1217
1235
  child.unref();
1236
+
1218
1237
  // Wait up to 10s for daemon to be ready
1219
1238
  for (let i = 0; i < 20; i++) {
1220
1239
  await new Promise(r => setTimeout(r, 500));
1221
1240
  try {
1222
1241
  await fetch(`${BASE_URL}/api/health`, { signal: AbortSignal.timeout(500) });
1223
- return true;
1242
+ return 'ok';
1224
1243
  } catch {}
1225
1244
  }
1226
- return false;
1245
+ return 'timeout';
1227
1246
  }
1228
1247
 
1229
1248
  async function _safeJsonFetch(url, opts) {
@@ -1256,23 +1275,32 @@ async function _safeJsonFetch(url, opts) {
1256
1275
  // Daemon not running at all
1257
1276
  }
1258
1277
 
1278
+ let spawnResult = 'ok';
1259
1279
  if (needsRestart) {
1260
1280
  startSpin.start('Restarting daemon (new version)');
1261
- // Kill old daemon
1262
1281
  try {
1263
1282
  const { execSync } = await import('node:child_process');
1264
1283
  execSync('pkill -f "daemon.mjs" 2>/dev/null; true', { stdio: 'ignore' });
1265
1284
  } catch {}
1266
1285
  await new Promise(r => setTimeout(r, 800));
1267
- daemonOk = await _spawnDaemon();
1286
+ spawnResult = await _spawnDaemon();
1287
+ daemonOk = spawnResult === 'ok';
1268
1288
  } else if (!daemonOk) {
1269
1289
  startSpin.start('Starting daemon');
1270
- daemonOk = await _spawnDaemon();
1290
+ spawnResult = await _spawnDaemon();
1291
+ daemonOk = spawnResult === 'ok';
1271
1292
  }
1272
1293
 
1273
1294
  startSpin.stop();
1274
1295
  if (!daemonOk) {
1275
- console.log(` ${fmt(C.red, '')} Daemon failed to start. Run: 0agent start`);
1296
+ if (spawnResult === 'no-config') {
1297
+ console.log(` ${fmt(C.yellow, '!')} Not configured yet. Run: ${fmt(C.bold, '0agent init')}`);
1298
+ } else if (spawnResult === 'no-bundle') {
1299
+ console.log(` ${fmt(C.red, '✗')} Daemon bundle missing. Reinstall: ${fmt(C.bold, 'npm i -g 0agent@latest')}`);
1300
+ } else {
1301
+ const logPath = resolve(AGENT_DIR, 'logs', 'daemon.log');
1302
+ console.log(` ${fmt(C.red, '✗')} Daemon failed to start. Check logs: ${fmt(C.dim, logPath)}`);
1303
+ }
1276
1304
  rl.prompt();
1277
1305
  return;
1278
1306
  }
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * postinstall — runs automatically after `npm install -g 0agent`
4
+ *
5
+ * Ensures all runtime dependencies are installed in the package directory.
6
+ * This handles cases where native modules (better-sqlite3) failed to build,
7
+ * or where the package was installed in a non-standard way.
8
+ */
9
+
10
+ import { existsSync } from 'node:fs';
11
+ import { resolve, dirname } from 'node:path';
12
+ import { execSync } from 'node:child_process';
13
+ import { fileURLToPath } from 'node:url';
14
+
15
+ const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
16
+
17
+ // Runtime deps that must be present (matches bundle externals in scripts/bundle.mjs)
18
+ const REQUIRED = [
19
+ 'better-sqlite3',
20
+ 'hono',
21
+ '@hono/node-server',
22
+ 'ws',
23
+ 'yaml',
24
+ 'zod',
25
+ ];
26
+
27
+ function depInstalled(name) {
28
+ // Handle scoped packages like @hono/node-server
29
+ const modPath = resolve(pkgRoot, 'node_modules', name);
30
+ return existsSync(modPath);
31
+ }
32
+
33
+ const missing = REQUIRED.filter(d => !depInstalled(d));
34
+
35
+ if (missing.length === 0) {
36
+ // All present — check if better-sqlite3 native binary actually loads
37
+ try {
38
+ const { createRequire } = await import('node:module');
39
+ const req = createRequire(import.meta.url);
40
+ req('better-sqlite3');
41
+ } catch {
42
+ // Binary broken — try rebuild
43
+ try {
44
+ process.stdout.write(' Rebuilding better-sqlite3 for this platform…\n');
45
+ execSync('npm rebuild better-sqlite3', {
46
+ cwd: pkgRoot,
47
+ stdio: 'inherit',
48
+ timeout: 60_000,
49
+ });
50
+ } catch {
51
+ process.stdout.write(
52
+ ' ⚠ Could not rebuild better-sqlite3. Memory persistence will be disabled.\n' +
53
+ ' If you need it, install build tools:\n' +
54
+ ' macOS: xcode-select --install\n' +
55
+ ' Linux: sudo apt-get install build-essential python3\n'
56
+ );
57
+ }
58
+ }
59
+ process.exit(0);
60
+ }
61
+
62
+ process.stdout.write(` Installing dependencies: ${missing.join(', ')}\n`);
63
+
64
+ try {
65
+ execSync(
66
+ `npm install --omit=dev --prefix "${pkgRoot}" ${missing.join(' ')}`,
67
+ { stdio: 'inherit', timeout: 120_000 }
68
+ );
69
+ process.stdout.write(' ✓ Dependencies installed\n');
70
+ } catch (err) {
71
+ process.stderr.write(
72
+ ` ✗ Failed to install some dependencies: ${err.message}\n` +
73
+ ` Try manually: npm install --prefix "${pkgRoot}" ${missing.join(' ')}\n`
74
+ );
75
+ // Don't exit non-zero — let the agent start anyway; daemon will log the actual error
76
+ }
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * preuninstall — runs automatically before `npm uninstall -g 0agent`
4
+ *
5
+ * Kills the daemon and ALL processes spawned by 0agent so nothing
6
+ * keeps running (e.g. opening Brave) after the package is removed.
7
+ */
8
+
9
+ import { execSync } from 'node:child_process';
10
+ import { existsSync, readFileSync, unlinkSync } from 'node:fs';
11
+ import { resolve } from 'node:path';
12
+ import { homedir } from 'node:os';
13
+
14
+ const AGENT_DIR = resolve(homedir(), '.0agent');
15
+ const PID_PATH = resolve(AGENT_DIR, 'daemon.pid');
16
+
17
+ function run(cmd) {
18
+ try { execSync(cmd, { stdio: 'ignore', timeout: 5000 }); } catch {}
19
+ }
20
+
21
+ process.stdout.write(' Stopping 0agent daemon and background processes…\n');
22
+
23
+ // 1. Kill daemon by PID file
24
+ if (existsSync(PID_PATH)) {
25
+ try {
26
+ const pid = parseInt(readFileSync(PID_PATH, 'utf8').trim(), 10);
27
+ if (!isNaN(pid)) process.kill(pid, 'SIGTERM');
28
+ } catch {}
29
+ try { unlinkSync(PID_PATH); } catch {}
30
+ }
31
+
32
+ // 2. Kill by process name (catches daemons started by chat.js or other means)
33
+ run('pkill -f "daemon.mjs" 2>/dev/null; true');
34
+
35
+ // 3. Kill any GUI Python scripts still running
36
+ run('pkill -f "0agent_gui_" 2>/dev/null; true');
37
+ run('pkill -f "0agent-bg-" 2>/dev/null; true');
38
+
39
+ // 4. Free port 4200 (last resort)
40
+ run('lsof -ti:4200 | xargs kill -9 2>/dev/null; true');
41
+
42
+ // 5. Remove any launchd plists that 0agent may have registered
43
+ // (prevents apps from being re-launched on login)
44
+ const launchAgentsDir = resolve(homedir(), 'Library', 'LaunchAgents');
45
+ if (existsSync(launchAgentsDir)) {
46
+ try {
47
+ const { readdirSync } = await import('node:fs');
48
+ for (const f of readdirSync(launchAgentsDir)) {
49
+ if (f.includes('0agent') || f.includes('zeroagent')) {
50
+ const plistPath = resolve(launchAgentsDir, f);
51
+ run(`launchctl unload "${plistPath}" 2>/dev/null; true`);
52
+ try { unlinkSync(plistPath); } catch {}
53
+ process.stdout.write(` Removed launchd plist: ${f}\n`);
54
+ }
55
+ }
56
+ } catch {}
57
+ }
58
+
59
+ // 6. Remove 0agent crontab entries (if any were ever added)
60
+ run('crontab -l 2>/dev/null | grep -v "0agent" | crontab - 2>/dev/null; true');
61
+
62
+ process.stdout.write(' ✓ 0agent stopped and cleaned up\n');
package/dist/daemon.mjs CHANGED
@@ -2348,7 +2348,7 @@ var ShellCapability;
2348
2348
  var init_ShellCapability = __esm({
2349
2349
  "packages/daemon/src/capabilities/ShellCapability.ts"() {
2350
2350
  "use strict";
2351
- ShellCapability = class {
2351
+ ShellCapability = class _ShellCapability {
2352
2352
  name = "shell_exec";
2353
2353
  description = "Execute shell commands in the working directory.";
2354
2354
  toolDefinition = {
@@ -2363,10 +2363,24 @@ var init_ShellCapability = __esm({
2363
2363
  required: ["command"]
2364
2364
  }
2365
2365
  };
2366
- async execute(input, cwd) {
2366
+ // Commands that create persistent OS-level scheduled tasks.
2367
+ // These must never run autonomously — they survive uninstall and can
2368
+ // re-open apps (e.g. Brave) on every login or on a timer.
2369
+ static PERSISTENT_TASK_PATTERN = /crontab\s+-[eilr]|launchctl\s+load|launchctl\s+bootstrap|systemctl\s+enable|at\s+\d|make\s+login\s+item|LaunchAgents|LaunchDaemons|loginitems/i;
2370
+ async execute(input, cwd, signal) {
2367
2371
  let command = String(input.command ?? "");
2368
2372
  const timeout = Number(input.timeout_ms ?? 3e4);
2369
2373
  const start = Date.now();
2374
+ if (_ShellCapability.PERSISTENT_TASK_PATTERN.test(command)) {
2375
+ return {
2376
+ success: false,
2377
+ output: `Blocked: "${command.slice(0, 80)}" creates a persistent scheduled task (cron/launchd/login item). These survive uninstall and can keep launching apps autonomously. Ask the user explicitly before scheduling any persistent OS task.`,
2378
+ duration_ms: 0
2379
+ };
2380
+ }
2381
+ if (signal?.aborted) {
2382
+ return { success: false, output: "Cancelled.", duration_ms: 0 };
2383
+ }
2370
2384
  if (/&\s*$/.test(command) && !/[>|].*&\s*$/.test(command)) {
2371
2385
  const logFile = `/tmp/0agent-bg-${Date.now()}.log`;
2372
2386
  command = command.replace(/\s*&\s*$/, ` > ${logFile} 2>&1 &`);
@@ -2374,30 +2388,43 @@ var init_ShellCapability = __esm({
2374
2388
  return new Promise((resolve_) => {
2375
2389
  const chunks = [];
2376
2390
  let settled = false;
2377
- const done = (code, signal) => {
2391
+ const done = (code, killedBySignal) => {
2378
2392
  if (settled) return;
2379
2393
  settled = true;
2394
+ signal?.removeEventListener("abort", onAbort);
2380
2395
  clearTimeout(timer);
2381
2396
  const output = chunks.join("").trim();
2382
- const success = code === 0 || code === null && !!signal;
2397
+ const success = code === 0 || code === null && !!killedBySignal;
2383
2398
  resolve_({
2384
2399
  success,
2385
- output: output || (success ? "(no output)" : `exit ${code ?? signal}`),
2400
+ output: output || (success ? "(no output)" : `exit ${code ?? killedBySignal}`),
2386
2401
  duration_ms: Date.now() - start,
2387
- ...!success && { error: `exit ${code ?? signal}` }
2402
+ ...!success && { error: `exit ${code ?? killedBySignal}` }
2388
2403
  });
2389
2404
  };
2390
2405
  const proc = spawn2("bash", ["-c", command], {
2391
2406
  cwd,
2392
2407
  env: { ...process.env, TERM: "dumb" }
2393
- // DO NOT set `timeout` here — we manage it manually via timer
2394
- // so we can resolve on `exit` rather than waiting for `close`
2395
2408
  });
2409
+ const onAbort = () => {
2410
+ try {
2411
+ proc.kill("SIGKILL");
2412
+ } catch {
2413
+ }
2414
+ if (!settled) {
2415
+ settled = true;
2416
+ signal?.removeEventListener("abort", onAbort);
2417
+ clearTimeout(timer);
2418
+ resolve_({ success: false, output: chunks.join("").trim() || "Cancelled.", duration_ms: Date.now() - start });
2419
+ }
2420
+ };
2421
+ signal?.addEventListener("abort", onAbort, { once: true });
2396
2422
  proc.stdout.on("data", (d) => chunks.push(d.toString()));
2397
2423
  proc.stderr.on("data", (d) => chunks.push(d.toString()));
2398
2424
  proc.on("exit", (code) => done(code));
2399
2425
  proc.on("error", (err) => {
2400
2426
  settled = true;
2427
+ signal?.removeEventListener("abort", onAbort);
2401
2428
  clearTimeout(timer);
2402
2429
  resolve_({ success: false, output: err.message, error: err.message, duration_ms: Date.now() - start });
2403
2430
  });
@@ -2408,6 +2435,7 @@ var init_ShellCapability = __esm({
2408
2435
  } catch {
2409
2436
  }
2410
2437
  settled = true;
2438
+ signal?.removeEventListener("abort", onAbort);
2411
2439
  const output = chunks.join("").trim();
2412
2440
  resolve_({
2413
2441
  success: false,
@@ -2558,7 +2586,7 @@ var init_MemoryCapability = __esm({
2558
2586
  });
2559
2587
 
2560
2588
  // packages/daemon/src/capabilities/GUICapability.ts
2561
- import { spawnSync as spawnSync4 } from "node:child_process";
2589
+ import { spawn as spawn3, spawnSync as spawnSync4 } from "node:child_process";
2562
2590
  import { writeFileSync as writeFileSync2, unlinkSync } from "node:fs";
2563
2591
  import { resolve as resolve3 } from "node:path";
2564
2592
  import { tmpdir, platform as platform2 } from "node:os";
@@ -2571,13 +2599,13 @@ var init_GUICapability = __esm({
2571
2599
  description = "Automate desktop GUI \u2014 click, type, screenshot, hotkeys, find text on screen.";
2572
2600
  toolDefinition = {
2573
2601
  name: "gui_automation",
2574
- description: 'Automate desktop GUI interactions. Take screenshots to see the current screen state, click on buttons/links/fields, type text, press keyboard shortcuts, scroll, open apps. Always start with action="screenshot" to see what is on screen before clicking.',
2602
+ description: 'Automate desktop GUI interactions. Take screenshots to see the current screen state, click on buttons/links/fields, type text, press keyboard shortcuts, scroll, open apps. IMPORTANT: Limit screenshots to at most 3 per task \u2014 avoid re-screenshotting if you already know the layout. Prefer targeted actions (click, find_and_click, hotkey) over repeated screenshots. Use get_cursor_pos to check cursor position without a full screenshot. To open a website, ALWAYS use action="open_url" \u2014 never open_app + new tab, which creates duplicate windows.',
2575
2603
  input_schema: {
2576
2604
  type: "object",
2577
2605
  properties: {
2578
2606
  action: {
2579
2607
  type: "string",
2580
- description: '"screenshot" | "click" | "double_click" | "right_click" | "move" | "type" | "hotkey" | "scroll" | "drag" | "find_and_click" | "get_screen_size" | "open_app"'
2608
+ description: '"screenshot" | "click" | "double_click" | "right_click" | "move" | "type" | "hotkey" | "scroll" | "drag" | "find_and_click" | "get_screen_size" | "get_cursor_pos" | "open_url" | "open_app"'
2581
2609
  },
2582
2610
  x: { type: "number", description: "X coordinate (pixels from left)" },
2583
2611
  y: { type: "number", description: "Y coordinate (pixels from top)" },
@@ -2588,28 +2616,67 @@ var init_GUICapability = __esm({
2588
2616
  direction: { type: "string", description: '"up" | "down" | "left" | "right" for scroll' },
2589
2617
  amount: { type: "number", description: "Scroll clicks (default 3)" },
2590
2618
  app: { type: "string", description: 'App name to open e.g. "Safari", "Terminal", "Chrome"' },
2619
+ url: { type: "string", description: 'URL to open e.g. "https://example.com" (use with open_url)' },
2591
2620
  interval: { type: "number", description: "Seconds to wait between actions (default 0.05)" },
2592
2621
  duration: { type: "number", description: "Seconds for mouse movement animation (default 0.2)" }
2593
2622
  },
2594
2623
  required: ["action"]
2595
2624
  }
2596
2625
  };
2597
- async execute(input, _cwd) {
2626
+ async execute(input, _cwd, signal) {
2598
2627
  const action = String(input.action ?? "").toLowerCase().trim();
2599
2628
  const start = Date.now();
2600
2629
  const script = this._buildScript(action, input);
2601
2630
  if (!script) {
2602
- return { success: false, output: `Unknown GUI action: "${action}". Valid: screenshot, click, double_click, right_click, move, type, hotkey, scroll, drag, find_and_click, get_screen_size, open_app`, duration_ms: 0 };
2631
+ return { success: false, output: `Unknown GUI action: "${action}". Valid: screenshot, click, double_click, right_click, move, type, hotkey, scroll, drag, find_and_click, get_screen_size, get_cursor_pos, open_url, open_app`, duration_ms: 0 };
2632
+ }
2633
+ if (signal?.aborted) {
2634
+ return { success: false, output: "Cancelled.", duration_ms: 0 };
2603
2635
  }
2604
2636
  const tmpFile = resolve3(tmpdir(), `0agent_gui_${Date.now()}.py`);
2605
2637
  writeFileSync2(tmpFile, script, "utf8");
2606
- const result = spawnSync4("python3", [tmpFile], { timeout: 3e4, encoding: "utf8" });
2638
+ const runPy = (file) => new Promise((res) => {
2639
+ const proc = spawn3("python3", [file], { env: process.env });
2640
+ const out = [];
2641
+ const err = [];
2642
+ let settled = false;
2643
+ const finish = (code) => {
2644
+ if (settled) return;
2645
+ settled = true;
2646
+ signal?.removeEventListener("abort", onAbort);
2647
+ clearTimeout(timer);
2648
+ res({ stdout: out.join(""), stderr: err.join(""), code });
2649
+ };
2650
+ const onAbort = () => {
2651
+ try {
2652
+ proc.kill("SIGKILL");
2653
+ } catch {
2654
+ }
2655
+ finish(null);
2656
+ };
2657
+ signal?.addEventListener("abort", onAbort, { once: true });
2658
+ proc.stdout.on("data", (d) => out.push(d.toString()));
2659
+ proc.stderr.on("data", (d) => err.push(d.toString()));
2660
+ proc.on("exit", finish);
2661
+ proc.on("error", () => finish(-1));
2662
+ const timer = setTimeout(() => {
2663
+ try {
2664
+ proc.kill("SIGKILL");
2665
+ } catch {
2666
+ }
2667
+ finish(null);
2668
+ }, 3e4);
2669
+ });
2670
+ let result = await runPy(tmpFile);
2607
2671
  try {
2608
2672
  unlinkSync(tmpFile);
2609
2673
  } catch {
2610
2674
  }
2611
- if (result.status !== 0) {
2612
- const err = String(result.stderr ?? "").trim();
2675
+ if (signal?.aborted) {
2676
+ return { success: false, output: "Cancelled.", duration_ms: Date.now() - start };
2677
+ }
2678
+ if (result.code !== 0 && result.code !== null) {
2679
+ const err = result.stderr.trim();
2613
2680
  if (err.includes("No module named") || err.includes("ModuleNotFoundError")) {
2614
2681
  const missing = err.includes("pyautogui") ? "pyautogui pillow pytesseract" : err.includes("PIL") ? "pillow" : err.includes("tesseract") ? "pytesseract" : "pyautogui pillow";
2615
2682
  const install = spawnSync4("pip3", ["install", ...missing.split(" "), "-q"], {
@@ -2619,17 +2686,15 @@ var init_GUICapability = __esm({
2619
2686
  if (install.status !== 0) {
2620
2687
  return { success: false, output: `Auto-install failed: ${install.stderr?.slice(0, 200)}. Run: pip3 install ${missing}`, duration_ms: Date.now() - start };
2621
2688
  }
2622
- const retry = spawnSync4("python3", [tmpFile], { timeout: 3e4, encoding: "utf8" });
2623
2689
  writeFileSync2(tmpFile, script, "utf8");
2624
- const retry2 = spawnSync4("python3", [tmpFile], { timeout: 3e4, encoding: "utf8" });
2690
+ result = await runPy(tmpFile);
2625
2691
  try {
2626
2692
  unlinkSync(tmpFile);
2627
2693
  } catch {
2628
2694
  }
2629
- if (retry2.status === 0) {
2630
- return { success: true, output: retry2.stdout.trim() || "Done", duration_ms: Date.now() - start };
2631
- }
2632
- return { success: false, output: retry2.stderr?.trim() || "Unknown error after install", duration_ms: Date.now() - start };
2695
+ if (signal?.aborted) return { success: false, output: "Cancelled.", duration_ms: Date.now() - start };
2696
+ if (result.code === 0) return { success: true, output: result.stdout.trim() || "Done", duration_ms: Date.now() - start };
2697
+ return { success: false, output: result.stderr.trim() || "Unknown error after install", duration_ms: Date.now() - start };
2633
2698
  }
2634
2699
  if (err.includes("accessibility") || err.includes("permission") || err.includes("AXIsProcessTrusted")) {
2635
2700
  return {
@@ -2652,6 +2717,7 @@ var init_GUICapability = __esm({
2652
2717
  const dir = input.direction != null ? String(input.direction) : "down";
2653
2718
  const amount = input.amount != null ? Number(input.amount) : 3;
2654
2719
  const app = input.app != null ? String(input.app) : "";
2720
+ const url = input.url != null ? String(input.url) : "";
2655
2721
  const interval = input.interval != null ? Number(input.interval) : 0.05;
2656
2722
  const duration = input.duration != null ? Number(input.duration) : 0.2;
2657
2723
  const header = `
@@ -2666,6 +2732,11 @@ pyautogui.PAUSE = ${interval}
2666
2732
  return header + `
2667
2733
  w, h = pyautogui.size()
2668
2734
  print(f"Screen size: {w} x {h}")
2735
+ `;
2736
+ case "get_cursor_pos":
2737
+ return header + `
2738
+ x, y = pyautogui.position()
2739
+ print(f"Cursor position: ({x}, {y})")
2669
2740
  `;
2670
2741
  case "screenshot": {
2671
2742
  return header + `
@@ -2705,10 +2776,13 @@ try:
2705
2776
  print("\\n".join(hits[:40]))
2706
2777
  except ImportError:
2707
2778
  print("(pytesseract not installed \u2014 install it for OCR: pip3 install pytesseract)")
2708
- print(f"Screenshot saved: {shot_path}")
2709
2779
  except Exception as e:
2710
2780
  print(f"OCR failed: {e}")
2711
- print(f"Screenshot saved: {shot_path}")
2781
+ finally:
2782
+ try:
2783
+ os.remove(shot_path)
2784
+ except Exception:
2785
+ pass
2712
2786
  `;
2713
2787
  }
2714
2788
  case "click":
@@ -2790,13 +2864,107 @@ for i, word in enumerate(data['text']):
2790
2864
  cy = data['top'][i] + data['height'][i] // 2
2791
2865
  found.append((cx, cy, word))
2792
2866
 
2793
- if found:
2794
- cx, cy, word = found[0]
2795
- pyautogui.click(cx, cy, duration=${duration})
2796
- print(f"Found '{word}' at ({cx},{cy}) \u2014 clicked")
2867
+ try:
2868
+ if found:
2869
+ cx, cy, word = found[0]
2870
+ pyautogui.click(cx, cy, duration=${duration})
2871
+ print(f"Found '{word}' at ({cx},{cy}) \u2014 clicked")
2872
+ else:
2873
+ print(f"Text '${safeText}' not found on screen. Take a screenshot to see current state.")
2874
+ sys.exit(1)
2875
+ finally:
2876
+ try:
2877
+ os.remove(shot_path)
2878
+ except Exception:
2879
+ pass
2880
+ `;
2881
+ }
2882
+ case "open_url": {
2883
+ if (!url) return null;
2884
+ const safeUrl = url.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
2885
+ const osName = platform2();
2886
+ if (osName === "darwin") {
2887
+ return header + `
2888
+ import subprocess
2889
+
2890
+ url = '${safeUrl}'
2891
+
2892
+ # Check if Chrome is running
2893
+ chrome_running = subprocess.run(['pgrep', '-x', 'Google Chrome'], capture_output=True).returncode == 0
2894
+ firefox_running = subprocess.run(['pgrep', '-x', 'firefox'], capture_output=True).returncode == 0
2895
+ safari_running = subprocess.run(['pgrep', '-x', 'Safari'], capture_output=True).returncode == 0
2896
+
2897
+ import urllib.parse
2898
+ domain = urllib.parse.urlparse(url).netloc
2899
+
2900
+ if chrome_running:
2901
+ # Check if URL domain is already open in an existing tab \u2014 switch to it instead of opening new tab
2902
+ check_script = f"""
2903
+ tell application "Google Chrome"
2904
+ set foundTab to false
2905
+ repeat with w in every window
2906
+ set tabIdx to 1
2907
+ repeat with t in every tab of w
2908
+ if URL of t contains "{domain}" then
2909
+ set active tab index of w to tabIdx
2910
+ set index of w to 1
2911
+ set foundTab to true
2912
+ exit repeat
2913
+ end if
2914
+ set tabIdx to tabIdx + 1
2915
+ end repeat
2916
+ if foundTab then exit repeat
2917
+ end repeat
2918
+ if foundTab then
2919
+ activate
2920
+ return "switched"
2921
+ else
2922
+ tell front window to make new tab with properties {{URL:"{url}"}}
2923
+ activate
2924
+ return "new-tab"
2925
+ end if
2926
+ end tell"""
2927
+ r = subprocess.run(['osascript', '-e', check_script], capture_output=True, text=True)
2928
+ if r.stdout.strip() == "switched":
2929
+ print(f"Switched to existing Chrome tab: {url}")
2930
+ else:
2931
+ print(f"Opened new Chrome tab: {url}")
2932
+ elif firefox_running:
2933
+ script = f'tell application "Firefox" to open location "{url}"'
2934
+ subprocess.run(['osascript', '-e', script])
2935
+ subprocess.run(['osascript', '-e', 'tell application "Firefox" to activate'])
2936
+ print(f"Navigated Firefox to: {url}")
2937
+ elif safari_running:
2938
+ script = f'tell application "Safari" to open location "{url}"'
2939
+ subprocess.run(['osascript', '-e', script])
2940
+ subprocess.run(['osascript', '-e', 'tell application "Safari" to activate'])
2941
+ print(f"Navigated Safari to: {url}")
2797
2942
  else:
2798
- print(f"Text '${safeText}' not found on screen. Take a screenshot to see current state.")
2799
- sys.exit(1)
2943
+ # No browser open \u2014 launch default browser with the URL
2944
+ subprocess.run(['open', url])
2945
+ print(f"Launched browser with: {url}")
2946
+ time.sleep(1.0)
2947
+ `;
2948
+ }
2949
+ return header + `
2950
+ import subprocess
2951
+
2952
+ url = '${safeUrl}'
2953
+
2954
+ # Try to reuse existing browser via wmctrl/xdotool, fall back to xdg-open
2955
+ chrome_pid = subprocess.run(['pgrep', '-x', 'chrome'], capture_output=True)
2956
+ firefox_pid = subprocess.run(['pgrep', '-x', 'firefox'], capture_output=True)
2957
+
2958
+ if chrome_pid.returncode == 0:
2959
+ subprocess.Popen(['google-chrome', '--new-tab', url])
2960
+ print(f"Opened in Chrome tab: {url}")
2961
+ elif firefox_pid.returncode == 0:
2962
+ subprocess.Popen(['firefox', '--new-tab', url])
2963
+ print(f"Opened in Firefox tab: {url}")
2964
+ else:
2965
+ subprocess.Popen(['xdg-open', url])
2966
+ print(f"Opened with default browser: {url}")
2967
+ time.sleep(1.0)
2800
2968
  `;
2801
2969
  }
2802
2970
  case "open_app": {
@@ -2963,13 +3131,13 @@ var init_CapabilityRegistry = __esm({
2963
3131
  getToolDefinitions() {
2964
3132
  return [...this.capabilities.values()].map((c) => c.toolDefinition);
2965
3133
  }
2966
- async execute(toolName, input, cwd) {
3134
+ async execute(toolName, input, cwd, signal) {
2967
3135
  const cap = this.capabilities.get(toolName);
2968
3136
  if (!cap) {
2969
3137
  return { success: false, output: `Unknown capability: ${toolName}`, duration_ms: 0 };
2970
3138
  }
2971
3139
  try {
2972
- return await cap.execute(input, cwd);
3140
+ return await cap.execute(input, cwd, signal);
2973
3141
  } catch (err) {
2974
3142
  return {
2975
3143
  success: false,
@@ -2999,7 +3167,7 @@ var init_capabilities = __esm({
2999
3167
  });
3000
3168
 
3001
3169
  // packages/daemon/src/AgentExecutor.ts
3002
- import { spawn as spawn3 } from "node:child_process";
3170
+ import { spawn as spawn4 } from "node:child_process";
3003
3171
  import { writeFileSync as writeFileSync3, readFileSync as readFileSync3, readdirSync as readdirSync2, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "node:fs";
3004
3172
  import { resolve as resolve4, dirname as dirname2, relative } from "node:path";
3005
3173
  var SELF_MOD_PATTERN, AgentExecutor;
@@ -3009,8 +3177,8 @@ var init_AgentExecutor = __esm({
3009
3177
  init_capabilities();
3010
3178
  SELF_MOD_PATTERN = /\b(yourself|the agent|this agent|this cli|0agent|your code|your source|agent cli|improve.*agent|update.*agent|add.*to.*agent|fix.*agent|self.?improv)\b/i;
3011
3179
  AgentExecutor = class {
3012
- constructor(llm2, config, onStep, onToken) {
3013
- this.llm = llm2;
3180
+ constructor(llm, config, onStep, onToken) {
3181
+ this.llm = llm;
3014
3182
  this.config = config;
3015
3183
  this.onStep = onStep;
3016
3184
  this.onToken = onToken;
@@ -3025,7 +3193,7 @@ var init_AgentExecutor = __esm({
3025
3193
  maxCommandMs;
3026
3194
  registry;
3027
3195
  agentRoot;
3028
- async execute(task, systemContext) {
3196
+ async execute(task, systemContext, signal) {
3029
3197
  const filesWritten = [];
3030
3198
  const commandsRun = [];
3031
3199
  let totalTokens = 0;
@@ -3041,6 +3209,10 @@ var init_AgentExecutor = __esm({
3041
3209
  }
3042
3210
  let finalOutput = "";
3043
3211
  for (let i = 0; i < this.maxIterations; i++) {
3212
+ if (signal?.aborted) {
3213
+ finalOutput = "Cancelled.";
3214
+ break;
3215
+ }
3044
3216
  this.onStep(i === 0 ? "Thinking\u2026" : "Continuing\u2026");
3045
3217
  let response;
3046
3218
  let llmFailed = false;
@@ -3056,7 +3228,8 @@ var init_AgentExecutor = __esm({
3056
3228
  (token) => {
3057
3229
  this.onToken(token);
3058
3230
  finalOutput += token;
3059
- }
3231
+ },
3232
+ signal
3060
3233
  );
3061
3234
  break;
3062
3235
  } catch (err) {
@@ -3092,7 +3265,7 @@ var init_AgentExecutor = __esm({
3092
3265
  this.onStep(`\u25B6 ${tc.name}(${this.summariseInput(tc.name, tc.input)})`);
3093
3266
  let result;
3094
3267
  try {
3095
- const capResult = await this.registry.execute(tc.name, tc.input, this.cwd);
3268
+ const capResult = await this.registry.execute(tc.name, tc.input, this.cwd, signal);
3096
3269
  result = capResult.output;
3097
3270
  if (capResult.fallback_used) {
3098
3271
  this.onStep(` (used fallback: ${capResult.fallback_used})`);
@@ -3156,7 +3329,7 @@ var init_AgentExecutor = __esm({
3156
3329
  shellExec(command, timeoutMs) {
3157
3330
  return new Promise((resolve16) => {
3158
3331
  const chunks = [];
3159
- const proc = spawn3("bash", ["-c", command], {
3332
+ const proc = spawn4("bash", ["-c", command], {
3160
3333
  cwd: this.cwd,
3161
3334
  env: { ...process.env, TERM: "dumb" },
3162
3335
  timeout: timeoutMs
@@ -3475,7 +3648,7 @@ var init_ExecutionVerifier = __esm({
3475
3648
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync6 } from "node:fs";
3476
3649
  import { resolve as resolve6, dirname as dirname3 } from "node:path";
3477
3650
  import { fileURLToPath } from "node:url";
3478
- import { execSync as execSync4, spawn as spawn4 } from "node:child_process";
3651
+ import { execSync as execSync4, spawn as spawn5 } from "node:child_process";
3479
3652
  function isRuntimeBug(error) {
3480
3653
  if (TASK_FAILURE_PATTERNS.some((p) => p.test(error))) return false;
3481
3654
  return RUNTIME_BUG_PATTERNS.some((p) => p.test(error));
@@ -3541,8 +3714,8 @@ var init_RuntimeSelfHeal = __esm({
3541
3714
  // network issue
3542
3715
  ];
3543
3716
  RuntimeSelfHeal = class {
3544
- constructor(llm2, eventBus) {
3545
- this.llm = llm2;
3717
+ constructor(llm, eventBus) {
3718
+ this.llm = llm;
3546
3719
  this.eventBus = eventBus;
3547
3720
  let dir = dirname3(fileURLToPath(import.meta.url));
3548
3721
  while (dir !== "/" && !existsSync6(resolve6(dir, "package.json"))) {
@@ -3710,7 +3883,7 @@ Rules:
3710
3883
  restartDaemon() {
3711
3884
  const bundlePath = resolve6(this.projectRoot, "dist", "daemon.mjs");
3712
3885
  if (existsSync6(bundlePath)) {
3713
- const child = spawn4(process.execPath, [bundlePath], {
3886
+ const child = spawn5(process.execPath, [bundlePath], {
3714
3887
  detached: true,
3715
3888
  stdio: "ignore",
3716
3889
  env: process.env
@@ -3736,8 +3909,8 @@ var init_SelfHealLoop = __esm({
3736
3909
  init_ExecutionVerifier();
3737
3910
  init_RuntimeSelfHeal();
3738
3911
  SelfHealLoop = class {
3739
- constructor(llm2, config, onStep, onToken, maxAttempts = 3, runtimeHealer) {
3740
- this.llm = llm2;
3912
+ constructor(llm, config, onStep, onToken, maxAttempts = 3, runtimeHealer) {
3913
+ this.llm = llm;
3741
3914
  this.config = config;
3742
3915
  this.onStep = onStep;
3743
3916
  this.onToken = onToken;
@@ -3746,17 +3919,18 @@ var init_SelfHealLoop = __esm({
3746
3919
  this.verifier = new ExecutionVerifier(config.cwd);
3747
3920
  }
3748
3921
  verifier;
3749
- async executeWithHealing(task, systemContext) {
3922
+ async executeWithHealing(task, systemContext, signal) {
3750
3923
  const attempts = [];
3751
3924
  let currentContext = systemContext;
3752
3925
  let finalResult = null;
3753
3926
  let lastVerification = { success: true, method: "none", details: "", retryable: false, elapsed_ms: 0 };
3754
3927
  for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {
3928
+ if (signal?.aborted) break;
3755
3929
  if (attempt > 1) {
3756
3930
  this.onStep(`\u21BA Self-healing (attempt ${attempt}/${this.maxAttempts}): ${lastVerification.details}`);
3757
3931
  }
3758
3932
  const executor = new AgentExecutor(this.llm, this.config, this.onStep, this.onToken);
3759
- const result = await executor.execute(task, currentContext);
3933
+ const result = await executor.execute(task, currentContext, signal);
3760
3934
  finalResult = result;
3761
3935
  lastVerification = await this.verifier.verify(result);
3762
3936
  attempts.push({ attempt_number: attempt, error_context: currentContext ?? "", result, verification: lastVerification });
@@ -4207,24 +4381,24 @@ var LLMExecutor = class {
4207
4381
  return { content: res.content, tokens_used: res.tokens_used, model: res.model };
4208
4382
  }
4209
4383
  // ─── Tool-calling completion with optional streaming ─────────────────────
4210
- async completeWithTools(messages, tools, system, onToken) {
4384
+ async completeWithTools(messages, tools, system, onToken, signal) {
4211
4385
  switch (this.config.provider) {
4212
4386
  case "anthropic":
4213
- return this.anthropic(messages, tools, system, onToken);
4387
+ return this.anthropic(messages, tools, system, onToken, signal);
4214
4388
  case "openai":
4215
- return this.openai(messages, tools, system, onToken);
4389
+ return this.openai(messages, tools, system, onToken, void 0, signal);
4216
4390
  case "xai":
4217
- return this.openai(messages, tools, system, onToken, "https://api.x.ai/v1");
4391
+ return this.openai(messages, tools, system, onToken, "https://api.x.ai/v1", signal);
4218
4392
  case "gemini":
4219
- return this.openai(messages, tools, system, onToken, "https://generativelanguage.googleapis.com/v1beta/openai");
4393
+ return this.openai(messages, tools, system, onToken, "https://generativelanguage.googleapis.com/v1beta/openai", signal);
4220
4394
  case "ollama":
4221
4395
  return this.ollama(messages, system, onToken);
4222
4396
  default:
4223
- return this.openai(messages, tools, system, onToken);
4397
+ return this.openai(messages, tools, system, onToken, void 0, signal);
4224
4398
  }
4225
4399
  }
4226
4400
  // ─── Anthropic ───────────────────────────────────────────────────────────
4227
- async anthropic(messages, tools, system, onToken) {
4401
+ async anthropic(messages, tools, system, onToken, signal) {
4228
4402
  const sysContent = system ?? messages.find((m) => m.role === "system")?.content;
4229
4403
  const filtered = messages.filter((m) => m.role !== "system");
4230
4404
  const anthropicMsgs = filtered.map((m) => {
@@ -4272,8 +4446,7 @@ var LLMExecutor = class {
4272
4446
  "anthropic-version": "2023-06-01"
4273
4447
  },
4274
4448
  body: JSON.stringify(body),
4275
- signal: AbortSignal.timeout(12e4)
4276
- // 60s timeout
4449
+ signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(12e4)]) : AbortSignal.timeout(12e4)
4277
4450
  });
4278
4451
  if (!res.ok) {
4279
4452
  const err = await res.text();
@@ -4356,7 +4529,7 @@ var LLMExecutor = class {
4356
4529
  };
4357
4530
  }
4358
4531
  // ─── OpenAI (also xAI, Gemini) ───────────────────────────────────────────
4359
- async openai(messages, tools, system, onToken, baseUrl = "https://api.openai.com/v1") {
4532
+ async openai(messages, tools, system, onToken, baseUrl = "https://api.openai.com/v1", signal) {
4360
4533
  const allMessages = [];
4361
4534
  const sysContent = system ?? messages.find((m) => m.role === "system")?.content;
4362
4535
  if (sysContent) allMessages.push({ role: "system", content: sysContent });
@@ -4397,7 +4570,7 @@ var LLMExecutor = class {
4397
4570
  "Authorization": `Bearer ${this.config.api_key}`
4398
4571
  },
4399
4572
  body: JSON.stringify(body),
4400
- signal: AbortSignal.timeout(12e4)
4573
+ signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(12e4)]) : AbortSignal.timeout(12e4)
4401
4574
  });
4402
4575
  if (!res.ok) {
4403
4576
  const err = await res.text();
@@ -4816,6 +4989,7 @@ import { homedir as homedir2 } from "node:os";
4816
4989
  import YAML2 from "yaml";
4817
4990
  var SessionManager = class {
4818
4991
  sessions = /* @__PURE__ */ new Map();
4992
+ abortControllers = /* @__PURE__ */ new Map();
4819
4993
  inferenceEngine;
4820
4994
  eventBus;
4821
4995
  graph;
@@ -4944,6 +5118,11 @@ var SessionManager = class {
4944
5118
  session.status = "cancelled";
4945
5119
  session.completed_at = Date.now();
4946
5120
  session.error = "cancelled";
5121
+ const controller = this.abortControllers.get(id);
5122
+ if (controller) {
5123
+ controller.abort();
5124
+ this.abortControllers.delete(id);
5125
+ }
4947
5126
  this.emit({
4948
5127
  type: "session.failed",
4949
5128
  session_id: id,
@@ -5005,6 +5184,9 @@ var SessionManager = class {
5005
5184
  * All callers must have created the session first.
5006
5185
  */
5007
5186
  async _executeSession(sessionId, enrichedReq) {
5187
+ const abortController = new AbortController();
5188
+ this.abortControllers.set(sessionId, abortController);
5189
+ const signal = abortController.signal;
5008
5190
  try {
5009
5191
  await this.startSession(sessionId);
5010
5192
  this.addStep(sessionId, `Extracting entities from: "${enrichedReq.task.slice(0, 60)}${enrichedReq.task.length > 60 ? "\u2026" : ""}"`);
@@ -5082,9 +5264,9 @@ Current task:`;
5082
5264
  (step) => this.addStep(sessionId, step),
5083
5265
  (token) => this.emit({ type: "session.token", session_id: sessionId, token })
5084
5266
  );
5085
- agentResult = await healLoop.executeWithHealing(enrichedReq.task, systemContext);
5267
+ agentResult = await healLoop.executeWithHealing(enrichedReq.task, systemContext, signal);
5086
5268
  } catch {
5087
- agentResult = await executor.execute(enrichedReq.task, systemContext);
5269
+ agentResult = await executor.execute(enrichedReq.task, systemContext, signal);
5088
5270
  }
5089
5271
  if (this.conversationStore && userEntityId) {
5090
5272
  const now = Date.now();
@@ -5183,6 +5365,8 @@ Current task:`;
5183
5365
  } catch (err) {
5184
5366
  const message = err instanceof Error ? err.message : String(err);
5185
5367
  this.failSession(sessionId, message);
5368
+ } finally {
5369
+ this.abortControllers.delete(sessionId);
5186
5370
  }
5187
5371
  return this.sessions.get(sessionId);
5188
5372
  }
@@ -5288,7 +5472,7 @@ Conversation:
5288
5472
  User: ${task.slice(0, 600)}
5289
5473
  Agent: ${output.slice(0, 500)}`;
5290
5474
  try {
5291
- const resp = await llm.complete(
5475
+ const resp = await extractLLM.complete(
5292
5476
  [{ role: "user", content: prompt }],
5293
5477
  "You are a memory extraction system. Be concise. Extract only factual, durable information. Return valid JSON only."
5294
5478
  );
@@ -7202,7 +7386,7 @@ git checkout <commit> graph/ # restore graph files
7202
7386
  };
7203
7387
 
7204
7388
  // packages/daemon/src/CodespaceManager.ts
7205
- import { execSync as execSync5, spawn as spawn5 } from "node:child_process";
7389
+ import { execSync as execSync5, spawn as spawn6 } from "node:child_process";
7206
7390
  var BROWSER_PORT_REMOTE = 3e3;
7207
7391
  var BROWSER_PORT_LOCAL = 3001;
7208
7392
  var DISPLAY_NAME = "0agent-browser";
@@ -7297,7 +7481,7 @@ var CodespaceManager = class {
7297
7481
  async openTunnel(name) {
7298
7482
  this.closeTunnel();
7299
7483
  console.log(`[Codespace] Opening tunnel port ${BROWSER_PORT_REMOTE} \u2192 localhost:${BROWSER_PORT_LOCAL}...`);
7300
- this.forwardProcess = spawn5(
7484
+ this.forwardProcess = spawn6(
7301
7485
  "gh",
7302
7486
  ["codespace", "ports", "forward", `${BROWSER_PORT_REMOTE}:${BROWSER_PORT_LOCAL}`, "--codespace", name],
7303
7487
  { stdio: ["ignore", "ignore", "ignore"] }
package/package.json CHANGED
@@ -1,26 +1,27 @@
1
1
  {
2
2
  "name": "0agent",
3
- "version": "1.0.50",
3
+ "version": "1.0.55",
4
4
  "description": "A persistent, learning AI agent that runs on your machine. An agent that learns.",
5
5
  "private": false,
6
6
  "license": "Apache-2.0",
7
7
  "type": "module",
8
8
  "bin": {
9
- "0agent": "./bin/0agent.js",
10
- "0agent-chat": "./bin/chat.js"
9
+ "0agent": "bin/0agent.js",
10
+ "0agent-chat": "bin/chat.js"
11
11
  },
12
12
  "files": [
13
13
  "bin/",
14
14
  "dist/",
15
15
  "skills/",
16
- "seeds/",
17
- "bin/chat.js"
16
+ "seeds/"
18
17
  ],
19
18
  "scripts": {
20
19
  "build": "turbo run build",
21
20
  "bundle": "node scripts/bundle.mjs",
22
21
  "test": "turbo run test",
23
22
  "lint": "turbo run lint",
23
+ "postinstall": "node bin/postinstall.js",
24
+ "preuninstall": "node bin/preuninstall.js",
24
25
  "prepublishOnly": "pnpm build --filter='!@0agent/dashboard' --filter='!@0agent/core-native' && node scripts/bundle.mjs"
25
26
  },
26
27
  "dependencies": {
@@ -53,7 +54,7 @@
53
54
  ],
54
55
  "repository": {
55
56
  "type": "git",
56
- "url": "https://github.com/cadetmaze/0agentv1"
57
+ "url": "git+https://github.com/cadetmaze/0agentv1.git"
57
58
  },
58
59
  "homepage": "https://github.com/cadetmaze/0agentv1#readme",
59
60
  "packageManager": "pnpm@9.12.0"