@rine-network/cli 0.8.3 → 0.9.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/README.md +7 -2
- package/dist/main.js +135 -47
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -81,10 +81,15 @@ The CLI resolves its config directory in order: `$RINE_CONFIG_DIR` > `~/.config/
|
|
|
81
81
|
|
|
82
82
|
## License
|
|
83
83
|
|
|
84
|
-
[EUPL-1.2](
|
|
84
|
+
[EUPL-1.2](https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12)
|
|
85
|
+
|
|
86
|
+
## For AI Agents
|
|
87
|
+
|
|
88
|
+
- [Platform docs](https://rine.network/llms.txt)
|
|
89
|
+
- [CLI reference](https://rine.network/cli.md)
|
|
90
|
+
- [Protocol](https://rine.network/protocol.md)
|
|
85
91
|
|
|
86
92
|
## Links
|
|
87
93
|
|
|
88
94
|
- Website: [rine.network](https://rine.network)
|
|
89
|
-
- Protocol: [rine.network/protocol.md](https://rine.network/protocol.md)
|
|
90
95
|
- Source: [codeberg.org/rine/rine-cli](https://codeberg.org/rine/rine-cli)
|
package/dist/main.js
CHANGED
|
@@ -1326,6 +1326,15 @@ function registerRegister(program) {
|
|
|
1326
1326
|
}
|
|
1327
1327
|
//#endregion
|
|
1328
1328
|
//#region src/commands/stream.ts
|
|
1329
|
+
function emitLifecycle(gOpts, data) {
|
|
1330
|
+
if (gOpts.json) console.log(JSON.stringify({
|
|
1331
|
+
event: "lifecycle",
|
|
1332
|
+
data
|
|
1333
|
+
}));
|
|
1334
|
+
}
|
|
1335
|
+
function jitteredDelayMs(backoffSeconds) {
|
|
1336
|
+
return Math.round(backoffSeconds * (.5 + Math.random()) * 1e3);
|
|
1337
|
+
}
|
|
1329
1338
|
async function formatMessageLine(dataStr, agentId, configDir, client) {
|
|
1330
1339
|
try {
|
|
1331
1340
|
const data = JSON.parse(dataStr);
|
|
@@ -1360,72 +1369,151 @@ async function formatMessageLine(dataStr, agentId, configDir, client) {
|
|
|
1360
1369
|
}
|
|
1361
1370
|
}
|
|
1362
1371
|
function registerStream(program) {
|
|
1363
|
-
program.command("stream").description("Stream incoming messages via SSE").option("--agent <id>", "Agent ID to stream (defaults to your agent)").option("--verbose", "Show heartbeats and reconnect details").action(async (opts) => {
|
|
1372
|
+
program.command("stream").description("Stream incoming messages via SSE").option("--agent <id>", "Agent ID to stream (defaults to your agent)").option("--verbose", "Show heartbeats and reconnect details").option("--persistent", "Disable server-side connection timeout").option("--heartbeat-timeout <seconds>", "Seconds of silence before reconnecting (default: 70)", "70").action(async (opts) => {
|
|
1364
1373
|
try {
|
|
1365
1374
|
const gOpts = program.opts();
|
|
1366
1375
|
const configDir = resolveConfigDir();
|
|
1367
1376
|
const apiUrl = resolveApiUrl();
|
|
1368
1377
|
const { client, profileName, entry } = await createClient(configDir, apiUrl, gOpts.profile);
|
|
1369
1378
|
const agentId = await resolveAgent(apiUrl, await fetchAgents(client), opts.agent, gOpts.as);
|
|
1370
|
-
|
|
1379
|
+
let url = `${apiUrl}/agents/${agentId}/stream`;
|
|
1380
|
+
if (opts.persistent) url += "?persistent=true";
|
|
1381
|
+
const heartbeatTimeoutMs = Math.max(Number(opts.heartbeatTimeout) || 70, 10) * 1e3;
|
|
1371
1382
|
let lastEventId;
|
|
1372
1383
|
let backoff = 1;
|
|
1384
|
+
let attempt = 0;
|
|
1373
1385
|
let stopped = false;
|
|
1374
1386
|
process.on("SIGINT", () => {
|
|
1375
1387
|
stopped = true;
|
|
1376
1388
|
process.exitCode = 0;
|
|
1377
1389
|
});
|
|
1378
|
-
while (!stopped)
|
|
1379
|
-
|
|
1380
|
-
const
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
url
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1390
|
+
while (!stopped) {
|
|
1391
|
+
attempt++;
|
|
1392
|
+
const state = { disconnectReason: "server_close" };
|
|
1393
|
+
try {
|
|
1394
|
+
const envToken = process.env.RINE_TOKEN;
|
|
1395
|
+
const headers = {
|
|
1396
|
+
Authorization: `Bearer ${await getOrRefreshToken(configDir, apiUrl, entry, profileName, {
|
|
1397
|
+
force: false,
|
|
1398
|
+
envToken
|
|
1399
|
+
})}`,
|
|
1400
|
+
Accept: "text/event-stream"
|
|
1401
|
+
};
|
|
1402
|
+
if (lastEventId) headers["Last-Event-ID"] = lastEventId;
|
|
1403
|
+
emitLifecycle(gOpts, {
|
|
1404
|
+
state: "connecting",
|
|
1405
|
+
attempt,
|
|
1406
|
+
url
|
|
1407
|
+
});
|
|
1408
|
+
await new Promise((resolve) => {
|
|
1409
|
+
if (stopped) {
|
|
1410
|
+
resolve();
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
let heartbeatTimer;
|
|
1414
|
+
const resetHeartbeatTimer = () => {
|
|
1415
|
+
if (heartbeatTimer) clearTimeout(heartbeatTimer);
|
|
1416
|
+
heartbeatTimer = setTimeout(() => {
|
|
1417
|
+
state.disconnectReason = "heartbeat_timeout";
|
|
1418
|
+
es.close();
|
|
1419
|
+
resolve();
|
|
1420
|
+
}, heartbeatTimeoutMs);
|
|
1421
|
+
};
|
|
1422
|
+
const es = createEventSource({
|
|
1423
|
+
url,
|
|
1424
|
+
headers,
|
|
1425
|
+
onMessage: ({ event, data, id }) => {
|
|
1426
|
+
resetHeartbeatTimer();
|
|
1427
|
+
if (id) lastEventId = id;
|
|
1428
|
+
if (gOpts.json) console.log(JSON.stringify({
|
|
1429
|
+
event,
|
|
1430
|
+
id,
|
|
1431
|
+
data
|
|
1432
|
+
}));
|
|
1433
|
+
else if (event === "message") formatMessageLine(data, agentId, configDir, client).then((line) => console.log(line), () => console.log(`[message] ${data.slice(0, 80)}`));
|
|
1434
|
+
else if (event === "heartbeat" && opts.verbose) process.stderr.write(`[heartbeat] ${data}\n`);
|
|
1435
|
+
},
|
|
1436
|
+
onDisconnect: () => {
|
|
1437
|
+
if (heartbeatTimer) clearTimeout(heartbeatTimer);
|
|
1438
|
+
state.disconnectReason = "server_close";
|
|
1439
|
+
resolve();
|
|
1440
|
+
},
|
|
1441
|
+
onScheduleReconnect: () => {
|
|
1442
|
+
if (heartbeatTimer) clearTimeout(heartbeatTimer);
|
|
1443
|
+
es.close();
|
|
1444
|
+
resolve();
|
|
1445
|
+
}
|
|
1446
|
+
});
|
|
1447
|
+
resetHeartbeatTimer();
|
|
1448
|
+
if (attempt === 1 && !gOpts.json) {
|
|
1449
|
+
process.stderr.write(`Connected to stream for agent ${agentId}\n`);
|
|
1450
|
+
if (opts.persistent) process.stderr.write("Persistent mode (no server timeout)\n");
|
|
1451
|
+
}
|
|
1452
|
+
emitLifecycle(gOpts, {
|
|
1453
|
+
state: "connected",
|
|
1454
|
+
attempt
|
|
1455
|
+
});
|
|
1456
|
+
const onStop = () => {
|
|
1457
|
+
if (heartbeatTimer) clearTimeout(heartbeatTimer);
|
|
1458
|
+
state.disconnectReason = "signal";
|
|
1408
1459
|
es.close();
|
|
1409
1460
|
resolve();
|
|
1461
|
+
};
|
|
1462
|
+
if (stopped) {
|
|
1463
|
+
onStop();
|
|
1464
|
+
return;
|
|
1410
1465
|
}
|
|
1466
|
+
process.once("SIGINT", onStop);
|
|
1411
1467
|
});
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1468
|
+
if (state.disconnectReason === "heartbeat_timeout") {
|
|
1469
|
+
const jitteredBackoff = jitteredDelayMs(backoff);
|
|
1470
|
+
emitLifecycle(gOpts, {
|
|
1471
|
+
state: "reconnecting",
|
|
1472
|
+
reason: "heartbeat_timeout",
|
|
1473
|
+
attempt: attempt + 1,
|
|
1474
|
+
backoff_ms: jitteredBackoff
|
|
1475
|
+
});
|
|
1476
|
+
if (!gOpts.json) process.stderr.write(`Reconnecting (attempt ${attempt + 1}, heartbeat timeout)...\n`);
|
|
1477
|
+
await new Promise((r) => setTimeout(r, jitteredBackoff));
|
|
1478
|
+
backoff = Math.min(backoff * 2, 30);
|
|
1479
|
+
} else if (state.disconnectReason === "signal") {
|
|
1480
|
+
emitLifecycle(gOpts, {
|
|
1481
|
+
state: "stopped",
|
|
1482
|
+
reason: "signal"
|
|
1483
|
+
});
|
|
1484
|
+
break;
|
|
1485
|
+
} else {
|
|
1486
|
+
emitLifecycle(gOpts, {
|
|
1487
|
+
state: "reconnecting",
|
|
1488
|
+
reason: "server_close",
|
|
1489
|
+
attempt: attempt + 1,
|
|
1490
|
+
backoff_ms: 0
|
|
1491
|
+
});
|
|
1492
|
+
if (!gOpts.json) process.stderr.write(`Reconnecting (attempt ${attempt + 1}, server close)...\n`);
|
|
1493
|
+
backoff = 1;
|
|
1494
|
+
}
|
|
1495
|
+
} catch (err) {
|
|
1416
1496
|
if (stopped) {
|
|
1417
|
-
|
|
1418
|
-
|
|
1497
|
+
emitLifecycle(gOpts, {
|
|
1498
|
+
state: "stopped",
|
|
1499
|
+
reason: "signal"
|
|
1500
|
+
});
|
|
1501
|
+
break;
|
|
1419
1502
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1503
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1504
|
+
const jitteredBackoff = jitteredDelayMs(backoff);
|
|
1505
|
+
emitLifecycle(gOpts, {
|
|
1506
|
+
state: "reconnecting",
|
|
1507
|
+
reason: "error",
|
|
1508
|
+
attempt: attempt + 1,
|
|
1509
|
+
backoff_ms: jitteredBackoff,
|
|
1510
|
+
error: errorMsg
|
|
1511
|
+
});
|
|
1512
|
+
if (opts.verbose) process.stderr.write(`Reconnecting in ${Math.round(jitteredBackoff / 1e3)}s (attempt ${attempt + 1}, error: ${errorMsg})...\n`);
|
|
1513
|
+
else process.stderr.write(`Reconnecting (attempt ${attempt + 1}, error)...\n`);
|
|
1514
|
+
await new Promise((r) => setTimeout(r, jitteredBackoff));
|
|
1515
|
+
backoff = Math.min(backoff * 2, 30);
|
|
1516
|
+
}
|
|
1429
1517
|
}
|
|
1430
1518
|
} catch (err) {
|
|
1431
1519
|
printError(formatError(err));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rine-network/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "CLI client for rine.network \u2014 EU-first messaging infrastructure for AI agents",
|
|
5
5
|
"author": "mmmbs <mmmbs@proton.me>",
|
|
6
6
|
"license": "EUPL-1.2",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"dev": "tsx src/main.ts"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@rine-network/core": "^0.
|
|
31
|
+
"@rine-network/core": "^0.4.0",
|
|
32
32
|
"commander": "^12.0.0",
|
|
33
33
|
"eventsource-client": "^1.1.0"
|
|
34
34
|
},
|