0agent 1.0.16 → 1.0.17
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 +219 -3
- package/package.json +1 -1
package/bin/0agent.js
CHANGED
|
@@ -94,6 +94,10 @@ switch (cmd) {
|
|
|
94
94
|
await runServe(args.slice(1));
|
|
95
95
|
break;
|
|
96
96
|
|
|
97
|
+
case 'watch':
|
|
98
|
+
await runWatch();
|
|
99
|
+
break;
|
|
100
|
+
|
|
97
101
|
default:
|
|
98
102
|
showHelp();
|
|
99
103
|
break;
|
|
@@ -447,12 +451,12 @@ async function streamSession(sessionId) {
|
|
|
447
451
|
break;
|
|
448
452
|
case 'session.completed': {
|
|
449
453
|
if (streaming) { process.stdout.write('\n'); streaming = false; }
|
|
450
|
-
// Show files written + commands run
|
|
451
454
|
const r = event.result ?? {};
|
|
452
455
|
if (r.files_written?.length) console.log(`\n \x1b[32m✓\x1b[0m Files: ${r.files_written.join(', ')}`);
|
|
453
456
|
if (r.commands_run?.length) console.log(` \x1b[32m✓\x1b[0m Commands run: ${r.commands_run.length}`);
|
|
454
457
|
if (r.tokens_used) console.log(` \x1b[2m${r.tokens_used} tokens · ${r.model}\x1b[0m`);
|
|
455
458
|
console.log('\n \x1b[32m✓ Done\x1b[0m\n');
|
|
459
|
+
await showResultPreview(r); // confirm server/file actually exists
|
|
456
460
|
ws.close();
|
|
457
461
|
resolve();
|
|
458
462
|
break;
|
|
@@ -496,6 +500,7 @@ async function pollSession(sessionId) {
|
|
|
496
500
|
console.log('\n ✓ Done\n');
|
|
497
501
|
const out = s.result?.output ?? s.result;
|
|
498
502
|
if (out && typeof out === 'string') console.log(` ${out}\n`);
|
|
503
|
+
await showResultPreview(s.result ?? {});
|
|
499
504
|
return;
|
|
500
505
|
}
|
|
501
506
|
if (s.status === 'failed') {
|
|
@@ -804,6 +809,177 @@ async function waitForTunnelUrl(proc, pattern, timeout) {
|
|
|
804
809
|
});
|
|
805
810
|
}
|
|
806
811
|
|
|
812
|
+
// ─── Result preview — confirms the agent's work actually ran ────────────────
|
|
813
|
+
|
|
814
|
+
async function showResultPreview(result) {
|
|
815
|
+
if (!result) return;
|
|
816
|
+
const files = result.files_written ?? [];
|
|
817
|
+
const cmds = result.commands_run ?? [];
|
|
818
|
+
const out = result.output ?? '';
|
|
819
|
+
|
|
820
|
+
// 1. Server check — if a port was mentioned, verify HTTP response
|
|
821
|
+
const allText = [...cmds, out].join(' ');
|
|
822
|
+
const portMatch = allText.match(/(?:localhost:|port\s*[=:]?\s*)(\d{4,5})/i);
|
|
823
|
+
if (portMatch) {
|
|
824
|
+
const port = parseInt(portMatch[1], 10);
|
|
825
|
+
await sleep(1200); // give server a moment to bind
|
|
826
|
+
try {
|
|
827
|
+
const res = await fetch(`http://localhost:${port}/`, { signal: AbortSignal.timeout(2500) });
|
|
828
|
+
const body = await res.text();
|
|
829
|
+
const preview = body.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 120);
|
|
830
|
+
console.log(` \x1b[32m⬡ Confirmed live:\x1b[0m http://localhost:${port} (HTTP ${res.status})`);
|
|
831
|
+
if (preview) console.log(` \x1b[2m${preview}\x1b[0m`);
|
|
832
|
+
} catch {
|
|
833
|
+
// Server not up yet — non-fatal, ExecutionVerifier already handled this
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// 2. File preview — show first few lines of the most significant created file
|
|
838
|
+
if (files.length > 0) {
|
|
839
|
+
const mainFile = files.find(f => /\.(html|jsx?|tsx?|py|rs|go|md|css|json)$/.test(f)) ?? files[0];
|
|
840
|
+
try {
|
|
841
|
+
const { readFileSync } = await import('node:fs');
|
|
842
|
+
const { resolve: res } = await import('node:path');
|
|
843
|
+
const fullPath = res(process.env['ZEROAGENT_CWD'] ?? process.cwd(), mainFile);
|
|
844
|
+
const content = readFileSync(fullPath, 'utf8');
|
|
845
|
+
const lines = content.split('\n').slice(0, 6).join('\n');
|
|
846
|
+
console.log(`\n \x1b[2m── ${mainFile} ─────────────────────────────────\x1b[0m`);
|
|
847
|
+
console.log(` \x1b[2m${lines}\x1b[0m`);
|
|
848
|
+
if (content.split('\n').length > 6) console.log(` \x1b[2m...\x1b[0m`);
|
|
849
|
+
} catch {}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
console.log();
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// ─── Watch mode — ambient intelligence ──────────────────────────────────────
|
|
856
|
+
|
|
857
|
+
async function runWatch() {
|
|
858
|
+
// Ensure daemon is running (auto-starts if needed)
|
|
859
|
+
await requireDaemon();
|
|
860
|
+
|
|
861
|
+
const { basename } = await import('node:path');
|
|
862
|
+
const cwdName = basename(process.cwd());
|
|
863
|
+
|
|
864
|
+
// Header
|
|
865
|
+
console.log(`\n \x1b[1m0agent\x1b[0m watching \x1b[36m${cwdName}\x1b[0m`);
|
|
866
|
+
console.log(` ${'─'.repeat(42)}`);
|
|
867
|
+
|
|
868
|
+
// Show current graph state
|
|
869
|
+
try {
|
|
870
|
+
const h = await fetch(`${BASE_URL}/api/health`).then(r => r.json()).catch(() => null);
|
|
871
|
+
if (h) {
|
|
872
|
+
console.log(` Graph: ${h.graph_nodes ?? 0} nodes · ${h.graph_edges ?? 0} edges`);
|
|
873
|
+
console.log(` Uptime: ${Math.round((h.uptime_ms ?? 0) / 60000)}m · Sandbox: ${h.sandbox_backend ?? '—'}`);
|
|
874
|
+
}
|
|
875
|
+
} catch {}
|
|
876
|
+
|
|
877
|
+
// Show any unseen insights immediately
|
|
878
|
+
try {
|
|
879
|
+
const insights = await fetch(`${BASE_URL}/api/insights?seen=false`).then(r => r.json()).catch(() => []);
|
|
880
|
+
if (Array.isArray(insights) && insights.length > 0) {
|
|
881
|
+
console.log(`\n \x1b[33m${insights.length} unseen insight${insights.length > 1 ? 's' : ''}:\x1b[0m`);
|
|
882
|
+
for (const ins of insights.slice(0, 3)) {
|
|
883
|
+
const icon = ins.type === 'test_failure' ? '\x1b[31m●\x1b[0m' : ins.type === 'git_anomaly' ? '\x1b[33m⚡\x1b[0m' : '\x1b[36m◆\x1b[0m';
|
|
884
|
+
console.log(` ${icon} ${ins.summary}`);
|
|
885
|
+
if (ins.suggested_action) console.log(` \x1b[2m→ ${ins.suggested_action}\x1b[0m`);
|
|
886
|
+
}
|
|
887
|
+
} else {
|
|
888
|
+
console.log(`\n Watching for insights...`);
|
|
889
|
+
}
|
|
890
|
+
} catch {}
|
|
891
|
+
|
|
892
|
+
console.log(`\n \x1b[2mPress Enter to run suggested action · q to quit\x1b[0m\n`);
|
|
893
|
+
|
|
894
|
+
// Connect WebSocket for live events
|
|
895
|
+
const WS = await importWS();
|
|
896
|
+
let lastSuggestion = null;
|
|
897
|
+
let ws;
|
|
898
|
+
|
|
899
|
+
const connect = () => {
|
|
900
|
+
ws = new WS(`ws://localhost:4200/ws`);
|
|
901
|
+
|
|
902
|
+
ws.on('open', () => {
|
|
903
|
+
ws.send(JSON.stringify({ type: 'subscribe', topics: ['sessions', 'graph', 'insights', 'stats'] }));
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
ws.on('message', (data) => {
|
|
907
|
+
try {
|
|
908
|
+
const event = JSON.parse(data.toString());
|
|
909
|
+
const ts = new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
|
|
910
|
+
|
|
911
|
+
switch (event.type) {
|
|
912
|
+
case 'agent.insight': {
|
|
913
|
+
const ins = event.insight ?? {};
|
|
914
|
+
const icon = ins.type === 'test_failure' ? '\x1b[31m● test\x1b[0m'
|
|
915
|
+
: ins.type === 'git_anomaly' ? '\x1b[33m⚡ git\x1b[0m'
|
|
916
|
+
: '\x1b[36m◆ insight\x1b[0m';
|
|
917
|
+
console.log(` [${ts}] ${icon} ${ins.summary}`);
|
|
918
|
+
if (ins.suggested_action) {
|
|
919
|
+
console.log(` \x1b[36m→ ${ins.suggested_action}\x1b[0m`);
|
|
920
|
+
lastSuggestion = ins.suggested_action;
|
|
921
|
+
}
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
case 'session.completed':
|
|
925
|
+
console.log(` [${ts}] \x1b[32m✓\x1b[0m Session completed`);
|
|
926
|
+
break;
|
|
927
|
+
case 'session.failed':
|
|
928
|
+
console.log(` [${ts}] \x1b[31m✗\x1b[0m Session failed: ${event.error}`);
|
|
929
|
+
break;
|
|
930
|
+
case 'graph.weight_updated':
|
|
931
|
+
// Subtle learning indicator — one dot per weight change
|
|
932
|
+
process.stdout.write('\x1b[2m·\x1b[0m');
|
|
933
|
+
break;
|
|
934
|
+
case 'team.synced':
|
|
935
|
+
console.log(` [${ts}] \x1b[35m⬡\x1b[0m Team synced (↑${event.deltas_pushed ?? 0} ↓${event.deltas_pulled ?? 0})`);
|
|
936
|
+
break;
|
|
937
|
+
}
|
|
938
|
+
} catch {}
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
ws.on('error', () => {});
|
|
942
|
+
ws.on('close', () => {
|
|
943
|
+
setTimeout(connect, 3000); // reconnect on daemon restart
|
|
944
|
+
});
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
connect();
|
|
948
|
+
|
|
949
|
+
// Keyboard handling — Enter = act, q = quit
|
|
950
|
+
if (process.stdin.isTTY) {
|
|
951
|
+
process.stdin.setRawMode(true);
|
|
952
|
+
process.stdin.resume();
|
|
953
|
+
process.stdin.setEncoding('utf8');
|
|
954
|
+
process.stdin.on('data', async (key) => {
|
|
955
|
+
if (key === '\u0003' || key === 'q') { // Ctrl+C or q
|
|
956
|
+
process.stdout.write('\n');
|
|
957
|
+
ws?.close();
|
|
958
|
+
process.stdin.setRawMode(false);
|
|
959
|
+
process.exit(0);
|
|
960
|
+
}
|
|
961
|
+
if (key === '\r' && lastSuggestion) {
|
|
962
|
+
// Extract executable part from suggestion
|
|
963
|
+
const cmd = lastSuggestion.match(/(?:0agent\s+)?(\/?[\w-]+(?:\s+"[^"]*")?)/);
|
|
964
|
+
if (cmd) {
|
|
965
|
+
process.stdout.write('\n');
|
|
966
|
+
const parts = cmd[1].trim().split(/\s+/);
|
|
967
|
+
if (parts[0].startsWith('/')) {
|
|
968
|
+
await runSkill(parts[0].slice(1), parts.slice(1));
|
|
969
|
+
} else if (parts[0] === 'run' || !['start','stop','init','chat'].includes(parts[0])) {
|
|
970
|
+
await runTask(parts[0] === 'run' ? parts.slice(1) : parts);
|
|
971
|
+
}
|
|
972
|
+
lastSuggestion = null;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
} else {
|
|
977
|
+
// Non-interactive: just watch, no keyboard
|
|
978
|
+
process.on('SIGINT', () => { ws?.close(); process.exit(0); });
|
|
979
|
+
await new Promise(() => {}); // run forever
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
807
983
|
function showHelp() {
|
|
808
984
|
console.log(`
|
|
809
985
|
0agent — An agent that learns.
|
|
@@ -838,6 +1014,10 @@ function showHelp() {
|
|
|
838
1014
|
0agent /build --task next
|
|
839
1015
|
0agent /qa --url https://staging.myapp.com
|
|
840
1016
|
0agent serve --tunnel # then share the URL + 0agent team join <CODE>
|
|
1017
|
+
0agent watch # ambient mode — live insights, press Enter to act
|
|
1018
|
+
|
|
1019
|
+
Auto-start:
|
|
1020
|
+
The daemon auto-starts on first 0agent run. No need for 0agent start.
|
|
841
1021
|
`);
|
|
842
1022
|
}
|
|
843
1023
|
|
|
@@ -853,10 +1033,46 @@ async function isDaemonRunning() {
|
|
|
853
1033
|
}
|
|
854
1034
|
|
|
855
1035
|
async function requireDaemon() {
|
|
856
|
-
if (
|
|
857
|
-
|
|
1036
|
+
if (await isDaemonRunning()) return;
|
|
1037
|
+
|
|
1038
|
+
// Auto-start if config exists — no manual `0agent start` needed
|
|
1039
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
1040
|
+
console.log('\n Not initialised. Run: 0agent init\n');
|
|
858
1041
|
process.exit(1);
|
|
859
1042
|
}
|
|
1043
|
+
|
|
1044
|
+
process.stdout.write(' Starting daemon');
|
|
1045
|
+
await _startDaemonBackground();
|
|
1046
|
+
|
|
1047
|
+
for (let i = 0; i < 24; i++) {
|
|
1048
|
+
await sleep(500);
|
|
1049
|
+
process.stdout.write('.');
|
|
1050
|
+
if (await isDaemonRunning()) {
|
|
1051
|
+
process.stdout.write(' ✓\n\n');
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
process.stdout.write(' ✗\n');
|
|
1056
|
+
console.log(' Daemon failed to start. Check: 0agent logs\n');
|
|
1057
|
+
process.exit(1);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// Internal: spawn daemon process without printing the full startup banner
|
|
1061
|
+
async function _startDaemonBackground() {
|
|
1062
|
+
const { resolve: res, dirname: dn, existsSync: ex } = await import('node:path').then(m => m);
|
|
1063
|
+
const pkgRoot = res(dn(new URL(import.meta.url).pathname), '..');
|
|
1064
|
+
const bundled = res(pkgRoot, 'dist', 'daemon.mjs');
|
|
1065
|
+
const devPath = res(pkgRoot, 'packages', 'daemon', 'dist', 'start.js');
|
|
1066
|
+
const script = ex(bundled) ? bundled : devPath;
|
|
1067
|
+
if (!ex(script)) return;
|
|
1068
|
+
|
|
1069
|
+
mkdirSync(resolve(AGENT_DIR, 'logs'), { recursive: true });
|
|
1070
|
+
const child = spawn(process.execPath, [script], {
|
|
1071
|
+
detached: true,
|
|
1072
|
+
stdio: 'ignore',
|
|
1073
|
+
env: { ...process.env, ZEROAGENT_CONFIG: CONFIG_PATH },
|
|
1074
|
+
});
|
|
1075
|
+
child.unref();
|
|
860
1076
|
}
|
|
861
1077
|
|
|
862
1078
|
async function importWS() {
|