@addai/node 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,10 +10,16 @@ export declare const CANCELLED: Set<string>;
10
10
  * this column needs.
11
11
  */
12
12
  export declare function shortVia(via: string | null | undefined): string;
13
+ /**
14
+ * Model ids carry a vendor prefix and a release date the column has no room
15
+ * for: `claude-haiku-4-5-20251001` was rendering as `haiku-4-…`, which loses
16
+ * the one part you were reading it for.
17
+ */
18
+ export declare function shortModel(model: string | null | undefined): string;
13
19
  export declare function statusLabel(status: string): string;
14
20
  export declare function statusColour(status: string): (s: string) => string;
15
21
  /** Live rows get a moving spinner; settled rows get a dot. */
16
- export declare function marker(status: string, spin: number): string;
22
+ export declare function marker(status: string, spin: number, handedOff?: boolean): string;
17
23
  export declare function durationOf(r: RequestRow, now: number): number | null;
18
24
  /**
19
25
  * Column widths for the request table.
@@ -8,6 +8,7 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.CANCELLED = exports.ACTIVE = void 0;
10
10
  exports.shortVia = shortVia;
11
+ exports.shortModel = shortModel;
11
12
  exports.statusLabel = statusLabel;
12
13
  exports.statusColour = statusColour;
13
14
  exports.marker = marker;
@@ -48,14 +49,24 @@ function shortVia(via) {
48
49
  return map[via];
49
50
  return via.replace(/^entity_/, '').replace(/_trigger$/, '');
50
51
  }
52
+ /**
53
+ * Model ids carry a vendor prefix and a release date the column has no room
54
+ * for: `claude-haiku-4-5-20251001` was rendering as `haiku-4-…`, which loses
55
+ * the one part you were reading it for.
56
+ */
57
+ function shortModel(model) {
58
+ if (!model)
59
+ return '—';
60
+ return model.replace(/^claude-/, '').replace(/-\d{8}$/, '');
61
+ }
51
62
  function statusLabel(status) {
52
63
  if (status === 'completed')
53
- return 'DONE';
64
+ return 'Done';
54
65
  if (exports.CANCELLED.has(status))
55
- return 'CANCEL';
66
+ return 'Cancelled';
56
67
  if (status === 'dead_letter')
57
- return 'DEAD';
58
- return status.toUpperCase();
68
+ return 'Dead';
69
+ return status.charAt(0).toUpperCase() + status.slice(1);
59
70
  }
60
71
  function statusColour(status) {
61
72
  if (exports.ACTIVE.has(status))
@@ -67,7 +78,12 @@ function statusColour(status) {
67
78
  return render_1.red;
68
79
  }
69
80
  /** Live rows get a moving spinner; settled rows get a dot. */
70
- function marker(status, spin) {
81
+ function marker(status, spin, handedOff = false) {
82
+ // A run this node handed on didn't finish here, whatever the row's terminal
83
+ // status says. Marking it as an outcome would credit this machine with
84
+ // another machine's work.
85
+ if (handedOff)
86
+ return (0, render_1.grey)('↗');
71
87
  const colour = statusColour(status);
72
88
  return colour(exports.ACTIVE.has(status) ? render_1.SPIN[spin % render_1.SPIN.length] : '⏺');
73
89
  }
@@ -91,11 +107,11 @@ function requestColumns(width) {
91
107
  // marker, status, entity, agent/mode, model, via, duration, prompt
92
108
  return (0, render_1.layoutColumns)([
93
109
  { min: 1 },
94
- // STARTING is the longest label; anything shorter than it clips the
110
+ // 'Cancelled' is the longest label; anything shorter than it clips the
95
111
  // status, which is the one column that must never be ambiguous.
96
- { min: 8 },
112
+ { min: 9 },
97
113
  { min: 10, grow: 1 },
98
- { min: 12 },
114
+ { min: 13 },
99
115
  // 'haiku-4-5' is nine columns once the claude- prefix is stripped.
100
116
  { min: 9 },
101
117
  { min: 8 },
@@ -106,24 +122,33 @@ function requestColumns(width) {
106
122
  const ALIGN = ['left', 'left', 'left', 'left', 'left', 'left', 'right', 'left'];
107
123
  function requestHeader(width) {
108
124
  const w = requestColumns(width);
109
- return (0, render_1.dim)(' ' + (0, render_1.tableRow)(['', 'STATUS', 'ENTITY', 'AGENT', 'MODEL', 'VIA', 'DUR', 'PROMPT'], w, ALIGN));
125
+ return (0, render_1.dim)(' ' + (0, render_1.tableRow)(['', 'Status', 'Entity', 'Agent', 'Model', 'Via', 'Dur', 'Prompt'], w, ALIGN));
110
126
  }
111
127
  function requestLine(r, now, selected, width, spin = 0) {
112
128
  const w = requestColumns(width);
113
129
  const colour = statusColour(r.status);
130
+ const handedOff = r.handed_off === true;
114
131
  // A run that failed once and then succeeded on retry still carries the
115
132
  // old error_code. Showing it on a DONE row reads as "this broke" when
116
133
  // the work actually landed — so the code is only for rows that ended badly.
117
134
  const endedBadly = r.status !== 'completed' && !exports.ACTIVE.has(r.status);
118
- const tail = r.error_code && endedBadly
119
- ? (0, render_1.red)(`[${r.error_code}]`)
120
- : (r.prompt ?? '').replace(/\s+/g, ' ');
135
+ const tail = handedOff
136
+ // Say where it went. Without this the row reads as a run that simply
137
+ // stopped, when in fact it continued somewhere else.
138
+ ? (0, render_1.grey)(`→ ${r.to_runtime_name ?? 'another node'} `) + (0, render_1.dim)((r.prompt ?? '').replace(/\s+/g, ' '))
139
+ : (r.error_code && endedBadly
140
+ ? (0, render_1.red)(`[${r.error_code}]`)
141
+ : (r.prompt ?? '').replace(/\s+/g, ' '));
142
+ // `agent` is what this node spawned. A run that laddered ended somewhere
143
+ // else, and saying so is the difference between a history and a guess.
144
+ const hopped = (r.ladder_hops ?? 0) > 0 && r.current_agent && r.current_agent !== r.agent;
145
+ const agentCell = hopped ? `${r.agent}→${r.current_agent}` : `${r.agent}/${r.mode}`;
121
146
  const cells = [
122
- marker(r.status, spin),
123
- colour(statusLabel(r.status)),
147
+ marker(r.status, spin, handedOff),
148
+ handedOff ? (0, render_1.grey)('Moved') : colour(statusLabel(r.status)),
124
149
  r.entity_name ?? '—',
125
- (0, render_1.dim)(`${r.agent}/${r.mode}`),
126
- (0, render_1.dim)((r.model ?? '—').replace(/^claude-/, '')),
150
+ (0, render_1.dim)(agentCell),
151
+ (0, render_1.dim)(shortModel(r.model)),
127
152
  (0, render_1.dim)(shortVia(r.issued_via)),
128
153
  (0, render_1.dim)((0, render_1.fmtDuration)(durationOf(r, now))),
129
154
  tail,
@@ -41,10 +41,10 @@ function renderRequests(st, width, height) {
41
41
  st.top = windowTop(st.sel, st.top, rows, shown.length);
42
42
  const out = [];
43
43
  const scope = st.filter ? `${shown.length} matching “${st.filter}”` : `${shown.length} loaded`;
44
- out.push((0, app_1.heading)('ACTIVITY', scope));
44
+ out.push((0, app_1.heading)('Activity', scope));
45
45
  out.push('');
46
46
  if (shown.length === 0) {
47
- out.push((0, render_1.dim)(st.filter ? ' no requests match that filter' : ' no requests yet'));
47
+ out.push((0, render_1.dim)(st.filter ? ' No requests match that filter' : ' No requests yet'));
48
48
  }
49
49
  else {
50
50
  out.push((0, request_row_1.requestHeader)(width));
@@ -57,9 +57,9 @@ function renderRequests(st, width, height) {
57
57
  while (out.length < height - 2)
58
58
  out.push('');
59
59
  if (st.loading)
60
- out.push((0, render_1.dim)(' loading more…'));
60
+ out.push((0, render_1.dim)(' Loading more…'));
61
61
  else if (st.exhausted)
62
- out.push((0, render_1.dim)(` end of history — ${shown.length} rows`));
62
+ out.push((0, render_1.dim)(` End of history — ${shown.length} rows`));
63
63
  else
64
64
  out.push((0, render_1.dim)(' ↓ past the last row loads more'));
65
65
  out.push(st.filtering
package/dist/tui/run.js CHANGED
@@ -197,6 +197,9 @@ async function runDashboard(opts) {
197
197
  inflight: 0, paired: (0, store_1.isPaired)(), viewerMode: opts.viewerMode,
198
198
  offline: false, now: Date.now(), version: opts.version,
199
199
  logCount: 0,
200
+ // Read on the first poll rather than here — the first frame must paint
201
+ // before anything touches the filesystem.
202
+ autostart: null, autostartNote: null,
200
203
  };
201
204
  const ui = createConsole((host, suspend) => {
202
205
  const openTranscript = (r) => host.push((0, transcript_1.createTranscriptScreen)({ data, host, request: r }));
package/dist/tui.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function runHarnessesTui(): Promise<void>;
package/dist/tui.js ADDED
@@ -0,0 +1,314 @@
1
+ "use strict";
2
+ // `entities-runtime harnesses` — interactive harness manager in the
3
+ // Claude Code terminal idiom: alt-screen, rounded panels, dim chrome,
4
+ // ❯ selection, braille spinner, ⏺ status dots. Hand-rolled ANSI — the
5
+ // daemon deliberately has no TUI framework dependency.
6
+ //
7
+ // Local actions hand the real terminal to the harness CLI itself (there
8
+ // is nothing to bridge when you're already at the machine): install
9
+ // streams npm output into a panel; login leaves the alt screen, runs the
10
+ // CLI's own login flow attached to your TTY, then re-probes on return.
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.runHarnessesTui = runHarnessesTui;
46
+ const child_process_1 = require("child_process");
47
+ const readline = __importStar(require("readline"));
48
+ const capabilities_1 = require("./capabilities");
49
+ const win_1 = require("./win");
50
+ const fs = __importStar(require("fs"));
51
+ const os = __importStar(require("os"));
52
+ const path = __importStar(require("path"));
53
+ const harness_registry_1 = require("./harness-registry");
54
+ /* ── ansi helpers ────────────────────────────────────────────────────── */
55
+ const ESC = '\x1b[';
56
+ const dim = (s) => `${ESC}2m${s}${ESC}22m`;
57
+ const bold = (s) => `${ESC}1m${s}${ESC}22m`;
58
+ const fg = (n, s) => `${ESC}38;5;${n}m${s}${ESC}39m`;
59
+ const green = (s) => fg(114, s);
60
+ const yellow = (s) => fg(179, s);
61
+ const grey = (s) => fg(244, s);
62
+ const cyan = (s) => fg(80, s);
63
+ const altOn = () => process.stdout.write(`${ESC}?1049h${ESC}?25l`);
64
+ const altOff = () => process.stdout.write(`${ESC}?1049l${ESC}?25h`);
65
+ const home = () => process.stdout.write(`${ESC}H${ESC}2J`);
66
+ const SPIN = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
67
+ const width = () => Math.min(process.stdout.columns || 100, 110);
68
+ function panel(title, lines) {
69
+ const w = width() - 2;
70
+ const strip = (s) => s.replace(/\x1b\[[0-9;]*m/g, '');
71
+ const top = `${dim('╭─')} ${bold(title)} ${dim('─'.repeat(Math.max(1, w - strip(title).length - 4)) + '╮')}`;
72
+ const body = lines.map(l => {
73
+ const pad = Math.max(0, w - 2 - strip(l).length);
74
+ return `${dim('│')} ${l}${' '.repeat(pad)} ${dim('│')}`;
75
+ });
76
+ const bottom = dim(`╰${'─'.repeat(w)}╯`);
77
+ return [top, ...body, bottom].join('\n');
78
+ }
79
+ function dot(p) {
80
+ if (!p?.available)
81
+ return grey('⏺');
82
+ return p.authed ? green('⏺') : yellow('⏺');
83
+ }
84
+ function actionFor(id, p) {
85
+ if (!p?.available)
86
+ return (0, harness_registry_1.isInstallable)(id) ? 'install' : 'how to install';
87
+ if (!p.authed)
88
+ return 'log in';
89
+ return harness_registry_1.HARNESSES[id].logout ? 'log out' : 'ok';
90
+ }
91
+ function row(id, p, selected) {
92
+ const spec = harness_registry_1.HARNESSES[id];
93
+ const name = spec.label.padEnd(13);
94
+ const ver = (p?.version ? `v${p.version}` : '—').padEnd(12).slice(0, 12);
95
+ const who = (p?.authed
96
+ ? `${p.account ?? p.accountKind ?? 'logged in'}${p.plan ? ` · ${p.plan}` : ''}`
97
+ : p?.available ? 'not logged in' : 'not installed').padEnd(28).slice(0, 28);
98
+ const models = `${p?.models?.length ?? 0} models`.padEnd(10);
99
+ const efforts = spec.efforts.length ? spec.efforts.join('/') : '—';
100
+ const act = actionFor(id, p);
101
+ const actTxt = act === 'ok' ? green('✓') : act === 'log out' ? dim('↵ log out') : cyan(`↵ ${act}`);
102
+ const line = `${dot(p)} ${name} ${dim(ver)} ${who} ${dim(models)} ${dim(efforts.padEnd(26).slice(0, 26))} ${actTxt}`;
103
+ return selected ? `${cyan('❯')} ${bold(line)}` : ` ${line}`;
104
+ }
105
+ function render(st) {
106
+ home();
107
+ const lines = [];
108
+ if (!st.caps) {
109
+ lines.push(`${cyan(SPIN[st.spin % SPIN.length])} probing installed harnesses…`);
110
+ }
111
+ else {
112
+ lines.push(dim(' harness version account models efforts'));
113
+ harness_registry_1.HARNESS_IDS.forEach((id, i) => lines.push(row(id, st.caps[id], i === st.sel)));
114
+ }
115
+ if (st.busy)
116
+ lines.push('', `${cyan(SPIN[st.spin % SPIN.length])} ${st.busy}`);
117
+ if (st.log.length) {
118
+ lines.push('');
119
+ st.log.slice(-8).forEach(l => lines.push(dim(l.slice(0, width() - 8))));
120
+ }
121
+ if (st.note)
122
+ lines.push('', st.note);
123
+ process.stdout.write(panel('Harnesses', lines) + '\n');
124
+ process.stdout.write(dim(' ↑/↓ move · ↵ install / log in · r refresh · q quit\n'));
125
+ }
126
+ /* ── actions ─────────────────────────────────────────────────────────── */
127
+ async function installSelected(st, id, redraw) {
128
+ const spec = harness_registry_1.HARNESSES[id];
129
+ if (!(0, harness_registry_1.isInstallable)(id)) {
130
+ st.note = yellow(`${spec.label}: ${spec.login.instructions}`);
131
+ return;
132
+ }
133
+ st.busy = `installing ${spec.label} (npm i -g ${spec.npmPackage})`;
134
+ st.log = [];
135
+ redraw();
136
+ const inv = (0, win_1.resolveCliInvocation)('npm', ['install', '-g', spec.npmPackage]);
137
+ await new Promise(resolve => {
138
+ const child = (0, child_process_1.spawn)(inv.file, inv.args, { env: process.env });
139
+ const onChunk = (b) => {
140
+ st.log.push(...b.toString('utf8').split('\n').filter(Boolean));
141
+ redraw();
142
+ };
143
+ child.stdout?.on('data', onChunk);
144
+ child.stderr?.on('data', onChunk);
145
+ child.on('exit', code => {
146
+ st.busy = null;
147
+ st.note = code === 0 ? green(`✓ ${spec.label} installed`) : fg(203, `✗ npm exited ${code}`);
148
+ resolve();
149
+ });
150
+ child.on('error', err => { st.busy = null; st.note = fg(203, `✗ ${err.message}`); resolve(); });
151
+ });
152
+ }
153
+ function logoutSelected(id) {
154
+ const spec = harness_registry_1.HARNESSES[id];
155
+ if (!spec.logout)
156
+ return `${spec.label} can only be logged out on the device`;
157
+ if (spec.logout.cmd) {
158
+ const bin = spec.findBinary();
159
+ if (!bin)
160
+ return `${spec.label} is not installed`;
161
+ const inv = (0, win_1.resolveCliInvocation)(bin, spec.logout.cmd);
162
+ const res = (0, child_process_1.spawnSync)(inv.file, inv.args, { stdio: 'ignore', env: process.env, timeout: 60_000 });
163
+ if (res.status !== 0)
164
+ return `logout exited with code ${res.status ?? '?'}`;
165
+ }
166
+ for (const rel of spec.logout.files ?? []) {
167
+ try {
168
+ fs.rmSync(path.join(os.homedir(), rel), { force: true });
169
+ }
170
+ catch { /* best effort */ }
171
+ }
172
+ for (const [rel, varName] of spec.logout.envStrip ?? []) {
173
+ const envPath = path.join(os.homedir(), rel);
174
+ try {
175
+ const body = fs.readFileSync(envPath, 'utf8');
176
+ fs.writeFileSync(envPath, body.split('\n').filter(l => !l.startsWith(`${varName}=`)).join('\n'), { mode: 0o600 });
177
+ }
178
+ catch { /* nothing to strip */ }
179
+ }
180
+ return null;
181
+ }
182
+ function loginSelected(id) {
183
+ const spec = harness_registry_1.HARNESSES[id];
184
+ const bin = spec.findBinary();
185
+ if (!bin)
186
+ return `${spec.label} is not installed`;
187
+ // Hand the real terminal to the CLI — leave the alt screen first.
188
+ altOff();
189
+ console.log(cyan(`\n— ${spec.label} login —`));
190
+ console.log(dim(spec.login.instructions) + '\n');
191
+ const args = spec.login.strategy === 'pty-url-code' ? (spec.login.loginArgs ?? []) : [];
192
+ const inv = (0, win_1.resolveCliInvocation)(bin, args);
193
+ const res = (0, child_process_1.spawnSync)(inv.file, inv.args, { stdio: 'inherit', env: process.env });
194
+ altOn();
195
+ return res.status === 0 ? null : `login exited with code ${res.status ?? '?'}`;
196
+ }
197
+ /* ── entry ───────────────────────────────────────────────────────────── */
198
+ async function plainStatus() {
199
+ const caps = await (0, capabilities_1.probeCapabilities)();
200
+ for (const id of harness_registry_1.HARNESS_IDS) {
201
+ const p = caps[id];
202
+ const state = !p?.available ? 'not installed' : p.authed ? `authed (${p.account ?? p.accountKind ?? '?'})` : 'not logged in';
203
+ console.log(`${id.padEnd(8)} ${(p?.version ?? '—').padEnd(12)} ${state}`);
204
+ }
205
+ }
206
+ async function runHarnessesTui() {
207
+ if (!process.stdout.isTTY || !process.stdin.isTTY) {
208
+ await plainStatus();
209
+ return;
210
+ }
211
+ const st = { caps: null, sel: 0, spin: 0, busy: null, log: [], note: null, confirmLogout: null };
212
+ const redraw = () => render(st);
213
+ altOn();
214
+ readline.emitKeypressEvents(process.stdin);
215
+ process.stdin.setRawMode(true);
216
+ process.stdin.resume();
217
+ const spinTimer = setInterval(() => { st.spin++; if (st.busy || !st.caps)
218
+ redraw(); }, 90);
219
+ const quit = () => {
220
+ clearInterval(spinTimer);
221
+ process.stdin.setRawMode(false);
222
+ process.stdin.pause();
223
+ altOff();
224
+ process.exit(0);
225
+ };
226
+ const reprobe = async () => {
227
+ st.caps = null;
228
+ redraw();
229
+ st.caps = await (0, capabilities_1.probeCapabilities)();
230
+ redraw();
231
+ };
232
+ let acting = false;
233
+ process.stdin.on('keypress', (_str, key) => {
234
+ void (async () => {
235
+ if (!key || acting)
236
+ return;
237
+ if (key.name === 'q' || (key.ctrl && key.name === 'c')) {
238
+ quit();
239
+ return;
240
+ }
241
+ if (!st.caps)
242
+ return;
243
+ if (st.confirmLogout) {
244
+ const target = st.confirmLogout;
245
+ st.confirmLogout = null;
246
+ if (key.name === 'y') {
247
+ acting = true;
248
+ try {
249
+ const err = logoutSelected(target);
250
+ st.note = err ? fg(203, `✗ ${err}`) : green(`✓ ${harness_registry_1.HARNESSES[target].label} logged out`);
251
+ await reprobe();
252
+ }
253
+ finally {
254
+ acting = false;
255
+ }
256
+ }
257
+ else {
258
+ st.note = dim('logout cancelled');
259
+ redraw();
260
+ }
261
+ return;
262
+ }
263
+ if (key.name === 'up' || key.name === 'k') {
264
+ st.sel = (st.sel + harness_registry_1.HARNESS_IDS.length - 1) % harness_registry_1.HARNESS_IDS.length;
265
+ redraw();
266
+ return;
267
+ }
268
+ if (key.name === 'down' || key.name === 'j') {
269
+ st.sel = (st.sel + 1) % harness_registry_1.HARNESS_IDS.length;
270
+ redraw();
271
+ return;
272
+ }
273
+ if (key.name === 'r') {
274
+ await reprobe();
275
+ return;
276
+ }
277
+ if (key.name !== 'return')
278
+ return;
279
+ const id = harness_registry_1.HARNESS_IDS[st.sel];
280
+ const p = st.caps[id];
281
+ acting = true;
282
+ try {
283
+ if (!p?.available) {
284
+ await installSelected(st, id, redraw);
285
+ await reprobe();
286
+ }
287
+ else if (!p.authed) {
288
+ const err = loginSelected(id);
289
+ st.note = err ? fg(203, `✗ ${err}`) : green('✓ login flow finished — verifying…');
290
+ await reprobe();
291
+ const now = st.caps?.[id];
292
+ if (now?.authed)
293
+ st.note = green(`✓ ${harness_registry_1.HARNESSES[id].label} logged in as ${now.account ?? now.accountKind ?? '?'}`);
294
+ else if (!err)
295
+ st.note = yellow(`${harness_registry_1.HARNESSES[id].label} still reports unauthenticated`);
296
+ redraw();
297
+ }
298
+ else if (harness_registry_1.HARNESSES[id].logout) {
299
+ st.confirmLogout = id;
300
+ st.note = yellow(`log out of ${harness_registry_1.HARNESSES[id].label}? press y to confirm`);
301
+ redraw();
302
+ }
303
+ else {
304
+ st.note = green(`${harness_registry_1.HARNESSES[id].label} is installed and logged in.`);
305
+ redraw();
306
+ }
307
+ }
308
+ finally {
309
+ acting = false;
310
+ }
311
+ })();
312
+ });
313
+ await reprobe();
314
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@addai/node",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Daemon that pairs a machine with your +Ai account and runs Claude / Codex / Kimi / Gemini agents on its behalf. Reachable via Supabase from Vault, Entity Studio, or any other +Ai surface.",
5
5
  "license": "MIT",
6
6
  "keywords": [