@addai/node 0.10.0 → 0.11.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.
package/dist/cli.js CHANGED
@@ -4,6 +4,7 @@
4
4
  //
5
5
  // ainode run this node: daemon + live dashboard
6
6
  // ainode entities the same, pairing into +Ai Entities rather than Vault
7
+ // ainode entities CODE pair straight away with a code the page minted
7
8
  // ainode harnesses interactive harness manager (install / login)
8
9
  // ainode startup … start this node when the machine starts
9
10
  // ainode unpair --yes disconnect from +Ai, cancel in-flight work
@@ -194,6 +195,7 @@ async function main() {
194
195
  usage:
195
196
  ainode run this +Ai Node — daemon + live dashboard
196
197
  ainode entities the same, but pair into +Ai Entities
198
+ ainode entities <CODE> pair with a code from the page — no browser
197
199
  ainode harnesses install / log in agent harnesses (interactive)
198
200
  ainode startup enable start this node whenever you log in
199
201
  ainode startup disable stop starting at login
@@ -18,3 +18,13 @@ export interface BrowserEnv {
18
18
  }
19
19
  /** Would opening a browser here actually show the user anything? */
20
20
  export declare function canOpenBrowser({ isTTY, platform, env }: BrowserEnv): boolean;
21
+ /**
22
+ * The pairing code someone pasted into the command, if there is one.
23
+ *
24
+ * `npx -y @addai/node@latest entities WXYZ4` is the flow where the page mints
25
+ * the code and the browser never opens. Everything else here — no argument, a
26
+ * flag, something that is not code-shaped — falls through to the normal flow.
27
+ * Refusing to guess matters: a mistyped code that we accepted anyway would
28
+ * sit in the finalize loop for fifteen minutes before admitting it.
29
+ */
30
+ export declare function pairCodeFromArgv(argv: string[]): string | null;
@@ -15,6 +15,7 @@
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.pairDestination = pairDestination;
17
17
  exports.canOpenBrowser = canOpenBrowser;
18
+ exports.pairCodeFromArgv = pairCodeFromArgv;
18
19
  const config_1 = require("./config");
19
20
  const destination = (product, url) => ({
20
21
  product,
@@ -57,3 +58,30 @@ function canOpenBrowser({ isTTY, platform, env }) {
57
58
  return false;
58
59
  return true;
59
60
  }
61
+ /** Pairing codes are five characters of A–Z and 0–9. */
62
+ const CODE_SHAPE = /^[A-Z0-9]{5}$/;
63
+ /**
64
+ * The pairing code someone pasted into the command, if there is one.
65
+ *
66
+ * `npx -y @addai/node@latest entities WXYZ4` is the flow where the page mints
67
+ * the code and the browser never opens. Everything else here — no argument, a
68
+ * flag, something that is not code-shaped — falls through to the normal flow.
69
+ * Refusing to guess matters: a mistyped code that we accepted anyway would
70
+ * sit in the finalize loop for fifteen minutes before admitting it.
71
+ */
72
+ function pairCodeFromArgv(argv) {
73
+ for (const raw of argv) {
74
+ if (raw.startsWith('-'))
75
+ continue;
76
+ // Skip the subcommand itself.
77
+ if (['entities', 'run', 'start'].includes(raw))
78
+ continue;
79
+ const cleaned = raw.replace(/[^a-zA-Z0-9]/g, '').toUpperCase();
80
+ if (CODE_SHAPE.test(cleaned))
81
+ return cleaned;
82
+ // A non-flag argument that is not a code is a mistake, not a code we
83
+ // should keep hunting for behind it.
84
+ return null;
85
+ }
86
+ return null;
87
+ }
package/dist/pairing.js CHANGED
@@ -82,7 +82,11 @@ async function awaitPairing(code) {
82
82
  const deadline = Date.now() + config_1.PAIR_TIMEOUT_MS;
83
83
  while (Date.now() < deadline) {
84
84
  try {
85
- const rows = await (0, supabase_client_1.rpc)('runtime_pair_finalize', { p_code: code });
85
+ const rows = await (0, supabase_client_1.rpc)('runtime_pair_finalize',
86
+ // The machine is the authority on what it is called. A code minted by
87
+ // the browser carries no hostname, so without this every node paired
88
+ // from the page arrives called "Untitled runtime".
89
+ { p_code: code, p_hostname: os.hostname(), p_platform: platform() });
86
90
  // PostgREST returns a `setof record` as an array of objects.
87
91
  const row = Array.isArray(rows) ? rows[0] : rows;
88
92
  if (row?.daemon_token) {
@@ -131,6 +135,16 @@ async function awaitPairing(code) {
131
135
  async function runPairing(argv) {
132
136
  const dest = (0, pair_destination_1.pairDestination)(argv);
133
137
  const hostname = os.hostname();
138
+ // A code handed to us on the command line was minted by a page the user was
139
+ // already looking at, and claimed by them as it was made. There is nothing
140
+ // to ask and nobody to send anywhere: finalize it and get on with running.
141
+ const given = (0, pair_destination_1.pairCodeFromArgv)(argv);
142
+ if (given) {
143
+ process.stdout.write(`\n Connecting ${hostname} to ${dest.product}…\n`);
144
+ const result = await awaitPairing(given);
145
+ process.stdout.write(` ✓ Connected as ${hostname}\n\n`);
146
+ return result;
147
+ }
134
148
  const interactive = (0, pair_destination_1.canOpenBrowser)({
135
149
  isTTY: Boolean(process.stdout.isTTY) && Boolean(process.stdin.isTTY),
136
150
  platform: process.platform,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@addai/node",
3
- "version": "0.10.0",
3
+ "version": "0.11.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": [