@addai/node 0.8.0 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,122 +1,122 @@
1
- #!/usr/bin/env node
2
- // Drive the console UI in a real pty and dump the frames a user would see.
3
- //
4
- // Unit tests assert on the pure render functions; this drives the actual
5
- // binary — alt screen, raw keys, live data, the lot. Every defect in the
6
- // 2026-07-30 console pass was found with this and none of them were visible
7
- // from the code.
8
- //
9
- // node scripts/probe-tui.mjs "w4000,shome,ka,w2500,sactivity"
10
- //
11
- // Script steps, comma separated:
12
- // w<ms> wait
13
- // k<key> send a key: a literal, or CR UP DOWN LEFT RIGHT ESC TAB
14
- // s<label> snapshot the screen under that label
15
- //
16
- // Frames are painted with cursor addressing, so the raw pty stream is
17
- // replayed through a tiny terminal model to reconstruct what is on screen.
18
-
19
- import { spawn } from 'node:child_process';
20
- import { createRequire } from 'node:module';
21
- import * as path from 'node:path';
22
- import * as url from 'node:url';
23
-
24
- const here = path.dirname(url.fileURLToPath(import.meta.url));
25
- const root = path.resolve(here, '..');
26
- const require = createRequire(import.meta.url);
27
-
28
- let pty;
29
- try {
30
- pty = require('node-pty');
31
- } catch {
32
- console.error('node-pty is not installed — run npm install first');
33
- process.exit(1);
34
- }
35
-
36
- const COLS = Number(process.env.PROBE_COLS ?? 120);
37
- const ROWS = Number(process.env.PROBE_ROWS ?? 40);
38
- const steps = (process.argv[2] ?? 'w4000,shome').split(',').filter(Boolean);
39
-
40
- const KEYS = {
41
- CR: '\r', ESC: '\x1b', TAB: '\t',
42
- UP: '\x1b[A', DOWN: '\x1b[B', RIGHT: '\x1b[C', LEFT: '\x1b[D',
43
- PGUP: '\x1b[5~', PGDN: '\x1b[6~',
44
- };
45
-
46
- /** Just enough terminal to reconstruct a screen from cursor-addressed writes. */
47
- function createScreen(rows, cols) {
48
- let grid = Array.from({ length: rows }, () => '');
49
- let row = 0;
50
- let col = 0;
51
-
52
- const put = text => {
53
- if (!text) return;
54
- const line = grid[row] ?? '';
55
- grid[row] = (line.padEnd(col, ' ')).slice(0, col) + text;
56
- col += text.length;
57
- };
58
-
59
- // A pty delivers bytes, not frames: an escape sequence is routinely split
60
- // across two chunks. Anything that looks like the start of one is held
61
- // back until the rest arrives, or it lands in the output as literal text.
62
- let pending = '';
63
-
64
- return {
65
- write(raw) {
66
- const chunk = pending + raw;
67
- pending = '';
68
- const dangling = chunk.match(/\x1b(\[[0-9;]*|\][^\x07]*|[()]?)?$/);
69
- const body = dangling ? chunk.slice(0, dangling.index) : chunk;
70
- if (dangling) pending = dangling[0];
71
-
72
- // Split into escape sequences and printable runs.
73
- const re = /\x1b\[([0-9;]*)([A-Za-z])|\x1b\][^\x07]*\x07|\x1b[()][A-Za-z0-9]|[^\x1b]+/g;
74
- let m;
75
- while ((m = re.exec(body)) !== null) {
76
- const [seq, params, cmd] = m;
77
- if (cmd === undefined) {
78
- if (seq.startsWith('\x1b')) continue; // OSC / charset — ignore
79
- for (const part of seq.split(/(\r\n|\n|\r)/)) {
80
- if (part === '\n' || part === '\r\n') { row = Math.min(rows - 1, row + 1); col = 0; }
81
- else if (part === '\r') { col = 0; }
82
- else put(part);
83
- }
84
- continue;
85
- }
86
- const n = params.split(';').map(x => Number(x || 0));
87
- if (cmd === 'H' || cmd === 'f') { row = Math.max(0, (n[0] || 1) - 1); col = Math.max(0, (n[1] || 1) - 1); }
88
- else if (cmd === 'J') { if ((n[0] ?? 0) === 2) { grid = Array.from({ length: rows }, () => ''); row = 0; col = 0; } }
89
- else if (cmd === 'K') { grid[row] = (grid[row] ?? '').slice(0, col); }
90
- else if (cmd === 'A') { row = Math.max(0, row - (n[0] || 1)); }
91
- else if (cmd === 'B') { row = Math.min(rows - 1, row + (n[0] || 1)); }
92
- // m (colour), h/l (modes) do not move the cursor or change content
93
- }
94
- },
95
- snapshot() {
96
- return grid.map(l => l.replace(/\s+$/, '')).join('\n').replace(/\n{3,}$/, '\n');
97
- },
98
- };
99
- }
100
-
101
- const screen = createScreen(ROWS, COLS);
102
- const child = pty.spawn(process.execPath, ['dist/cli.js'], {
103
- name: 'xterm-256color', cols: COLS, rows: ROWS, cwd: root,
104
- env: { ...process.env, TERM: 'xterm-256color' },
105
- });
106
- child.onData(d => screen.write(d));
107
-
108
- const wait = ms => new Promise(r => setTimeout(r, ms));
109
- const strip = s => s.replace(/\x1b\[[0-9;]*m/g, '');
110
-
111
- for (const step of steps) {
112
- const kind = step[0];
113
- const arg = step.slice(1);
114
- if (kind === 'w') await wait(Number(arg));
115
- else if (kind === 'k') child.write(KEYS[arg] ?? arg);
116
- else if (kind === 's') {
117
- process.stdout.write(`\n===== ${arg} =====\n${strip(screen.snapshot())}\n`);
118
- }
119
- }
120
-
121
- child.kill();
122
- process.exit(0);
1
+ #!/usr/bin/env node
2
+ // Drive the console UI in a real pty and dump the frames a user would see.
3
+ //
4
+ // Unit tests assert on the pure render functions; this drives the actual
5
+ // binary — alt screen, raw keys, live data, the lot. Every defect in the
6
+ // 2026-07-30 console pass was found with this and none of them were visible
7
+ // from the code.
8
+ //
9
+ // node scripts/probe-tui.mjs "w4000,shome,ka,w2500,sactivity"
10
+ //
11
+ // Script steps, comma separated:
12
+ // w<ms> wait
13
+ // k<key> send a key: a literal, or CR UP DOWN LEFT RIGHT ESC TAB
14
+ // s<label> snapshot the screen under that label
15
+ //
16
+ // Frames are painted with cursor addressing, so the raw pty stream is
17
+ // replayed through a tiny terminal model to reconstruct what is on screen.
18
+
19
+ import { spawn } from 'node:child_process';
20
+ import { createRequire } from 'node:module';
21
+ import * as path from 'node:path';
22
+ import * as url from 'node:url';
23
+
24
+ const here = path.dirname(url.fileURLToPath(import.meta.url));
25
+ const root = path.resolve(here, '..');
26
+ const require = createRequire(import.meta.url);
27
+
28
+ let pty;
29
+ try {
30
+ pty = require('node-pty');
31
+ } catch {
32
+ console.error('node-pty is not installed — run npm install first');
33
+ process.exit(1);
34
+ }
35
+
36
+ const COLS = Number(process.env.PROBE_COLS ?? 120);
37
+ const ROWS = Number(process.env.PROBE_ROWS ?? 40);
38
+ const steps = (process.argv[2] ?? 'w4000,shome').split(',').filter(Boolean);
39
+
40
+ const KEYS = {
41
+ CR: '\r', ESC: '\x1b', TAB: '\t',
42
+ UP: '\x1b[A', DOWN: '\x1b[B', RIGHT: '\x1b[C', LEFT: '\x1b[D',
43
+ PGUP: '\x1b[5~', PGDN: '\x1b[6~',
44
+ };
45
+
46
+ /** Just enough terminal to reconstruct a screen from cursor-addressed writes. */
47
+ function createScreen(rows, cols) {
48
+ let grid = Array.from({ length: rows }, () => '');
49
+ let row = 0;
50
+ let col = 0;
51
+
52
+ const put = text => {
53
+ if (!text) return;
54
+ const line = grid[row] ?? '';
55
+ grid[row] = (line.padEnd(col, ' ')).slice(0, col) + text;
56
+ col += text.length;
57
+ };
58
+
59
+ // A pty delivers bytes, not frames: an escape sequence is routinely split
60
+ // across two chunks. Anything that looks like the start of one is held
61
+ // back until the rest arrives, or it lands in the output as literal text.
62
+ let pending = '';
63
+
64
+ return {
65
+ write(raw) {
66
+ const chunk = pending + raw;
67
+ pending = '';
68
+ const dangling = chunk.match(/\x1b(\[[0-9;]*|\][^\x07]*|[()]?)?$/);
69
+ const body = dangling ? chunk.slice(0, dangling.index) : chunk;
70
+ if (dangling) pending = dangling[0];
71
+
72
+ // Split into escape sequences and printable runs.
73
+ const re = /\x1b\[([0-9;]*)([A-Za-z])|\x1b\][^\x07]*\x07|\x1b[()][A-Za-z0-9]|[^\x1b]+/g;
74
+ let m;
75
+ while ((m = re.exec(body)) !== null) {
76
+ const [seq, params, cmd] = m;
77
+ if (cmd === undefined) {
78
+ if (seq.startsWith('\x1b')) continue; // OSC / charset — ignore
79
+ for (const part of seq.split(/(\r\n|\n|\r)/)) {
80
+ if (part === '\n' || part === '\r\n') { row = Math.min(rows - 1, row + 1); col = 0; }
81
+ else if (part === '\r') { col = 0; }
82
+ else put(part);
83
+ }
84
+ continue;
85
+ }
86
+ const n = params.split(';').map(x => Number(x || 0));
87
+ if (cmd === 'H' || cmd === 'f') { row = Math.max(0, (n[0] || 1) - 1); col = Math.max(0, (n[1] || 1) - 1); }
88
+ else if (cmd === 'J') { if ((n[0] ?? 0) === 2) { grid = Array.from({ length: rows }, () => ''); row = 0; col = 0; } }
89
+ else if (cmd === 'K') { grid[row] = (grid[row] ?? '').slice(0, col); }
90
+ else if (cmd === 'A') { row = Math.max(0, row - (n[0] || 1)); }
91
+ else if (cmd === 'B') { row = Math.min(rows - 1, row + (n[0] || 1)); }
92
+ // m (colour), h/l (modes) do not move the cursor or change content
93
+ }
94
+ },
95
+ snapshot() {
96
+ return grid.map(l => l.replace(/\s+$/, '')).join('\n').replace(/\n{3,}$/, '\n');
97
+ },
98
+ };
99
+ }
100
+
101
+ const screen = createScreen(ROWS, COLS);
102
+ const child = pty.spawn(process.execPath, ['dist/cli.js'], {
103
+ name: 'xterm-256color', cols: COLS, rows: ROWS, cwd: root,
104
+ env: { ...process.env, TERM: 'xterm-256color' },
105
+ });
106
+ child.onData(d => screen.write(d));
107
+
108
+ const wait = ms => new Promise(r => setTimeout(r, ms));
109
+ const strip = s => s.replace(/\x1b\[[0-9;]*m/g, '');
110
+
111
+ for (const step of steps) {
112
+ const kind = step[0];
113
+ const arg = step.slice(1);
114
+ if (kind === 'w') await wait(Number(arg));
115
+ else if (kind === 'k') child.write(KEYS[arg] ?? arg);
116
+ else if (kind === 's') {
117
+ process.stdout.write(`\n===== ${arg} =====\n${strip(screen.snapshot())}\n`);
118
+ }
119
+ }
120
+
121
+ child.kill();
122
+ process.exit(0);
@@ -1,74 +1,74 @@
1
- #!/usr/bin/env bash
2
- # Daemon smoke test — verifies the running daemon's health + each agent's
3
- # binary + auth state. Run AFTER upgrading the daemon to 0.2.14+.
4
- #
5
- # Usage: bash scripts/smoke-test.sh
6
-
7
- set -uo pipefail
8
- LOCKFILE="${HOME}/.entities-runtime/runtime.json"
9
-
10
- if [ ! -f "$LOCKFILE" ]; then
11
- echo "✗ No daemon lockfile at $LOCKFILE — daemon not running"
12
- exit 1
13
- fi
14
-
15
- PORT=$(python3 -c "import json; print(json.load(open('${LOCKFILE}'))['port'])")
16
- echo "Daemon lockfile port: $PORT"
17
-
18
- probe() {
19
- local ep="$1"
20
- local expect_field="$2"
21
- local body
22
- body=$(curl -s --max-time 3 "http://127.0.0.1:${PORT}${ep}")
23
- if [ -z "$body" ]; then
24
- echo " ✗ $ep — no response"
25
- return 1
26
- fi
27
- if [ -n "$expect_field" ]; then
28
- if echo "$body" | python3 -c "import json,sys; d=json.load(sys.stdin); assert '$expect_field' in d or any('$expect_field' in str(v) for v in d.values())" 2>/dev/null; then
29
- echo " ✓ $ep — $(echo "$body" | python3 -c "import json,sys; print(json.dumps(json.load(sys.stdin), separators=(',',':'))[:120])")"
30
- else
31
- echo " ⚠ $ep — missing field '$expect_field': $body" | head -c 200
32
- echo
33
- fi
34
- else
35
- echo " ✓ $ep — $(echo "$body" | head -c 100)"
36
- fi
37
- }
38
-
39
- echo
40
- echo "=== Daemon control-server endpoints ==="
41
- probe /status "version"
42
- probe /self "runtime"
43
- probe /stats "inflight" # inflight field added in 0.2.11
44
- probe /requests "requests"
45
- probe /projects "projects"
46
-
47
- echo
48
- echo "=== Agent binary + auth checks ==="
49
- check_agent() {
50
- local name="$1" bin="$2" auth_file="$3" env_var="$4"
51
- printf " %s: " "$name"
52
- if ! command -v "$bin" >/dev/null 2>&1; then
53
- echo "✗ binary missing"
54
- return
55
- fi
56
- local ver
57
- ver=$("$bin" --version 2>&1 | head -1 | tr -d '\n')
58
- if [ -n "$env_var" ] && [ -n "${!env_var:-}" ]; then
59
- echo "✓ $ver — authed via \$$env_var"
60
- return
61
- fi
62
- if [ -f "$HOME/$auth_file" ] && [ "$(stat -f %z "$HOME/$auth_file" 2>/dev/null || stat -c %s "$HOME/$auth_file" 2>/dev/null)" -gt 16 ]; then
63
- echo "✓ $ver — authed via ~/$auth_file"
64
- else
65
- echo "⚠ $ver — NO AUTH (set $env_var or login)"
66
- fi
67
- }
68
- check_agent "claude" claude .claude/.credentials.json ANTHROPIC_API_KEY
69
- check_agent "codex" codex .codex/auth.json OPENAI_API_KEY
70
- check_agent "kimi" kimi .kimi/auth.toml KIMI_API_KEY
71
- check_agent "gemini" gemini .gemini/oauth_creds.json GEMINI_API_KEY
72
-
73
- echo
74
- echo "=== Done ==="
1
+ #!/usr/bin/env bash
2
+ # Daemon smoke test — verifies the running daemon's health + each agent's
3
+ # binary + auth state. Run AFTER upgrading the daemon to 0.2.14+.
4
+ #
5
+ # Usage: bash scripts/smoke-test.sh
6
+
7
+ set -uo pipefail
8
+ LOCKFILE="${HOME}/.entities-runtime/runtime.json"
9
+
10
+ if [ ! -f "$LOCKFILE" ]; then
11
+ echo "✗ No daemon lockfile at $LOCKFILE — daemon not running"
12
+ exit 1
13
+ fi
14
+
15
+ PORT=$(python3 -c "import json; print(json.load(open('${LOCKFILE}'))['port'])")
16
+ echo "Daemon lockfile port: $PORT"
17
+
18
+ probe() {
19
+ local ep="$1"
20
+ local expect_field="$2"
21
+ local body
22
+ body=$(curl -s --max-time 3 "http://127.0.0.1:${PORT}${ep}")
23
+ if [ -z "$body" ]; then
24
+ echo " ✗ $ep — no response"
25
+ return 1
26
+ fi
27
+ if [ -n "$expect_field" ]; then
28
+ if echo "$body" | python3 -c "import json,sys; d=json.load(sys.stdin); assert '$expect_field' in d or any('$expect_field' in str(v) for v in d.values())" 2>/dev/null; then
29
+ echo " ✓ $ep — $(echo "$body" | python3 -c "import json,sys; print(json.dumps(json.load(sys.stdin), separators=(',',':'))[:120])")"
30
+ else
31
+ echo " ⚠ $ep — missing field '$expect_field': $body" | head -c 200
32
+ echo
33
+ fi
34
+ else
35
+ echo " ✓ $ep — $(echo "$body" | head -c 100)"
36
+ fi
37
+ }
38
+
39
+ echo
40
+ echo "=== Daemon control-server endpoints ==="
41
+ probe /status "version"
42
+ probe /self "runtime"
43
+ probe /stats "inflight" # inflight field added in 0.2.11
44
+ probe /requests "requests"
45
+ probe /projects "projects"
46
+
47
+ echo
48
+ echo "=== Agent binary + auth checks ==="
49
+ check_agent() {
50
+ local name="$1" bin="$2" auth_file="$3" env_var="$4"
51
+ printf " %s: " "$name"
52
+ if ! command -v "$bin" >/dev/null 2>&1; then
53
+ echo "✗ binary missing"
54
+ return
55
+ fi
56
+ local ver
57
+ ver=$("$bin" --version 2>&1 | head -1 | tr -d '\n')
58
+ if [ -n "$env_var" ] && [ -n "${!env_var:-}" ]; then
59
+ echo "✓ $ver — authed via \$$env_var"
60
+ return
61
+ fi
62
+ if [ -f "$HOME/$auth_file" ] && [ "$(stat -f %z "$HOME/$auth_file" 2>/dev/null || stat -c %s "$HOME/$auth_file" 2>/dev/null)" -gt 16 ]; then
63
+ echo "✓ $ver — authed via ~/$auth_file"
64
+ else
65
+ echo "⚠ $ver — NO AUTH (set $env_var or login)"
66
+ fi
67
+ }
68
+ check_agent "claude" claude .claude/.credentials.json ANTHROPIC_API_KEY
69
+ check_agent "codex" codex .codex/auth.json OPENAI_API_KEY
70
+ check_agent "kimi" kimi .kimi/auth.toml KIMI_API_KEY
71
+ check_agent "gemini" gemini .gemini/oauth_creds.json GEMINI_API_KEY
72
+
73
+ echo
74
+ echo "=== Done ==="