@ouro.bot/cli 0.1.0-alpha.837 → 0.1.0-alpha.839
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/changelog.json +13 -0
- package/deploy/unraid/sanctuary-butler-upgrade.mjs +22 -4
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +1 -1
- package/deploy/unraid/sanctuary.xml +1 -1
- package/dist/a2a/onboarding.js +23 -0
- package/dist/heart/daemon/cli-exec.js +53 -6
- package/dist/heart/daemon/cli-help.js +2 -2
- package/dist/heart/daemon/cli-parse.js +72 -23
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.839",
|
|
6
|
+
"changes": [
|
|
7
|
+
"CLI: provision A2A peers without editing files. ouro connect a2a --host/--port/--public-url stores this machine's A2A bind address and advertised URL (D-038); ouro a2a onboard --did <did:key> --name <name> onboards a card-less client such as a coding harness, keyed on its DID; ouro friend update sets --admission, --initiative and --profile as well as --trust (D-039).",
|
|
8
|
+
"CLI: a2a onboard keeps the card and DID paths in one command block so its coverage exemptions stay attached to the right code."
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"version": "0.1.0-alpha.838",
|
|
13
|
+
"changes": [
|
|
14
|
+
"Upgrade orchestrator: pausing host supervision first installs this version's keeper scripts and raises the shutdown flag every watchdog honours, so an older keeper can no longer restart the resident mid-upgrade (found by the first 830 to 835 rehearsal, which rolled back cleanly). A rehearsal now reports an unplanned failure as a failure instead of as the planned stop."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
4
17
|
{
|
|
5
18
|
"version": "0.1.0-alpha.837",
|
|
6
19
|
"changes": [
|
|
@@ -59,6 +59,8 @@ const UPGRADE_JOURNAL = `${ROOT}/upgrade.json`
|
|
|
59
59
|
// Host supervision skips its work while this flag is fresh (< 30 min), so a crashed
|
|
60
60
|
// upgrade can never leave self-heal off for longer than that.
|
|
61
61
|
const MAINTENANCE = "/run/ouro-authority-maintenance"
|
|
62
|
+
// The stop hook's flag: every keeper watchdog version stands down while it exists.
|
|
63
|
+
const SHUTDOWN_FLAG = "/run/ouro-authority-shutdown"
|
|
62
64
|
const UPGRADE_STEPS = ["stop", "switch", "resident", "migrate", "start"]
|
|
63
65
|
|
|
64
66
|
const sh = (file, args, opts = {}) => execFileSync(file, args, { encoding: "utf8", maxBuffer: 64 << 20, ...opts })
|
|
@@ -782,13 +784,25 @@ function buildFinalProof(version, out) {
|
|
|
782
784
|
|
|
783
785
|
// ---- upgrade (in place, installed authority) --------------------------------
|
|
784
786
|
|
|
787
|
+
// A host still running an older keeper has a cron watchdog that ignores the maintenance
|
|
788
|
+
// flag: it resurrected the paused supervisor, which restarted the resident mid-upgrade
|
|
789
|
+
// (the first 830 -> 835 rehearsal). So put this version's keeper scripts in place first
|
|
790
|
+
// (they honour the flag) and also raise the shutdown flag every watchdog version honours;
|
|
791
|
+
// resumeSupervision clears both.
|
|
785
792
|
function pauseSupervision() {
|
|
786
793
|
writeFileSync(MAINTENANCE, `${new Date().toISOString()} ${process.pid}\n`)
|
|
794
|
+
writeFileSync(SHUTDOWN_FLAG, `upgrade ${process.pid}\n`)
|
|
795
|
+
for (const [path, body] of [[GATEWAY_SUPERVISOR, GATEWAY_SUPERVISOR_SH], [GATEWAY_WATCHDOG, GATEWAY_WATCHDOG_SH]]) {
|
|
796
|
+
writeFileSync(path, body)
|
|
797
|
+
sh("/bin/chown", ["0:0", path]); chmodSync(path, 0o700)
|
|
798
|
+
}
|
|
787
799
|
sh("/bin/sh", ["-c", "for p in $(pgrep -f '^/bin/sh /boot/config/custom/ouro-authority/[g]ateway-supervisor' || true); do kill $p; done"])
|
|
788
|
-
|
|
800
|
+
if (sh("/bin/sh", ["-c", "pgrep -fc '^/bin/sh /boot/config/custom/ouro-authority/[g]ateway-supervisor' || true"]).trim() !== "0") fail("a gateway supervisor survived the pause")
|
|
801
|
+
ok("host supervision paused (keeper scripts current; flags expire on their own)")
|
|
789
802
|
}
|
|
790
803
|
function resumeSupervision() {
|
|
791
804
|
rmSync(MAINTENANCE, { force: true })
|
|
805
|
+
rmSync(SHUTDOWN_FLAG, { force: true })
|
|
792
806
|
installGatewaySupervisor()
|
|
793
807
|
}
|
|
794
808
|
|
|
@@ -839,8 +853,12 @@ function upgrade(version, rehearse) {
|
|
|
839
853
|
say("lifecycle upgrade (stop → switch → resident → migrate → start)")
|
|
840
854
|
ok(sh("/usr/local/bin/node", [lifecycle, "upgrade", id, image(version), ...(rehearse ? ["--fail-after", rehearse] : [])], { stdio: ["ignore", "pipe", "pipe"] }).trim())
|
|
841
855
|
} catch (e) { failure = e }
|
|
856
|
+
// Only the lifecycle's own rehearsal stop is planned; anything else is a real failure,
|
|
857
|
+
// even during a rehearsal (the first 830 -> 835 rehearsal was mislabelled "as planned").
|
|
858
|
+
const reason = failure ? lifecycleFailure(failure) : ""
|
|
859
|
+
const planned = Boolean(failure && rehearse && reason.includes(`rehearsal stopped after ${rehearse}`))
|
|
842
860
|
if (failure) {
|
|
843
|
-
console.log(`\n!! ${
|
|
861
|
+
console.log(`\n!! ${planned ? "rehearsal stopped as planned" : "upgrade FAILED"}: ${reason}\n!! rolling back to the predecessor`)
|
|
844
862
|
try { ok(`rollback: ${sh("/usr/local/bin/node", [lifecycle, "upgrade-rollback"], { stdio: ["ignore", "pipe", "pipe"] }).trim()}`) }
|
|
845
863
|
catch (e) {
|
|
846
864
|
resumeSupervision()
|
|
@@ -853,8 +871,8 @@ function upgrade(version, rehearse) {
|
|
|
853
871
|
sha12(POLICY) === policyBefore ? ok("steward policy unchanged") : fail("STEWARD POLICY CHANGED")
|
|
854
872
|
const live = docker(["inspect", CONTAINER, "--format", "{{.Config.Image}} {{.State.Status}}/{{.State.Health.Status}}"]).trim()
|
|
855
873
|
ok(`butler: ${live}`)
|
|
856
|
-
if (failure && !
|
|
857
|
-
console.log(rehearse ? "\nREHEARSAL done — the rollback restored the predecessor." : `\nUPGRADE done — ${version} live. Run verify.`)
|
|
874
|
+
if (failure && !planned) fail("upgrade rolled back; the Butler is on its prior version (see above)")
|
|
875
|
+
console.log(rehearse ? "\nREHEARSAL done — the upgrade reached the planned step and the rollback restored the predecessor." : `\nUPGRADE done — ${version} live. Run verify.`)
|
|
858
876
|
}
|
|
859
877
|
|
|
860
878
|
// ---- verify ---------------------------------------------------------------
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<?xml version="1.0"?>
|
|
2
2
|
<Container version="2">
|
|
3
3
|
<Name>ouro-butler</Name>
|
|
4
|
-
<Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.
|
|
4
|
+
<Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.839</Repository>
|
|
5
5
|
<Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
|
|
6
6
|
<Network>host</Network>
|
|
7
7
|
<Shell>sh</Shell>
|
package/dist/a2a/onboarding.js
CHANGED
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.onboardA2APeer = onboardA2APeer;
|
|
37
|
+
exports.onboardA2AClientByDid = onboardA2AClientByDid;
|
|
37
38
|
const path = __importStar(require("node:path"));
|
|
38
39
|
const identity_1 = require("../heart/identity");
|
|
39
40
|
const runtime_1 = require("../nerves/runtime");
|
|
@@ -108,3 +109,25 @@ async function onboardA2APeer(options) {
|
|
|
108
109
|
});
|
|
109
110
|
return record;
|
|
110
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Onboard a client that serves no agent card (for example a coding harness with a
|
|
114
|
+
* file-backed identity) by its did:key alone. The record is keyed on the DID, so its
|
|
115
|
+
* signed, sealed messages resolve to this friend; the key must parse, or nothing is written.
|
|
116
|
+
*/
|
|
117
|
+
async function onboardA2AClientByDid(options) {
|
|
118
|
+
if ((0, a2a_client_1.parseDidKey)(options.did) === null)
|
|
119
|
+
throw new Error(`not a valid did:key: ${options.did}`);
|
|
120
|
+
const record = await (0, friends_1.upsertAgentPeer)(storeFor(options), {
|
|
121
|
+
name: options.name,
|
|
122
|
+
agentId: options.did,
|
|
123
|
+
...(options.trustLevel ? { trustLevel: options.trustLevel } : {}),
|
|
124
|
+
a2a: { did: options.did, agentId: options.did },
|
|
125
|
+
});
|
|
126
|
+
(0, runtime_1.emitNervesEvent)({
|
|
127
|
+
component: "friends",
|
|
128
|
+
event: "friends.a2a_client_onboarded",
|
|
129
|
+
message: "onboarded card-less A2A client into friend model",
|
|
130
|
+
meta: { agentName: options.agentName, friendId: record.id, peerName: record.name, trustLevel: record.trustLevel, did: options.did },
|
|
131
|
+
});
|
|
132
|
+
return record;
|
|
133
|
+
}
|
|
@@ -5053,13 +5053,23 @@ async function executeConnectVoice(agent, deps) {
|
|
|
5053
5053
|
deps.writeStdout(message);
|
|
5054
5054
|
return message;
|
|
5055
5055
|
}
|
|
5056
|
-
async function executeConnectA2A(agent, deps) {
|
|
5056
|
+
async function executeConnectA2A(agent, deps, bind = {}) {
|
|
5057
5057
|
const { defaultA2APort } = await Promise.resolve().then(() => __importStar(require("../../a2a/config")));
|
|
5058
5058
|
enableAgentSense(agent, "a2a", deps);
|
|
5059
5059
|
const syncSummary = pushAgentBundleAfterCliMutation(agent, deps);
|
|
5060
|
-
|
|
5060
|
+
// Bind address, port and advertised URL are per machine (D-038): a tailnet IP on one
|
|
5061
|
+
// host means nothing on another, so they live in this machine's runtime config.
|
|
5062
|
+
const a2a = {
|
|
5063
|
+
...(bind.host !== undefined ? { host: bind.host } : {}),
|
|
5064
|
+
...(bind.port !== undefined ? { port: bind.port } : {}),
|
|
5065
|
+
...(bind.publicUrl !== undefined ? { publicUrl: bind.publicUrl } : {}),
|
|
5066
|
+
};
|
|
5067
|
+
if (Object.keys(a2a).length > 0)
|
|
5068
|
+
await (0, runtime_credentials_1.mergeMachineRuntimeCredentialConfig)(agent, currentMachineId(deps), { a2a }, providerCliNow(deps));
|
|
5069
|
+
const port = bind.port ?? defaultA2APort(agent);
|
|
5061
5070
|
const message = [
|
|
5062
5071
|
`A2A connected for ${agent}`,
|
|
5072
|
+
...(Object.keys(a2a).length > 0 ? [`This machine's A2A listener: ${bind.host ?? "127.0.0.1"}:${port}${bind.publicUrl ? `, advertised as ${bind.publicUrl}` : ""} (applies on the next \`ouro up\`).`] : []),
|
|
5063
5073
|
`The daemon-managed A2A sense will listen locally on port ${port} after \`ouro up\`.`,
|
|
5064
5074
|
`Local agent card: http://127.0.0.1:${port}/.well-known/agent-card.json`,
|
|
5065
5075
|
`Local JSON-RPC endpoint: http://127.0.0.1:${port}/a2a`,
|
|
@@ -5184,7 +5194,7 @@ async function executeConnect(command, deps) {
|
|
|
5184
5194
|
if (command.target === "voice")
|
|
5185
5195
|
return executeConnectVoice(command.agent, deps);
|
|
5186
5196
|
if (command.target === "a2a")
|
|
5187
|
-
return executeConnectA2A(command.agent, deps);
|
|
5197
|
+
return executeConnectA2A(command.agent, deps, { host: command.a2aHost, port: command.a2aPort, publicUrl: command.a2aPublicUrl });
|
|
5188
5198
|
if (command.target === "telegram")
|
|
5189
5199
|
return executeConnectTelegram(command.agent, deps);
|
|
5190
5200
|
if (command.target === "workbench")
|
|
@@ -6034,10 +6044,26 @@ async function executeFriendCommand(command, store) {
|
|
|
6034
6044
|
return `created: ${id} (${command.name}, ${trustLevel})`;
|
|
6035
6045
|
}
|
|
6036
6046
|
if (command.kind === "friend.update") {
|
|
6037
|
-
|
|
6038
|
-
if (result.status === "not_found")
|
|
6047
|
+
if (!await store.get(command.friendId))
|
|
6039
6048
|
return `friend not found: ${command.friendId}`;
|
|
6040
|
-
|
|
6049
|
+
if (command.trustLevel)
|
|
6050
|
+
await (0, friends_1.setFriendTrust)(store, command.friendId, command.trustLevel);
|
|
6051
|
+
const relationship = {
|
|
6052
|
+
...(command.admissionState ? { admissionState: command.admissionState } : {}),
|
|
6053
|
+
...(command.initiativePolicy ? { initiativePolicy: command.initiativePolicy } : {}),
|
|
6054
|
+
...(command.capabilityProfileId ? { capabilityProfileId: command.capabilityProfileId } : {}),
|
|
6055
|
+
};
|
|
6056
|
+
if (Object.keys(relationship).length > 0) {
|
|
6057
|
+
const current = (await store.get(command.friendId));
|
|
6058
|
+
await store.put(command.friendId, { ...current, ...relationship, updatedAt: new Date().toISOString() });
|
|
6059
|
+
}
|
|
6060
|
+
const changed = [
|
|
6061
|
+
command.trustLevel ? `trust=${command.trustLevel}` : "",
|
|
6062
|
+
command.admissionState ? `admission=${command.admissionState}` : "",
|
|
6063
|
+
command.initiativePolicy ? `initiative=${command.initiativePolicy}` : "",
|
|
6064
|
+
command.capabilityProfileId ? `profile=${command.capabilityProfileId}` : "",
|
|
6065
|
+
].filter(Boolean).join(", ");
|
|
6066
|
+
return `updated: ${command.friendId} → ${changed}`;
|
|
6041
6067
|
}
|
|
6042
6068
|
if (command.kind === "friend.link") {
|
|
6043
6069
|
const result = await (0, friends_1.linkExternalId)(store, command.friendId, {
|
|
@@ -6107,6 +6133,27 @@ async function executeA2ACommand(command, deps) {
|
|
|
6107
6133
|
}
|
|
6108
6134
|
/* v8 ignore next -- false branch is foreground serve, intentionally ignored below because it waits for signals @preserve */
|
|
6109
6135
|
if (command.kind === "a2a.onboard") {
|
|
6136
|
+
if (command.did) {
|
|
6137
|
+
const { onboardA2AClientByDid } = await Promise.resolve().then(() => __importStar(require("../../a2a/onboarding")));
|
|
6138
|
+
const record = await onboardA2AClientByDid({
|
|
6139
|
+
agentName: command.agent,
|
|
6140
|
+
did: command.did,
|
|
6141
|
+
name: command.name,
|
|
6142
|
+
...(command.trustLevel ? { trustLevel: command.trustLevel } : {}),
|
|
6143
|
+
/* v8 ignore next -- production CLI falls back to the canonical bundle root; tests inject isolated roots @preserve */
|
|
6144
|
+
...(deps.bundlesRoot ? { bundlesRoot: deps.bundlesRoot } : {}),
|
|
6145
|
+
});
|
|
6146
|
+
const message = [
|
|
6147
|
+
`onboarded A2A client: ${record.name}`,
|
|
6148
|
+
`friend id: ${record.id}`,
|
|
6149
|
+
/* v8 ignore next -- upsertAgentPeer always writes an explicit TrustLevel @preserve */
|
|
6150
|
+
`trust: ${record.trustLevel ?? "unknown"}`,
|
|
6151
|
+
`did: ${command.did}`,
|
|
6152
|
+
`Next: ouro friend update ${record.id} --agent ${command.agent} --admission active --initiative reactive_only --profile <capability-profile-id>`,
|
|
6153
|
+
].join("\n");
|
|
6154
|
+
deps.writeStdout(message);
|
|
6155
|
+
return message;
|
|
6156
|
+
}
|
|
6110
6157
|
const { onboardA2APeer } = await Promise.resolve().then(() => __importStar(require("../../a2a/onboarding")));
|
|
6111
6158
|
const record = await onboardA2APeer({
|
|
6112
6159
|
agentName: command.agent,
|
|
@@ -448,7 +448,7 @@ const SUBCOMMAND_HELP = {
|
|
|
448
448
|
},
|
|
449
449
|
"connect a2a": {
|
|
450
450
|
description: "Enable the agent-to-agent A2A sense",
|
|
451
|
-
usage: "ouro connect a2a [--agent <name>]",
|
|
451
|
+
usage: "ouro connect a2a [--agent <name>] [--host <address>] [--port <port>] [--public-url <url>]",
|
|
452
452
|
example: "ouro connect a2a --agent <agent>",
|
|
453
453
|
},
|
|
454
454
|
"connect telegram": {
|
|
@@ -468,7 +468,7 @@ const SUBCOMMAND_HELP = {
|
|
|
468
468
|
},
|
|
469
469
|
"a2a onboard": {
|
|
470
470
|
description: "Onboard an A2A peer into the existing friend model",
|
|
471
|
-
usage: "ouro a2a onboard [--agent <name>] --card-url <url> [--trust <level>] [--name <name>]",
|
|
471
|
+
usage: "ouro a2a onboard [--agent <name>] (--card-url <url>|--did <did:key> --name <name>) [--trust <level>] [--name <name>]",
|
|
472
472
|
example: "ouro a2a onboard --agent <agent> --card-url https://peer.example/.well-known/agent-card.json --trust friend",
|
|
473
473
|
},
|
|
474
474
|
"a2a serve": {
|
|
@@ -141,7 +141,7 @@ function usage() {
|
|
|
141
141
|
" ouro friend list [--agent <name>]",
|
|
142
142
|
" ouro friend show <id> [--agent <name>]",
|
|
143
143
|
" ouro friend create --name <name> [--trust <level>] [--agent <name>]",
|
|
144
|
-
" ouro friend update <id> --trust <level> [--agent <name>]",
|
|
144
|
+
" ouro friend update <id> [--trust <level>] [--admission <state>] [--initiative <policy>] [--profile <id>] [--agent <name>]",
|
|
145
145
|
" ouro thoughts [--last <n>] [--json] [--follow] [--agent <name>]",
|
|
146
146
|
" ouro private decisions [--agent <name>] [--limit <n>] [--json]",
|
|
147
147
|
" ouro private status [--agent <name>] [--json]",
|
|
@@ -150,7 +150,7 @@ function usage() {
|
|
|
150
150
|
" ouro friend link <agent> --friend <id> --provider <p> --external-id <eid>",
|
|
151
151
|
" ouro friend unlink <agent> --friend <id> --provider <p> --external-id <eid>",
|
|
152
152
|
" ouro a2a card [--agent <name>] [--base-url <url>] [--json]",
|
|
153
|
-
" ouro a2a onboard [--agent <name>] --card-url <url> [--trust <level>] [--name <name>]",
|
|
153
|
+
" ouro a2a onboard [--agent <name>] (--card-url <url>|--did <did:key> --name <name>) [--trust <level>] [--name <name>]",
|
|
154
154
|
" ouro a2a serve [--agent <name>] [--host <host>] [--port <port>] [--base-url <url>] [--path <path>]",
|
|
155
155
|
" ouro a2a identity [--identity-file <path>] [--json]",
|
|
156
156
|
" ouro a2a message --to <card-url> --text <text> [--context <id>] [--identity-file <path>] [--json]",
|
|
@@ -1099,20 +1099,58 @@ function extractMailSourceFlags(args, usageText) {
|
|
|
1099
1099
|
hasMailSourceFlags,
|
|
1100
1100
|
};
|
|
1101
1101
|
}
|
|
1102
|
+
// `ouro connect a2a` bind flags: where the listener binds and the URL its card advertises (D-038).
|
|
1103
|
+
function extractA2AConnectFlags(args) {
|
|
1104
|
+
const rest = [];
|
|
1105
|
+
const flags = {};
|
|
1106
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
1107
|
+
const value = args[i + 1];
|
|
1108
|
+
if (args[i] === "--host" && value) {
|
|
1109
|
+
flags.host = value;
|
|
1110
|
+
i += 1;
|
|
1111
|
+
continue;
|
|
1112
|
+
}
|
|
1113
|
+
if (args[i] === "--public-url" && value) {
|
|
1114
|
+
const url = new URL(value);
|
|
1115
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
1116
|
+
throw new Error("A2A public URL must be http(s)");
|
|
1117
|
+
flags.publicUrl = value.replace(/\/+$/u, "");
|
|
1118
|
+
i += 1;
|
|
1119
|
+
continue;
|
|
1120
|
+
}
|
|
1121
|
+
if (args[i] === "--port" && value) {
|
|
1122
|
+
const port = Number(value);
|
|
1123
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
1124
|
+
throw new Error("A2A port must be 1-65535");
|
|
1125
|
+
flags.port = port;
|
|
1126
|
+
i += 1;
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
rest.push(args[i]);
|
|
1130
|
+
}
|
|
1131
|
+
return { rest, ...flags };
|
|
1132
|
+
}
|
|
1102
1133
|
function parseConnectCommand(args) {
|
|
1103
|
-
const usageText = "Usage: ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a|telegram|workbench] [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys]";
|
|
1134
|
+
const usageText = "Usage: ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a|telegram|workbench] [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys] [a2a: --host <address> --port <port> --public-url <url>]";
|
|
1104
1135
|
const { agent, rest: afterAgent } = extractAgentFlag(args);
|
|
1105
|
-
const
|
|
1136
|
+
const a2aFlags = extractA2AConnectFlags(afterAgent);
|
|
1137
|
+
const mailFlags = extractMailSourceFlags(a2aFlags.rest, usageText);
|
|
1106
1138
|
if (mailFlags.rest.length > 1)
|
|
1107
1139
|
throw new Error(usageText);
|
|
1108
1140
|
const target = normalizeConnectTarget(mailFlags.rest[0]);
|
|
1109
1141
|
if (mailFlags.hasMailSourceFlags && target !== "mail") {
|
|
1110
1142
|
throw new Error("Mail source flags require `ouro connect mail`.");
|
|
1111
1143
|
}
|
|
1144
|
+
const hasA2AFlags = a2aFlags.host !== undefined || a2aFlags.port !== undefined || a2aFlags.publicUrl !== undefined;
|
|
1145
|
+
if (hasA2AFlags && target !== "a2a")
|
|
1146
|
+
throw new Error("--host, --port and --public-url require `ouro connect a2a`.");
|
|
1112
1147
|
return {
|
|
1113
1148
|
kind: "connect",
|
|
1114
1149
|
...(agent ? { agent } : {}),
|
|
1115
1150
|
...(target ? { target } : {}),
|
|
1151
|
+
...(a2aFlags.host !== undefined ? { a2aHost: a2aFlags.host } : {}),
|
|
1152
|
+
...(a2aFlags.port !== undefined ? { a2aPort: a2aFlags.port } : {}),
|
|
1153
|
+
...(a2aFlags.publicUrl !== undefined ? { a2aPublicUrl: a2aFlags.publicUrl } : {}),
|
|
1116
1154
|
...(mailFlags.ownerEmail !== undefined ? { ownerEmail: mailFlags.ownerEmail } : {}),
|
|
1117
1155
|
...(mailFlags.source !== undefined ? { source: mailFlags.source } : {}),
|
|
1118
1156
|
...(mailFlags.noDelegatedSource ? { noDelegatedSource: true } : {}),
|
|
@@ -1405,25 +1443,30 @@ function parseFriendCommand(args) {
|
|
|
1405
1443
|
}
|
|
1406
1444
|
if (sub === "update") {
|
|
1407
1445
|
const friendId = rest[0];
|
|
1446
|
+
const usage = "Usage: ouro friend update <id> [--trust <stranger|acquaintance|friend|family>] [--admission <unverified|active|revoked>] [--initiative <none|reactive_only|request_follow_up_only|proactive>] [--profile <capability-profile-id>]";
|
|
1408
1447
|
if (!friendId)
|
|
1409
|
-
throw new Error(
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1448
|
+
throw new Error(usage);
|
|
1449
|
+
// Every relationship field an owner provisions, not just trust (D-039).
|
|
1450
|
+
const choices = {
|
|
1451
|
+
"--trust": { key: "trustLevel", values: ["stranger", "acquaintance", "friend", "family"] },
|
|
1452
|
+
"--admission": { key: "admissionState", values: ["unverified", "active", "revoked"] },
|
|
1453
|
+
"--initiative": { key: "initiativePolicy", values: ["none", "reactive_only", "request_follow_up_only", "proactive"] },
|
|
1454
|
+
"--profile": { key: "capabilityProfileId" },
|
|
1455
|
+
};
|
|
1456
|
+
const fields = {};
|
|
1457
|
+
for (let i = 1; i < rest.length; i += 2) {
|
|
1458
|
+
const choice = choices[rest[i]];
|
|
1459
|
+
const value = rest[i + 1];
|
|
1460
|
+
if (!choice || !value || (choice.values && !choice.values.includes(value)))
|
|
1461
|
+
throw new Error(usage);
|
|
1462
|
+
fields[choice.key] = value;
|
|
1422
1463
|
}
|
|
1464
|
+
if (Object.keys(fields).length === 0)
|
|
1465
|
+
throw new Error(usage);
|
|
1423
1466
|
return {
|
|
1424
1467
|
kind: "friend.update",
|
|
1425
1468
|
friendId,
|
|
1426
|
-
|
|
1469
|
+
...fields,
|
|
1427
1470
|
...(agent ? { agent } : {}),
|
|
1428
1471
|
};
|
|
1429
1472
|
}
|
|
@@ -1504,6 +1547,7 @@ function parseA2ACommand(args) {
|
|
|
1504
1547
|
}
|
|
1505
1548
|
if (sub === "onboard") {
|
|
1506
1549
|
let cardUrl;
|
|
1550
|
+
let did;
|
|
1507
1551
|
let trustLevel;
|
|
1508
1552
|
let name;
|
|
1509
1553
|
const VALID_TRUST_LEVELS = new Set(["stranger", "acquaintance", "friend", "family"]);
|
|
@@ -1512,10 +1556,14 @@ function parseA2ACommand(args) {
|
|
|
1512
1556
|
cardUrl = rest[++i];
|
|
1513
1557
|
continue;
|
|
1514
1558
|
}
|
|
1559
|
+
if (rest[i] === "--did" && rest[i + 1]) {
|
|
1560
|
+
did = rest[++i];
|
|
1561
|
+
continue;
|
|
1562
|
+
}
|
|
1515
1563
|
if (rest[i] === "--trust" && rest[i + 1]) {
|
|
1516
1564
|
const raw = rest[++i];
|
|
1517
1565
|
if (!VALID_TRUST_LEVELS.has(raw))
|
|
1518
|
-
throw new Error("Usage: ouro a2a onboard [--agent <name>] --card-url <url> [--trust <stranger|acquaintance|friend|family>] [--name <name>]");
|
|
1566
|
+
throw new Error("Usage: ouro a2a onboard [--agent <name>] (--card-url <url>|--did <did:key> --name <name>) [--trust <stranger|acquaintance|friend|family>] [--name <name>]");
|
|
1519
1567
|
trustLevel = raw;
|
|
1520
1568
|
continue;
|
|
1521
1569
|
}
|
|
@@ -1523,11 +1571,12 @@ function parseA2ACommand(args) {
|
|
|
1523
1571
|
name = rest[++i];
|
|
1524
1572
|
continue;
|
|
1525
1573
|
}
|
|
1526
|
-
throw new Error("Usage: ouro a2a onboard [--agent <name>] --card-url <url> [--trust <level>] [--name <name>]");
|
|
1574
|
+
throw new Error("Usage: ouro a2a onboard [--agent <name>] (--card-url <url>|--did <did:key> --name <name>) [--trust <level>] [--name <name>]");
|
|
1527
1575
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1576
|
+
// A client that serves no card (a coding harness) is onboarded by its did:key alone.
|
|
1577
|
+
if (!cardUrl === !did || (did && !name))
|
|
1578
|
+
throw new Error("Usage: ouro a2a onboard [--agent <name>] (--card-url <url>|--did <did:key> --name <name>) [--trust <level>] [--name <name>]");
|
|
1579
|
+
return { kind: "a2a.onboard", ...(cardUrl ? { cardUrl } : { did: did }), ...(agent ? { agent } : {}), ...(trustLevel ? { trustLevel } : {}), ...(name ? { name } : {}) };
|
|
1531
1580
|
}
|
|
1532
1581
|
if (sub === "serve") {
|
|
1533
1582
|
let host;
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.839",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@ouro.bot/cli",
|
|
9
|
-
"version": "0.1.0-alpha.
|
|
9
|
+
"version": "0.1.0-alpha.839",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
12
12
|
"@azure/identity": "^4.13.0",
|