@addai/node 0.11.0 → 0.11.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.
- package/dist/cli.js +35 -2
- package/dist/tui/data.js +15 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -54,6 +54,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
54
54
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
55
|
const lockfile_1 = require("./lockfile");
|
|
56
56
|
const index_1 = require("./index");
|
|
57
|
+
const pair_destination_1 = require("./pair-destination");
|
|
57
58
|
const store_1 = require("./store");
|
|
58
59
|
const unpair_1 = require("./unpair");
|
|
59
60
|
const run_1 = require("./tui/run");
|
|
@@ -140,6 +141,15 @@ async function main() {
|
|
|
140
141
|
stdinTTY: Boolean(process.stdin.isTTY),
|
|
141
142
|
noTui: process.env.AINODE_NO_TUI === '1',
|
|
142
143
|
});
|
|
144
|
+
// A code on the command line is an instruction to pair THIS machine.
|
|
145
|
+
// Anything that quietly does something else instead is worse than an
|
|
146
|
+
// error, so deal with it before the attach-to-a-running-daemon path below.
|
|
147
|
+
const given = (0, pair_destination_1.pairCodeFromArgv)(args);
|
|
148
|
+
if (given && (0, store_1.isPaired)()) {
|
|
149
|
+
console.log('This machine is already paired to +Ai — the code is not needed.');
|
|
150
|
+
console.log('Run `ainode unpair --yes` first if you want to pair it somewhere else.');
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
143
153
|
// A daemon may already own this node. Rather than refusing, attach
|
|
144
154
|
// read-only so "is it running?" is answerable from any terminal.
|
|
145
155
|
//
|
|
@@ -148,8 +158,14 @@ async function main() {
|
|
|
148
158
|
// later `ainode` into the viewer and returned before start() — so a node
|
|
149
159
|
// could never come back from a crash without someone deleting the file
|
|
150
160
|
// by hand. Same liveness check acquireLockfile() uses.
|
|
161
|
+
//
|
|
162
|
+
// And only when there is something to watch. Attaching to a daemon that
|
|
163
|
+
// is NOT paired makes `ainode` a closed loop: the screen it shows says
|
|
164
|
+
// "run `ainode` to pair again", and running it lands straight back here.
|
|
165
|
+
// A machine in that state — a daemon left over from before an unpair —
|
|
166
|
+
// could never be paired again from its own terminal.
|
|
151
167
|
const existing = (0, lockfile_1.readLockfile)();
|
|
152
|
-
if ((0, lockfile_1.lockfileAlive)(existing) && existing && tui) {
|
|
168
|
+
if (!given && (0, store_1.isPaired)() && (0, lockfile_1.lockfileAlive)(existing) && existing && tui) {
|
|
153
169
|
await (0, run_1.runDashboard)({
|
|
154
170
|
viewerMode: true,
|
|
155
171
|
pid: existing.pid,
|
|
@@ -158,7 +174,24 @@ async function main() {
|
|
|
158
174
|
});
|
|
159
175
|
return;
|
|
160
176
|
}
|
|
161
|
-
|
|
177
|
+
let runtime;
|
|
178
|
+
try {
|
|
179
|
+
runtime = await (0, index_1.start)(args);
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
const msg = err?.message ?? String(err);
|
|
183
|
+
if (/already running/.test(msg)) {
|
|
184
|
+
// Reached when another process holds the lock — commonly one left
|
|
185
|
+
// over from before an unpair. Naming the pid is the difference
|
|
186
|
+
// between a dead end and a ten-second fix.
|
|
187
|
+
console.error(msg);
|
|
188
|
+
const pid = /pid (\d+)/.exec(msg)?.[1];
|
|
189
|
+
// A pid is a diagnosis; a command is a fix.
|
|
190
|
+
console.error(pid ? `Stop it with \`kill ${pid}\`, then run this again.` : 'Stop it, then run this again.');
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
throw err;
|
|
194
|
+
}
|
|
162
195
|
if (!tui) {
|
|
163
196
|
console.log(`ainode v${index_1.VERSION} running (pid ${runtime.pid}, port ${runtime.port})`);
|
|
164
197
|
await new Promise(() => { });
|
package/dist/tui/data.js
CHANGED
|
@@ -15,8 +15,19 @@ function createDataLayer(deps) {
|
|
|
15
15
|
const inflight = new Map();
|
|
16
16
|
let lastError = null;
|
|
17
17
|
let offline = false;
|
|
18
|
+
/* One failed call is not an outage.
|
|
19
|
+
RPCs time out at 4s, which a laptop on a busy connection exceeds now and
|
|
20
|
+
then without anything being wrong — the request pump already treats a
|
|
21
|
+
single failure as noise and logs "recovered after 1 consecutive failures".
|
|
22
|
+
The dashboard did not: one blip flipped it to "Offline · retrying" and it
|
|
23
|
+
stayed there until the next success, so the screen contradicted itself,
|
|
24
|
+
reporting the node Online in the header and offline directly underneath.
|
|
25
|
+
Two in a row is a signal; one is weather. */
|
|
26
|
+
let consecutiveFailures = 0;
|
|
27
|
+
const OFFLINE_AFTER = 2;
|
|
18
28
|
async function fetch(key, name, args) {
|
|
19
29
|
if (breakerOpen()) {
|
|
30
|
+
// The breaker only opens after repeated failures, so this one is real.
|
|
20
31
|
offline = true;
|
|
21
32
|
return cache.get(key) ?? null;
|
|
22
33
|
}
|
|
@@ -27,12 +38,15 @@ function createDataLayer(deps) {
|
|
|
27
38
|
try {
|
|
28
39
|
const res = await call(name, { p_token: deps.token, ...args });
|
|
29
40
|
cache.set(key, res);
|
|
41
|
+
consecutiveFailures = 0;
|
|
30
42
|
offline = false;
|
|
31
43
|
lastError = null;
|
|
32
44
|
return res;
|
|
33
45
|
}
|
|
34
46
|
catch (err) {
|
|
35
|
-
|
|
47
|
+
consecutiveFailures += 1;
|
|
48
|
+
if (consecutiveFailures >= OFFLINE_AFTER)
|
|
49
|
+
offline = true;
|
|
36
50
|
lastError = err.message;
|
|
37
51
|
return cache.get(key) ?? null;
|
|
38
52
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@addai/node",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
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": [
|