@openagents-org/agent-launcher 0.2.192 → 0.2.194
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 +32 -2
- package/package.json +1 -1
- package/registry.json +9 -3
- package/src/adapters/antigravity-stream.js +65 -5
- package/src/adapters/base.js +332 -35
- package/src/adapters/claude.js +152 -21
- package/src/adapters/codex.js +37 -3
- package/src/adapters/cursor-node-entry.js +64 -0
- package/src/adapters/cursor.js +31 -1
- package/src/adapters/failure-streak.js +89 -0
- package/src/adapters/hermes.js +354 -58
- package/src/adapters/opencode.js +25 -3
- package/src/adapters/response-artifacts.js +72 -0
- package/src/adapters/thread-history.js +49 -0
- package/src/adapters/workspace-prompt.js +102 -12
- package/src/cli-models.js +244 -0
- package/src/cli.js +69 -13
- package/src/config.js +13 -3
- package/src/daemon.js +183 -50
- package/src/index.js +10 -1
- package/src/installer.js +413 -16
- package/src/log-writer.js +351 -0
- package/src/mcp-server.js +138 -9
- package/src/node-config.js +6 -4
- package/src/postinstall-stub.js +69 -0
- package/src/tools-v11/brief.js +97 -0
- package/src/tools-v11/index.js +28 -0
- package/src/tools-v11/presence-handoff.js +199 -0
- package/src/workspace-client.js +274 -32
package/src/cli.js
CHANGED
|
@@ -217,7 +217,7 @@ async function cmdCreate(connector, flags, positional) {
|
|
|
217
217
|
}
|
|
218
218
|
} else {
|
|
219
219
|
print(` agn connect ${name} <workspace-token>`);
|
|
220
|
-
print('Or pair this device first: agn
|
|
220
|
+
print('Or pair this device first: agn device connect <pairing-code>');
|
|
221
221
|
}
|
|
222
222
|
} else {
|
|
223
223
|
print(`Agent '${name}' created (type: ${type})`);
|
|
@@ -441,7 +441,7 @@ async function cmdConnect(connector, flags, positional) {
|
|
|
441
441
|
// no server round-trip; the credential is already on disk. This is the
|
|
442
442
|
// pairing-first path; the token form below stays for manual connection.
|
|
443
443
|
if (flags.workspace && typeof flags.workspace === 'string') {
|
|
444
|
-
connectKnownWorkspace(connector, name, flags.workspace);
|
|
444
|
+
connectKnownWorkspace(connector, name, flags.workspace, flags.connection);
|
|
445
445
|
return;
|
|
446
446
|
}
|
|
447
447
|
|
|
@@ -480,7 +480,7 @@ async function cmdConnect(connector, flags, positional) {
|
|
|
480
480
|
// CLI half of the dismissible launcher notice.
|
|
481
481
|
if (!process.env.OPENAGENTS_NO_DEPRECATION_NOTES) {
|
|
482
482
|
print('note: manual token connection is being retired in favor of device pairing.');
|
|
483
|
-
print(' Pair once with `agn
|
|
483
|
+
print(' Pair once with `agn device connect <code>`, then `agn connect <agent> --workspace <slug>`.');
|
|
484
484
|
print(' (Silence this note with OPENAGENTS_NO_DEPRECATION_NOTES=1.)');
|
|
485
485
|
}
|
|
486
486
|
|
|
@@ -504,7 +504,12 @@ async function cmdConnect(connector, flags, positional) {
|
|
|
504
504
|
* (node.json) — a pairing registers its network on the spot so the daemon can
|
|
505
505
|
* pick up the token. Never touches the server.
|
|
506
506
|
*/
|
|
507
|
-
function connectKnownWorkspace(connector, name, ref) {
|
|
507
|
+
function connectKnownWorkspace(connector, name, ref, connectionId) {
|
|
508
|
+
const matches = require('./node-config').listPairings().filter((p) => p.workspace_slug === ref || p.workspace_id === ref);
|
|
509
|
+
if ((!connectionId && matches.length > 1) || (connectionId && !matches.some((p) => p.node_id === connectionId))) {
|
|
510
|
+
throw new Error("Choose a device connection with --connection <id>");
|
|
511
|
+
}
|
|
512
|
+
const chosen = matches.find((p) => !connectionId || p.node_id === connectionId);
|
|
508
513
|
const networks = connector.config.getNetworks() || [];
|
|
509
514
|
let net = networks.find((n) => n.slug === ref || n.id === ref);
|
|
510
515
|
|
|
@@ -538,13 +543,14 @@ function connectKnownWorkspace(connector, name, ref) {
|
|
|
538
543
|
].filter(Boolean);
|
|
539
544
|
print(`No workspace '${ref}' is known on this device.`);
|
|
540
545
|
if (known.length) print(`Known workspaces: ${known.join(', ')}`);
|
|
541
|
-
print('Pair this device first (`agn
|
|
546
|
+
print('Pair this device first (`agn device connect <code>`), or connect with a token.');
|
|
542
547
|
process.exitCode = 1;
|
|
543
548
|
return;
|
|
544
549
|
}
|
|
545
550
|
|
|
546
551
|
const slug = net.slug || ref;
|
|
547
552
|
connector.connectWorkspace(name, slug);
|
|
553
|
+
if (chosen) connector.config.updateAgent(name, { node_id: chosen.node_id });
|
|
548
554
|
print(`'${name}' connected to workspace '${net.name || slug}'`);
|
|
549
555
|
|
|
550
556
|
const pid = connector.getDaemonPid();
|
|
@@ -570,6 +576,11 @@ function workspaceWebUrl(slug, endpoint) {
|
|
|
570
576
|
: `https://workspace.openagents.org/${slug}`;
|
|
571
577
|
}
|
|
572
578
|
|
|
579
|
+
/**
|
|
580
|
+
* `agn device <connect|status>` — pair this device (formerly "node") to a
|
|
581
|
+
* workspace. `agn node`, `agn devices` and `agn nodes` are aliases so existing
|
|
582
|
+
* scripts and older docs keep working; the product wording is "device".
|
|
583
|
+
*/
|
|
573
584
|
async function cmdNode(connector, flags, positional) {
|
|
574
585
|
const sub = positional[0] || 'status';
|
|
575
586
|
const nodeCfg = require('./node-config');
|
|
@@ -577,8 +588,8 @@ async function cmdNode(connector, flags, positional) {
|
|
|
577
588
|
if (sub === 'connect') {
|
|
578
589
|
const code = positional[1] || flags.code;
|
|
579
590
|
if (!code) {
|
|
580
|
-
print('Usage: agn
|
|
581
|
-
print('Get a pairing code from your workspace: Connect a
|
|
591
|
+
print('Usage: agn device connect <pairing-code>');
|
|
592
|
+
print('Get a pairing code from your workspace: Devices → Connect a device.');
|
|
582
593
|
process.exitCode = 1;
|
|
583
594
|
return;
|
|
584
595
|
}
|
|
@@ -616,12 +627,19 @@ async function cmdNode(connector, flags, positional) {
|
|
|
616
627
|
// credential in place — but the user should hear "already paired", not
|
|
617
628
|
// a duplicate "connected".
|
|
618
629
|
const alreadyPaired = priorPairings.some(
|
|
619
|
-
(p) => p.
|
|
630
|
+
(p) => p.node_id === res.nodeId,
|
|
620
631
|
);
|
|
621
632
|
// recordPairing, not saveNode: this device can be a node in several
|
|
622
633
|
// workspaces at once, and a bare save would drop the others' pairings.
|
|
634
|
+
// Freeze legacy bindings before another owner adds a connection here.
|
|
635
|
+
const prior = priorPairings.filter((p) => p.workspace_id === res.workspaceId);
|
|
636
|
+
if (prior.length === 1) for (const agent of connector.config.getAgents()) {
|
|
637
|
+
if (!agent.node_id && (agent.network === prior[0].workspace_slug || agent.network === prior[0].workspace_id))
|
|
638
|
+
connector.config.updateAgent(agent.name, { node_id: prior[0].node_id });
|
|
639
|
+
}
|
|
623
640
|
nodeCfg.recordPairing(info.nodeKey, {
|
|
624
641
|
node_id: res.nodeId,
|
|
642
|
+
owner_email: res.ownerEmail,
|
|
625
643
|
workspace_id: res.workspaceId,
|
|
626
644
|
workspace_slug: res.workspaceSlug,
|
|
627
645
|
workspace_name: res.workspaceName,
|
|
@@ -639,7 +657,7 @@ async function cmdNode(connector, flags, positional) {
|
|
|
639
657
|
if (alreadyPaired) {
|
|
640
658
|
print(`Already paired with '${res.workspaceName}' (${res.workspaceSlug}) — refreshed this device's credential.`);
|
|
641
659
|
} else {
|
|
642
|
-
print(`
|
|
660
|
+
print(`Device connected to workspace '${res.workspaceName}' (${res.workspaceSlug})`);
|
|
643
661
|
}
|
|
644
662
|
print(` This device: ${info.hostname} (${info.deviceType})`);
|
|
645
663
|
|
|
@@ -672,10 +690,10 @@ async function cmdNode(connector, flags, positional) {
|
|
|
672
690
|
if (sub === 'status') {
|
|
673
691
|
const pairings = nodeCfg.listPairings().filter((p) => p.node_id);
|
|
674
692
|
if (!pairings.length) {
|
|
675
|
-
print('No
|
|
693
|
+
print('No device connected. Run: agn device connect <pairing-code>');
|
|
676
694
|
return;
|
|
677
695
|
}
|
|
678
|
-
print(`
|
|
696
|
+
print(`Device connected to ${pairings.length} workspace(s):`);
|
|
679
697
|
for (const p of pairings) {
|
|
680
698
|
const slug = p.workspace_slug || p.workspace_id;
|
|
681
699
|
print(` ${p.workspace_name || slug} (${slug}) — ${workspaceWebUrl(slug, p.endpoint)}`);
|
|
@@ -683,7 +701,7 @@ async function cmdNode(connector, flags, positional) {
|
|
|
683
701
|
return;
|
|
684
702
|
}
|
|
685
703
|
|
|
686
|
-
print('Usage: agn
|
|
704
|
+
print('Usage: agn device <connect|status> [pairing-code] (alias: agn node)');
|
|
687
705
|
process.exitCode = 1;
|
|
688
706
|
}
|
|
689
707
|
|
|
@@ -1024,6 +1042,37 @@ async function cmdProbe(connector, flags, positional) {
|
|
|
1024
1042
|
}
|
|
1025
1043
|
}
|
|
1026
1044
|
|
|
1045
|
+
async function cmdModels(connector, flags, positional) {
|
|
1046
|
+
const target = positional[0];
|
|
1047
|
+
if (!target) { print('Usage: agn models <type|agent> [--json]'); return; }
|
|
1048
|
+
|
|
1049
|
+
// Accept an agent name, as `agn probe` does: its CLI is asked on that
|
|
1050
|
+
// agent's own env, so a signed-in agent answers for its sign-in.
|
|
1051
|
+
let type = target;
|
|
1052
|
+
let agentEnv;
|
|
1053
|
+
try {
|
|
1054
|
+
const agent = connector.config.getAgent(target);
|
|
1055
|
+
if (agent) { type = agent.type || 'openclaw'; agentEnv = agent.env || {}; }
|
|
1056
|
+
} catch {}
|
|
1057
|
+
|
|
1058
|
+
const result = await connector.listCliModels(type, { agentEnv });
|
|
1059
|
+
|
|
1060
|
+
if (flags && flags.json) {
|
|
1061
|
+
// Machine consumers (the daemon's model sweep) read ONLY stdout JSON —
|
|
1062
|
+
// same contract as `agn probe --json`.
|
|
1063
|
+
process.stdout.write(JSON.stringify(result));
|
|
1064
|
+
if (!result.ok) process.exitCode = 1;
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
if (!result.ok) {
|
|
1069
|
+
print(`FAILED ${result.error || ''}`.trim());
|
|
1070
|
+
process.exitCode = 1;
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
for (const m of result.models) print(m.label === m.id ? m.id : `${m.id} ${m.label}`);
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1027
1076
|
async function cmdVersion() {
|
|
1028
1077
|
const pkg = require('../package.json');
|
|
1029
1078
|
print(`${pkg.name} v${pkg.version}`);
|
|
@@ -1086,6 +1135,8 @@ Commands:
|
|
|
1086
1135
|
uninstall <type> Uninstall an agent runtime
|
|
1087
1136
|
search [query] Browse agent catalog
|
|
1088
1137
|
runtimes List installed runtimes
|
|
1138
|
+
device connect <code> Pair this device (formerly "node") to a workspace with a pairing code
|
|
1139
|
+
device status Show the workspaces this device is paired with (alias: node)
|
|
1089
1140
|
connect <agent> <token> Connect agent to workspace (or --workspace <slug> for a paired one)
|
|
1090
1141
|
disconnect <agent> Disconnect agent from workspace
|
|
1091
1142
|
env <type> [--set K=V] View/set env vars for agent type
|
|
@@ -1094,6 +1145,7 @@ Commands:
|
|
|
1094
1145
|
autostart [--disable] Enable/disable auto-start on login
|
|
1095
1146
|
test-llm <type> Test LLM connection
|
|
1096
1147
|
probe <type|agent> [--json] Smoke-test an agent (tiny end-to-end prompt)
|
|
1148
|
+
models <type|agent> [--json] List the models the agent's CLI offers its signed-in account
|
|
1097
1149
|
logs [agent] [--lines N] View daemon logs
|
|
1098
1150
|
workspace create [name] Create a new workspace
|
|
1099
1151
|
workspace join <token> Join workspace with token
|
|
@@ -1173,7 +1225,10 @@ async function main() {
|
|
|
1173
1225
|
search: () => cmdSearch(connector, flags, positional),
|
|
1174
1226
|
runtimes: () => cmdRuntimes(connector, flags),
|
|
1175
1227
|
connect: () => cmdConnect(connector, flags, positional),
|
|
1176
|
-
|
|
1228
|
+
device: () => cmdNode(connector, flags, positional),
|
|
1229
|
+
devices: () => cmdNode(connector, flags, positional),
|
|
1230
|
+
node: () => cmdNode(connector, flags, positional), // legacy spelling
|
|
1231
|
+
nodes: () => cmdNode(connector, flags, positional), // legacy spelling
|
|
1177
1232
|
disconnect: () => cmdDisconnect(connector, flags, positional),
|
|
1178
1233
|
logs: () => cmdLogs(connector, flags, positional),
|
|
1179
1234
|
autostart: () => cmdAutostart(connector, flags),
|
|
@@ -1183,6 +1238,7 @@ async function main() {
|
|
|
1183
1238
|
'tool-mode': () => cmdToolMode(connector, flags, positional),
|
|
1184
1239
|
'test-llm': () => cmdTestLLM(connector, flags, positional),
|
|
1185
1240
|
probe: () => cmdProbe(connector, flags, positional),
|
|
1241
|
+
models: () => cmdModels(connector, flags, positional),
|
|
1186
1242
|
update: () => cmdUpdate(connector),
|
|
1187
1243
|
'mcp-server': () => {
|
|
1188
1244
|
const { runMcpServer } = require('./mcp-server');
|
package/src/config.js
CHANGED
|
@@ -110,6 +110,7 @@ class Config {
|
|
|
110
110
|
agent.network = networkSlug;
|
|
111
111
|
} else {
|
|
112
112
|
delete agent.network;
|
|
113
|
+
delete agent.node_id;
|
|
113
114
|
}
|
|
114
115
|
this.save(config);
|
|
115
116
|
}
|
|
@@ -269,9 +270,18 @@ class Config {
|
|
|
269
270
|
const { keptLines, removed } = filterLogsByTimeRange(allLines, start, end);
|
|
270
271
|
|
|
271
272
|
const nextContent = keptLines.join('\n') + (hasTrailingNewline && keptLines.length > 0 ? '\n' : '');
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
273
|
+
// Rewrite in place, not write-temp + rename: the running daemon's stdout
|
|
274
|
+
// and stderr are append-mode handles to daemon.log. A rename leaves them
|
|
275
|
+
// writing into the replaced file (and fails outright on Windows while they
|
|
276
|
+
// are open); truncating in place keeps them appending to daemon.log.
|
|
277
|
+
const nextBytes = Buffer.from(nextContent, 'utf-8');
|
|
278
|
+
const fd = fs.openSync(this.logFile, 'r+');
|
|
279
|
+
try {
|
|
280
|
+
if (nextBytes.length > 0) fs.writeSync(fd, nextBytes, 0, nextBytes.length, 0);
|
|
281
|
+
fs.ftruncateSync(fd, nextBytes.length);
|
|
282
|
+
} finally {
|
|
283
|
+
fs.closeSync(fd);
|
|
284
|
+
}
|
|
275
285
|
|
|
276
286
|
return { removed, remaining: keptLines.length };
|
|
277
287
|
}
|