@agentproto/cli 0.1.0-alpha.8 → 0.1.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 +4 -4
- package/dist/cli.mjs +1848 -256
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +391 -41
- package/dist/index.mjs.map +1 -1
- package/dist/registry/builtins.d.ts +14 -0
- package/dist/registry/builtins.mjs +329 -0
- package/dist/registry/builtins.mjs.map +1 -0
- package/dist/registry/manifest.d.ts +88 -0
- package/dist/registry/manifest.mjs +42 -0
- package/dist/registry/manifest.mjs.map +1 -0
- package/dist/registry/plugins.d.ts +23 -0
- package/dist/registry/plugins.mjs +201 -0
- package/dist/registry/plugins.mjs.map +1 -0
- package/dist/registry/runtime.d.ts +70 -0
- package/dist/registry/runtime.mjs +52 -0
- package/dist/registry/runtime.mjs.map +1 -0
- package/dist/util/credentials.d.ts +71 -0
- package/dist/util/credentials.mjs +88 -0
- package/dist/util/credentials.mjs.map +1 -0
- package/package.json +35 -7
package/dist/cli.mjs
CHANGED
|
@@ -5,16 +5,18 @@ import { resolve, basename, join, dirname, isAbsolute, normalize, relative } fro
|
|
|
5
5
|
import * as childProc from 'child_process';
|
|
6
6
|
import { execFile, spawn } from 'child_process';
|
|
7
7
|
import { promisify, parseArgs } from 'util';
|
|
8
|
-
import { unlink, readFile, readdir, writeFile, mkdir, stat,
|
|
8
|
+
import { unlink, readFile, chmod, readdir, writeFile, mkdir, stat, mkdtemp, rm, appendFile } from 'fs/promises';
|
|
9
9
|
import { randomUUID, randomBytes, createHash } from 'crypto';
|
|
10
10
|
import { createInterface } from 'readline/promises';
|
|
11
11
|
import { stdout, stdin } from 'process';
|
|
12
|
+
import { createRequire } from 'module';
|
|
13
|
+
import { pathToFileURL } from 'url';
|
|
14
|
+
import { z } from 'zod';
|
|
12
15
|
import { createAgentCliRuntime } from '@agentproto/driver-agent-cli';
|
|
16
|
+
import matter3 from 'gray-matter';
|
|
13
17
|
import { wrapWebSocket, createTunnelServer } from '@agentproto/acp/tunnel';
|
|
14
18
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
15
19
|
import '@modelcontextprotocol/sdk/server/stdio.js';
|
|
16
|
-
import { z } from 'zod';
|
|
17
|
-
import matter from 'gray-matter';
|
|
18
20
|
import { EventEmitter } from 'events';
|
|
19
21
|
import http, { createServer } from 'http';
|
|
20
22
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
@@ -114,7 +116,7 @@ __export(daemon_exports, {
|
|
|
114
116
|
});
|
|
115
117
|
async function runDaemon(args) {
|
|
116
118
|
if (args.includes("--help") || args.includes("-h") || args.length === 0) {
|
|
117
|
-
process.stdout.write(
|
|
119
|
+
process.stdout.write(USAGE6);
|
|
118
120
|
return args.length === 0 ? 2 : 0;
|
|
119
121
|
}
|
|
120
122
|
if (platform() !== "darwin") {
|
|
@@ -128,9 +130,9 @@ async function runDaemon(args) {
|
|
|
128
130
|
const rest = args.slice(1);
|
|
129
131
|
switch (sub) {
|
|
130
132
|
case "install":
|
|
131
|
-
return
|
|
133
|
+
return runInstall3(rest);
|
|
132
134
|
case "uninstall":
|
|
133
|
-
return
|
|
135
|
+
return runUninstall2();
|
|
134
136
|
case "start":
|
|
135
137
|
return runStart2();
|
|
136
138
|
case "stop":
|
|
@@ -143,7 +145,7 @@ async function runDaemon(args) {
|
|
|
143
145
|
process.stderr.write(
|
|
144
146
|
`agentproto daemon: unknown sub-verb "${sub}".
|
|
145
147
|
|
|
146
|
-
${
|
|
148
|
+
${USAGE6}`
|
|
147
149
|
);
|
|
148
150
|
return 2;
|
|
149
151
|
}
|
|
@@ -162,7 +164,7 @@ function paths() {
|
|
|
162
164
|
]
|
|
163
165
|
};
|
|
164
166
|
}
|
|
165
|
-
async function
|
|
167
|
+
async function runInstall3(args) {
|
|
166
168
|
const { values } = parseArgs({
|
|
167
169
|
args: [...args],
|
|
168
170
|
allowPositionals: false,
|
|
@@ -226,7 +228,7 @@ ${boot.stderr}
|
|
|
226
228
|
);
|
|
227
229
|
return 0;
|
|
228
230
|
}
|
|
229
|
-
async function
|
|
231
|
+
async function runUninstall2() {
|
|
230
232
|
const p = paths();
|
|
231
233
|
const target = `gui/${process.getuid?.() ?? 0}/${LABEL}`;
|
|
232
234
|
const out = await launchctl(["bootout", target]);
|
|
@@ -387,7 +389,7 @@ function xmlEscape(s) {
|
|
|
387
389
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
388
390
|
}
|
|
389
391
|
function launchctl(args) {
|
|
390
|
-
return new Promise((
|
|
392
|
+
return new Promise((resolve9) => {
|
|
391
393
|
const child = spawn("launchctl", args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
392
394
|
let stdout = "";
|
|
393
395
|
let stderr = "";
|
|
@@ -395,9 +397,9 @@ function launchctl(args) {
|
|
|
395
397
|
child.stderr?.setEncoding("utf8").on("data", (c) => stderr += c);
|
|
396
398
|
child.on(
|
|
397
399
|
"error",
|
|
398
|
-
(err) =>
|
|
400
|
+
(err) => resolve9({ code: 127, stdout, stderr: err.message })
|
|
399
401
|
);
|
|
400
|
-
child.on("exit", (code) =>
|
|
402
|
+
child.on("exit", (code) => resolve9({ code: code ?? 1, stdout, stderr }));
|
|
401
403
|
});
|
|
402
404
|
}
|
|
403
405
|
function humaniseUptime(ms) {
|
|
@@ -410,12 +412,12 @@ function humaniseUptime(ms) {
|
|
|
410
412
|
function indent(s, prefix) {
|
|
411
413
|
return s.split("\n").map((l) => prefix + l).join("\n");
|
|
412
414
|
}
|
|
413
|
-
var LABEL,
|
|
415
|
+
var LABEL, USAGE6;
|
|
414
416
|
var init_daemon = __esm({
|
|
415
417
|
"src/commands/daemon.ts"() {
|
|
416
418
|
init_config();
|
|
417
419
|
LABEL = "sh.agentproto";
|
|
418
|
-
|
|
420
|
+
USAGE6 = `agentproto daemon \u2014 run agentproto serve as a background service
|
|
419
421
|
|
|
420
422
|
Usage:
|
|
421
423
|
agentproto daemon install [--dry-run] register service + start it (macOS launchd today)
|
|
@@ -842,7 +844,7 @@ async function postForm(url, body) {
|
|
|
842
844
|
return await res.json();
|
|
843
845
|
}
|
|
844
846
|
function sleep(ms) {
|
|
845
|
-
return new Promise((
|
|
847
|
+
return new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
846
848
|
}
|
|
847
849
|
function formatDuration(ms) {
|
|
848
850
|
if (ms < 6e4) return `${Math.round(ms / 1e3)}s`;
|
|
@@ -853,16 +855,16 @@ function openBrowser(url) {
|
|
|
853
855
|
const p = platform();
|
|
854
856
|
const cmd = p === "darwin" ? "open" : p === "win32" ? "cmd" : "xdg-open";
|
|
855
857
|
const args = p === "win32" ? ["/c", "start", url] : [url];
|
|
856
|
-
return new Promise((
|
|
858
|
+
return new Promise((resolve9) => {
|
|
857
859
|
try {
|
|
858
860
|
const child = spawn(cmd, args, { stdio: "ignore", detached: true });
|
|
859
|
-
child.once("error", () =>
|
|
861
|
+
child.once("error", () => resolve9());
|
|
860
862
|
child.once("spawn", () => {
|
|
861
863
|
child.unref();
|
|
862
|
-
|
|
864
|
+
resolve9();
|
|
863
865
|
});
|
|
864
866
|
} catch {
|
|
865
|
-
|
|
867
|
+
resolve9();
|
|
866
868
|
}
|
|
867
869
|
});
|
|
868
870
|
}
|
|
@@ -991,7 +993,7 @@ async function runEdit() {
|
|
|
991
993
|
const cfg = await loadConfig();
|
|
992
994
|
await saveConfig(cfg);
|
|
993
995
|
const editor = process.env.EDITOR ?? process.env.VISUAL ?? "vi";
|
|
994
|
-
return new Promise((
|
|
996
|
+
return new Promise((resolve9) => {
|
|
995
997
|
const child = spawn(editor, [CONFIG_FILE_PATH()], {
|
|
996
998
|
stdio: "inherit"
|
|
997
999
|
});
|
|
@@ -1000,9 +1002,9 @@ async function runEdit() {
|
|
|
1000
1002
|
`agentproto config edit: failed to launch ${editor}: ${err.message}
|
|
1001
1003
|
`
|
|
1002
1004
|
);
|
|
1003
|
-
|
|
1005
|
+
resolve9(1);
|
|
1004
1006
|
});
|
|
1005
|
-
child.on("exit", (code) =>
|
|
1007
|
+
child.on("exit", (code) => resolve9(code ?? 0));
|
|
1006
1008
|
});
|
|
1007
1009
|
}
|
|
1008
1010
|
function parseValue(raw) {
|
|
@@ -1099,8 +1101,8 @@ async function collectAgentprotoNamespaceRoots(start) {
|
|
|
1099
1101
|
for (let depth = 0; depth < 16; depth++) {
|
|
1100
1102
|
for (const candidate of candidatesAt(cur)) {
|
|
1101
1103
|
try {
|
|
1102
|
-
const
|
|
1103
|
-
if (
|
|
1104
|
+
const stat5 = await promises.stat(candidate);
|
|
1105
|
+
if (stat5.isDirectory()) roots.push(candidate);
|
|
1104
1106
|
} catch {
|
|
1105
1107
|
}
|
|
1106
1108
|
}
|
|
@@ -1283,10 +1285,10 @@ async function runExternalStep(step, ledger, head) {
|
|
|
1283
1285
|
`);
|
|
1284
1286
|
const opener = platform() === "darwin" ? "open" : platform() === "win32" ? "cmd" : "xdg-open";
|
|
1285
1287
|
const args = platform() === "win32" ? ["/c", "start", step.url] : [step.url];
|
|
1286
|
-
await new Promise((
|
|
1288
|
+
await new Promise((resolve9) => {
|
|
1287
1289
|
const child = spawn(opener, args, { stdio: "ignore", detached: true });
|
|
1288
|
-
child.once("error", () =>
|
|
1289
|
-
child.once("spawn", () =>
|
|
1290
|
+
child.once("error", () => resolve9());
|
|
1291
|
+
child.once("spawn", () => resolve9());
|
|
1290
1292
|
});
|
|
1291
1293
|
let value = "";
|
|
1292
1294
|
if (step.callback?.param) {
|
|
@@ -1359,7 +1361,7 @@ async function promptString(question, opts) {
|
|
|
1359
1361
|
process.stdin.setRawMode?.(true);
|
|
1360
1362
|
let buf = "";
|
|
1361
1363
|
process.stdout.write(prompt);
|
|
1362
|
-
return new Promise((
|
|
1364
|
+
return new Promise((resolve9) => {
|
|
1363
1365
|
const onData = (chunk) => {
|
|
1364
1366
|
for (const code of chunk) {
|
|
1365
1367
|
if (code === 13 || code === 10) {
|
|
@@ -1367,7 +1369,7 @@ async function promptString(question, opts) {
|
|
|
1367
1369
|
process.stdin.setRawMode?.(false);
|
|
1368
1370
|
process.stdout.write("\n");
|
|
1369
1371
|
rl.close();
|
|
1370
|
-
|
|
1372
|
+
resolve9(buf || opts.defaultValue || "");
|
|
1371
1373
|
return;
|
|
1372
1374
|
}
|
|
1373
1375
|
if (code === 3) {
|
|
@@ -1449,7 +1451,7 @@ async function resolveSelectOptions(options) {
|
|
|
1449
1451
|
});
|
|
1450
1452
|
}
|
|
1451
1453
|
async function runShellCapturing(cmd, opts) {
|
|
1452
|
-
return new Promise((
|
|
1454
|
+
return new Promise((resolve9) => {
|
|
1453
1455
|
const child = spawn("bash", ["-lc", cmd], {
|
|
1454
1456
|
stdio: opts.interactive ? "inherit" : ["ignore", "pipe", "pipe"]
|
|
1455
1457
|
});
|
|
@@ -1467,11 +1469,11 @@ async function runShellCapturing(cmd, opts) {
|
|
|
1467
1469
|
}, opts.timeoutMs);
|
|
1468
1470
|
child.once("error", () => {
|
|
1469
1471
|
clearTimeout(timer);
|
|
1470
|
-
|
|
1472
|
+
resolve9({ exitCode: 127, stdout, stderr: stderr || "spawn error" });
|
|
1471
1473
|
});
|
|
1472
1474
|
child.once("exit", (code) => {
|
|
1473
1475
|
clearTimeout(timer);
|
|
1474
|
-
|
|
1476
|
+
resolve9({ exitCode: code ?? 0, stdout, stderr });
|
|
1475
1477
|
});
|
|
1476
1478
|
});
|
|
1477
1479
|
}
|
|
@@ -1505,9 +1507,315 @@ async function saveLedger(path, ledger) {
|
|
|
1505
1507
|
await chmod(path, 384).catch(() => {
|
|
1506
1508
|
});
|
|
1507
1509
|
}
|
|
1510
|
+
async function runInstallProfile(slug, args) {
|
|
1511
|
+
const { values } = parseArgs({
|
|
1512
|
+
args: [...args],
|
|
1513
|
+
allowPositionals: true,
|
|
1514
|
+
strict: true,
|
|
1515
|
+
options: {
|
|
1516
|
+
force: { type: "boolean", short: "f" },
|
|
1517
|
+
"dry-run": { type: "boolean" },
|
|
1518
|
+
"skip-setup": { type: "boolean" },
|
|
1519
|
+
cwd: { type: "string" },
|
|
1520
|
+
package: { type: "string" }
|
|
1521
|
+
}
|
|
1522
|
+
});
|
|
1523
|
+
if (!slug.startsWith("runtime-profile/")) {
|
|
1524
|
+
process.stderr.write(
|
|
1525
|
+
`agentproto install: expected slug starting with 'runtime-profile/', got '${slug}'.
|
|
1526
|
+
`
|
|
1527
|
+
);
|
|
1528
|
+
return 2;
|
|
1529
|
+
}
|
|
1530
|
+
const profileName = slug.slice("runtime-profile/".length);
|
|
1531
|
+
if (!/^[a-z][a-z0-9-]*$/.test(profileName)) {
|
|
1532
|
+
process.stderr.write(
|
|
1533
|
+
`agentproto install: invalid profile name '${profileName}'. Lower-kebab only.
|
|
1534
|
+
`
|
|
1535
|
+
);
|
|
1536
|
+
return 2;
|
|
1537
|
+
}
|
|
1538
|
+
let packageName;
|
|
1539
|
+
if (typeof values.package === "string" && values.package) {
|
|
1540
|
+
packageName = values.package;
|
|
1541
|
+
} else {
|
|
1542
|
+
const aliased = await readProfileAlias(profileName);
|
|
1543
|
+
packageName = aliased ?? `@agentproto/runtime-profile-${profileName}`;
|
|
1544
|
+
}
|
|
1545
|
+
let mod;
|
|
1546
|
+
try {
|
|
1547
|
+
mod = await import(packageName);
|
|
1548
|
+
} catch (err) {
|
|
1549
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
1550
|
+
process.stderr.write(
|
|
1551
|
+
`agentproto install: could not load profile package '${packageName}'. Install it first with: npm i -g ${packageName}
|
|
1552
|
+
cause: ${cause}
|
|
1553
|
+
`
|
|
1554
|
+
);
|
|
1555
|
+
return 1;
|
|
1556
|
+
}
|
|
1557
|
+
const filesDir = typeof mod.FILES_DIR === "string" ? mod.FILES_DIR : null;
|
|
1558
|
+
const loadProfileManifest = mod.loadProfileManifest;
|
|
1559
|
+
if (!filesDir || !loadProfileManifest) {
|
|
1560
|
+
process.stderr.write(
|
|
1561
|
+
`agentproto install: profile package '${packageName}' does not export FILES_DIR + loadProfileManifest.
|
|
1562
|
+
`
|
|
1563
|
+
);
|
|
1564
|
+
return 1;
|
|
1565
|
+
}
|
|
1566
|
+
const manifest = await loadProfileManifest();
|
|
1567
|
+
const cwd = values.cwd ? resolve(values.cwd) : process.cwd();
|
|
1568
|
+
const dryRun = values["dry-run"] === true;
|
|
1569
|
+
const force = values.force === true;
|
|
1570
|
+
process.stdout.write(
|
|
1571
|
+
`agentproto install ${slug} v${manifest.version} \u2192 ${cwd}
|
|
1572
|
+
${manifest.description}
|
|
1573
|
+
`
|
|
1574
|
+
);
|
|
1575
|
+
const ledgerPath = ledgerPathFor2(profileName);
|
|
1576
|
+
const prevLedger = await loadLedger2(ledgerPath);
|
|
1577
|
+
const newLedger = {
|
|
1578
|
+
slug,
|
|
1579
|
+
version: manifest.version,
|
|
1580
|
+
installedAt: prevLedger?.installedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1581
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1582
|
+
files: []
|
|
1583
|
+
};
|
|
1584
|
+
let applied = 0;
|
|
1585
|
+
let skipped = 0;
|
|
1586
|
+
let failed = 0;
|
|
1587
|
+
for (const entry of manifest.files) {
|
|
1588
|
+
const src = resolve(filesDir, entry.src);
|
|
1589
|
+
const dest = resolve(cwd, entry.dest);
|
|
1590
|
+
const prev = prevLedger?.files.find((f) => f.dest === entry.dest) ?? null;
|
|
1591
|
+
try {
|
|
1592
|
+
const srcBuf = await readFile(src);
|
|
1593
|
+
const result = await applyStrategy({
|
|
1594
|
+
strategy: entry.strategy,
|
|
1595
|
+
srcBuf,
|
|
1596
|
+
dest,
|
|
1597
|
+
prev,
|
|
1598
|
+
force,
|
|
1599
|
+
dryRun
|
|
1600
|
+
});
|
|
1601
|
+
newLedger.files.push({
|
|
1602
|
+
dest: entry.dest,
|
|
1603
|
+
strategy: entry.strategy,
|
|
1604
|
+
hashAfter: result.hashAfter,
|
|
1605
|
+
appliedAt: result.wrote ? (/* @__PURE__ */ new Date()).toISOString() : prev?.appliedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1606
|
+
});
|
|
1607
|
+
if (result.wrote) {
|
|
1608
|
+
applied += 1;
|
|
1609
|
+
process.stdout.write(` ${result.action.padEnd(10)} ${entry.dest}
|
|
1610
|
+
`);
|
|
1611
|
+
if (entry.executable && !dryRun) {
|
|
1612
|
+
await chmod(dest, 493);
|
|
1613
|
+
}
|
|
1614
|
+
} else {
|
|
1615
|
+
skipped += 1;
|
|
1616
|
+
process.stdout.write(` ${result.action.padEnd(10)} ${entry.dest}
|
|
1617
|
+
`);
|
|
1618
|
+
}
|
|
1619
|
+
} catch (err) {
|
|
1620
|
+
failed += 1;
|
|
1621
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1622
|
+
process.stderr.write(` FAIL ${entry.dest} \u2014 ${msg}
|
|
1623
|
+
`);
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
if (!dryRun) {
|
|
1627
|
+
await saveLedger2(ledgerPath, newLedger);
|
|
1628
|
+
}
|
|
1629
|
+
process.stdout.write(
|
|
1630
|
+
`agentproto install ${slug}: ${applied} applied, ${skipped} skipped, ${failed} failed${dryRun ? " (dry-run)" : ""}
|
|
1631
|
+
`
|
|
1632
|
+
);
|
|
1633
|
+
return failed === 0 ? 0 : 1;
|
|
1634
|
+
}
|
|
1635
|
+
async function applyStrategy(opts) {
|
|
1636
|
+
const { strategy, srcBuf, dest, prev, force, dryRun } = opts;
|
|
1637
|
+
const destExists = await fileExists(dest);
|
|
1638
|
+
if (strategy === "preserve") {
|
|
1639
|
+
if (destExists) {
|
|
1640
|
+
const currentHash = await hashFile(dest);
|
|
1641
|
+
return { wrote: false, action: "preserve", hashAfter: currentHash };
|
|
1642
|
+
}
|
|
1643
|
+
if (!dryRun) {
|
|
1644
|
+
await ensureDir(dirname(dest));
|
|
1645
|
+
await writeFile(dest, srcBuf);
|
|
1646
|
+
}
|
|
1647
|
+
return { wrote: true, action: "create", hashAfter: sha256(srcBuf) };
|
|
1648
|
+
}
|
|
1649
|
+
if (strategy === "overwrite") {
|
|
1650
|
+
const srcHash = sha256(srcBuf);
|
|
1651
|
+
if (destExists && !force) {
|
|
1652
|
+
const destHash = await hashFile(dest);
|
|
1653
|
+
if (destHash === srcHash) {
|
|
1654
|
+
return { wrote: false, action: "unchanged", hashAfter: destHash };
|
|
1655
|
+
}
|
|
1656
|
+
if (prev && prev.hashAfter !== destHash) {
|
|
1657
|
+
return {
|
|
1658
|
+
wrote: false,
|
|
1659
|
+
action: "user-edit",
|
|
1660
|
+
hashAfter: destHash
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
if (!dryRun) {
|
|
1665
|
+
await ensureDir(dirname(dest));
|
|
1666
|
+
await writeFile(dest, srcBuf);
|
|
1667
|
+
}
|
|
1668
|
+
return {
|
|
1669
|
+
wrote: true,
|
|
1670
|
+
action: destExists ? "update" : "create",
|
|
1671
|
+
hashAfter: srcHash
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
if (strategy === "merge-json-deep") {
|
|
1675
|
+
const srcJson = parseJsonOrThrow(srcBuf.toString("utf8"), "src");
|
|
1676
|
+
let merged = srcJson;
|
|
1677
|
+
if (destExists) {
|
|
1678
|
+
const destRaw = await readFile(dest, "utf8");
|
|
1679
|
+
const destJson = parseJsonOrThrow(destRaw, "dest");
|
|
1680
|
+
merged = deepMerge(destJson, srcJson);
|
|
1681
|
+
}
|
|
1682
|
+
const out = `${JSON.stringify(merged, null, 2)}
|
|
1683
|
+
`;
|
|
1684
|
+
const outBuf = Buffer.from(out, "utf8");
|
|
1685
|
+
const outHash = sha256(outBuf);
|
|
1686
|
+
if (destExists) {
|
|
1687
|
+
const destHash = await hashFile(dest);
|
|
1688
|
+
if (destHash === outHash) {
|
|
1689
|
+
return { wrote: false, action: "unchanged", hashAfter: destHash };
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
if (!dryRun) {
|
|
1693
|
+
await ensureDir(dirname(dest));
|
|
1694
|
+
await writeFile(dest, outBuf);
|
|
1695
|
+
}
|
|
1696
|
+
return {
|
|
1697
|
+
wrote: true,
|
|
1698
|
+
action: destExists ? "merge" : "create",
|
|
1699
|
+
hashAfter: outHash
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
if (strategy === "append") {
|
|
1703
|
+
const srcText = srcBuf.toString("utf8");
|
|
1704
|
+
if (destExists) {
|
|
1705
|
+
const destText = await readFile(dest, "utf8");
|
|
1706
|
+
if (destText.includes(srcText)) {
|
|
1707
|
+
return {
|
|
1708
|
+
wrote: false,
|
|
1709
|
+
action: "unchanged",
|
|
1710
|
+
hashAfter: sha256(Buffer.from(destText, "utf8"))
|
|
1711
|
+
};
|
|
1712
|
+
}
|
|
1713
|
+
const out = `${destText.endsWith("\n") ? destText : `${destText}
|
|
1714
|
+
`}${srcText}`;
|
|
1715
|
+
const outBuf = Buffer.from(out, "utf8");
|
|
1716
|
+
if (!dryRun) {
|
|
1717
|
+
await ensureDir(dirname(dest));
|
|
1718
|
+
await writeFile(dest, outBuf);
|
|
1719
|
+
}
|
|
1720
|
+
return { wrote: true, action: "append", hashAfter: sha256(outBuf) };
|
|
1721
|
+
}
|
|
1722
|
+
if (!dryRun) {
|
|
1723
|
+
await ensureDir(dirname(dest));
|
|
1724
|
+
await writeFile(dest, srcBuf);
|
|
1725
|
+
}
|
|
1726
|
+
return { wrote: true, action: "create", hashAfter: sha256(srcBuf) };
|
|
1727
|
+
}
|
|
1728
|
+
throw new Error(`Unknown strategy: ${strategy}`);
|
|
1729
|
+
}
|
|
1730
|
+
function deepMerge(target, source) {
|
|
1731
|
+
if (target && source && typeof target === "object" && typeof source === "object" && !Array.isArray(target) && !Array.isArray(source)) {
|
|
1732
|
+
const out = { ...target };
|
|
1733
|
+
for (const [k, v] of Object.entries(source)) {
|
|
1734
|
+
out[k] = deepMerge(target[k], v);
|
|
1735
|
+
}
|
|
1736
|
+
return out;
|
|
1737
|
+
}
|
|
1738
|
+
if (Array.isArray(target) && Array.isArray(source)) {
|
|
1739
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1740
|
+
const out = [];
|
|
1741
|
+
for (const item of [...target, ...source]) {
|
|
1742
|
+
const key = JSON.stringify(item);
|
|
1743
|
+
if (seen.has(key)) continue;
|
|
1744
|
+
seen.add(key);
|
|
1745
|
+
out.push(item);
|
|
1746
|
+
}
|
|
1747
|
+
return out;
|
|
1748
|
+
}
|
|
1749
|
+
return source ?? target;
|
|
1750
|
+
}
|
|
1751
|
+
function parseJsonOrThrow(raw, side) {
|
|
1752
|
+
try {
|
|
1753
|
+
return JSON.parse(raw);
|
|
1754
|
+
} catch (err) {
|
|
1755
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1756
|
+
throw new Error(`merge-json-deep ${side} not valid JSON: ${msg}`);
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
function sha256(buf) {
|
|
1760
|
+
return createHash("sha256").update(buf).digest("hex");
|
|
1761
|
+
}
|
|
1762
|
+
async function hashFile(path) {
|
|
1763
|
+
const buf = await readFile(path);
|
|
1764
|
+
return sha256(buf);
|
|
1765
|
+
}
|
|
1766
|
+
async function fileExists(path) {
|
|
1767
|
+
try {
|
|
1768
|
+
await stat(path);
|
|
1769
|
+
return true;
|
|
1770
|
+
} catch {
|
|
1771
|
+
return false;
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
async function ensureDir(dir) {
|
|
1775
|
+
try {
|
|
1776
|
+
await stat(dir);
|
|
1777
|
+
} catch {
|
|
1778
|
+
await mkdir(dir, { recursive: true });
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
function ledgerPathFor2(profileName) {
|
|
1782
|
+
return join(homedir(), ".agentproto", "profiles", `${profileName}.json`);
|
|
1783
|
+
}
|
|
1784
|
+
async function loadLedger2(path) {
|
|
1785
|
+
try {
|
|
1786
|
+
const raw = await readFile(path, "utf8");
|
|
1787
|
+
return JSON.parse(raw);
|
|
1788
|
+
} catch {
|
|
1789
|
+
return null;
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
async function saveLedger2(path, ledger) {
|
|
1793
|
+
await ensureDir(dirname(path));
|
|
1794
|
+
await writeFile(path, `${JSON.stringify(ledger, null, 2)}
|
|
1795
|
+
`);
|
|
1796
|
+
}
|
|
1797
|
+
async function readProfileAlias(profileName) {
|
|
1798
|
+
const base = process.env["AGENTPROTO_HOME"] ?? join(homedir(), ".agentproto");
|
|
1799
|
+
const path = join(base, "config.json");
|
|
1800
|
+
try {
|
|
1801
|
+
const raw = await readFile(path, "utf8");
|
|
1802
|
+
const parsed = JSON.parse(raw);
|
|
1803
|
+
const aliased = parsed.profileAliases?.[profileName];
|
|
1804
|
+
return typeof aliased === "string" ? aliased : null;
|
|
1805
|
+
} catch {
|
|
1806
|
+
return null;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1508
1809
|
|
|
1509
1810
|
// src/commands/install.ts
|
|
1510
1811
|
async function runInstall(args) {
|
|
1812
|
+
const slugPeek = args.find((a) => !a.startsWith("-"));
|
|
1813
|
+
if (slugPeek?.startsWith("runtime-profile/")) {
|
|
1814
|
+
return runInstallProfile(
|
|
1815
|
+
slugPeek,
|
|
1816
|
+
args.filter((a) => a !== slugPeek)
|
|
1817
|
+
);
|
|
1818
|
+
}
|
|
1511
1819
|
const { values, positionals } = parseArgs({
|
|
1512
1820
|
args: [...args],
|
|
1513
1821
|
allowPositionals: true,
|
|
@@ -1800,7 +2108,7 @@ async function runDownloadInstaller(opts) {
|
|
|
1800
2108
|
}
|
|
1801
2109
|
if (extractCode !== 0) return extractCode;
|
|
1802
2110
|
const srcBin = join(extractDir, opts.extractBin);
|
|
1803
|
-
const binDir = process.env["AGENTPROTO_BIN_DIR"] ?? join(
|
|
2111
|
+
const binDir = process.env["AGENTPROTO_BIN_DIR"] ?? join(homedir5(), ".local", "bin");
|
|
1804
2112
|
await mkdir(binDir, { recursive: true });
|
|
1805
2113
|
const destBin = join(binDir, opts.extractBin.split("/").pop());
|
|
1806
2114
|
const cpCode = await spawnInherit("cp", ["-p", srcBin, destBin]);
|
|
@@ -1826,19 +2134,19 @@ async function runDownloadInstaller(opts) {
|
|
|
1826
2134
|
});
|
|
1827
2135
|
}
|
|
1828
2136
|
}
|
|
1829
|
-
function
|
|
2137
|
+
function homedir5() {
|
|
1830
2138
|
return process.env["HOME"] ?? process.env["USERPROFILE"] ?? "/";
|
|
1831
2139
|
}
|
|
1832
2140
|
function spawnInherit(cmd, argv) {
|
|
1833
|
-
return new Promise((
|
|
2141
|
+
return new Promise((resolve9, reject) => {
|
|
1834
2142
|
const child = spawn(cmd, argv, { stdio: "inherit" });
|
|
1835
2143
|
child.once("error", reject);
|
|
1836
|
-
child.once("exit", (code) =>
|
|
2144
|
+
child.once("exit", (code) => resolve9(code ?? 0));
|
|
1837
2145
|
});
|
|
1838
2146
|
}
|
|
1839
2147
|
async function runVersionCheck(check) {
|
|
1840
2148
|
if (!check) return { ok: false, message: "no version_check declared" };
|
|
1841
|
-
return new Promise((
|
|
2149
|
+
return new Promise((resolve9) => {
|
|
1842
2150
|
const child = spawn("bash", ["-lc", check.cmd], {
|
|
1843
2151
|
stdio: ["ignore", "pipe", "ignore"]
|
|
1844
2152
|
});
|
|
@@ -1846,154 +2154,1382 @@ async function runVersionCheck(check) {
|
|
|
1846
2154
|
child.stdout.on("data", (c) => {
|
|
1847
2155
|
buf += c.toString("utf8");
|
|
1848
2156
|
});
|
|
1849
|
-
child.once("error", () =>
|
|
1850
|
-
child.once("exit", (code) => {
|
|
1851
|
-
if (code !== 0) {
|
|
1852
|
-
|
|
1853
|
-
return;
|
|
1854
|
-
}
|
|
1855
|
-
const re = new RegExp(check.parse);
|
|
1856
|
-
const m = buf.match(re);
|
|
1857
|
-
if (!m || !m[1]) {
|
|
1858
|
-
|
|
2157
|
+
child.once("error", () => resolve9({ ok: false, message: "check failed" }));
|
|
2158
|
+
child.once("exit", (code) => {
|
|
2159
|
+
if (code !== 0) {
|
|
2160
|
+
resolve9({ ok: false, message: `check exited ${code}` });
|
|
2161
|
+
return;
|
|
2162
|
+
}
|
|
2163
|
+
const re = new RegExp(check.parse);
|
|
2164
|
+
const m = buf.match(re);
|
|
2165
|
+
if (!m || !m[1]) {
|
|
2166
|
+
resolve9({ ok: false, message: "could not parse version" });
|
|
2167
|
+
return;
|
|
2168
|
+
}
|
|
2169
|
+
resolve9({ ok: true, message: `version ${m[1]}` });
|
|
2170
|
+
});
|
|
2171
|
+
});
|
|
2172
|
+
}
|
|
2173
|
+
var PLUGIN_MANIFEST_SCHEMA = "agentproto/plugin/v1";
|
|
2174
|
+
var AdapterEntrySchema = z.object({
|
|
2175
|
+
/** The `kind` string the manifest's substrate/dispatcher/etc. block uses. */
|
|
2176
|
+
kind: z.string().min(1),
|
|
2177
|
+
/**
|
|
2178
|
+
* Path to the entry module, relative to the plugin package root.
|
|
2179
|
+
* Resolved via the plugin's `package.json#main`/`exports` (i.e. you
|
|
2180
|
+
* can use a subpath like `./dist/substrates.mjs` or an export name
|
|
2181
|
+
* like `.` if the plugin re-exports everything from its root).
|
|
2182
|
+
*/
|
|
2183
|
+
entry: z.string().min(1),
|
|
2184
|
+
/** Named export inside `entry` — the factory function. */
|
|
2185
|
+
export: z.string().min(1),
|
|
2186
|
+
/** Free-form description; shown by `agentproto plugins show`. */
|
|
2187
|
+
description: z.string().optional()
|
|
2188
|
+
}).loose();
|
|
2189
|
+
var SubstrateEntrySchema = AdapterEntrySchema.extend({
|
|
2190
|
+
/**
|
|
2191
|
+
* Free-form capability tags. Not gated by the kernel yet, but
|
|
2192
|
+
* surfaced by `agentproto plugins show` so users can see what the
|
|
2193
|
+
* substrate claims to support (mentions, reactions, visibility, …).
|
|
2194
|
+
*/
|
|
2195
|
+
capabilities: z.array(z.string()).optional()
|
|
2196
|
+
});
|
|
2197
|
+
var PluginManifestSchema = z.object({
|
|
2198
|
+
schema: z.literal(PLUGIN_MANIFEST_SCHEMA),
|
|
2199
|
+
substrates: z.array(SubstrateEntrySchema).default([]),
|
|
2200
|
+
dispatchers: z.array(AdapterEntrySchema).default([]),
|
|
2201
|
+
executors: z.array(AdapterEntrySchema).default([]),
|
|
2202
|
+
stateStores: z.array(AdapterEntrySchema).default([])
|
|
2203
|
+
}).loose();
|
|
2204
|
+
|
|
2205
|
+
// src/registry/runtime.ts
|
|
2206
|
+
var substrates = /* @__PURE__ */ new Map();
|
|
2207
|
+
var dispatchers = /* @__PURE__ */ new Map();
|
|
2208
|
+
var executors = /* @__PURE__ */ new Map();
|
|
2209
|
+
var stateStores = /* @__PURE__ */ new Map();
|
|
2210
|
+
function registerSubstrate(kind, factory) {
|
|
2211
|
+
substrates.set(kind, factory);
|
|
2212
|
+
}
|
|
2213
|
+
function registerDispatcher(kind, factory) {
|
|
2214
|
+
dispatchers.set(kind, factory);
|
|
2215
|
+
}
|
|
2216
|
+
function registerExecutor(kind, factory) {
|
|
2217
|
+
executors.set(kind, factory);
|
|
2218
|
+
}
|
|
2219
|
+
function registerStateStore(kind, factory) {
|
|
2220
|
+
stateStores.set(kind, factory);
|
|
2221
|
+
}
|
|
2222
|
+
function getSubstrateFactory(kind) {
|
|
2223
|
+
return substrates.get(kind);
|
|
2224
|
+
}
|
|
2225
|
+
function getDispatcherFactory(kind) {
|
|
2226
|
+
return dispatchers.get(kind);
|
|
2227
|
+
}
|
|
2228
|
+
function getExecutorFactory(kind) {
|
|
2229
|
+
return executors.get(kind);
|
|
2230
|
+
}
|
|
2231
|
+
function getStateStoreFactory(kind) {
|
|
2232
|
+
return stateStores.get(kind);
|
|
2233
|
+
}
|
|
2234
|
+
function listRegisteredKinds() {
|
|
2235
|
+
return {
|
|
2236
|
+
substrates: [...substrates.keys()],
|
|
2237
|
+
dispatchers: [...dispatchers.keys()],
|
|
2238
|
+
executors: [...executors.keys()],
|
|
2239
|
+
stateStores: [...stateStores.keys()]
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
// src/registry/manifest-loader.ts
|
|
2244
|
+
async function loadPluginFromManifest(pluginId) {
|
|
2245
|
+
const result = await readPluginManifest(pluginId);
|
|
2246
|
+
if (!result) return null;
|
|
2247
|
+
const { manifest, packageRoot } = result;
|
|
2248
|
+
for (const sub of manifest.substrates) {
|
|
2249
|
+
const factory = await importFactory(
|
|
2250
|
+
packageRoot,
|
|
2251
|
+
pluginId,
|
|
2252
|
+
sub.entry,
|
|
2253
|
+
sub.export
|
|
2254
|
+
);
|
|
2255
|
+
registerSubstrate(sub.kind, factory);
|
|
2256
|
+
}
|
|
2257
|
+
for (const dis of manifest.dispatchers) {
|
|
2258
|
+
const factory = await importFactory(
|
|
2259
|
+
packageRoot,
|
|
2260
|
+
pluginId,
|
|
2261
|
+
dis.entry,
|
|
2262
|
+
dis.export
|
|
2263
|
+
);
|
|
2264
|
+
registerDispatcher(dis.kind, factory);
|
|
2265
|
+
}
|
|
2266
|
+
for (const exe of manifest.executors) {
|
|
2267
|
+
const factory = await importFactory(
|
|
2268
|
+
packageRoot,
|
|
2269
|
+
pluginId,
|
|
2270
|
+
exe.entry,
|
|
2271
|
+
exe.export
|
|
2272
|
+
);
|
|
2273
|
+
registerExecutor(exe.kind, factory);
|
|
2274
|
+
}
|
|
2275
|
+
for (const st of manifest.stateStores) {
|
|
2276
|
+
const factory = await importFactory(
|
|
2277
|
+
packageRoot,
|
|
2278
|
+
pluginId,
|
|
2279
|
+
st.entry,
|
|
2280
|
+
st.export
|
|
2281
|
+
);
|
|
2282
|
+
registerStateStore(st.kind, factory);
|
|
2283
|
+
}
|
|
2284
|
+
return manifest;
|
|
2285
|
+
}
|
|
2286
|
+
async function readPluginManifest(pluginId) {
|
|
2287
|
+
const packageJsonPath = resolvePluginPackageJson(pluginId);
|
|
2288
|
+
if (!packageJsonPath) return null;
|
|
2289
|
+
const packageRoot = dirname(packageJsonPath);
|
|
2290
|
+
const standalonePath = join(packageRoot, "agentproto.json");
|
|
2291
|
+
const standalone = await readJsonIfExists(standalonePath);
|
|
2292
|
+
if (standalone !== void 0) {
|
|
2293
|
+
return {
|
|
2294
|
+
manifest: PluginManifestSchema.parse(standalone),
|
|
2295
|
+
packageRoot
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
const pkgJson = await readJsonIfExists(packageJsonPath);
|
|
2299
|
+
if (pkgJson && typeof pkgJson === "object" && pkgJson.agentproto) {
|
|
2300
|
+
return {
|
|
2301
|
+
manifest: PluginManifestSchema.parse(pkgJson.agentproto),
|
|
2302
|
+
packageRoot
|
|
2303
|
+
};
|
|
2304
|
+
}
|
|
2305
|
+
return null;
|
|
2306
|
+
}
|
|
2307
|
+
async function importFactory(packageRoot, pluginId, entry, exportName) {
|
|
2308
|
+
const abs = entry.startsWith(".") ? join(packageRoot, entry) : entry;
|
|
2309
|
+
const url = entry.startsWith(".") ? pathToFileURL(abs).href : entry;
|
|
2310
|
+
const mod = await import(url);
|
|
2311
|
+
const exported = mod[exportName];
|
|
2312
|
+
if (typeof exported !== "function") {
|
|
2313
|
+
throw new Error(
|
|
2314
|
+
`agentproto plugin '${pluginId}': manifest entry '${entry}' has no callable export '${exportName}' (got ${typeof exported}).`
|
|
2315
|
+
);
|
|
2316
|
+
}
|
|
2317
|
+
return exported;
|
|
2318
|
+
}
|
|
2319
|
+
function resolvePluginPackageJson(pluginId) {
|
|
2320
|
+
const candidates = [
|
|
2321
|
+
import.meta.url,
|
|
2322
|
+
pathToFileURL(join(process.cwd(), "package.json")).href
|
|
2323
|
+
];
|
|
2324
|
+
for (const root of candidates) {
|
|
2325
|
+
try {
|
|
2326
|
+
return createRequire(root).resolve(`${pluginId}/package.json`);
|
|
2327
|
+
} catch {
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
return null;
|
|
2331
|
+
}
|
|
2332
|
+
async function readJsonIfExists(path) {
|
|
2333
|
+
try {
|
|
2334
|
+
const raw = await readFile(path, "utf8");
|
|
2335
|
+
return JSON.parse(raw);
|
|
2336
|
+
} catch (err) {
|
|
2337
|
+
if (err.code === "ENOENT") return void 0;
|
|
2338
|
+
throw err;
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2342
|
+
// src/commands/plugins.ts
|
|
2343
|
+
var USAGE3 = `agentproto plugins \u2014 manage runtime plugins
|
|
2344
|
+
|
|
2345
|
+
Usage:
|
|
2346
|
+
agentproto plugins list Show enabled plugins + adapters
|
|
2347
|
+
agentproto plugins show <pkg> Print a plugin's manifest
|
|
2348
|
+
agentproto plugins install <pkg> npm i -g + add to config
|
|
2349
|
+
agentproto plugins uninstall <pkg> Remove from config (+ npm rm)
|
|
2350
|
+
agentproto plugins enable <pkg> Add to config (assume installed)
|
|
2351
|
+
agentproto plugins disable <pkg> Remove from config (keep installed)
|
|
2352
|
+
agentproto plugins --help
|
|
2353
|
+
|
|
2354
|
+
Plugin list lives in ~/.agentproto/config.json under \`plugins[]\`.
|
|
2355
|
+
`;
|
|
2356
|
+
async function runPlugins(args) {
|
|
2357
|
+
const sub = args[0];
|
|
2358
|
+
const rest = args.slice(1);
|
|
2359
|
+
switch (sub) {
|
|
2360
|
+
case void 0:
|
|
2361
|
+
case "-h":
|
|
2362
|
+
case "--help":
|
|
2363
|
+
process.stdout.write(USAGE3);
|
|
2364
|
+
return 0;
|
|
2365
|
+
case "list":
|
|
2366
|
+
return runList(rest);
|
|
2367
|
+
case "show":
|
|
2368
|
+
return runShow2(rest);
|
|
2369
|
+
case "install":
|
|
2370
|
+
return runInstall2(rest);
|
|
2371
|
+
case "uninstall":
|
|
2372
|
+
return runUninstall(rest);
|
|
2373
|
+
case "enable":
|
|
2374
|
+
return runEnable(rest);
|
|
2375
|
+
case "disable":
|
|
2376
|
+
return runDisable(rest);
|
|
2377
|
+
default:
|
|
2378
|
+
process.stderr.write(
|
|
2379
|
+
`agentproto plugins: unknown subcommand '${sub}'.
|
|
2380
|
+
|
|
2381
|
+
${USAGE3}`
|
|
2382
|
+
);
|
|
2383
|
+
return 2;
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
async function runList(args) {
|
|
2387
|
+
const { values } = parseArgs({
|
|
2388
|
+
args: [...args],
|
|
2389
|
+
options: { json: { type: "boolean" } },
|
|
2390
|
+
strict: true
|
|
2391
|
+
});
|
|
2392
|
+
const cfg = await loadConfig2();
|
|
2393
|
+
const plugins = cfg.plugins ?? [];
|
|
2394
|
+
if (plugins.length === 0) {
|
|
2395
|
+
if (values.json) process.stdout.write("[]\n");
|
|
2396
|
+
else
|
|
2397
|
+
process.stdout.write(
|
|
2398
|
+
"agentproto plugins: no plugins enabled. Try `agentproto plugins install <pkg>`.\n"
|
|
2399
|
+
);
|
|
2400
|
+
return 0;
|
|
2401
|
+
}
|
|
2402
|
+
const entries = [];
|
|
2403
|
+
for (const id of plugins) {
|
|
2404
|
+
try {
|
|
2405
|
+
const result = await readPluginManifest(id);
|
|
2406
|
+
entries.push({ id, manifest: result?.manifest ?? null });
|
|
2407
|
+
} catch (err) {
|
|
2408
|
+
entries.push({
|
|
2409
|
+
id,
|
|
2410
|
+
manifest: null,
|
|
2411
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2412
|
+
});
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
if (values.json) {
|
|
2416
|
+
process.stdout.write(`${JSON.stringify(entries, null, 2)}
|
|
2417
|
+
`);
|
|
2418
|
+
return 0;
|
|
2419
|
+
}
|
|
2420
|
+
for (const e of entries) {
|
|
2421
|
+
process.stdout.write(`\u2022 ${e.id}
|
|
2422
|
+
`);
|
|
2423
|
+
if (e.error) {
|
|
2424
|
+
process.stdout.write(` error: ${e.error}
|
|
2425
|
+
`);
|
|
2426
|
+
continue;
|
|
2427
|
+
}
|
|
2428
|
+
if (!e.manifest) {
|
|
2429
|
+
process.stdout.write(` (no manifest \u2014 legacy side-effect plugin)
|
|
2430
|
+
`);
|
|
2431
|
+
continue;
|
|
2432
|
+
}
|
|
2433
|
+
printAdapterSummary("substrates", e.manifest.substrates);
|
|
2434
|
+
printAdapterSummary("dispatchers", e.manifest.dispatchers);
|
|
2435
|
+
printAdapterSummary("executors", e.manifest.executors);
|
|
2436
|
+
printAdapterSummary("state stores", e.manifest.stateStores);
|
|
2437
|
+
}
|
|
2438
|
+
return 0;
|
|
2439
|
+
}
|
|
2440
|
+
function printAdapterSummary(label, entries) {
|
|
2441
|
+
if (entries.length === 0) return;
|
|
2442
|
+
const kinds = entries.map((e) => e.kind).join(", ");
|
|
2443
|
+
process.stdout.write(` ${label}: ${kinds}
|
|
2444
|
+
`);
|
|
2445
|
+
}
|
|
2446
|
+
async function runShow2(args) {
|
|
2447
|
+
const { values, positionals } = parseArgs({
|
|
2448
|
+
args: [...args],
|
|
2449
|
+
allowPositionals: true,
|
|
2450
|
+
options: { json: { type: "boolean" } },
|
|
2451
|
+
strict: true
|
|
2452
|
+
});
|
|
2453
|
+
const pkg = positionals[0];
|
|
2454
|
+
if (!pkg) {
|
|
2455
|
+
process.stderr.write(
|
|
2456
|
+
"agentproto plugins show: missing <pkg>. Try: agentproto plugins show @guilde/agentproto-bridge\n"
|
|
2457
|
+
);
|
|
2458
|
+
return 2;
|
|
2459
|
+
}
|
|
2460
|
+
let result;
|
|
2461
|
+
try {
|
|
2462
|
+
result = await readPluginManifest(pkg);
|
|
2463
|
+
} catch (err) {
|
|
2464
|
+
process.stderr.write(
|
|
2465
|
+
`agentproto plugins show: ${err instanceof Error ? err.message : String(err)}
|
|
2466
|
+
`
|
|
2467
|
+
);
|
|
2468
|
+
return 1;
|
|
2469
|
+
}
|
|
2470
|
+
if (!result) {
|
|
2471
|
+
process.stderr.write(
|
|
2472
|
+
`agentproto plugins show: '${pkg}' is not installed (or doesn't declare an agentproto manifest).
|
|
2473
|
+
`
|
|
2474
|
+
);
|
|
2475
|
+
return 1;
|
|
2476
|
+
}
|
|
2477
|
+
if (values.json) {
|
|
2478
|
+
process.stdout.write(`${JSON.stringify(result.manifest, null, 2)}
|
|
2479
|
+
`);
|
|
2480
|
+
return 0;
|
|
2481
|
+
}
|
|
2482
|
+
const m = result.manifest;
|
|
2483
|
+
process.stdout.write(`${pkg}
|
|
2484
|
+
`);
|
|
2485
|
+
process.stdout.write(` schema: ${m.schema}
|
|
2486
|
+
`);
|
|
2487
|
+
printAdapterDetail("substrates", m.substrates);
|
|
2488
|
+
printAdapterDetail("dispatchers", m.dispatchers);
|
|
2489
|
+
printAdapterDetail("executors", m.executors);
|
|
2490
|
+
printAdapterDetail("state stores", m.stateStores);
|
|
2491
|
+
return 0;
|
|
2492
|
+
}
|
|
2493
|
+
function printAdapterDetail(label, entries) {
|
|
2494
|
+
if (entries.length === 0) return;
|
|
2495
|
+
process.stdout.write(` ${label}:
|
|
2496
|
+
`);
|
|
2497
|
+
for (const e of entries) {
|
|
2498
|
+
process.stdout.write(` \u2022 kind: ${e.kind}
|
|
2499
|
+
`);
|
|
2500
|
+
process.stdout.write(` entry: ${e.entry} \u2192 ${e.export}
|
|
2501
|
+
`);
|
|
2502
|
+
const caps = "capabilities" in e ? e.capabilities : void 0;
|
|
2503
|
+
if (Array.isArray(caps) && caps.length > 0) {
|
|
2504
|
+
process.stdout.write(` capabilities: ${caps.join(", ")}
|
|
2505
|
+
`);
|
|
2506
|
+
}
|
|
2507
|
+
if (e.description) {
|
|
2508
|
+
process.stdout.write(` ${e.description}
|
|
2509
|
+
`);
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
async function runInstall2(args) {
|
|
2514
|
+
const { values, positionals } = parseArgs({
|
|
2515
|
+
args: [...args],
|
|
2516
|
+
allowPositionals: true,
|
|
2517
|
+
options: {
|
|
2518
|
+
local: { type: "boolean" },
|
|
2519
|
+
"skip-npm": { type: "boolean" }
|
|
2520
|
+
},
|
|
2521
|
+
strict: true
|
|
2522
|
+
});
|
|
2523
|
+
const pkg = positionals[0];
|
|
2524
|
+
if (!pkg) {
|
|
2525
|
+
process.stderr.write("agentproto plugins install: missing <pkg>.\n");
|
|
2526
|
+
return 2;
|
|
2527
|
+
}
|
|
2528
|
+
if (!values["skip-npm"]) {
|
|
2529
|
+
const code = await spawnInherit2("npm", [
|
|
2530
|
+
"install",
|
|
2531
|
+
values.local ? "" : "-g",
|
|
2532
|
+
pkg
|
|
2533
|
+
].filter(Boolean));
|
|
2534
|
+
if (code !== 0) {
|
|
2535
|
+
process.stderr.write(
|
|
2536
|
+
`agentproto plugins install: npm failed with exit ${code}; config not modified.
|
|
2537
|
+
`
|
|
2538
|
+
);
|
|
2539
|
+
return code;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
await addToConfig(pkg);
|
|
2543
|
+
process.stdout.write(`agentproto plugins: enabled '${pkg}'.
|
|
2544
|
+
`);
|
|
2545
|
+
return 0;
|
|
2546
|
+
}
|
|
2547
|
+
async function runUninstall(args) {
|
|
2548
|
+
const { values, positionals } = parseArgs({
|
|
2549
|
+
args: [...args],
|
|
2550
|
+
allowPositionals: true,
|
|
2551
|
+
options: {
|
|
2552
|
+
local: { type: "boolean" },
|
|
2553
|
+
"skip-npm": { type: "boolean" }
|
|
2554
|
+
},
|
|
2555
|
+
strict: true
|
|
2556
|
+
});
|
|
2557
|
+
const pkg = positionals[0];
|
|
2558
|
+
if (!pkg) {
|
|
2559
|
+
process.stderr.write("agentproto plugins uninstall: missing <pkg>.\n");
|
|
2560
|
+
return 2;
|
|
2561
|
+
}
|
|
2562
|
+
await removeFromConfig(pkg);
|
|
2563
|
+
if (!values["skip-npm"]) {
|
|
2564
|
+
const code = await spawnInherit2("npm", [
|
|
2565
|
+
"uninstall",
|
|
2566
|
+
values.local ? "" : "-g",
|
|
2567
|
+
pkg
|
|
2568
|
+
].filter(Boolean));
|
|
2569
|
+
if (code !== 0) {
|
|
2570
|
+
process.stderr.write(
|
|
2571
|
+
`agentproto plugins uninstall: removed from config but npm uninstall failed with exit ${code}.
|
|
2572
|
+
`
|
|
2573
|
+
);
|
|
2574
|
+
return code;
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
process.stdout.write(`agentproto plugins: disabled '${pkg}'.
|
|
2578
|
+
`);
|
|
2579
|
+
return 0;
|
|
2580
|
+
}
|
|
2581
|
+
async function runEnable(args) {
|
|
2582
|
+
const [pkg] = args;
|
|
2583
|
+
if (!pkg) {
|
|
2584
|
+
process.stderr.write("agentproto plugins enable: missing <pkg>.\n");
|
|
2585
|
+
return 2;
|
|
2586
|
+
}
|
|
2587
|
+
await addToConfig(pkg);
|
|
2588
|
+
process.stdout.write(`agentproto plugins: enabled '${pkg}'.
|
|
2589
|
+
`);
|
|
2590
|
+
return 0;
|
|
2591
|
+
}
|
|
2592
|
+
async function runDisable(args) {
|
|
2593
|
+
const [pkg] = args;
|
|
2594
|
+
if (!pkg) {
|
|
2595
|
+
process.stderr.write("agentproto plugins disable: missing <pkg>.\n");
|
|
2596
|
+
return 2;
|
|
2597
|
+
}
|
|
2598
|
+
await removeFromConfig(pkg);
|
|
2599
|
+
process.stdout.write(`agentproto plugins: disabled '${pkg}'.
|
|
2600
|
+
`);
|
|
2601
|
+
return 0;
|
|
2602
|
+
}
|
|
2603
|
+
function configPath() {
|
|
2604
|
+
const base = process.env["AGENTPROTO_HOME"] ?? join(homedir(), ".agentproto");
|
|
2605
|
+
return join(base, "config.json");
|
|
2606
|
+
}
|
|
2607
|
+
async function loadConfig2() {
|
|
2608
|
+
try {
|
|
2609
|
+
const raw = await readFile(configPath(), "utf8");
|
|
2610
|
+
return JSON.parse(raw);
|
|
2611
|
+
} catch (err) {
|
|
2612
|
+
if (err.code === "ENOENT") return {};
|
|
2613
|
+
throw err;
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
async function saveConfig2(cfg) {
|
|
2617
|
+
const path = configPath();
|
|
2618
|
+
await mkdir(dirname(path), { recursive: true });
|
|
2619
|
+
await writeFile(path, `${JSON.stringify(cfg, null, 2)}
|
|
2620
|
+
`);
|
|
2621
|
+
}
|
|
2622
|
+
async function addToConfig(pkg) {
|
|
2623
|
+
const cfg = await loadConfig2();
|
|
2624
|
+
const plugins = cfg.plugins ?? [];
|
|
2625
|
+
if (!plugins.includes(pkg)) plugins.push(pkg);
|
|
2626
|
+
cfg.plugins = plugins;
|
|
2627
|
+
await saveConfig2(cfg);
|
|
2628
|
+
}
|
|
2629
|
+
async function removeFromConfig(pkg) {
|
|
2630
|
+
const cfg = await loadConfig2();
|
|
2631
|
+
cfg.plugins = (cfg.plugins ?? []).filter((p) => p !== pkg);
|
|
2632
|
+
await saveConfig2(cfg);
|
|
2633
|
+
}
|
|
2634
|
+
function spawnInherit2(cmd, argv) {
|
|
2635
|
+
return new Promise((resolveP, rejectP) => {
|
|
2636
|
+
const child = spawn(cmd, argv, { stdio: "inherit" });
|
|
2637
|
+
child.once("error", rejectP);
|
|
2638
|
+
child.once("exit", (code) => resolveP(code ?? 0));
|
|
2639
|
+
});
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
// src/util/stdin.ts
|
|
2643
|
+
async function readStdinIfPiped() {
|
|
2644
|
+
if (process.stdin.isTTY) return null;
|
|
2645
|
+
const chunks = [];
|
|
2646
|
+
for await (const chunk of process.stdin) {
|
|
2647
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
2648
|
+
}
|
|
2649
|
+
const text3 = Buffer.concat(chunks).toString("utf8").trim();
|
|
2650
|
+
return text3.length > 0 ? text3 : null;
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2653
|
+
// src/commands/run.ts
|
|
2654
|
+
async function runRun(args) {
|
|
2655
|
+
const { values, positionals } = parseArgs({
|
|
2656
|
+
args: [...args],
|
|
2657
|
+
allowPositionals: true,
|
|
2658
|
+
strict: true,
|
|
2659
|
+
options: {
|
|
2660
|
+
cwd: { type: "string" },
|
|
2661
|
+
prompt: { type: "string", short: "p" },
|
|
2662
|
+
resume: { type: "string" },
|
|
2663
|
+
json: { type: "boolean" }
|
|
2664
|
+
}
|
|
2665
|
+
});
|
|
2666
|
+
const slug = positionals[0];
|
|
2667
|
+
if (!slug) {
|
|
2668
|
+
process.stderr.write(
|
|
2669
|
+
"agentproto run: missing adapter slug. Try: agentproto run claude-code\n"
|
|
2670
|
+
);
|
|
2671
|
+
return 2;
|
|
2672
|
+
}
|
|
2673
|
+
const cwd = values.cwd ? resolve(values.cwd) : process.cwd();
|
|
2674
|
+
const promptArg = values.prompt ?? await readStdinIfPiped();
|
|
2675
|
+
if (!promptArg) {
|
|
2676
|
+
process.stderr.write(
|
|
2677
|
+
"agentproto run: no prompt provided. Pass --prompt or pipe one over stdin.\n"
|
|
2678
|
+
);
|
|
2679
|
+
return 2;
|
|
2680
|
+
}
|
|
2681
|
+
const adapter = await resolveAdapter(slug);
|
|
2682
|
+
const runtime = createAgentCliRuntime(adapter.handle);
|
|
2683
|
+
const controller = new AbortController();
|
|
2684
|
+
const onSignal = (sig) => {
|
|
2685
|
+
process.stderr.write(`
|
|
2686
|
+
agentproto: received ${sig}, cancelling\u2026
|
|
2687
|
+
`);
|
|
2688
|
+
controller.abort();
|
|
2689
|
+
};
|
|
2690
|
+
process.once("SIGINT", onSignal);
|
|
2691
|
+
process.once("SIGTERM", onSignal);
|
|
2692
|
+
let session = null;
|
|
2693
|
+
try {
|
|
2694
|
+
session = await runtime.start({
|
|
2695
|
+
cwd,
|
|
2696
|
+
signal: controller.signal,
|
|
2697
|
+
resumeSessionId: values.resume
|
|
2698
|
+
});
|
|
2699
|
+
const printer = values.json ? printJson : printPretty;
|
|
2700
|
+
let exit = 0;
|
|
2701
|
+
for await (const ev of session.send(promptArg)) {
|
|
2702
|
+
printer(ev);
|
|
2703
|
+
if (ev.kind === "turn-end" && ev.reason !== "completed") exit = 1;
|
|
2704
|
+
if (ev.kind === "error") exit = 1;
|
|
2705
|
+
}
|
|
2706
|
+
return exit;
|
|
2707
|
+
} finally {
|
|
2708
|
+
process.off("SIGINT", onSignal);
|
|
2709
|
+
process.off("SIGTERM", onSignal);
|
|
2710
|
+
if (session) await session.close().catch(() => {
|
|
2711
|
+
});
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
function printJson(ev) {
|
|
2715
|
+
process.stdout.write(`${JSON.stringify(ev)}
|
|
2716
|
+
`);
|
|
2717
|
+
}
|
|
2718
|
+
function printPretty(ev) {
|
|
2719
|
+
switch (ev.kind) {
|
|
2720
|
+
case "text-delta":
|
|
2721
|
+
process.stdout.write(ev.text);
|
|
2722
|
+
break;
|
|
2723
|
+
case "thought":
|
|
2724
|
+
process.stderr.write(`\x1B[2m[thought] ${ev.text}\x1B[0m
|
|
2725
|
+
`);
|
|
2726
|
+
break;
|
|
2727
|
+
case "tool-call":
|
|
2728
|
+
process.stderr.write(`\x1B[36m[tool] ${ev.toolName}\x1B[0m
|
|
2729
|
+
`);
|
|
2730
|
+
break;
|
|
2731
|
+
case "tool-result":
|
|
2732
|
+
if (ev.isError)
|
|
2733
|
+
process.stderr.write(`\x1B[31m[tool-error]\x1B[0m
|
|
2734
|
+
`);
|
|
2735
|
+
break;
|
|
2736
|
+
case "agent-prompt":
|
|
2737
|
+
process.stderr.write(`\x1B[33m[agent-prompt: needs input]\x1B[0m
|
|
2738
|
+
`);
|
|
2739
|
+
break;
|
|
2740
|
+
case "turn-end":
|
|
2741
|
+
process.stdout.write(`
|
|
2742
|
+
\x1B[2m[turn-end: ${ev.reason}]\x1B[0m
|
|
2743
|
+
`);
|
|
2744
|
+
break;
|
|
2745
|
+
case "error": {
|
|
2746
|
+
const code = typeof ev.error.code === "number" ? ` (code ${ev.error.code})` : "";
|
|
2747
|
+
process.stderr.write(
|
|
2748
|
+
`\x1B[31m[error]${code} ${ev.error.message}\x1B[0m
|
|
2749
|
+
`
|
|
2750
|
+
);
|
|
2751
|
+
const data = ev.error.data;
|
|
2752
|
+
if (data && typeof data === "object") {
|
|
2753
|
+
const stderr = data.stderr;
|
|
2754
|
+
if (typeof stderr === "string" && stderr.trim()) {
|
|
2755
|
+
process.stderr.write(`\x1B[2m\u2500\u2500 child stderr \u2500\u2500
|
|
2756
|
+
${stderr}\x1B[0m
|
|
2757
|
+
`);
|
|
2758
|
+
}
|
|
2759
|
+
const rest = { ...data };
|
|
2760
|
+
delete rest.stderr;
|
|
2761
|
+
if (Object.keys(rest).length > 0) {
|
|
2762
|
+
process.stderr.write(
|
|
2763
|
+
`\x1B[2m\u2500\u2500 error.data \u2500\u2500
|
|
2764
|
+
${JSON.stringify(rest, null, 2)}\x1B[0m
|
|
2765
|
+
`
|
|
2766
|
+
);
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
break;
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
async function runTurn(ports, options = {}) {
|
|
2774
|
+
const historyLimit = options.historyLimit ?? 30;
|
|
2775
|
+
const cycleId = newCycleId();
|
|
2776
|
+
const cycleStart = Date.now();
|
|
2777
|
+
emit(ports.telemetry, {
|
|
2778
|
+
kind: "cycle.started",
|
|
2779
|
+
cycleId,
|
|
2780
|
+
at: nowIso(),
|
|
2781
|
+
...options.since !== void 0 ? { since: options.since } : {}
|
|
2782
|
+
});
|
|
2783
|
+
const readStart = Date.now();
|
|
2784
|
+
const all = await ports.substrate.read(options.since);
|
|
2785
|
+
const recentTurns = all.slice(-historyLimit);
|
|
2786
|
+
emit(ports.telemetry, {
|
|
2787
|
+
kind: "substrate.read",
|
|
2788
|
+
cycleId,
|
|
2789
|
+
at: nowIso(),
|
|
2790
|
+
substrateKind: ports.substrate.kind,
|
|
2791
|
+
turnCount: recentTurns.length,
|
|
2792
|
+
durationMs: Date.now() - readStart
|
|
2793
|
+
});
|
|
2794
|
+
const dispatchStart = Date.now();
|
|
2795
|
+
const selected = await ports.dispatcher.selectNext({
|
|
2796
|
+
recentTurns,
|
|
2797
|
+
participants: ports.participants
|
|
2798
|
+
});
|
|
2799
|
+
emit(ports.telemetry, {
|
|
2800
|
+
kind: "dispatch.decided",
|
|
2801
|
+
cycleId,
|
|
2802
|
+
at: nowIso(),
|
|
2803
|
+
dispatcherKind: ports.dispatcher.kind,
|
|
2804
|
+
selected,
|
|
2805
|
+
durationMs: Date.now() - dispatchStart
|
|
2806
|
+
});
|
|
2807
|
+
if (selected.length === 0) {
|
|
2808
|
+
await ports.lifecycle?.onIdle?.();
|
|
2809
|
+
emit(ports.telemetry, { kind: "cycle.idle", cycleId, at: nowIso() });
|
|
2810
|
+
emit(ports.telemetry, {
|
|
2811
|
+
kind: "cycle.finished",
|
|
2812
|
+
cycleId,
|
|
2813
|
+
at: nowIso(),
|
|
2814
|
+
outcome: "idle",
|
|
2815
|
+
turnsAppended: 0,
|
|
2816
|
+
durationMs: Date.now() - cycleStart
|
|
2817
|
+
});
|
|
2818
|
+
return { cycle: "idle", selected: [], turnsAppended: [] };
|
|
2819
|
+
}
|
|
2820
|
+
const triggerTurn = recentTurns[recentTurns.length - 1];
|
|
2821
|
+
if (!triggerTurn) {
|
|
2822
|
+
await ports.lifecycle?.onIdle?.();
|
|
2823
|
+
emit(ports.telemetry, { kind: "cycle.idle", cycleId, at: nowIso() });
|
|
2824
|
+
emit(ports.telemetry, {
|
|
2825
|
+
kind: "cycle.finished",
|
|
2826
|
+
cycleId,
|
|
2827
|
+
at: nowIso(),
|
|
2828
|
+
outcome: "idle",
|
|
2829
|
+
turnsAppended: 0,
|
|
2830
|
+
durationMs: Date.now() - cycleStart
|
|
2831
|
+
});
|
|
2832
|
+
return { cycle: "idle", selected: [], turnsAppended: [] };
|
|
2833
|
+
}
|
|
2834
|
+
const appended = [];
|
|
2835
|
+
for (const participantId of selected) {
|
|
2836
|
+
const participant = findParticipant(ports.participants, participantId);
|
|
2837
|
+
if (!participant) {
|
|
2838
|
+
continue;
|
|
2839
|
+
}
|
|
2840
|
+
const executor = ports.executors.get(participant.executor);
|
|
2841
|
+
if (!executor) {
|
|
2842
|
+
continue;
|
|
2843
|
+
}
|
|
2844
|
+
await ports.lifecycle?.onMention?.(participantId, triggerTurn);
|
|
2845
|
+
const state = await ports.state.read(participantId);
|
|
2846
|
+
emit(ports.telemetry, {
|
|
2847
|
+
kind: "participant.started",
|
|
2848
|
+
cycleId,
|
|
2849
|
+
at: nowIso(),
|
|
2850
|
+
participantId,
|
|
2851
|
+
executorKind: participant.executor
|
|
2852
|
+
});
|
|
2853
|
+
const execStart = Date.now();
|
|
2854
|
+
let output2;
|
|
2855
|
+
try {
|
|
2856
|
+
output2 = await executor.executeTurn({
|
|
2857
|
+
participant,
|
|
2858
|
+
recentTurns,
|
|
2859
|
+
triggerTurn,
|
|
2860
|
+
state,
|
|
2861
|
+
signal: options.signal
|
|
2862
|
+
});
|
|
2863
|
+
} catch (err) {
|
|
2864
|
+
emit(ports.telemetry, {
|
|
2865
|
+
kind: "participant.failed",
|
|
2866
|
+
cycleId,
|
|
2867
|
+
at: nowIso(),
|
|
2868
|
+
participantId,
|
|
2869
|
+
executorKind: participant.executor,
|
|
2870
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2871
|
+
});
|
|
2872
|
+
throw err;
|
|
2873
|
+
}
|
|
2874
|
+
emit(ports.telemetry, {
|
|
2875
|
+
kind: "participant.finished",
|
|
2876
|
+
cycleId,
|
|
2877
|
+
at: nowIso(),
|
|
2878
|
+
participantId,
|
|
2879
|
+
executorKind: participant.executor,
|
|
2880
|
+
durationMs: Date.now() - execStart,
|
|
2881
|
+
contentLength: output2.content.length
|
|
2882
|
+
});
|
|
2883
|
+
const turn = await ports.substrate.append({
|
|
2884
|
+
participantId,
|
|
2885
|
+
content: output2.content,
|
|
2886
|
+
meta: output2.meta
|
|
2887
|
+
});
|
|
2888
|
+
appended.push(turn);
|
|
2889
|
+
emit(ports.telemetry, {
|
|
2890
|
+
kind: "substrate.appended",
|
|
2891
|
+
cycleId,
|
|
2892
|
+
at: nowIso(),
|
|
2893
|
+
turnId: turn.id,
|
|
2894
|
+
participantId
|
|
2895
|
+
});
|
|
2896
|
+
if (output2.stateUpdate) {
|
|
2897
|
+
await ports.state.write(participantId, output2.stateUpdate);
|
|
2898
|
+
emit(ports.telemetry, {
|
|
2899
|
+
kind: "state.written",
|
|
2900
|
+
cycleId,
|
|
2901
|
+
at: nowIso(),
|
|
2902
|
+
participantId
|
|
2903
|
+
});
|
|
2904
|
+
}
|
|
2905
|
+
await ports.lifecycle?.onTurnEnd?.(turn);
|
|
2906
|
+
}
|
|
2907
|
+
emit(ports.telemetry, {
|
|
2908
|
+
kind: "cycle.finished",
|
|
2909
|
+
cycleId,
|
|
2910
|
+
at: nowIso(),
|
|
2911
|
+
outcome: "executed",
|
|
2912
|
+
turnsAppended: appended.length,
|
|
2913
|
+
durationMs: Date.now() - cycleStart
|
|
2914
|
+
});
|
|
2915
|
+
return { cycle: "executed", selected, turnsAppended: appended };
|
|
2916
|
+
}
|
|
2917
|
+
function findParticipant(participants, id) {
|
|
2918
|
+
for (const p of participants) {
|
|
2919
|
+
if (p.id === id) return p;
|
|
2920
|
+
}
|
|
2921
|
+
return void 0;
|
|
2922
|
+
}
|
|
2923
|
+
function emit(telemetry, event) {
|
|
2924
|
+
if (!telemetry) return;
|
|
2925
|
+
try {
|
|
2926
|
+
telemetry.emit(event);
|
|
2927
|
+
} catch {
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
function nowIso() {
|
|
2931
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
2932
|
+
}
|
|
2933
|
+
function newCycleId() {
|
|
2934
|
+
return `c_${randomBytes(6).toString("hex")}`;
|
|
2935
|
+
}
|
|
2936
|
+
var MULTI_AGENT_RUNTIME_SCHEMA = "agentruntimes/v1";
|
|
2937
|
+
var MULTI_AGENT_RUNTIME_KIND = "MultiAgentRuntime";
|
|
2938
|
+
var AdapterConfigSchema = z.object({
|
|
2939
|
+
kind: z.string().min(1)
|
|
2940
|
+
}).loose();
|
|
2941
|
+
var ParticipantManifestSchema = z.object({
|
|
2942
|
+
id: z.string().min(1),
|
|
2943
|
+
executor: z.string().min(1),
|
|
2944
|
+
displayName: z.string().min(1).optional(),
|
|
2945
|
+
role: z.string().optional(),
|
|
2946
|
+
meta: z.record(z.string(), z.unknown()).optional()
|
|
2947
|
+
}).loose();
|
|
2948
|
+
var MultiAgentRuntimeManifestSchema = z.object({
|
|
2949
|
+
schema: z.literal(MULTI_AGENT_RUNTIME_SCHEMA),
|
|
2950
|
+
kind: z.literal(MULTI_AGENT_RUNTIME_KIND),
|
|
2951
|
+
id: z.string().min(1),
|
|
2952
|
+
participants: z.array(ParticipantManifestSchema).min(1),
|
|
2953
|
+
substrate: AdapterConfigSchema,
|
|
2954
|
+
dispatcher: AdapterConfigSchema,
|
|
2955
|
+
state: AdapterConfigSchema.optional(),
|
|
2956
|
+
lifecycle: z.object({
|
|
2957
|
+
onTurnEnd: z.boolean().optional(),
|
|
2958
|
+
onMention: z.boolean().optional(),
|
|
2959
|
+
onIdle: z.boolean().optional()
|
|
2960
|
+
}).optional()
|
|
2961
|
+
}).loose();
|
|
2962
|
+
async function loadManifest(path) {
|
|
2963
|
+
const abs = resolve(path);
|
|
2964
|
+
const raw = await readFile(abs, "utf8");
|
|
2965
|
+
const parsed = matter3(raw);
|
|
2966
|
+
const manifest = MultiAgentRuntimeManifestSchema.parse(parsed.data);
|
|
2967
|
+
return {
|
|
2968
|
+
manifest,
|
|
2969
|
+
body: parsed.content,
|
|
2970
|
+
path: abs,
|
|
2971
|
+
baseDir: dirname(abs)
|
|
2972
|
+
};
|
|
2973
|
+
}
|
|
2974
|
+
|
|
2975
|
+
// ../agent-runtime/dist/adapters/telemetry.mjs
|
|
2976
|
+
function stderrTelemetry(options = {}) {
|
|
2977
|
+
const prefix = options.prefix ?? "agentproto: ";
|
|
2978
|
+
return {
|
|
2979
|
+
emit(event) {
|
|
2980
|
+
if (options.exclude?.has(event.kind)) return;
|
|
2981
|
+
if (options.include && !options.include.has(event.kind)) return;
|
|
2982
|
+
process.stderr.write(`${prefix}${formatEvent(event)}
|
|
2983
|
+
`);
|
|
2984
|
+
}
|
|
2985
|
+
};
|
|
2986
|
+
}
|
|
2987
|
+
function formatEvent(event) {
|
|
2988
|
+
switch (event.kind) {
|
|
2989
|
+
case "cycle.started":
|
|
2990
|
+
return `${event.cycleId} cycle.started${event.since !== void 0 ? ` since=${event.since}` : ""}`;
|
|
2991
|
+
case "substrate.read":
|
|
2992
|
+
return `${event.cycleId} substrate.read ${event.substrateKind} turns=${event.turnCount} ${event.durationMs}ms`;
|
|
2993
|
+
case "dispatch.decided":
|
|
2994
|
+
return `${event.cycleId} dispatch.decided ${event.dispatcherKind} selected=[${event.selected.join(",") || "(none)"}] ${event.durationMs}ms`;
|
|
2995
|
+
case "participant.started":
|
|
2996
|
+
return `${event.cycleId} participant.started ${event.participantId} (${event.executorKind})`;
|
|
2997
|
+
case "participant.finished":
|
|
2998
|
+
return `${event.cycleId} participant.finished ${event.participantId} ${event.durationMs}ms content=${event.contentLength}b`;
|
|
2999
|
+
case "participant.failed":
|
|
3000
|
+
return `${event.cycleId} participant.failed ${event.participantId} \u2014 ${event.error}`;
|
|
3001
|
+
case "substrate.appended":
|
|
3002
|
+
return `${event.cycleId} substrate.appended turn=${event.turnId} by=${event.participantId}`;
|
|
3003
|
+
case "state.written":
|
|
3004
|
+
return `${event.cycleId} state.written ${event.participantId}`;
|
|
3005
|
+
case "cycle.idle":
|
|
3006
|
+
return `${event.cycleId} cycle.idle`;
|
|
3007
|
+
case "cycle.finished":
|
|
3008
|
+
return `${event.cycleId} cycle.finished ${event.outcome} appended=${event.turnsAppended} ${event.durationMs}ms`;
|
|
3009
|
+
default: {
|
|
3010
|
+
const _exhaustive = event;
|
|
3011
|
+
return JSON.stringify(_exhaustive);
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
var HEADER_RE = /^=== TURN id=([^\s]+) participant=([^\s]+) ts=([^\s]+) ===$/;
|
|
3016
|
+
var FileSubstrate = class {
|
|
3017
|
+
constructor(opts) {
|
|
3018
|
+
this.opts = opts;
|
|
3019
|
+
}
|
|
3020
|
+
kind = "file";
|
|
3021
|
+
async append(input2) {
|
|
3022
|
+
const timestamp = input2.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3023
|
+
const id = hashTurnId(input2.participantId, timestamp, input2.content);
|
|
3024
|
+
const turn = {
|
|
3025
|
+
id,
|
|
3026
|
+
participantId: input2.participantId,
|
|
3027
|
+
timestamp,
|
|
3028
|
+
content: input2.content,
|
|
3029
|
+
meta: input2.meta
|
|
3030
|
+
};
|
|
3031
|
+
await ensureDir2(this.opts.path);
|
|
3032
|
+
const block = formatTurnBlock(turn);
|
|
3033
|
+
await appendFile(this.opts.path, block, "utf8");
|
|
3034
|
+
return turn;
|
|
3035
|
+
}
|
|
3036
|
+
async read(since) {
|
|
3037
|
+
let raw;
|
|
3038
|
+
try {
|
|
3039
|
+
raw = await readFile(this.opts.path, "utf8");
|
|
3040
|
+
} catch (err) {
|
|
3041
|
+
if (isNotFound(err)) return [];
|
|
3042
|
+
throw err;
|
|
3043
|
+
}
|
|
3044
|
+
const turns = parseJournal(raw);
|
|
3045
|
+
if (since === void 0) return turns;
|
|
3046
|
+
const cutoff = turns.findIndex((t) => t.id === since);
|
|
3047
|
+
if (cutoff === -1) return turns;
|
|
3048
|
+
return turns.slice(cutoff + 1);
|
|
3049
|
+
}
|
|
3050
|
+
};
|
|
3051
|
+
function formatTurnBlock(turn) {
|
|
3052
|
+
const header = `=== TURN id=${turn.id} participant=${turn.participantId} ts=${turn.timestamp} ===
|
|
3053
|
+
`;
|
|
3054
|
+
const body = turn.content.endsWith("\n") ? turn.content : `${turn.content}
|
|
3055
|
+
`;
|
|
3056
|
+
return `${header}${body}`;
|
|
3057
|
+
}
|
|
3058
|
+
function parseJournal(raw) {
|
|
3059
|
+
const lines = raw.split("\n");
|
|
3060
|
+
const turns = [];
|
|
3061
|
+
let current = null;
|
|
3062
|
+
for (const line of lines) {
|
|
3063
|
+
const match = HEADER_RE.exec(line);
|
|
3064
|
+
if (match && match[1] && match[2] && match[3]) {
|
|
3065
|
+
if (current) turns.push(materialise(current));
|
|
3066
|
+
current = { id: match[1], pid: match[2], ts: match[3], buf: [] };
|
|
3067
|
+
continue;
|
|
3068
|
+
}
|
|
3069
|
+
if (current) current.buf.push(line);
|
|
3070
|
+
}
|
|
3071
|
+
if (current) turns.push(materialise(current));
|
|
3072
|
+
return turns;
|
|
3073
|
+
}
|
|
3074
|
+
function materialise(c) {
|
|
3075
|
+
let body = c.buf.join("\n");
|
|
3076
|
+
while (body.endsWith("\n")) body = body.slice(0, -1);
|
|
3077
|
+
return {
|
|
3078
|
+
id: c.id,
|
|
3079
|
+
participantId: c.pid,
|
|
3080
|
+
timestamp: c.ts,
|
|
3081
|
+
content: body
|
|
3082
|
+
};
|
|
3083
|
+
}
|
|
3084
|
+
function hashTurnId(pid, ts, content) {
|
|
3085
|
+
const h = createHash("sha256");
|
|
3086
|
+
h.update(pid);
|
|
3087
|
+
h.update("\0");
|
|
3088
|
+
h.update(ts);
|
|
3089
|
+
h.update("\0");
|
|
3090
|
+
h.update(content);
|
|
3091
|
+
return `t_${h.digest("hex").slice(0, 12)}`;
|
|
3092
|
+
}
|
|
3093
|
+
async function ensureDir2(path) {
|
|
3094
|
+
const dir = dirname(path);
|
|
3095
|
+
try {
|
|
3096
|
+
await stat(dir);
|
|
3097
|
+
} catch (err) {
|
|
3098
|
+
if (isNotFound(err)) {
|
|
3099
|
+
await mkdir(dir, { recursive: true });
|
|
3100
|
+
return;
|
|
3101
|
+
}
|
|
3102
|
+
throw err;
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
function isNotFound(err) {
|
|
3106
|
+
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
// ../agent-runtime/dist/adapters/dispatcher-mention.mjs
|
|
3110
|
+
function textContainsMention(text3, name) {
|
|
3111
|
+
if (text3.includes(`@${name}`)) return true;
|
|
3112
|
+
if (name.includes(" ")) {
|
|
3113
|
+
const firstName = name.split(" ")[0]?.trim();
|
|
3114
|
+
if (!firstName) return false;
|
|
3115
|
+
const regex = new RegExp(`@${firstName}(?!\\w)`, "i");
|
|
3116
|
+
return regex.test(text3);
|
|
3117
|
+
}
|
|
3118
|
+
return false;
|
|
3119
|
+
}
|
|
3120
|
+
var MentionDispatcher = class {
|
|
3121
|
+
kind = "mention";
|
|
3122
|
+
lastProcessedTriggerId;
|
|
3123
|
+
async selectNext(input2) {
|
|
3124
|
+
const trigger = input2.recentTurns[input2.recentTurns.length - 1];
|
|
3125
|
+
if (!trigger) return [];
|
|
3126
|
+
if (trigger.id === this.lastProcessedTriggerId) return [];
|
|
3127
|
+
const text3 = trigger.content;
|
|
3128
|
+
const selected = [];
|
|
3129
|
+
for (const p of input2.participants) {
|
|
3130
|
+
if (p.id === trigger.participantId) continue;
|
|
3131
|
+
if (textContainsMention(text3, p.displayName)) {
|
|
3132
|
+
selected.push(p.id);
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
if (selected.length > 0) {
|
|
3136
|
+
this.lastProcessedTriggerId = trigger.id;
|
|
3137
|
+
}
|
|
3138
|
+
return selected;
|
|
3139
|
+
}
|
|
3140
|
+
};
|
|
3141
|
+
var FileStateStore = class {
|
|
3142
|
+
constructor(opts) {
|
|
3143
|
+
this.opts = opts;
|
|
3144
|
+
}
|
|
3145
|
+
kind = "fs";
|
|
3146
|
+
async read(participantId) {
|
|
3147
|
+
const path = this.pathFor(participantId);
|
|
3148
|
+
try {
|
|
3149
|
+
const raw = await readFile(path, "utf8");
|
|
3150
|
+
return JSON.parse(raw);
|
|
3151
|
+
} catch (err) {
|
|
3152
|
+
if (isNotFound2(err)) return Object.freeze({});
|
|
3153
|
+
throw err;
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
async write(participantId, state) {
|
|
3157
|
+
await ensureDir3(this.opts.dir);
|
|
3158
|
+
const path = this.pathFor(participantId);
|
|
3159
|
+
await writeFile(path, JSON.stringify(state, null, 2), "utf8");
|
|
3160
|
+
}
|
|
3161
|
+
pathFor(participantId) {
|
|
3162
|
+
const safe = participantId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
3163
|
+
return resolve(this.opts.dir, `${safe}.json`);
|
|
3164
|
+
}
|
|
3165
|
+
};
|
|
3166
|
+
async function ensureDir3(dir) {
|
|
3167
|
+
try {
|
|
3168
|
+
await stat(dir);
|
|
3169
|
+
} catch (err) {
|
|
3170
|
+
if (isNotFound2(err)) {
|
|
3171
|
+
await mkdir(dir, { recursive: true });
|
|
3172
|
+
return;
|
|
3173
|
+
}
|
|
3174
|
+
throw err;
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
function isNotFound2(err) {
|
|
3178
|
+
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
3179
|
+
}
|
|
3180
|
+
var AgentCliParticipant = class {
|
|
3181
|
+
constructor(opts) {
|
|
3182
|
+
this.opts = opts;
|
|
3183
|
+
}
|
|
3184
|
+
kind = "agent-cli";
|
|
3185
|
+
async executeTurn(input2) {
|
|
3186
|
+
const prompt = await assemblePrompt(input2);
|
|
3187
|
+
const stdout = await spawnWithStdin({
|
|
3188
|
+
command: this.opts.command,
|
|
3189
|
+
args: this.opts.args ?? [],
|
|
3190
|
+
cwd: this.opts.cwd ?? process.cwd(),
|
|
3191
|
+
timeoutMs: this.opts.timeoutMs ?? 9e4,
|
|
3192
|
+
stdin: prompt,
|
|
3193
|
+
signal: input2.signal
|
|
3194
|
+
});
|
|
3195
|
+
const parsed = this.opts.parseOutput?.(stdout) ?? null;
|
|
3196
|
+
const content = parsed ?? stdout.trimEnd();
|
|
3197
|
+
return { content };
|
|
3198
|
+
}
|
|
3199
|
+
};
|
|
3200
|
+
async function assemblePrompt(input2) {
|
|
3201
|
+
const roleText = input2.participant.role ? await loadRole(input2.participant.role) : "";
|
|
3202
|
+
const transcript = input2.recentTurns.map((t) => `[${t.participantId}] ${t.content}`).join("\n\n");
|
|
3203
|
+
return [
|
|
3204
|
+
roleText && `# Your role
|
|
3205
|
+
|
|
3206
|
+
${roleText}`,
|
|
3207
|
+
`# Recent conversation
|
|
3208
|
+
|
|
3209
|
+
${transcript}`,
|
|
3210
|
+
`# Your turn
|
|
3211
|
+
|
|
3212
|
+
You are ${input2.participant.displayName}. The latest message in the conversation triggered you (most likely because it mentions you). Read the transcript above and reply in character. Keep it conversational unless the trigger asks for detailed work. Output only your reply \u2014 no preamble, no role labels, no quotes around the response.`
|
|
3213
|
+
].filter(Boolean).join("\n\n");
|
|
3214
|
+
}
|
|
3215
|
+
var ROLE_FILE_EXTENSIONS = [".md", ".markdown", ".txt"];
|
|
3216
|
+
async function loadRole(roleField) {
|
|
3217
|
+
if (!looksLikeRoleFile(roleField)) return roleField;
|
|
3218
|
+
try {
|
|
3219
|
+
const raw = await readFile(roleField, "utf8");
|
|
3220
|
+
const parsed = matter3(raw);
|
|
3221
|
+
return parsed.content.trim();
|
|
3222
|
+
} catch (err) {
|
|
3223
|
+
const code = err.code;
|
|
3224
|
+
process.stderr.write(
|
|
3225
|
+
`agent-cli participant: role path '${roleField}' not readable (${code ?? "unknown"}); using the literal string as inline role text.
|
|
3226
|
+
`
|
|
3227
|
+
);
|
|
3228
|
+
return roleField;
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
function looksLikeRoleFile(s) {
|
|
3232
|
+
const lower = s.toLowerCase();
|
|
3233
|
+
return ROLE_FILE_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
3234
|
+
}
|
|
3235
|
+
async function spawnWithStdin(args) {
|
|
3236
|
+
return new Promise((resolveP, rejectP) => {
|
|
3237
|
+
const proc = spawn(args.command, [...args.args], {
|
|
3238
|
+
cwd: args.cwd,
|
|
3239
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3240
|
+
});
|
|
3241
|
+
let stdout = "";
|
|
3242
|
+
let stderr = "";
|
|
3243
|
+
const onAbort = () => proc.kill("SIGTERM");
|
|
3244
|
+
args.signal?.addEventListener("abort", onAbort);
|
|
3245
|
+
const timer = setTimeout(() => {
|
|
3246
|
+
proc.kill("SIGTERM");
|
|
3247
|
+
}, args.timeoutMs);
|
|
3248
|
+
proc.stdout.on("data", (chunk) => {
|
|
3249
|
+
stdout += chunk.toString("utf8");
|
|
3250
|
+
});
|
|
3251
|
+
proc.stderr.on("data", (chunk) => {
|
|
3252
|
+
stderr += chunk.toString("utf8");
|
|
3253
|
+
});
|
|
3254
|
+
proc.on("error", (err) => {
|
|
3255
|
+
clearTimeout(timer);
|
|
3256
|
+
args.signal?.removeEventListener("abort", onAbort);
|
|
3257
|
+
rejectP(err);
|
|
3258
|
+
});
|
|
3259
|
+
proc.on("close", (code) => {
|
|
3260
|
+
clearTimeout(timer);
|
|
3261
|
+
args.signal?.removeEventListener("abort", onAbort);
|
|
3262
|
+
if (code === 0) {
|
|
3263
|
+
resolveP(stdout);
|
|
1859
3264
|
return;
|
|
1860
3265
|
}
|
|
1861
|
-
|
|
3266
|
+
rejectP(
|
|
3267
|
+
new Error(
|
|
3268
|
+
`agent-cli participant "${args.command}" exited with code ${code}: ${stderr.trim() || "(no stderr)"}`
|
|
3269
|
+
)
|
|
3270
|
+
);
|
|
1862
3271
|
});
|
|
3272
|
+
proc.stdin.write(args.stdin);
|
|
3273
|
+
proc.stdin.end();
|
|
1863
3274
|
});
|
|
1864
3275
|
}
|
|
3276
|
+
function parseClaudeJsonOutput(stdout) {
|
|
3277
|
+
try {
|
|
3278
|
+
const parsed = JSON.parse(stdout);
|
|
3279
|
+
if (typeof parsed === "object" && parsed !== null && "result" in parsed && typeof parsed.result === "string") {
|
|
3280
|
+
return parsed.result;
|
|
3281
|
+
}
|
|
3282
|
+
return null;
|
|
3283
|
+
} catch {
|
|
3284
|
+
return null;
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
1865
3287
|
|
|
1866
|
-
// src/
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
3288
|
+
// src/registry/builtins.ts
|
|
3289
|
+
function registerBuiltins() {
|
|
3290
|
+
registerSubstrate("file", (cfg, ctx) => {
|
|
3291
|
+
const path = typeof cfg.path === "string" ? cfg.path : ".runtime/conversation.md";
|
|
3292
|
+
return new FileSubstrate({ path: resolve(ctx.baseDir, path) });
|
|
3293
|
+
});
|
|
3294
|
+
registerDispatcher("mention", () => new MentionDispatcher());
|
|
3295
|
+
registerStateStore("fs", (cfg, ctx) => {
|
|
3296
|
+
const dir = typeof cfg.dir === "string" ? cfg.dir : ".runtime/state";
|
|
3297
|
+
return new FileStateStore({ dir: resolve(ctx.baseDir, dir) });
|
|
3298
|
+
});
|
|
3299
|
+
registerExecutor("agent-cli", (cfg) => buildAgentCli(cfg));
|
|
3300
|
+
}
|
|
3301
|
+
function buildAgentCli(cfg) {
|
|
3302
|
+
const command = typeof cfg.command === "string" ? cfg.command : "claude";
|
|
3303
|
+
const args = Array.isArray(cfg.args) ? cfg.args.filter((a) => typeof a === "string") : ["--print", "--output-format=json"];
|
|
3304
|
+
const useClaudeJson = cfg.parseJson !== false && command === "claude";
|
|
3305
|
+
return new AgentCliParticipant({
|
|
3306
|
+
command,
|
|
3307
|
+
args,
|
|
3308
|
+
parseOutput: useClaudeJson ? parseClaudeJsonOutput : void 0
|
|
3309
|
+
});
|
|
3310
|
+
}
|
|
3311
|
+
async function loadPluginsFromConfig() {
|
|
3312
|
+
const path = configPath2();
|
|
3313
|
+
try {
|
|
3314
|
+
const raw = await readFile(path, "utf8");
|
|
3315
|
+
const parsed = JSON.parse(raw);
|
|
3316
|
+
return parsed.plugins ?? [];
|
|
3317
|
+
} catch (err) {
|
|
3318
|
+
if (err.code === "ENOENT") return [];
|
|
3319
|
+
process.stderr.write(
|
|
3320
|
+
`agentproto: failed to read ${path} \u2014 ${err instanceof Error ? err.message : String(err)}
|
|
3321
|
+
`
|
|
3322
|
+
);
|
|
3323
|
+
return [];
|
|
1872
3324
|
}
|
|
1873
|
-
|
|
1874
|
-
|
|
3325
|
+
}
|
|
3326
|
+
async function loadPlugins(moduleIds) {
|
|
3327
|
+
for (const id of moduleIds) {
|
|
3328
|
+
try {
|
|
3329
|
+
const manifest = await loadPluginFromManifest(id);
|
|
3330
|
+
if (manifest === null) {
|
|
3331
|
+
await import(id);
|
|
3332
|
+
}
|
|
3333
|
+
} catch (err) {
|
|
3334
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3335
|
+
process.stderr.write(
|
|
3336
|
+
`agentproto: plugin '${id}' failed to load \u2014 ${msg}
|
|
3337
|
+
`
|
|
3338
|
+
);
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
}
|
|
3342
|
+
function configPath2() {
|
|
3343
|
+
const base = process.env["AGENTPROTO_HOME"] ?? join(homedir(), ".agentproto");
|
|
3344
|
+
return join(base, "config.json");
|
|
1875
3345
|
}
|
|
1876
3346
|
|
|
1877
|
-
// src/commands/run.ts
|
|
1878
|
-
async function
|
|
1879
|
-
const { values
|
|
3347
|
+
// src/commands/run-swarm.ts
|
|
3348
|
+
async function runRunSwarm(args) {
|
|
3349
|
+
const { values } = parseArgs({
|
|
1880
3350
|
args: [...args],
|
|
1881
|
-
allowPositionals:
|
|
3351
|
+
allowPositionals: false,
|
|
1882
3352
|
strict: true,
|
|
1883
3353
|
options: {
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
3354
|
+
manifest: { type: "string", short: "m" },
|
|
3355
|
+
once: { type: "boolean" },
|
|
3356
|
+
interval: { type: "string" },
|
|
3357
|
+
verbose: { type: "boolean", short: "v" },
|
|
3358
|
+
plugin: { type: "string", multiple: true }
|
|
1888
3359
|
}
|
|
1889
3360
|
});
|
|
1890
|
-
|
|
1891
|
-
if (!slug) {
|
|
3361
|
+
if (!values.manifest) {
|
|
1892
3362
|
process.stderr.write(
|
|
1893
|
-
"agentproto run:
|
|
3363
|
+
"agentproto run-swarm: --manifest <path> is required.\n"
|
|
1894
3364
|
);
|
|
1895
3365
|
return 2;
|
|
1896
3366
|
}
|
|
1897
|
-
|
|
1898
|
-
const
|
|
1899
|
-
|
|
3367
|
+
registerBuiltins();
|
|
3368
|
+
const cliPlugins = values.plugin ?? [];
|
|
3369
|
+
const configPlugins = await loadPluginsFromConfig();
|
|
3370
|
+
await loadPlugins([...configPlugins, ...cliPlugins]);
|
|
3371
|
+
const loaded = await loadManifest(resolve(values.manifest));
|
|
3372
|
+
const verbose = values.verbose === true;
|
|
3373
|
+
const cleanups = [];
|
|
3374
|
+
const ctx = {
|
|
3375
|
+
baseDir: loaded.baseDir,
|
|
3376
|
+
registerCleanup: (fn) => cleanups.push(fn)
|
|
3377
|
+
};
|
|
3378
|
+
const telemetry = verbose ? stderrTelemetry({ prefix: "agentproto run-swarm: " }) : void 0;
|
|
3379
|
+
let ports;
|
|
3380
|
+
try {
|
|
3381
|
+
ports = await buildPorts(loaded, ctx, telemetry);
|
|
3382
|
+
} catch (err) {
|
|
1900
3383
|
process.stderr.write(
|
|
1901
|
-
|
|
3384
|
+
`agentproto run-swarm: ${err instanceof Error ? err.message : String(err)}
|
|
3385
|
+
`
|
|
3386
|
+
);
|
|
3387
|
+
await runCleanups(cleanups);
|
|
3388
|
+
return 1;
|
|
3389
|
+
}
|
|
3390
|
+
const onceMode = values.once === true;
|
|
3391
|
+
const intervalMs = parseInterval(values.interval) ?? 2e3;
|
|
3392
|
+
if (verbose) {
|
|
3393
|
+
logVerbose(`loaded manifest "${loaded.manifest.id}" from ${loaded.path}`);
|
|
3394
|
+
logVerbose(
|
|
3395
|
+
`participants: ${ports.participants.map((p) => p.displayName).join(", ")}`
|
|
3396
|
+
);
|
|
3397
|
+
logVerbose(
|
|
3398
|
+
`substrate=${ports.substrate.kind} dispatcher=${ports.dispatcher.kind} state=${ports.state.kind}`
|
|
3399
|
+
);
|
|
3400
|
+
const registered = listRegisteredKinds();
|
|
3401
|
+
logVerbose(
|
|
3402
|
+
`registered: substrates=[${registered.substrates.join(",")}] dispatchers=[${registered.dispatchers.join(",")}] executors=[${registered.executors.join(",")}] stateStores=[${registered.stateStores.join(",")}]`
|
|
1902
3403
|
);
|
|
1903
|
-
return 2;
|
|
1904
3404
|
}
|
|
1905
|
-
const adapter = await resolveAdapter(slug);
|
|
1906
|
-
const runtime = createAgentCliRuntime(adapter.handle);
|
|
1907
3405
|
const controller = new AbortController();
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
agentproto: received ${sig}, cancelling\u2026
|
|
1911
|
-
`);
|
|
3406
|
+
process.once("SIGINT", () => {
|
|
3407
|
+
if (verbose) logVerbose("SIGINT received, draining current cycle\u2026");
|
|
1912
3408
|
controller.abort();
|
|
1913
|
-
};
|
|
1914
|
-
process.once("SIGINT", onSignal);
|
|
1915
|
-
process.once("SIGTERM", onSignal);
|
|
1916
|
-
let session = null;
|
|
3409
|
+
});
|
|
1917
3410
|
try {
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
if (
|
|
1928
|
-
|
|
1929
|
-
}
|
|
1930
|
-
return exit;
|
|
3411
|
+
do {
|
|
3412
|
+
if (controller.signal.aborted) break;
|
|
3413
|
+
try {
|
|
3414
|
+
await runTurn(ports, { signal: controller.signal });
|
|
3415
|
+
} catch (err) {
|
|
3416
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3417
|
+
process.stderr.write(`agentproto run-swarm: cycle failed \u2014 ${msg}
|
|
3418
|
+
`);
|
|
3419
|
+
}
|
|
3420
|
+
if (onceMode) break;
|
|
3421
|
+
await sleep2(intervalMs, controller.signal);
|
|
3422
|
+
} while (!controller.signal.aborted);
|
|
1931
3423
|
} finally {
|
|
1932
|
-
|
|
1933
|
-
process.off("SIGTERM", onSignal);
|
|
1934
|
-
if (session) await session.close().catch(() => {
|
|
1935
|
-
});
|
|
3424
|
+
await runCleanups(cleanups);
|
|
1936
3425
|
}
|
|
3426
|
+
return 0;
|
|
1937
3427
|
}
|
|
1938
|
-
function
|
|
1939
|
-
|
|
1940
|
-
|
|
3428
|
+
async function buildPorts(loaded, ctx, telemetry) {
|
|
3429
|
+
const manifest = loaded.manifest;
|
|
3430
|
+
const substrate = await buildSubstrate(manifest, ctx);
|
|
3431
|
+
const dispatcher = await buildDispatcher(manifest, ctx);
|
|
3432
|
+
const state = await buildState(manifest, ctx);
|
|
3433
|
+
const participants = buildParticipants(manifest);
|
|
3434
|
+
const executors2 = await buildExecutors(manifest, ctx);
|
|
3435
|
+
return {
|
|
3436
|
+
substrate,
|
|
3437
|
+
dispatcher,
|
|
3438
|
+
state,
|
|
3439
|
+
participants,
|
|
3440
|
+
executors: executors2,
|
|
3441
|
+
...telemetry ? { telemetry } : {}
|
|
3442
|
+
};
|
|
1941
3443
|
}
|
|
1942
|
-
function
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
`)
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
3444
|
+
async function buildSubstrate(manifest, ctx) {
|
|
3445
|
+
const cfg = manifest.substrate;
|
|
3446
|
+
const factory = getSubstrateFactory(cfg.kind);
|
|
3447
|
+
if (!factory) {
|
|
3448
|
+
throw new Error(
|
|
3449
|
+
`unknown substrate kind '${cfg.kind}'. Registered kinds: [${listRegisteredKinds().substrates.join(", ") || "(none)"}]. Pass --plugin <module-id> or add it to ~/.agentproto/config.json plugins[] to register a third-party substrate.`
|
|
3450
|
+
);
|
|
3451
|
+
}
|
|
3452
|
+
return factory(cfg, ctx);
|
|
3453
|
+
}
|
|
3454
|
+
async function buildDispatcher(manifest, ctx) {
|
|
3455
|
+
const cfg = manifest.dispatcher;
|
|
3456
|
+
const factory = getDispatcherFactory(cfg.kind);
|
|
3457
|
+
if (!factory) {
|
|
3458
|
+
throw new Error(
|
|
3459
|
+
`unknown dispatcher kind '${cfg.kind}'. Registered kinds: [${listRegisteredKinds().dispatchers.join(", ") || "(none)"}].`
|
|
3460
|
+
);
|
|
3461
|
+
}
|
|
3462
|
+
return factory(cfg, ctx);
|
|
3463
|
+
}
|
|
3464
|
+
async function buildState(manifest, ctx) {
|
|
3465
|
+
const cfg = manifest.state ?? { kind: "fs" };
|
|
3466
|
+
const factory = getStateStoreFactory(cfg.kind);
|
|
3467
|
+
if (!factory) {
|
|
3468
|
+
throw new Error(
|
|
3469
|
+
`unknown state-store kind '${cfg.kind}'. Registered kinds: [${listRegisteredKinds().stateStores.join(", ") || "(none)"}].`
|
|
3470
|
+
);
|
|
3471
|
+
}
|
|
3472
|
+
return factory(cfg, ctx);
|
|
3473
|
+
}
|
|
3474
|
+
function buildParticipants(manifest) {
|
|
3475
|
+
return manifest.participants.map((p) => ({
|
|
3476
|
+
id: p.id,
|
|
3477
|
+
displayName: p.displayName ?? p.id,
|
|
3478
|
+
executor: p.executor,
|
|
3479
|
+
role: p.role,
|
|
3480
|
+
meta: p.meta
|
|
3481
|
+
}));
|
|
3482
|
+
}
|
|
3483
|
+
async function buildExecutors(manifest, ctx) {
|
|
3484
|
+
const map = /* @__PURE__ */ new Map();
|
|
3485
|
+
const kinds = new Set(manifest.participants.map((p) => p.executor));
|
|
3486
|
+
for (const kind of kinds) {
|
|
3487
|
+
const factory = getExecutorFactory(kind);
|
|
3488
|
+
if (!factory) {
|
|
3489
|
+
throw new Error(
|
|
3490
|
+
`unknown executor kind '${kind}'. Registered kinds: [${listRegisteredKinds().executors.join(", ") || "(none)"}]. Pass --plugin <module-id> or add the executor's package to ~/.agentproto/config.json plugins[].`
|
|
1974
3491
|
);
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
3492
|
+
}
|
|
3493
|
+
map.set(kind, await factory({ kind }, ctx));
|
|
3494
|
+
}
|
|
3495
|
+
return map;
|
|
3496
|
+
}
|
|
3497
|
+
async function runCleanups(cleanups) {
|
|
3498
|
+
for (const fn of cleanups) {
|
|
3499
|
+
try {
|
|
3500
|
+
await fn();
|
|
3501
|
+
} catch (err) {
|
|
3502
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3503
|
+
process.stderr.write(`agentproto run-swarm: cleanup failed \u2014 ${msg}
|
|
1981
3504
|
`);
|
|
1982
|
-
}
|
|
1983
|
-
const rest = { ...data };
|
|
1984
|
-
delete rest.stderr;
|
|
1985
|
-
if (Object.keys(rest).length > 0) {
|
|
1986
|
-
process.stderr.write(
|
|
1987
|
-
`\x1B[2m\u2500\u2500 error.data \u2500\u2500
|
|
1988
|
-
${JSON.stringify(rest, null, 2)}\x1B[0m
|
|
1989
|
-
`
|
|
1990
|
-
);
|
|
1991
|
-
}
|
|
1992
|
-
}
|
|
1993
|
-
break;
|
|
1994
3505
|
}
|
|
1995
3506
|
}
|
|
1996
3507
|
}
|
|
3508
|
+
function parseInterval(value) {
|
|
3509
|
+
if (!value) return void 0;
|
|
3510
|
+
const match = /^(\d+)(ms|s)?$/.exec(value);
|
|
3511
|
+
if (!match) return void 0;
|
|
3512
|
+
const n = Number(match[1]);
|
|
3513
|
+
const unit = match[2] ?? "ms";
|
|
3514
|
+
return unit === "s" ? n * 1e3 : n;
|
|
3515
|
+
}
|
|
3516
|
+
function sleep2(ms, signal) {
|
|
3517
|
+
return new Promise((resolveP) => {
|
|
3518
|
+
if (signal.aborted) {
|
|
3519
|
+
resolveP();
|
|
3520
|
+
return;
|
|
3521
|
+
}
|
|
3522
|
+
const t = setTimeout(() => resolveP(), ms);
|
|
3523
|
+
signal.addEventListener("abort", () => {
|
|
3524
|
+
clearTimeout(t);
|
|
3525
|
+
resolveP();
|
|
3526
|
+
});
|
|
3527
|
+
});
|
|
3528
|
+
}
|
|
3529
|
+
function logVerbose(msg) {
|
|
3530
|
+
process.stderr.write(`agentproto run-swarm: ${msg}
|
|
3531
|
+
`);
|
|
3532
|
+
}
|
|
1997
3533
|
|
|
1998
3534
|
// src/util/pty-factory.ts
|
|
1999
3535
|
async function loadNodePtyFactory() {
|
|
@@ -2236,7 +3772,7 @@ function createVerbs(spec) {
|
|
|
2236
3772
|
const relativePath = spec.pathOf(handle);
|
|
2237
3773
|
const path = join(opts.dir, relativePath);
|
|
2238
3774
|
const frontmatter = toFrontmatter(params);
|
|
2239
|
-
const rendered =
|
|
3775
|
+
const rendered = matter3.stringify(
|
|
2240
3776
|
opts.body ?? defaultBody(spec, frontmatter),
|
|
2241
3777
|
frontmatter
|
|
2242
3778
|
);
|
|
@@ -2289,7 +3825,7 @@ function createVerbs(spec) {
|
|
|
2289
3825
|
const mutated = await mutator(params, { handle, body });
|
|
2290
3826
|
const newHandle = spec.define(mutated);
|
|
2291
3827
|
const newFrontmatter = toFrontmatter(mutated);
|
|
2292
|
-
const rendered =
|
|
3828
|
+
const rendered = matter3.stringify(
|
|
2293
3829
|
opts.body ?? body,
|
|
2294
3830
|
newFrontmatter
|
|
2295
3831
|
);
|
|
@@ -2299,7 +3835,7 @@ function createVerbs(spec) {
|
|
|
2299
3835
|
}
|
|
2300
3836
|
return { path, handle: newHandle, rendered };
|
|
2301
3837
|
}
|
|
2302
|
-
async function
|
|
3838
|
+
async function resolve9(block, ctx = {}) {
|
|
2303
3839
|
if ("inline" in block) {
|
|
2304
3840
|
return spec.define(block.inline);
|
|
2305
3841
|
}
|
|
@@ -2325,7 +3861,7 @@ function createVerbs(spec) {
|
|
|
2325
3861
|
async function deleteFn(path) {
|
|
2326
3862
|
await rm(path, { force: true });
|
|
2327
3863
|
}
|
|
2328
|
-
return { create, load, list, update, resolve:
|
|
3864
|
+
return { create, load, list, update, resolve: resolve9, delete: deleteFn };
|
|
2329
3865
|
}
|
|
2330
3866
|
function defaultBody(spec, frontmatter) {
|
|
2331
3867
|
const id = typeof frontmatter.name === "string" ? frontmatter.name : typeof frontmatter.id === "string" ? frontmatter.id : typeof frontmatter.slug === "string" ? frontmatter.slug : spec.name;
|
|
@@ -2343,28 +3879,51 @@ createDoctype({
|
|
|
2343
3879
|
validate(def) {
|
|
2344
3880
|
const result = extensionFrontmatterSchema.safeParse(def);
|
|
2345
3881
|
if (!result.success) {
|
|
2346
|
-
throw new Error(
|
|
3882
|
+
throw new Error(
|
|
3883
|
+
`defineExtension (AIP-40): ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
|
|
3884
|
+
);
|
|
2347
3885
|
}
|
|
2348
3886
|
},
|
|
2349
3887
|
build(def) {
|
|
2350
3888
|
return { ...def };
|
|
2351
3889
|
}
|
|
2352
3890
|
});
|
|
3891
|
+
function parseExtensionManifest(source) {
|
|
3892
|
+
const parsed = matter3(source);
|
|
3893
|
+
if (Object.keys(parsed.data).length === 0) {
|
|
3894
|
+
throw new Error("parseExtensionManifest: missing or empty frontmatter");
|
|
3895
|
+
}
|
|
3896
|
+
const result = extensionFrontmatterSchema.safeParse(parsed.data);
|
|
3897
|
+
if (!result.success) {
|
|
3898
|
+
throw new Error(
|
|
3899
|
+
`parseExtensionManifest: invalid frontmatter \u2014 ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
|
|
3900
|
+
);
|
|
3901
|
+
}
|
|
3902
|
+
return { frontmatter: result.data, body: parsed.content };
|
|
3903
|
+
}
|
|
3904
|
+
|
|
3905
|
+
// ../extension/dist/index.mjs
|
|
2353
3906
|
function specFromExtension(extension, opts = {}) {
|
|
2354
3907
|
const { parent } = opts;
|
|
2355
3908
|
const isRoot = extension.extends === "none";
|
|
2356
3909
|
if (!isRoot && !parent) {
|
|
2357
|
-
throw new Error(
|
|
3910
|
+
throw new Error(
|
|
3911
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' extends '${extension.extends}' but no parent spec was provided in opts.parent`
|
|
3912
|
+
);
|
|
2358
3913
|
}
|
|
2359
3914
|
if (isRoot && !extension.path_convention) {
|
|
2360
|
-
throw new Error(
|
|
3915
|
+
throw new Error(
|
|
3916
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' is a root doctype (extends: none) and MUST declare path_convention`
|
|
3917
|
+
);
|
|
2361
3918
|
}
|
|
2362
3919
|
if (parent && extension.tighten) {
|
|
2363
3920
|
verifyTightening(extension);
|
|
2364
3921
|
}
|
|
2365
3922
|
const slugName = extension.slug.split(":")[1] ?? extension.slug;
|
|
2366
3923
|
const pathTemplate = extension.path_convention ?? null;
|
|
2367
|
-
const extensionKeys = new Set(
|
|
3924
|
+
const extensionKeys = new Set(
|
|
3925
|
+
Object.keys(extension.add_fields?.properties ?? {})
|
|
3926
|
+
);
|
|
2368
3927
|
const define = (params) => {
|
|
2369
3928
|
const withDefaults = { ...params };
|
|
2370
3929
|
if (extension.defaults) {
|
|
@@ -2380,10 +3939,8 @@ function specFromExtension(extension, opts = {}) {
|
|
|
2380
3939
|
const parentOnly = {};
|
|
2381
3940
|
const extensionOnly = {};
|
|
2382
3941
|
for (const [key, value] of Object.entries(withDefaults)) {
|
|
2383
|
-
if (extensionKeys.has(key))
|
|
2384
|
-
|
|
2385
|
-
else
|
|
2386
|
-
parentOnly[key] = value;
|
|
3942
|
+
if (extensionKeys.has(key)) extensionOnly[key] = value;
|
|
3943
|
+
else parentOnly[key] = value;
|
|
2387
3944
|
}
|
|
2388
3945
|
const parentHandle = parent.define(parentOnly);
|
|
2389
3946
|
return Object.freeze({
|
|
@@ -2394,7 +3951,11 @@ function specFromExtension(extension, opts = {}) {
|
|
|
2394
3951
|
const parse = isRoot ? rootParse(extension) : parent.parse;
|
|
2395
3952
|
const pathOf = (handle) => {
|
|
2396
3953
|
if (pathTemplate) {
|
|
2397
|
-
return resolvePathTemplate(
|
|
3954
|
+
return resolvePathTemplate(
|
|
3955
|
+
pathTemplate,
|
|
3956
|
+
handle,
|
|
3957
|
+
slugName
|
|
3958
|
+
);
|
|
2398
3959
|
}
|
|
2399
3960
|
return parent.pathOf(handle);
|
|
2400
3961
|
};
|
|
@@ -2411,36 +3972,33 @@ function verifyTightening(extension, parent) {
|
|
|
2411
3972
|
const t = extension.tighten ?? {};
|
|
2412
3973
|
for (const [field, override] of Object.entries(t)) {
|
|
2413
3974
|
if (typeof override.minLength === "number" && typeof override.maxLength === "number" && override.minLength > override.maxLength) {
|
|
2414
|
-
throw new Error(
|
|
3975
|
+
throw new Error(
|
|
3976
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: minLength (${override.minLength}) > maxLength (${override.maxLength})`
|
|
3977
|
+
);
|
|
2415
3978
|
}
|
|
2416
3979
|
if (typeof override.minimum === "number" && typeof override.maximum === "number" && override.minimum > override.maximum) {
|
|
2417
|
-
throw new Error(
|
|
3980
|
+
throw new Error(
|
|
3981
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: minimum (${override.minimum}) > maximum (${override.maximum})`
|
|
3982
|
+
);
|
|
2418
3983
|
}
|
|
2419
3984
|
if (override.enum !== void 0 && !Array.isArray(override.enum)) {
|
|
2420
|
-
throw new Error(
|
|
3985
|
+
throw new Error(
|
|
3986
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: enum must be an array`
|
|
3987
|
+
);
|
|
2421
3988
|
}
|
|
2422
3989
|
}
|
|
2423
3990
|
}
|
|
2424
3991
|
function rootParse(extension) {
|
|
2425
3992
|
return (source) => {
|
|
2426
|
-
throw new Error(
|
|
3993
|
+
throw new Error(
|
|
3994
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' is a root doctype \u2014 supply your own parser or extend a public AIP for parser inheritance`
|
|
3995
|
+
);
|
|
2427
3996
|
};
|
|
2428
3997
|
}
|
|
2429
3998
|
function resolvePathTemplate(template, handle, doctypeSlug) {
|
|
2430
3999
|
const identity = typeof handle.id === "string" && handle.id || typeof handle.slug === "string" && handle.slug || typeof handle.name === "string" && handle.name || "unknown";
|
|
2431
4000
|
return template.replace(/<slug>/g, identity).replace(/<DOCTYPE>/g, doctypeSlug.toUpperCase());
|
|
2432
4001
|
}
|
|
2433
|
-
function parseExtensionManifest(source) {
|
|
2434
|
-
const parsed = matter(source);
|
|
2435
|
-
if (Object.keys(parsed.data).length === 0) {
|
|
2436
|
-
throw new Error("parseExtensionManifest: missing or empty frontmatter");
|
|
2437
|
-
}
|
|
2438
|
-
const result = extensionFrontmatterSchema.safeParse(parsed.data);
|
|
2439
|
-
if (!result.success) {
|
|
2440
|
-
throw new Error(`parseExtensionManifest: invalid frontmatter \u2014 ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`);
|
|
2441
|
-
}
|
|
2442
|
-
return { frontmatter: result.data, body: parsed.content };
|
|
2443
|
-
}
|
|
2444
4002
|
|
|
2445
4003
|
// ../mcp-server/dist/index.mjs
|
|
2446
4004
|
async function createMcpServer(opts) {
|
|
@@ -4203,7 +5761,7 @@ function startHeartbeat(opts) {
|
|
|
4203
5761
|
} catch {
|
|
4204
5762
|
return null;
|
|
4205
5763
|
}
|
|
4206
|
-
const parsed =
|
|
5764
|
+
const parsed = matter3(source);
|
|
4207
5765
|
const fm = parsed.data;
|
|
4208
5766
|
const agent = typeof fm.agent === "string" ? fm.agent : null;
|
|
4209
5767
|
if (!agent) return null;
|
|
@@ -4917,15 +6475,15 @@ async function startHttpServer(opts) {
|
|
|
4917
6475
|
});
|
|
4918
6476
|
});
|
|
4919
6477
|
const bind = opts.bind ?? "127.0.0.1";
|
|
4920
|
-
await new Promise((
|
|
6478
|
+
await new Promise((resolve82, reject) => {
|
|
4921
6479
|
server.once("error", reject);
|
|
4922
|
-
server.listen(opts.port, bind, () =>
|
|
6480
|
+
server.listen(opts.port, bind, () => resolve82());
|
|
4923
6481
|
});
|
|
4924
6482
|
return {
|
|
4925
6483
|
url: `http://${bind}:${opts.port}`,
|
|
4926
6484
|
async stop() {
|
|
4927
6485
|
wss.close();
|
|
4928
|
-
await new Promise((
|
|
6486
|
+
await new Promise((resolve82) => server.close(() => resolve82()));
|
|
4929
6487
|
}
|
|
4930
6488
|
};
|
|
4931
6489
|
}
|
|
@@ -6062,12 +7620,12 @@ function createSessionsRegistry(opts) {
|
|
|
6062
7620
|
} catch {
|
|
6063
7621
|
}
|
|
6064
7622
|
}
|
|
6065
|
-
const
|
|
7623
|
+
const nowIso2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
6066
7624
|
for (const rt of sessions.values()) {
|
|
6067
7625
|
rt.emitter.removeAllListeners();
|
|
6068
7626
|
if (rt.desc.status === "running" || rt.desc.status === "starting") {
|
|
6069
7627
|
rt.desc.status = "killed";
|
|
6070
|
-
rt.desc.endedAt =
|
|
7628
|
+
rt.desc.endedAt = nowIso2;
|
|
6071
7629
|
if (rt.agentSession) {
|
|
6072
7630
|
void rt.agentSession.close().catch(() => void 0);
|
|
6073
7631
|
}
|
|
@@ -6083,7 +7641,7 @@ function createSessionsRegistry(opts) {
|
|
|
6083
7641
|
if (persist) {
|
|
6084
7642
|
try {
|
|
6085
7643
|
const snapshot = {
|
|
6086
|
-
savedAt:
|
|
7644
|
+
savedAt: nowIso2,
|
|
6087
7645
|
sessions: Array.from(sessions.values()).map((s) => s.desc)
|
|
6088
7646
|
};
|
|
6089
7647
|
mkdirSync(dirname(persistPath), { recursive: true });
|
|
@@ -6378,7 +7936,7 @@ var URL_REGEX = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/i;
|
|
|
6378
7936
|
var STARTUP_TIMEOUT_MS = 3e4;
|
|
6379
7937
|
function quickTunnelProvider() {
|
|
6380
7938
|
let child = null;
|
|
6381
|
-
let
|
|
7939
|
+
let configPath3 = null;
|
|
6382
7940
|
return {
|
|
6383
7941
|
id: "quick",
|
|
6384
7942
|
async start(opts) {
|
|
@@ -6387,7 +7945,7 @@ function quickTunnelProvider() {
|
|
|
6387
7945
|
const id = randomBytes(4).toString("hex");
|
|
6388
7946
|
const dir = join(opts.workspace, ".agentproto");
|
|
6389
7947
|
await mkdir(dir, { recursive: true });
|
|
6390
|
-
|
|
7948
|
+
configPath3 = join(dir, `cloudflared-${id}.yml`);
|
|
6391
7949
|
const configBody = [
|
|
6392
7950
|
`# generated by agentproto-runtime \u2014 sentinel only`,
|
|
6393
7951
|
`# overrides ~/.cloudflared/config.yml so its ingress rules`,
|
|
@@ -6397,14 +7955,14 @@ function quickTunnelProvider() {
|
|
|
6397
7955
|
`loglevel: debug`,
|
|
6398
7956
|
``
|
|
6399
7957
|
].join("\n");
|
|
6400
|
-
await writeFile(
|
|
7958
|
+
await writeFile(configPath3, configBody, "utf8");
|
|
6401
7959
|
const proc = spawn(
|
|
6402
7960
|
"cloudflared",
|
|
6403
|
-
["tunnel", "--config",
|
|
7961
|
+
["tunnel", "--config", configPath3, "--url", targetUrl],
|
|
6404
7962
|
{ stdio: ["ignore", "pipe", "pipe"], shell: false }
|
|
6405
7963
|
);
|
|
6406
7964
|
child = proc;
|
|
6407
|
-
const url = await new Promise((
|
|
7965
|
+
const url = await new Promise((resolve82, reject) => {
|
|
6408
7966
|
let settled = false;
|
|
6409
7967
|
const timer = setTimeout(() => {
|
|
6410
7968
|
if (settled) return;
|
|
@@ -6429,7 +7987,7 @@ function quickTunnelProvider() {
|
|
|
6429
7987
|
if (match) {
|
|
6430
7988
|
settled = true;
|
|
6431
7989
|
clearTimeout(timer);
|
|
6432
|
-
|
|
7990
|
+
resolve82(match[0]);
|
|
6433
7991
|
}
|
|
6434
7992
|
};
|
|
6435
7993
|
proc.stderr?.on("data", onData);
|
|
@@ -6460,9 +8018,9 @@ function quickTunnelProvider() {
|
|
|
6460
8018
|
},
|
|
6461
8019
|
async stop() {
|
|
6462
8020
|
const proc = child;
|
|
6463
|
-
const cfg =
|
|
8021
|
+
const cfg = configPath3;
|
|
6464
8022
|
child = null;
|
|
6465
|
-
|
|
8023
|
+
configPath3 = null;
|
|
6466
8024
|
if (cfg) {
|
|
6467
8025
|
try {
|
|
6468
8026
|
await rm(cfg, { force: true });
|
|
@@ -6478,15 +8036,15 @@ function quickTunnelProvider() {
|
|
|
6478
8036
|
}
|
|
6479
8037
|
}, 3e3);
|
|
6480
8038
|
timer.unref();
|
|
6481
|
-
await new Promise((
|
|
8039
|
+
await new Promise((resolve82) => {
|
|
6482
8040
|
if (proc.exitCode !== null) {
|
|
6483
8041
|
clearTimeout(timer);
|
|
6484
|
-
|
|
8042
|
+
resolve82();
|
|
6485
8043
|
return;
|
|
6486
8044
|
}
|
|
6487
8045
|
proc.once("exit", () => {
|
|
6488
8046
|
clearTimeout(timer);
|
|
6489
|
-
|
|
8047
|
+
resolve82();
|
|
6490
8048
|
});
|
|
6491
8049
|
});
|
|
6492
8050
|
}
|
|
@@ -6565,7 +8123,7 @@ var RemoteController = class {
|
|
|
6565
8123
|
let bearerTokenHash = "";
|
|
6566
8124
|
if (exposesGateway) {
|
|
6567
8125
|
bearerToken = randomBytes(32).toString("base64url");
|
|
6568
|
-
bearerTokenHash =
|
|
8126
|
+
bearerTokenHash = sha2562(bearerToken);
|
|
6569
8127
|
}
|
|
6570
8128
|
const state = {
|
|
6571
8129
|
provider: providerId,
|
|
@@ -6630,7 +8188,7 @@ function pickProvider(id) {
|
|
|
6630
8188
|
const _exhaustive = id;
|
|
6631
8189
|
throw new Error(`unknown remote provider: ${String(_exhaustive)}`);
|
|
6632
8190
|
}
|
|
6633
|
-
function
|
|
8191
|
+
function sha2562(input2) {
|
|
6634
8192
|
return createHash("sha256").update(input2).digest("hex");
|
|
6635
8193
|
}
|
|
6636
8194
|
function errToMessage(err) {
|
|
@@ -6723,7 +8281,7 @@ var WorkspacePathError = class extends Error {
|
|
|
6723
8281
|
};
|
|
6724
8282
|
function createWorkspaceFs(opts) {
|
|
6725
8283
|
const root = resolve(opts.workspace);
|
|
6726
|
-
function
|
|
8284
|
+
function resolvePath42(path) {
|
|
6727
8285
|
if (typeof path !== "string" || path.length === 0) {
|
|
6728
8286
|
throw new WorkspacePathError("path must be a non-empty string");
|
|
6729
8287
|
}
|
|
@@ -6743,18 +8301,18 @@ function createWorkspaceFs(opts) {
|
|
|
6743
8301
|
}
|
|
6744
8302
|
return {
|
|
6745
8303
|
async readFile(path) {
|
|
6746
|
-
const abs =
|
|
8304
|
+
const abs = resolvePath42(path);
|
|
6747
8305
|
const buf = await readFile(abs);
|
|
6748
8306
|
return buf.toString("utf8");
|
|
6749
8307
|
},
|
|
6750
8308
|
async writeFile(path, content) {
|
|
6751
|
-
const abs =
|
|
8309
|
+
const abs = resolvePath42(path);
|
|
6752
8310
|
await mkdir(dirname(abs), { recursive: true });
|
|
6753
8311
|
await writeFile(abs, content);
|
|
6754
8312
|
},
|
|
6755
8313
|
async exists(path) {
|
|
6756
8314
|
try {
|
|
6757
|
-
const abs =
|
|
8315
|
+
const abs = resolvePath42(path);
|
|
6758
8316
|
return existsSync(abs);
|
|
6759
8317
|
} catch {
|
|
6760
8318
|
return false;
|
|
@@ -6949,18 +8507,40 @@ async function runServe(args) {
|
|
|
6949
8507
|
port: { type: "string", short: "p" },
|
|
6950
8508
|
bind: { type: "string", short: "b" },
|
|
6951
8509
|
"allow-origin": { type: "string", multiple: true },
|
|
6952
|
-
interactive: { type: "boolean", short: "i" }
|
|
8510
|
+
interactive: { type: "boolean", short: "i" },
|
|
8511
|
+
profile: { type: "string" }
|
|
6953
8512
|
}
|
|
6954
8513
|
});
|
|
6955
8514
|
const cfg = await loadConfig();
|
|
6956
|
-
const
|
|
6957
|
-
const
|
|
8515
|
+
const profileName = values.profile ?? cfg.activeProfile;
|
|
8516
|
+
const profile = profileName ? cfg.profiles?.[profileName] : void 0;
|
|
8517
|
+
if (values.profile && !profile) {
|
|
8518
|
+
process.stderr.write(
|
|
8519
|
+
`agentproto serve: profile "${values.profile}" not found in ~/.agentproto/config.json. Available: ${cfg.profiles ? Object.keys(cfg.profiles).join(", ") || "(none)" : "(no profiles block)"}
|
|
8520
|
+
`
|
|
8521
|
+
);
|
|
8522
|
+
return 2;
|
|
8523
|
+
}
|
|
8524
|
+
if (cfg.activeProfile && !values.profile && !profile) {
|
|
8525
|
+
process.stderr.write(
|
|
8526
|
+
`agentproto serve: \u26A0 activeProfile="${cfg.activeProfile}" but no matching entry in profiles[]; falling back to top-level config.
|
|
8527
|
+
`
|
|
8528
|
+
);
|
|
8529
|
+
}
|
|
8530
|
+
if (profile && process.stdout.isTTY) {
|
|
8531
|
+
process.stdout.write(
|
|
8532
|
+
`agentproto serve: using profile "${profileName}"
|
|
8533
|
+
`
|
|
8534
|
+
);
|
|
8535
|
+
}
|
|
8536
|
+
const cfgDaemon = { ...cfg.daemon ?? {}, ...profile?.daemon ?? {} };
|
|
8537
|
+
const cfgTunnel = { ...cfg.tunnel ?? {}, ...profile?.tunnel ?? {} };
|
|
6958
8538
|
const workspace = resolve(
|
|
6959
8539
|
values.workspace ?? cfgDaemon.workspace ?? process.cwd()
|
|
6960
8540
|
);
|
|
6961
8541
|
try {
|
|
6962
|
-
const
|
|
6963
|
-
if (!
|
|
8542
|
+
const stat5 = await promises.stat(workspace);
|
|
8543
|
+
if (!stat5.isDirectory()) {
|
|
6964
8544
|
process.stderr.write(
|
|
6965
8545
|
`agentproto serve: --workspace "${workspace}" is not a directory.
|
|
6966
8546
|
`
|
|
@@ -6983,7 +8563,7 @@ async function runServe(args) {
|
|
|
6983
8563
|
}
|
|
6984
8564
|
const label = values.label ?? cfgDaemon.label ?? `${userInfo().username}@${hostname()}`;
|
|
6985
8565
|
const connectFlag = values.connect ?? (cfgTunnel.autoconnect && cfgTunnel.host ? cfgTunnel.host : void 0);
|
|
6986
|
-
let token = values.token ?? process.env.AGENTPROTO_TOKEN;
|
|
8566
|
+
let token = values.token ?? process.env.AGENTPROTO_TOKEN ?? cfgTunnel.token;
|
|
6987
8567
|
if (!token && connectFlag) {
|
|
6988
8568
|
const cred = await readHost(connectFlag);
|
|
6989
8569
|
if (cred) {
|
|
@@ -7119,8 +8699,8 @@ ${color.dim}\u2500\u2500 shutting down (${signal}) \u2500\u2500${color.reset}
|
|
|
7119
8699
|
AGENTPROTO_DAEMON_TOKEN: gateway.token
|
|
7120
8700
|
}
|
|
7121
8701
|
});
|
|
7122
|
-
await new Promise((
|
|
7123
|
-
child.once("exit", () =>
|
|
8702
|
+
await new Promise((resolve9) => {
|
|
8703
|
+
child.once("exit", () => resolve9());
|
|
7124
8704
|
aborter.signal.addEventListener("abort", () => {
|
|
7125
8705
|
try {
|
|
7126
8706
|
child.kill("SIGTERM");
|
|
@@ -7140,8 +8720,8 @@ ${color.dim}\u2500\u2500 shutting down (${signal}) \u2500\u2500${color.reset}
|
|
|
7140
8720
|
`${color.dim}Press Ctrl-C to stop.${color.reset}
|
|
7141
8721
|
`
|
|
7142
8722
|
);
|
|
7143
|
-
await new Promise((
|
|
7144
|
-
aborter.signal.addEventListener("abort", () =>
|
|
8723
|
+
await new Promise((resolve9) => {
|
|
8724
|
+
aborter.signal.addEventListener("abort", () => resolve9());
|
|
7145
8725
|
});
|
|
7146
8726
|
return 0;
|
|
7147
8727
|
}
|
|
@@ -7160,7 +8740,7 @@ ${color.dim}\u2500\u2500 shutting down (${signal}) \u2500\u2500${color.reset}
|
|
|
7160
8740
|
reconnectState.immediate = false;
|
|
7161
8741
|
continue;
|
|
7162
8742
|
}
|
|
7163
|
-
await
|
|
8743
|
+
await sleep3(2e3, aborter.signal);
|
|
7164
8744
|
} catch (err) {
|
|
7165
8745
|
if (aborter.signal.aborted) break;
|
|
7166
8746
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -7169,7 +8749,7 @@ ${color.dim}\u2500\u2500 shutting down (${signal}) \u2500\u2500${color.reset}
|
|
|
7169
8749
|
reconnecting in ${backoffMs}ms\u2026
|
|
7170
8750
|
`
|
|
7171
8751
|
);
|
|
7172
|
-
await
|
|
8752
|
+
await sleep3(backoffMs, aborter.signal);
|
|
7173
8753
|
backoffMs = Math.min(backoffMs * 2, backoffMax);
|
|
7174
8754
|
}
|
|
7175
8755
|
}
|
|
@@ -7182,10 +8762,10 @@ async function runOneTunnel(opts, gateway, spawnPty, signal, reconnectState) {
|
|
|
7182
8762
|
};
|
|
7183
8763
|
if (opts.token) headers.authorization = `Bearer ${opts.token}`;
|
|
7184
8764
|
const ws = new WebSocket2(opts.connect, { headers });
|
|
7185
|
-
await new Promise((
|
|
8765
|
+
await new Promise((resolve9, reject) => {
|
|
7186
8766
|
const onOpen = () => {
|
|
7187
8767
|
ws.off("error", onError);
|
|
7188
|
-
|
|
8768
|
+
resolve9();
|
|
7189
8769
|
};
|
|
7190
8770
|
const onError = (err) => {
|
|
7191
8771
|
ws.off("open", onOpen);
|
|
@@ -7239,14 +8819,14 @@ async function runOneTunnel(opts, gateway, spawnPty, signal, reconnectState) {
|
|
|
7239
8819
|
} catch {
|
|
7240
8820
|
}
|
|
7241
8821
|
}
|
|
7242
|
-
return await new Promise((
|
|
8822
|
+
return await new Promise((resolve9, reject) => {
|
|
7243
8823
|
const sock = new WebSocket2(url, protocols ? [...protocols] : void 0, {
|
|
7244
8824
|
headers: upstreamHeaders
|
|
7245
8825
|
});
|
|
7246
8826
|
const onceOpen = () => {
|
|
7247
8827
|
sock.off("error", onceError);
|
|
7248
8828
|
sock.off("unexpected-response", onceUnexpected);
|
|
7249
|
-
|
|
8829
|
+
resolve9({
|
|
7250
8830
|
protocol: sock.protocol ?? "",
|
|
7251
8831
|
send: (data, sendOpts) => {
|
|
7252
8832
|
sock.send(data, { binary: sendOpts.binary });
|
|
@@ -7320,31 +8900,31 @@ async function runOneTunnel(opts, gateway, spawnPty, signal, reconnectState) {
|
|
|
7320
8900
|
}
|
|
7321
8901
|
}
|
|
7322
8902
|
});
|
|
7323
|
-
await new Promise((
|
|
8903
|
+
await new Promise((resolve9) => {
|
|
7324
8904
|
const offClose = sink.onClose(() => {
|
|
7325
8905
|
clearInterval(keepaliveInterval);
|
|
7326
8906
|
offClose();
|
|
7327
|
-
|
|
8907
|
+
resolve9();
|
|
7328
8908
|
});
|
|
7329
8909
|
if (signal.aborted) {
|
|
7330
8910
|
clearInterval(keepaliveInterval);
|
|
7331
|
-
server.close().finally(() =>
|
|
8911
|
+
server.close().finally(() => resolve9());
|
|
7332
8912
|
}
|
|
7333
8913
|
signal.addEventListener("abort", () => {
|
|
7334
8914
|
clearInterval(keepaliveInterval);
|
|
7335
|
-
server.close().finally(() =>
|
|
8915
|
+
server.close().finally(() => resolve9());
|
|
7336
8916
|
});
|
|
7337
8917
|
});
|
|
7338
8918
|
process.stderr.write(`agentproto serve: tunnel closed.
|
|
7339
8919
|
`);
|
|
7340
8920
|
}
|
|
7341
|
-
function
|
|
7342
|
-
return new Promise((
|
|
7343
|
-
if (signal.aborted) return
|
|
7344
|
-
const timer = setTimeout(
|
|
8921
|
+
function sleep3(ms, signal) {
|
|
8922
|
+
return new Promise((resolve9) => {
|
|
8923
|
+
if (signal.aborted) return resolve9();
|
|
8924
|
+
const timer = setTimeout(resolve9, ms);
|
|
7345
8925
|
signal.addEventListener("abort", () => {
|
|
7346
8926
|
clearTimeout(timer);
|
|
7347
|
-
|
|
8927
|
+
resolve9();
|
|
7348
8928
|
});
|
|
7349
8929
|
});
|
|
7350
8930
|
}
|
|
@@ -7391,7 +8971,7 @@ var color = _isTty ? {
|
|
|
7391
8971
|
cyan: "",
|
|
7392
8972
|
red: ""
|
|
7393
8973
|
};
|
|
7394
|
-
var
|
|
8974
|
+
var USAGE4 = `agentproto workspace \u2014 manage local workspaces
|
|
7395
8975
|
|
|
7396
8976
|
Usage:
|
|
7397
8977
|
agentproto workspace add <path> [--slug <slug>] [--label <text>]
|
|
@@ -7404,7 +8984,7 @@ Config file: ${DEFAULT_CONFIG_PATH()}
|
|
|
7404
8984
|
`;
|
|
7405
8985
|
async function runWorkspace(args) {
|
|
7406
8986
|
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
7407
|
-
process.stdout.write(
|
|
8987
|
+
process.stdout.write(USAGE4);
|
|
7408
8988
|
return 0;
|
|
7409
8989
|
}
|
|
7410
8990
|
const sub = args[0];
|
|
@@ -7414,7 +8994,7 @@ async function runWorkspace(args) {
|
|
|
7414
8994
|
return runAdd(rest);
|
|
7415
8995
|
case "list":
|
|
7416
8996
|
case "ls":
|
|
7417
|
-
return
|
|
8997
|
+
return runList2(rest);
|
|
7418
8998
|
case "remove":
|
|
7419
8999
|
case "rm":
|
|
7420
9000
|
return runRemove(rest);
|
|
@@ -7424,7 +9004,7 @@ async function runWorkspace(args) {
|
|
|
7424
9004
|
process.stderr.write(
|
|
7425
9005
|
`agentproto workspace: unknown subcommand '${sub}'
|
|
7426
9006
|
|
|
7427
|
-
${
|
|
9007
|
+
${USAGE4}`
|
|
7428
9008
|
);
|
|
7429
9009
|
return 2;
|
|
7430
9010
|
}
|
|
@@ -7469,7 +9049,7 @@ async function runAdd(args) {
|
|
|
7469
9049
|
);
|
|
7470
9050
|
return 0;
|
|
7471
9051
|
}
|
|
7472
|
-
async function
|
|
9052
|
+
async function runList2(args) {
|
|
7473
9053
|
const { values } = parseArgs({
|
|
7474
9054
|
args: [...args],
|
|
7475
9055
|
allowPositionals: false,
|
|
@@ -7619,7 +9199,7 @@ function probeMtimeLatestJsonl2(storeRel) {
|
|
|
7619
9199
|
}
|
|
7620
9200
|
|
|
7621
9201
|
// src/commands/sessions.ts
|
|
7622
|
-
var
|
|
9202
|
+
var USAGE5 = `agentproto sessions \u2014 browse and control daemon sessions
|
|
7623
9203
|
|
|
7624
9204
|
Usage:
|
|
7625
9205
|
agentproto sessions [--watch] [--json]
|
|
@@ -7643,7 +9223,7 @@ While attached:
|
|
|
7643
9223
|
`;
|
|
7644
9224
|
async function runSessions(args) {
|
|
7645
9225
|
if (args.includes("--help") || args.includes("-h")) {
|
|
7646
|
-
process.stdout.write(
|
|
9226
|
+
process.stdout.write(USAGE5);
|
|
7647
9227
|
return 0;
|
|
7648
9228
|
}
|
|
7649
9229
|
const sub = args[0];
|
|
@@ -7978,9 +9558,9 @@ function printNoDaemonError(report, verb) {
|
|
|
7978
9558
|
}
|
|
7979
9559
|
async function readRuntimeJsonWithStatus(workspacePath) {
|
|
7980
9560
|
const path = resolve(workspacePath, ".agentproto", "runtime.json");
|
|
7981
|
-
let
|
|
9561
|
+
let stat5;
|
|
7982
9562
|
try {
|
|
7983
|
-
|
|
9563
|
+
stat5 = await promises.stat(path);
|
|
7984
9564
|
} catch {
|
|
7985
9565
|
return { endpoint: null };
|
|
7986
9566
|
}
|
|
@@ -8003,7 +9583,7 @@ async function readRuntimeJsonWithStatus(workspacePath) {
|
|
|
8003
9583
|
stale: {
|
|
8004
9584
|
path,
|
|
8005
9585
|
pid: parsed.pid ?? null,
|
|
8006
|
-
mtime:
|
|
9586
|
+
mtime: stat5.mtime ?? null
|
|
8007
9587
|
}
|
|
8008
9588
|
};
|
|
8009
9589
|
}
|
|
@@ -8804,7 +10384,7 @@ function stripAnsi(s) {
|
|
|
8804
10384
|
return s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
8805
10385
|
}
|
|
8806
10386
|
function httpDelete(url, token) {
|
|
8807
|
-
return new Promise((
|
|
10387
|
+
return new Promise((resolve9, reject) => {
|
|
8808
10388
|
const u = new URL(url);
|
|
8809
10389
|
const lib = u.protocol === "https:" ? https : http;
|
|
8810
10390
|
const headers = {};
|
|
@@ -8820,7 +10400,7 @@ function httpDelete(url, token) {
|
|
|
8820
10400
|
return;
|
|
8821
10401
|
}
|
|
8822
10402
|
try {
|
|
8823
|
-
|
|
10403
|
+
resolve9(raw ? JSON.parse(raw) : {});
|
|
8824
10404
|
} catch (err) {
|
|
8825
10405
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
8826
10406
|
}
|
|
@@ -9387,21 +10967,21 @@ async function runPtyAttach(endpoint, desc, _colour) {
|
|
|
9387
10967
|
});
|
|
9388
10968
|
}
|
|
9389
10969
|
async function probeDaemonHealth(baseUrl) {
|
|
9390
|
-
return new Promise((
|
|
10970
|
+
return new Promise((resolve9) => {
|
|
9391
10971
|
try {
|
|
9392
10972
|
const u = new URL(`${baseUrl}/health`);
|
|
9393
10973
|
const lib = u.protocol === "https:" ? https : http;
|
|
9394
10974
|
const req = lib.get(u, { timeout: 500 }, (res) => {
|
|
9395
|
-
|
|
10975
|
+
resolve9((res.statusCode ?? 0) >= 200 && (res.statusCode ?? 0) < 300);
|
|
9396
10976
|
res.resume();
|
|
9397
10977
|
});
|
|
9398
|
-
req.on("error", () =>
|
|
10978
|
+
req.on("error", () => resolve9(false));
|
|
9399
10979
|
req.on("timeout", () => {
|
|
9400
10980
|
req.destroy();
|
|
9401
|
-
|
|
10981
|
+
resolve9(false);
|
|
9402
10982
|
});
|
|
9403
10983
|
} catch {
|
|
9404
|
-
|
|
10984
|
+
resolve9(false);
|
|
9405
10985
|
}
|
|
9406
10986
|
});
|
|
9407
10987
|
}
|
|
@@ -9441,7 +11021,7 @@ async function explain401(endpoint, verb) {
|
|
|
9441
11021
|
return lines.join("\n");
|
|
9442
11022
|
}
|
|
9443
11023
|
function httpPostJson(url, body, token) {
|
|
9444
|
-
return new Promise((
|
|
11024
|
+
return new Promise((resolve9, reject) => {
|
|
9445
11025
|
const u = new URL(url);
|
|
9446
11026
|
const payload = Buffer.from(JSON.stringify(body), "utf8");
|
|
9447
11027
|
const lib = u.protocol === "https:" ? https : http;
|
|
@@ -9464,7 +11044,7 @@ function httpPostJson(url, body, token) {
|
|
|
9464
11044
|
return;
|
|
9465
11045
|
}
|
|
9466
11046
|
try {
|
|
9467
|
-
|
|
11047
|
+
resolve9(JSON.parse(raw));
|
|
9468
11048
|
} catch (err) {
|
|
9469
11049
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
9470
11050
|
}
|
|
@@ -9480,7 +11060,7 @@ async function httpGetJson(url) {
|
|
|
9480
11060
|
return await httpGetJsonImpl(url);
|
|
9481
11061
|
}
|
|
9482
11062
|
function httpGetJsonImpl(url) {
|
|
9483
|
-
return new Promise((
|
|
11063
|
+
return new Promise((resolve9, reject) => {
|
|
9484
11064
|
const u = new URL(url);
|
|
9485
11065
|
const lib = u.protocol === "https:" ? https : http;
|
|
9486
11066
|
lib.get(u, (res) => {
|
|
@@ -9493,7 +11073,7 @@ function httpGetJsonImpl(url) {
|
|
|
9493
11073
|
return;
|
|
9494
11074
|
}
|
|
9495
11075
|
try {
|
|
9496
|
-
|
|
11076
|
+
resolve9(JSON.parse(body));
|
|
9497
11077
|
} catch (err) {
|
|
9498
11078
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
9499
11079
|
}
|
|
@@ -9503,16 +11083,20 @@ function httpGetJsonImpl(url) {
|
|
|
9503
11083
|
}
|
|
9504
11084
|
|
|
9505
11085
|
// src/cli.ts
|
|
9506
|
-
var
|
|
11086
|
+
var USAGE7 = `agentproto \u2014 AIP-45 agent CLI host
|
|
9507
11087
|
|
|
9508
11088
|
Usage:
|
|
9509
11089
|
agentproto auth <login|status|logout> [--host <url>] [--label <name>]
|
|
9510
11090
|
agentproto config <show|path|get|set|unset|edit> [args]
|
|
9511
11091
|
agentproto daemon <install|uninstall|start|stop|status|logs> [args]
|
|
9512
11092
|
agentproto install <slug> [--force] [--dry-run] [--skip-setup]
|
|
11093
|
+
<slug> \u2208 { <adapter-slug> | runtime-profile/<name> }
|
|
11094
|
+
agentproto plugins <list|show|install|uninstall|enable|disable> [args]
|
|
9513
11095
|
agentproto setup <slug> [--force] [--dry-run] [--only <stepId>...]
|
|
9514
11096
|
agentproto run <slug> [--cwd <dir>] [--prompt <text>] [--resume <session-id>]
|
|
9515
|
-
agentproto
|
|
11097
|
+
agentproto run-swarm --manifest <path> [--once] [--interval <ms|Ns>] [--verbose]
|
|
11098
|
+
agentproto serve [--profile <name>]
|
|
11099
|
+
[--workspace <dir>] [--port <n>] [--bind <ip>]
|
|
9516
11100
|
[--connect <url> [--token <jwt>] [--label <name>]]
|
|
9517
11101
|
[--allow-origin <url> \u2026] [--interactive | -i]
|
|
9518
11102
|
agentproto workspace <add|list|remove|use> [args]
|
|
@@ -9530,12 +11114,14 @@ Usage:
|
|
|
9530
11114
|
Examples:
|
|
9531
11115
|
agentproto auth login --host wss://guilde.work # device flow \u2192 ~/.agentproto/credentials.json
|
|
9532
11116
|
agentproto install claude-code
|
|
11117
|
+
agentproto install runtime-profile/standard # drops .claude/ swarm scaffolding into the cwd
|
|
9533
11118
|
agentproto setup openclaw # re-run setup (idempotent via skip_if + ledger)
|
|
9534
11119
|
agentproto run claude-code --cwd . --prompt "summarise this repo"
|
|
11120
|
+
agentproto run-swarm --manifest .runtime/local.yaml --verbose
|
|
9535
11121
|
agentproto workspace add ~/code/my-project --slug my-project
|
|
9536
11122
|
agentproto workspace list
|
|
9537
11123
|
agentproto serve --connect wss://guilde.work/api/v1/agentproto/tunnel
|
|
9538
|
-
agentproto sessions start claude-code --workspace
|
|
11124
|
+
agentproto sessions start claude-code --workspace my-app --attach
|
|
9539
11125
|
agentproto sessions terminal --name claude-tui --attach -- claude
|
|
9540
11126
|
agentproto sessions stop claude-tui
|
|
9541
11127
|
agentproto config set daemon.port 18791
|
|
@@ -9548,8 +11134,10 @@ var VERBS = /* @__PURE__ */ new Set([
|
|
|
9548
11134
|
"config",
|
|
9549
11135
|
"daemon",
|
|
9550
11136
|
"install",
|
|
11137
|
+
"plugins",
|
|
9551
11138
|
"setup",
|
|
9552
11139
|
"run",
|
|
11140
|
+
"run-swarm",
|
|
9553
11141
|
"serve",
|
|
9554
11142
|
"workspace",
|
|
9555
11143
|
"sessions"
|
|
@@ -9562,13 +11150,13 @@ async function main(argv) {
|
|
|
9562
11150
|
return 0;
|
|
9563
11151
|
}
|
|
9564
11152
|
if (argv.includes("--help") || argv.includes("-h") || argv.length === 0) {
|
|
9565
|
-
process.stdout.write(
|
|
11153
|
+
process.stdout.write(USAGE7);
|
|
9566
11154
|
return 0;
|
|
9567
11155
|
}
|
|
9568
11156
|
process.stderr.write(
|
|
9569
11157
|
`agentproto: unrecognised argument(s): ${argv.join(" ")}
|
|
9570
11158
|
|
|
9571
|
-
${
|
|
11159
|
+
${USAGE7}`
|
|
9572
11160
|
);
|
|
9573
11161
|
return 2;
|
|
9574
11162
|
}
|
|
@@ -9585,10 +11173,14 @@ ${USAGE6}`
|
|
|
9585
11173
|
}
|
|
9586
11174
|
case "install":
|
|
9587
11175
|
return runInstall(rest);
|
|
11176
|
+
case "plugins":
|
|
11177
|
+
return runPlugins(rest);
|
|
9588
11178
|
case "setup":
|
|
9589
11179
|
return runSetupCommand(rest);
|
|
9590
11180
|
case "run":
|
|
9591
11181
|
return runRun(rest);
|
|
11182
|
+
case "run-swarm":
|
|
11183
|
+
return runRunSwarm(rest);
|
|
9592
11184
|
case "serve":
|
|
9593
11185
|
return runServe(rest);
|
|
9594
11186
|
case "workspace":
|
|
@@ -9598,7 +11190,7 @@ ${USAGE6}`
|
|
|
9598
11190
|
default:
|
|
9599
11191
|
process.stderr.write(`agentproto: unknown verb '${verb}'
|
|
9600
11192
|
|
|
9601
|
-
${
|
|
11193
|
+
${USAGE7}`);
|
|
9602
11194
|
return 2;
|
|
9603
11195
|
}
|
|
9604
11196
|
}
|