@cello-protocol/cli 0.0.33 → 0.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/cello.js +22 -0
- package/dist/bin/cello.js.map +1 -1
- package/dist/cli-args.d.ts +1 -1
- package/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +7 -1
- package/dist/cli-args.js.map +1 -1
- package/dist/hermes/assets.d.ts +35 -0
- package/dist/hermes/assets.d.ts.map +1 -0
- package/dist/hermes/assets.js +602 -0
- package/dist/hermes/assets.js.map +1 -0
- package/dist/hermes/install-hermes.d.ts +35 -0
- package/dist/hermes/install-hermes.d.ts.map +1 -0
- package/dist/hermes/install-hermes.js +140 -0
- package/dist/hermes/install-hermes.js.map +1 -0
- package/package.json +2 -2
package/dist/bin/cello.js
CHANGED
|
@@ -161,6 +161,28 @@ async function main() {
|
|
|
161
161
|
}
|
|
162
162
|
break;
|
|
163
163
|
}
|
|
164
|
+
case "install": {
|
|
165
|
+
// cello install hermes --agent <name> [--hermes-home <path>] (HERMES-001)
|
|
166
|
+
const args = process.argv.slice(3);
|
|
167
|
+
const agentIdx = args.indexOf("--agent");
|
|
168
|
+
const homeIdx = args.indexOf("--hermes-home");
|
|
169
|
+
// Find the target positional, excluding both flags AND their values — so
|
|
170
|
+
// `cello install --agent alice hermes` still resolves target=hermes (mirrors
|
|
171
|
+
// the `contact` case's positional filter).
|
|
172
|
+
const target = args.find((a, i) => !a.startsWith("-") &&
|
|
173
|
+
!(agentIdx !== -1 && i === agentIdx + 1) &&
|
|
174
|
+
!(homeIdx !== -1 && i === homeIdx + 1));
|
|
175
|
+
if (target !== "hermes") {
|
|
176
|
+
result = { exitCode: 1, output: helpForCommand("install") };
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
const { installHermes } = await import("../hermes/install-hermes.js");
|
|
180
|
+
result = await installHermes({
|
|
181
|
+
agentName: agentIdx !== -1 ? (args[agentIdx + 1] ?? "") : "",
|
|
182
|
+
hermesHome: homeIdx !== -1 ? args[homeIdx + 1] : undefined,
|
|
183
|
+
});
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
164
186
|
default:
|
|
165
187
|
// M8B F1: the usage string lists EVERY command (refresh/receipts were missing).
|
|
166
188
|
process.stdout.write(USAGE + "\n");
|
package/dist/bin/cello.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cello.js","sourceRoot":"","sources":["../../src/bin/cello.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cello.js","sourceRoot":"","sources":["../../src/bin/cello.ts"],"names":[],"mappings":"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"}
|
package/dist/cli-args.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* instead of being coerced into a positional argument (previously
|
|
8
8
|
* `cello register --help` tried to register an agent literally named "--help").
|
|
9
9
|
*/
|
|
10
|
-
export declare const KNOWN_COMMANDS: Set<"login" | "logout" | "status" | "register" | "create-agent" | "remove-agent" | "refresh" | "receipts" | "sessions" | "contact" | "telegram">;
|
|
10
|
+
export declare const KNOWN_COMMANDS: Set<"login" | "logout" | "status" | "register" | "create-agent" | "remove-agent" | "refresh" | "receipts" | "sessions" | "contact" | "telegram" | "install">;
|
|
11
11
|
export type KnownCommand = typeof KNOWN_COMMANDS extends Set<infer T> ? T : never;
|
|
12
12
|
export declare const USAGE: string;
|
|
13
13
|
export declare function helpForCommand(command: string): string;
|
package/dist/cli-args.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-args.d.ts","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"cli-args.d.ts","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,cAAc,8JAahB,CAAC;AAEZ,MAAM,MAAM,YAAY,GAAG,OAAO,cAAc,SAAS,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAIlF,eAAO,MAAM,KAAK,QAO0C,CAAC;AAsC7D,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtD;AASD,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GACd;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3C;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS,CAWpE"}
|
package/dist/cli-args.js
CHANGED
|
@@ -19,6 +19,7 @@ export const KNOWN_COMMANDS = new Set([
|
|
|
19
19
|
"sessions",
|
|
20
20
|
"contact",
|
|
21
21
|
"telegram",
|
|
22
|
+
"install",
|
|
22
23
|
]);
|
|
23
24
|
// CC-7 (P2-5 / DOD-ONBOARD-HELP-1): the top level opens with what CELLO is + the onboarding path a
|
|
24
25
|
// first-time user needs, then the command list. (Per-command `cello <cmd> --help` was already good.)
|
|
@@ -27,7 +28,7 @@ export const USAGE = "CELLO — a peer-to-peer identity & trust layer for agent-
|
|
|
27
28
|
"First-time setup: cello login → cello create-agent <name> → cello register <name> <token> → cello status\n" +
|
|
28
29
|
" (get <token> from the CELLO Operations Agent on Telegram; 'cello login' starts the local daemon.)\n" +
|
|
29
30
|
"\n" +
|
|
30
|
-
"Usage: cello <login|logout|status|register|create-agent|remove-agent|refresh|receipts|sessions|contact|telegram>\n" +
|
|
31
|
+
"Usage: cello <login|logout|status|register|create-agent|remove-agent|refresh|receipts|sessions|contact|telegram|install>\n" +
|
|
31
32
|
"Run 'cello <command> --help' for details on any command.";
|
|
32
33
|
/** Per-command usage lines (the same text the commands print on bad input). */
|
|
33
34
|
const COMMAND_HELP = {
|
|
@@ -53,6 +54,10 @@ const COMMAND_HELP = {
|
|
|
53
54
|
" Example: cello contact list --agent alice",
|
|
54
55
|
telegram: "Usage: cello telegram set-token <bot_token> <allowlisted_chat_id> — configure the daemon-owned Telegram doorbell (M8C-TGDOOR-1).\n" +
|
|
55
56
|
" Starts a single long-lived poller immediately; the operator chat given is the ONLY one that ever receives doorbell events.",
|
|
57
|
+
install: "Usage: cello install hermes --agent <name> [--hermes-home <path>] — wire the local CELLO daemon into a Hermes Agent installation.\n" +
|
|
58
|
+
" Scaffolds the CELLO platform-adapter plugin into the Hermes home (default ~/.hermes), binds CELLO_AGENT_NAME in its .env,\n" +
|
|
59
|
+
" and registers via 'hermes plugins enable cello' + 'hermes mcp add cello'. Idempotent — re-run to upgrade.\n" +
|
|
60
|
+
" After installing, restart the gateway: hermes gateway restart",
|
|
56
61
|
};
|
|
57
62
|
export function helpForCommand(command) {
|
|
58
63
|
return COMMAND_HELP[command] ?? USAGE;
|
|
@@ -61,6 +66,7 @@ export function helpForCommand(command) {
|
|
|
61
66
|
const COMMAND_FLAGS = {
|
|
62
67
|
sessions: new Set(["--open", "--closed", "--failed", "--all", "--limit"]),
|
|
63
68
|
contact: new Set(["--agent"]),
|
|
69
|
+
install: new Set(["--agent", "--hermes-home"]),
|
|
64
70
|
};
|
|
65
71
|
/**
|
|
66
72
|
* F2: validate a subcommand's arguments BEFORE dispatch.
|
package/dist/cli-args.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-args.js","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IACpC,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,cAAc;IACd,cAAc;IACd,SAAS;IACT,UAAU;IACV,UAAU;IACV,SAAS;IACT,UAAU;
|
|
1
|
+
{"version":3,"file":"cli-args.js","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IACpC,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,cAAc;IACd,cAAc;IACd,SAAS;IACT,UAAU;IACV,UAAU;IACV,SAAS;IACT,UAAU;IACV,SAAS;CACD,CAAC,CAAC;AAIZ,mGAAmG;AACnG,qGAAqG;AACrG,MAAM,CAAC,MAAM,KAAK,GAChB,mFAAmF;IACnF,IAAI;IACJ,mHAAmH;IACnH,uGAAuG;IACvG,IAAI;IACJ,4HAA4H;IAC5H,0DAA0D,CAAC;AAE7D,+EAA+E;AAC/E,MAAM,YAAY,GAA2B;IAC3C,KAAK,EAAE,yEAAyE;IAChF,MAAM,EAAE,6DAA6D;IACrE,MAAM,EAAE,+EAA+E;IACvF,QAAQ,EACN,gGAAgG;QAChG,6JAA6J;QAC7J,iJAAiJ;QACjJ,kDAAkD;QAClD,6FAA6F;QAC7F,wFAAwF;IAC1F,cAAc,EACZ,yGAAyG;QACzG,uGAAuG;QACvG,0FAA0F;IAC5F,cAAc,EAAE,yFAAyF;IACzG,OAAO,EAAE,8FAA8F;IACvG,QAAQ,EAAE,kFAAkF;IAC5F,QAAQ,EACN,gHAAgH;IAClH,OAAO,EACL,6IAA6I;QAC7I,qGAAqG;QACrG,gHAAgH;QAChH,8CAA8C;IAChD,QAAQ,EACN,qIAAqI;QACrI,8HAA8H;IAChI,OAAO,EACL,sIAAsI;QACtI,+HAA+H;QAC/H,+GAA+G;QAC/G,iEAAiE;CACpE,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,OAAO,YAAY,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC;AACxC,CAAC;AAED,6EAA6E;AAC7E,MAAM,aAAa,GAAwC;IACzD,QAAQ,EAAE,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IACzE,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;IAC7B,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;CAC/C,CAAC;AAOF;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,IAAc;IACvD,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC5E,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;IAC1D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;YAChE,IAAI,GAAG,KAAK,SAAS;gBAAE,CAAC,EAAE,CAAC,CAAC,qBAAqB;QACnD,CAAC;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HERMES-001 — file assets scaffolded by `cello install hermes`.
|
|
3
|
+
*
|
|
4
|
+
* These are the exact file contents written into the operator's Hermes Agent home
|
|
5
|
+
* (default ~/.hermes). They are embedded as string constants because the published
|
|
6
|
+
* CLI package ships only dist/ (see core/cli package.json "files") — a separate asset
|
|
7
|
+
* directory would not survive npm packaging without build-pipeline changes.
|
|
8
|
+
*
|
|
9
|
+
* Design source: trustless-cello docs/planning/discussion_logs/
|
|
10
|
+
* 2026-07-09_1915_hermes-agent-integration-plan.md §3–§5. The adapter mirrors the
|
|
11
|
+
* shape of Hermes' bundled Raft platform adapter (hermes-agent
|
|
12
|
+
* plugins/platforms/raft/adapter.py) minus every piece of bridge machinery that only
|
|
13
|
+
* exists to cross a process/language boundary: CELLO's daemon speaks newline-delimited
|
|
14
|
+
* JSON over a Unix socket, so a pure-stdlib asyncio client talks to it in-process —
|
|
15
|
+
* no subprocess, no HTTP hop, no bridge token.
|
|
16
|
+
*/
|
|
17
|
+
/** Hermes plugin manifest — `~/.hermes/plugins/cello/plugin.yaml`. */
|
|
18
|
+
export declare const HERMES_PLUGIN_YAML = "name: cello\nlabel: CELLO\nkind: platform\nversion: 0.1.0\ndescription: >\n CELLO trust-layer platform adapter for Hermes Agent. Connects to the local\n CELLO daemon over its Unix-socket IPC, binds this Hermes instance to one\n registered CELLO agent, and injects content-free wake hints when a session\n changes state or a message arrives. The agent reads and replies through the\n cello_* MCP tools - the adapter never touches message content.\nauthor: cello-protocol\nrequires_env:\n - name: CELLO_AGENT_NAME\n description: \"Registered CELLO agent this Hermes instance binds to - auto-enables the adapter when set\"\n prompt: \"CELLO agent name\"\n password: false\n category: setting\n";
|
|
19
|
+
/**
|
|
20
|
+
* The CELLO platform adapter — `~/.hermes/plugins/cello/__init__.py`.
|
|
21
|
+
*
|
|
22
|
+
* Python, stdlib-only. Runs inside the Hermes gateway process, where the
|
|
23
|
+
* `gateway` package is already importable (the plugin loader imports this module
|
|
24
|
+
* in-process). Speaks the daemon IPC protocol directly:
|
|
25
|
+
* request {"id": str, "method": str, "params": {...}}\n
|
|
26
|
+
* response {"id": str, "result": ...} | {"id": str, "error": {code,message,guidance}}
|
|
27
|
+
* notification {"notification": str, "data": {...}} (server-initiated, no id)
|
|
28
|
+
* Handshake: ipc.connect {clientType} -> cello_use_agent {name} (binds notification
|
|
29
|
+
* routing: session_state_changed / cello_message reach only connections whose
|
|
30
|
+
* currentAgent matches — see daemon NotificationDispatcher).
|
|
31
|
+
*/
|
|
32
|
+
export declare const HERMES_PLUGIN_INIT_PY: string;
|
|
33
|
+
/** The setup skill — `~/.hermes/skills/cello-bridge-setup/SKILL.md`. */
|
|
34
|
+
export declare const HERMES_SKILL_MD = "---\nname: cello-bridge-setup\ndescription: \"Install and configure the CELLO agent-to-agent bridge for this Hermes instance.\"\nversion: 1.0.0\nplatforms: [linux, macos]\nmetadata:\n hermes:\n tags: [cello, messaging, integration, agent-to-agent]\n related_skills: []\n---\n\n# CELLO Bridge Setup\n\nCELLO is a peer-to-peer identity and trust layer for agent-to-agent communication:\nsplit-key signing, tamper-evident hash chains, and content-free wake notifications.\nThis skill wires the local CELLO daemon into Hermes so this agent can talk to other\nCELLO agents anywhere.\n\nTrigger: /cello-bridge-setup, or \"install the CELLO bridge\".\n\n## Steps\n\n1. **Check CELLO is set up.** Run `cello status`. If the CLI is missing or the daemon\n is not running, walk the user through CELLO onboarding first:\n `npx --yes @cello-protocol/cli@latest login`, then `cello create-agent <name>`,\n then `cello register <name> <pre-auth-token>` (token from the CELLO Operations\n Agent on Telegram). Confirm with `cello status`.\n2. **Pick the agent.** Ask the user which registered CELLO agent this Hermes instance\n should bind to (the `cello status` output lists them).\n3. **Run the installer** \u2014 one command does all the work (plugin scaffold, env binding,\n `hermes plugins enable cello`, `hermes mcp add cello`):\n\n cello install hermes --agent <name>\n\n Pass `--hermes-home <path>` only if Hermes does not live at ~/.hermes.\n4. **Restart the gateway:** `hermes gateway restart`.\n5. **Verify.** Call the `cello_status` MCP tool and report the bound agent's state and\n `standing_receiver_ready`. The bridge is live when the agent shows online.\n\n## How to operate CELLO (after setup)\n\n- Wake notices from the CELLO platform are **content-free** \u2014 they name a session and a\n counterparty pubkey, never the message. Fetch content with `cello_check_notifications`\n and `cello_receive`.\n- **Read before you send.** Call `cello_receive` on a session before `cello_send`; the\n daemon rejects out-of-cursor sends with `session_not_current`.\n- If a wake needs no reply, answer with exactly `[SILENT]` and make no `cello_send` call.\n";
|
|
35
|
+
//# sourceMappingURL=assets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/hermes/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,ysBAiB9B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,QAufjC,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,eAAe,yoEA+C3B,CAAC"}
|
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HERMES-001 — file assets scaffolded by `cello install hermes`.
|
|
3
|
+
*
|
|
4
|
+
* These are the exact file contents written into the operator's Hermes Agent home
|
|
5
|
+
* (default ~/.hermes). They are embedded as string constants because the published
|
|
6
|
+
* CLI package ships only dist/ (see core/cli package.json "files") — a separate asset
|
|
7
|
+
* directory would not survive npm packaging without build-pipeline changes.
|
|
8
|
+
*
|
|
9
|
+
* Design source: trustless-cello docs/planning/discussion_logs/
|
|
10
|
+
* 2026-07-09_1915_hermes-agent-integration-plan.md §3–§5. The adapter mirrors the
|
|
11
|
+
* shape of Hermes' bundled Raft platform adapter (hermes-agent
|
|
12
|
+
* plugins/platforms/raft/adapter.py) minus every piece of bridge machinery that only
|
|
13
|
+
* exists to cross a process/language boundary: CELLO's daemon speaks newline-delimited
|
|
14
|
+
* JSON over a Unix socket, so a pure-stdlib asyncio client talks to it in-process —
|
|
15
|
+
* no subprocess, no HTTP hop, no bridge token.
|
|
16
|
+
*/
|
|
17
|
+
/** Hermes plugin manifest — `~/.hermes/plugins/cello/plugin.yaml`. */
|
|
18
|
+
export const HERMES_PLUGIN_YAML = `name: cello
|
|
19
|
+
label: CELLO
|
|
20
|
+
kind: platform
|
|
21
|
+
version: 0.1.0
|
|
22
|
+
description: >
|
|
23
|
+
CELLO trust-layer platform adapter for Hermes Agent. Connects to the local
|
|
24
|
+
CELLO daemon over its Unix-socket IPC, binds this Hermes instance to one
|
|
25
|
+
registered CELLO agent, and injects content-free wake hints when a session
|
|
26
|
+
changes state or a message arrives. The agent reads and replies through the
|
|
27
|
+
cello_* MCP tools - the adapter never touches message content.
|
|
28
|
+
author: cello-protocol
|
|
29
|
+
requires_env:
|
|
30
|
+
- name: CELLO_AGENT_NAME
|
|
31
|
+
description: "Registered CELLO agent this Hermes instance binds to - auto-enables the adapter when set"
|
|
32
|
+
prompt: "CELLO agent name"
|
|
33
|
+
password: false
|
|
34
|
+
category: setting
|
|
35
|
+
`;
|
|
36
|
+
/**
|
|
37
|
+
* The CELLO platform adapter — `~/.hermes/plugins/cello/__init__.py`.
|
|
38
|
+
*
|
|
39
|
+
* Python, stdlib-only. Runs inside the Hermes gateway process, where the
|
|
40
|
+
* `gateway` package is already importable (the plugin loader imports this module
|
|
41
|
+
* in-process). Speaks the daemon IPC protocol directly:
|
|
42
|
+
* request {"id": str, "method": str, "params": {...}}\n
|
|
43
|
+
* response {"id": str, "result": ...} | {"id": str, "error": {code,message,guidance}}
|
|
44
|
+
* notification {"notification": str, "data": {...}} (server-initiated, no id)
|
|
45
|
+
* Handshake: ipc.connect {clientType} -> cello_use_agent {name} (binds notification
|
|
46
|
+
* routing: session_state_changed / cello_message reach only connections whose
|
|
47
|
+
* currentAgent matches — see daemon NotificationDispatcher).
|
|
48
|
+
*/
|
|
49
|
+
export const HERMES_PLUGIN_INIT_PY = String.raw `"""CELLO platform adapter for Hermes Agent.
|
|
50
|
+
|
|
51
|
+
Connects to the local CELLO daemon over its Unix-socket IPC (newline-delimited
|
|
52
|
+
JSON), binds this Hermes instance to one registered CELLO agent, and injects
|
|
53
|
+
content-free wake hints into the normal gateway session pipeline when a CELLO
|
|
54
|
+
session changes state or a message arrives. The agent itself reads and replies
|
|
55
|
+
through the cello_* MCP tools (the 'cello' entry in mcp_servers) - this adapter
|
|
56
|
+
never touches message content, and its send() is a deliberate no-op.
|
|
57
|
+
|
|
58
|
+
Installed and kept up to date by 'cello install hermes'. Do not edit in place;
|
|
59
|
+
re-run the installer to upgrade.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
from __future__ import annotations
|
|
63
|
+
|
|
64
|
+
import asyncio
|
|
65
|
+
import json
|
|
66
|
+
import logging
|
|
67
|
+
import os
|
|
68
|
+
import re
|
|
69
|
+
import uuid
|
|
70
|
+
from pathlib import Path
|
|
71
|
+
from typing import Any, Dict, Optional
|
|
72
|
+
|
|
73
|
+
from gateway.config import Platform, PlatformConfig
|
|
74
|
+
from gateway.platforms.base import (
|
|
75
|
+
BasePlatformAdapter,
|
|
76
|
+
MessageEvent,
|
|
77
|
+
MessageType,
|
|
78
|
+
SendResult,
|
|
79
|
+
merge_pending_message_event,
|
|
80
|
+
)
|
|
81
|
+
from gateway.session import build_session_key
|
|
82
|
+
|
|
83
|
+
logger = logging.getLogger(__name__)
|
|
84
|
+
|
|
85
|
+
DEFAULT_RUNTIME_SESSION = "default"
|
|
86
|
+
# The two daemon notifications that mean "this agent's attention is needed".
|
|
87
|
+
# agent_state_changed / agent_current_changed are connection bookkeeping, not wakes.
|
|
88
|
+
WAKE_NOTIFICATIONS = {"session_state_changed", "cello_message"}
|
|
89
|
+
RECONNECT_INITIAL_DELAY = 1.0
|
|
90
|
+
RECONNECT_MAX_DELAY = 30.0
|
|
91
|
+
CALL_TIMEOUT_SECONDS = 30.0
|
|
92
|
+
# Matches the daemon IPC server's MAX_BUFFER_SIZE (4 MB).
|
|
93
|
+
MAX_LINE_BYTES = 4 * 1024 * 1024
|
|
94
|
+
|
|
95
|
+
# Same recursive content-free guard the Raft adapter applies to wake payloads.
|
|
96
|
+
# The daemon's INV-CONTENTFREE invariant means these keys never appear in a
|
|
97
|
+
# notification; if one does, something upstream is violating the protocol and
|
|
98
|
+
# the wake is dropped loudly rather than relayed.
|
|
99
|
+
_CONTENT_FIELD_NAMES = {
|
|
100
|
+
"body",
|
|
101
|
+
"content",
|
|
102
|
+
"message",
|
|
103
|
+
"messages",
|
|
104
|
+
"preview",
|
|
105
|
+
"snippet",
|
|
106
|
+
"text",
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
# Pubkeys are base64/hex-ish, session ids are uuid-ish; anything outside this
|
|
110
|
+
# conservative charset (or overlong) renders as 'unknown' in the wake prompt.
|
|
111
|
+
_SAFE_SCALAR_RE = re.compile(r"^[A-Za-z0-9+/=_.:@-]{1,120}$")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _cello_socket_path() -> Path:
|
|
115
|
+
cello_dir = os.environ.get("CELLO_DIR") or str(Path.home() / ".cello")
|
|
116
|
+
return Path(cello_dir) / "daemon.sock"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def check_cello_requirements() -> bool:
|
|
120
|
+
"""Passive dependency probe (platform check_fn) - intentionally silent.
|
|
121
|
+
|
|
122
|
+
Called on every gateway config load; the registry logs its own warning when
|
|
123
|
+
requirements are unmet and the adapter is actually requested.
|
|
124
|
+
"""
|
|
125
|
+
return _cello_socket_path().exists()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _has_content_field(value: Any) -> bool:
|
|
129
|
+
if isinstance(value, dict):
|
|
130
|
+
for key, nested in value.items():
|
|
131
|
+
if str(key).strip().lower() in _CONTENT_FIELD_NAMES:
|
|
132
|
+
return True
|
|
133
|
+
if _has_content_field(nested):
|
|
134
|
+
return True
|
|
135
|
+
elif isinstance(value, list):
|
|
136
|
+
return any(_has_content_field(item) for item in value)
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _safe_scalar(value: Any, default: str = "unknown") -> str:
|
|
141
|
+
if not isinstance(value, str) or not value:
|
|
142
|
+
return default
|
|
143
|
+
if not _SAFE_SCALAR_RE.match(value):
|
|
144
|
+
return default
|
|
145
|
+
return value
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class CelloAdapter(BasePlatformAdapter):
|
|
149
|
+
"""Wake-only adapter: daemon IPC notifications in, nothing out.
|
|
150
|
+
|
|
151
|
+
Replies happen through the cello_* MCP tools as the agent's own tool
|
|
152
|
+
actions - never through the gateway delivery pipeline - so send() is a
|
|
153
|
+
no-op, exactly like the Raft adapter.
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
def __init__(self, config: PlatformConfig):
|
|
157
|
+
super().__init__(config, Platform("cello"))
|
|
158
|
+
extra = config.extra or {}
|
|
159
|
+
self._agent_name: str = str(
|
|
160
|
+
extra.get("agent_name") or os.environ.get("CELLO_AGENT_NAME", "")
|
|
161
|
+
).strip()
|
|
162
|
+
self._runtime_session: str = str(
|
|
163
|
+
extra.get("runtime_session", DEFAULT_RUNTIME_SESSION)
|
|
164
|
+
or DEFAULT_RUNTIME_SESSION
|
|
165
|
+
)
|
|
166
|
+
self._writer: Optional[asyncio.StreamWriter] = None
|
|
167
|
+
self._read_task: Optional[asyncio.Task] = None
|
|
168
|
+
self._reconnect_task: Optional[asyncio.Task] = None
|
|
169
|
+
self._pending: Dict[str, asyncio.Future] = {}
|
|
170
|
+
self._next_id = 1
|
|
171
|
+
self._closing = False
|
|
172
|
+
|
|
173
|
+
# ------------------------------------------------------------------ lifecycle
|
|
174
|
+
|
|
175
|
+
async def connect(self, *, is_reconnect: bool = False) -> bool:
|
|
176
|
+
if not self._agent_name:
|
|
177
|
+
logger.error(
|
|
178
|
+
"[cello] CELLO_AGENT_NAME is not set - cannot bind this Hermes "
|
|
179
|
+
"instance to a CELLO agent. Run 'cello install hermes --agent <name>' "
|
|
180
|
+
"or set CELLO_AGENT_NAME in the Hermes env file, then restart the gateway."
|
|
181
|
+
)
|
|
182
|
+
return False
|
|
183
|
+
self._closing = False
|
|
184
|
+
try:
|
|
185
|
+
await self._establish()
|
|
186
|
+
except Exception as exc:
|
|
187
|
+
logger.error(
|
|
188
|
+
"[cello] Could not connect to the CELLO daemon at %s: %s. "
|
|
189
|
+
"Is the daemon running? Start it with 'cello login'.",
|
|
190
|
+
_cello_socket_path(),
|
|
191
|
+
exc,
|
|
192
|
+
)
|
|
193
|
+
# Suppress the read loop's auto-reconnect: a failed INITIAL connect must
|
|
194
|
+
# report failure and stop, not keep retrying behind a False return.
|
|
195
|
+
self._closing = True
|
|
196
|
+
if self._read_task is not None and not self._read_task.done():
|
|
197
|
+
self._read_task.cancel()
|
|
198
|
+
await self._teardown_socket()
|
|
199
|
+
return False
|
|
200
|
+
self._mark_connected()
|
|
201
|
+
logger.info(
|
|
202
|
+
"[cello] Connected to the CELLO daemon; bound to agent '%s'",
|
|
203
|
+
self._agent_name,
|
|
204
|
+
)
|
|
205
|
+
return True
|
|
206
|
+
|
|
207
|
+
async def disconnect(self) -> None:
|
|
208
|
+
self._closing = True
|
|
209
|
+
for task in (self._read_task, self._reconnect_task):
|
|
210
|
+
if task is not None and not task.done():
|
|
211
|
+
task.cancel()
|
|
212
|
+
self._read_task = None
|
|
213
|
+
self._reconnect_task = None
|
|
214
|
+
await self._teardown_socket()
|
|
215
|
+
self._mark_disconnected()
|
|
216
|
+
logger.info("[cello] Disconnected")
|
|
217
|
+
|
|
218
|
+
async def _establish(self) -> None:
|
|
219
|
+
"""Open the socket, start the frame reader, and run the IPC handshake."""
|
|
220
|
+
reader, writer = await asyncio.open_unix_connection(
|
|
221
|
+
str(_cello_socket_path()), limit=MAX_LINE_BYTES
|
|
222
|
+
)
|
|
223
|
+
self._writer = writer
|
|
224
|
+
self._read_task = asyncio.create_task(self._read_loop(reader))
|
|
225
|
+
|
|
226
|
+
await self._call("ipc.connect", {"clientType": "hermes"})
|
|
227
|
+
result = await self._call("cello_use_agent", {"name": self._agent_name})
|
|
228
|
+
if isinstance(result, dict) and result.get("ok") is False:
|
|
229
|
+
reason = str(result.get("reason", "unknown"))
|
|
230
|
+
# agent_already_current means a previous connection for this agent is
|
|
231
|
+
# simply still selected - a fine state to land in on reconnect.
|
|
232
|
+
if reason != "agent_already_current":
|
|
233
|
+
raise RuntimeError(
|
|
234
|
+
"cello_use_agent failed: "
|
|
235
|
+
+ reason
|
|
236
|
+
+ " - "
|
|
237
|
+
+ str(result.get("guidance", ""))
|
|
238
|
+
)
|
|
239
|
+
if isinstance(result, dict) and result.get("warning"):
|
|
240
|
+
# e.g. not_registered: selected and usable locally, but no directory
|
|
241
|
+
# sessions until 'cello register' - surface it, do not block the bind.
|
|
242
|
+
logger.warning(
|
|
243
|
+
"[cello] %s",
|
|
244
|
+
result.get("warning_guidance") or result.get("warning"),
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
async def _teardown_socket(self) -> None:
|
|
248
|
+
writer = self._writer
|
|
249
|
+
self._writer = None
|
|
250
|
+
if writer is not None:
|
|
251
|
+
try:
|
|
252
|
+
writer.close()
|
|
253
|
+
await writer.wait_closed()
|
|
254
|
+
except Exception:
|
|
255
|
+
pass
|
|
256
|
+
self._fail_pending("connection closed")
|
|
257
|
+
|
|
258
|
+
def _fail_pending(self, reason: str) -> None:
|
|
259
|
+
pending = list(self._pending.values())
|
|
260
|
+
self._pending.clear()
|
|
261
|
+
for fut in pending:
|
|
262
|
+
if not fut.done():
|
|
263
|
+
fut.set_exception(ConnectionError(reason))
|
|
264
|
+
|
|
265
|
+
async def _reconnect_forever(self) -> None:
|
|
266
|
+
"""Background retry after a lost daemon connection (e.g. daemon restart)."""
|
|
267
|
+
delay = RECONNECT_INITIAL_DELAY
|
|
268
|
+
while not self._closing:
|
|
269
|
+
await asyncio.sleep(delay)
|
|
270
|
+
try:
|
|
271
|
+
await self._teardown_socket()
|
|
272
|
+
await self._establish()
|
|
273
|
+
except Exception as exc:
|
|
274
|
+
logger.warning(
|
|
275
|
+
"[cello] Reconnect to the CELLO daemon failed (%s); retrying in %.0fs",
|
|
276
|
+
exc,
|
|
277
|
+
min(delay * 2, RECONNECT_MAX_DELAY),
|
|
278
|
+
)
|
|
279
|
+
delay = min(delay * 2, RECONNECT_MAX_DELAY)
|
|
280
|
+
continue
|
|
281
|
+
self._mark_connected()
|
|
282
|
+
logger.info(
|
|
283
|
+
"[cello] Reconnected to the CELLO daemon; agent '%s' re-bound",
|
|
284
|
+
self._agent_name,
|
|
285
|
+
)
|
|
286
|
+
return
|
|
287
|
+
|
|
288
|
+
# ------------------------------------------------------------------ IPC client
|
|
289
|
+
|
|
290
|
+
async def _call(
|
|
291
|
+
self,
|
|
292
|
+
method: str,
|
|
293
|
+
params: Optional[Dict[str, Any]] = None,
|
|
294
|
+
timeout: float = CALL_TIMEOUT_SECONDS,
|
|
295
|
+
) -> Any:
|
|
296
|
+
writer = self._writer
|
|
297
|
+
if writer is None:
|
|
298
|
+
raise ConnectionError("IPC socket is not connected")
|
|
299
|
+
req_id = str(self._next_id)
|
|
300
|
+
self._next_id += 1
|
|
301
|
+
fut: asyncio.Future = asyncio.get_running_loop().create_future()
|
|
302
|
+
self._pending[req_id] = fut
|
|
303
|
+
frame = json.dumps({"id": req_id, "method": method, "params": params or {}})
|
|
304
|
+
try:
|
|
305
|
+
writer.write((frame + "\n").encode("utf-8"))
|
|
306
|
+
await writer.drain()
|
|
307
|
+
return await asyncio.wait_for(fut, timeout)
|
|
308
|
+
finally:
|
|
309
|
+
self._pending.pop(req_id, None)
|
|
310
|
+
|
|
311
|
+
async def _read_loop(self, reader: asyncio.StreamReader) -> None:
|
|
312
|
+
try:
|
|
313
|
+
while True:
|
|
314
|
+
line = await reader.readline()
|
|
315
|
+
if not line:
|
|
316
|
+
break
|
|
317
|
+
stripped = line.strip()
|
|
318
|
+
if not stripped:
|
|
319
|
+
continue
|
|
320
|
+
try:
|
|
321
|
+
frame = json.loads(stripped)
|
|
322
|
+
except json.JSONDecodeError:
|
|
323
|
+
logger.warning(
|
|
324
|
+
"[cello] Malformed IPC frame (%d bytes) - skipped", len(stripped)
|
|
325
|
+
)
|
|
326
|
+
continue
|
|
327
|
+
if not isinstance(frame, dict):
|
|
328
|
+
continue
|
|
329
|
+
# Notification frames (server-initiated, no id) are checked FIRST and
|
|
330
|
+
# never consume a pending request - mirrors cello-mcp's IpcProxy.
|
|
331
|
+
if "notification" in frame:
|
|
332
|
+
try:
|
|
333
|
+
await self._on_notification(frame)
|
|
334
|
+
except Exception:
|
|
335
|
+
logger.exception("[cello] Failed to handle daemon notification")
|
|
336
|
+
continue
|
|
337
|
+
fut = self._pending.pop(str(frame.get("id", "")), None)
|
|
338
|
+
if fut is None or fut.done():
|
|
339
|
+
continue
|
|
340
|
+
if "error" in frame:
|
|
341
|
+
err = frame.get("error") or {}
|
|
342
|
+
fut.set_result(
|
|
343
|
+
{
|
|
344
|
+
"ok": False,
|
|
345
|
+
"reason": err.get("code"),
|
|
346
|
+
"message": err.get("message"),
|
|
347
|
+
"guidance": err.get("guidance"),
|
|
348
|
+
}
|
|
349
|
+
)
|
|
350
|
+
else:
|
|
351
|
+
fut.set_result(frame.get("result"))
|
|
352
|
+
except asyncio.CancelledError:
|
|
353
|
+
raise
|
|
354
|
+
except Exception:
|
|
355
|
+
logger.exception("[cello] IPC read loop crashed")
|
|
356
|
+
finally:
|
|
357
|
+
self._fail_pending("connection closed")
|
|
358
|
+
# Spawn at most ONE reconnect loop: read tasks created by failed reconnect
|
|
359
|
+
# attempts also land here when their socket is torn down, and must not
|
|
360
|
+
# stack additional loops on top of the one already running.
|
|
361
|
+
if not self._closing and (
|
|
362
|
+
self._reconnect_task is None or self._reconnect_task.done()
|
|
363
|
+
):
|
|
364
|
+
logger.warning(
|
|
365
|
+
"[cello] Lost the CELLO daemon connection - reconnecting in the background"
|
|
366
|
+
)
|
|
367
|
+
self._reconnect_task = asyncio.create_task(self._reconnect_forever())
|
|
368
|
+
|
|
369
|
+
# ------------------------------------------------------------------ wake path
|
|
370
|
+
|
|
371
|
+
async def _on_notification(self, frame: Dict[str, Any]) -> None:
|
|
372
|
+
kind = str(frame.get("notification", ""))
|
|
373
|
+
if kind not in WAKE_NOTIFICATIONS:
|
|
374
|
+
logger.debug("[cello] Ignoring notification type '%s'", kind)
|
|
375
|
+
return
|
|
376
|
+
raw_data = frame.get("data")
|
|
377
|
+
data: Dict[str, Any] = raw_data if isinstance(raw_data, dict) else {}
|
|
378
|
+
if _has_content_field(data):
|
|
379
|
+
logger.error(
|
|
380
|
+
"[cello] Dropping wake notification carrying a content field - "
|
|
381
|
+
"INV-CONTENTFREE violation upstream (daemon must never push content)"
|
|
382
|
+
)
|
|
383
|
+
return
|
|
384
|
+
if not self._message_handler:
|
|
385
|
+
logger.warning(
|
|
386
|
+
"[cello] Wake received before the gateway message handler was attached - dropped"
|
|
387
|
+
)
|
|
388
|
+
return
|
|
389
|
+
source = self.build_source(
|
|
390
|
+
chat_id=self._runtime_session,
|
|
391
|
+
chat_name="CELLO",
|
|
392
|
+
chat_type="dm",
|
|
393
|
+
user_id="cello-daemon",
|
|
394
|
+
user_name="CELLO",
|
|
395
|
+
)
|
|
396
|
+
event = MessageEvent(
|
|
397
|
+
text=self._wake_prompt(kind, data),
|
|
398
|
+
message_type=MessageType.TEXT,
|
|
399
|
+
source=source,
|
|
400
|
+
raw_message=data,
|
|
401
|
+
message_id="cello-wake-" + uuid.uuid4().hex[:12],
|
|
402
|
+
internal=True,
|
|
403
|
+
)
|
|
404
|
+
await self.handle_message(event)
|
|
405
|
+
|
|
406
|
+
async def handle_message(self, event: MessageEvent) -> None:
|
|
407
|
+
"""Queue wake hints for a busy session instead of interrupting the turn."""
|
|
408
|
+
if not self._message_handler:
|
|
409
|
+
return
|
|
410
|
+
session_key = build_session_key(
|
|
411
|
+
event.source,
|
|
412
|
+
group_sessions_per_user=self.config.extra.get("group_sessions_per_user", True),
|
|
413
|
+
thread_sessions_per_user=self.config.extra.get("thread_sessions_per_user", False),
|
|
414
|
+
)
|
|
415
|
+
if session_key in self._active_sessions:
|
|
416
|
+
logger.debug("[cello] Wake queued for busy session %s", session_key)
|
|
417
|
+
merge_pending_message_event(self._pending_messages, session_key, event)
|
|
418
|
+
return
|
|
419
|
+
await super().handle_message(event)
|
|
420
|
+
|
|
421
|
+
def _wake_prompt(self, kind: str, data: Dict[str, Any]) -> str:
|
|
422
|
+
# Unlike Raft's hardcoded generic hint, surface the (content-free) metadata the
|
|
423
|
+
# daemon pushes - session id, counterparty pubkey, state - so the agent can act
|
|
424
|
+
# without an extra discovery round-trip. Message CONTENT is never present here.
|
|
425
|
+
session_id = _safe_scalar(data.get("session_id") or data.get("sessionId"))
|
|
426
|
+
if kind == "cello_message":
|
|
427
|
+
sender = _safe_scalar(data.get("from"))
|
|
428
|
+
headline = (
|
|
429
|
+
"CELLO wake: a new message arrived on session "
|
|
430
|
+
+ session_id
|
|
431
|
+
+ " from counterparty pubkey "
|
|
432
|
+
+ sender
|
|
433
|
+
+ "."
|
|
434
|
+
)
|
|
435
|
+
else:
|
|
436
|
+
state = _safe_scalar(data.get("state"))
|
|
437
|
+
counterparty = _safe_scalar(data.get("counterpartyPubkey"))
|
|
438
|
+
headline = (
|
|
439
|
+
"CELLO wake: session "
|
|
440
|
+
+ session_id
|
|
441
|
+
+ " changed state to '"
|
|
442
|
+
+ state
|
|
443
|
+
+ "' (counterparty pubkey "
|
|
444
|
+
+ counterparty
|
|
445
|
+
+ ")."
|
|
446
|
+
)
|
|
447
|
+
return headline + (
|
|
448
|
+
" Message content is never pushed - act through the cello MCP tools: "
|
|
449
|
+
"call cello_check_notifications first, then cello_receive for this session "
|
|
450
|
+
"BEFORE any cello_send (the daemon rejects out-of-cursor sends with "
|
|
451
|
+
"session_not_current until you have read). "
|
|
452
|
+
"If no action is needed, reply with exactly [SILENT]."
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
# ------------------------------------------------------------------ outbound (no-op)
|
|
456
|
+
|
|
457
|
+
async def send(
|
|
458
|
+
self,
|
|
459
|
+
chat_id: str,
|
|
460
|
+
content: str,
|
|
461
|
+
reply_to: Optional[str] = None,
|
|
462
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
463
|
+
) -> SendResult:
|
|
464
|
+
logger.debug(
|
|
465
|
+
"[cello] adapter send is a no-op; the agent delivers via cello_send (MCP)"
|
|
466
|
+
)
|
|
467
|
+
return SendResult(success=True)
|
|
468
|
+
|
|
469
|
+
async def get_chat_info(self, chat_id: str) -> Dict[str, Any]:
|
|
470
|
+
return {"name": "cello/" + str(chat_id), "type": "cello"}
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
# ---------------------------------------------------------------------- registry
|
|
474
|
+
|
|
475
|
+
def _is_connected(config: PlatformConfig) -> bool:
|
|
476
|
+
extra = config.extra or {}
|
|
477
|
+
return bool(extra.get("enabled") or extra.get("agent_name"))
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def _env_enablement() -> Optional[dict]:
|
|
481
|
+
"""Auto-enable the platform when CELLO_AGENT_NAME is set (Raft's RAFT_PROFILE pattern)."""
|
|
482
|
+
if not os.getenv("CELLO_AGENT_NAME"):
|
|
483
|
+
return None
|
|
484
|
+
return {"enabled": True}
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def interactive_setup() -> None:
|
|
488
|
+
"""Interactive 'hermes gateway setup' flow for the CELLO platform."""
|
|
489
|
+
from hermes_cli.cli_output import (
|
|
490
|
+
print_header,
|
|
491
|
+
print_info,
|
|
492
|
+
print_success,
|
|
493
|
+
print_warning,
|
|
494
|
+
prompt,
|
|
495
|
+
prompt_yes_no,
|
|
496
|
+
)
|
|
497
|
+
from hermes_cli.config import get_env_value, save_env_value
|
|
498
|
+
|
|
499
|
+
print_header("CELLO")
|
|
500
|
+
existing = get_env_value("CELLO_AGENT_NAME")
|
|
501
|
+
if existing:
|
|
502
|
+
print_info("CELLO: already configured (agent: " + existing + ")")
|
|
503
|
+
if not prompt_yes_no("Reconfigure CELLO?", False):
|
|
504
|
+
print_info("Keeping CELLO_AGENT_NAME=" + existing + ".")
|
|
505
|
+
return
|
|
506
|
+
|
|
507
|
+
print_info("Bind this Hermes instance to a registered CELLO agent.")
|
|
508
|
+
print_info("If you have not set up CELLO yet, run: cello login, then")
|
|
509
|
+
print_info("cello create-agent <name> and cello register <name> <token>.")
|
|
510
|
+
print()
|
|
511
|
+
|
|
512
|
+
agent = prompt("CELLO agent name", default=existing or "")
|
|
513
|
+
if not agent:
|
|
514
|
+
print_warning("CELLO agent name is required; skipping CELLO setup")
|
|
515
|
+
return
|
|
516
|
+
|
|
517
|
+
save_env_value("CELLO_AGENT_NAME", agent.strip())
|
|
518
|
+
|
|
519
|
+
print()
|
|
520
|
+
print_success("CELLO configuration saved")
|
|
521
|
+
print_info("Restart the gateway for changes to take effect: hermes gateway restart")
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def register(ctx) -> None:
|
|
525
|
+
"""Plugin entry point - called by the Hermes plugin system."""
|
|
526
|
+
ctx.register_platform(
|
|
527
|
+
name="cello",
|
|
528
|
+
label="CELLO",
|
|
529
|
+
adapter_factory=lambda cfg: CelloAdapter(cfg),
|
|
530
|
+
check_fn=check_cello_requirements,
|
|
531
|
+
is_connected=_is_connected,
|
|
532
|
+
required_env=["CELLO_AGENT_NAME"],
|
|
533
|
+
install_hint=(
|
|
534
|
+
"Install the CELLO client and start its daemon: "
|
|
535
|
+
"npx --yes @cello-protocol/cli@latest login "
|
|
536
|
+
"(then 'cello install hermes --agent <name>')"
|
|
537
|
+
),
|
|
538
|
+
setup_fn=interactive_setup,
|
|
539
|
+
env_enablement_fn=_env_enablement,
|
|
540
|
+
emoji="\U0001F3BB",
|
|
541
|
+
platform_hint=(
|
|
542
|
+
"You are connected to CELLO, a peer-to-peer identity and trust layer for "
|
|
543
|
+
"agent-to-agent communication. Operate it exclusively through the cello_* "
|
|
544
|
+
"MCP tools (the 'cello' MCP server). Wake notices from this platform are "
|
|
545
|
+
"content-free by design - fetch actual messages with cello_check_notifications "
|
|
546
|
+
"and cello_receive. Always read before you send: call cello_receive on a "
|
|
547
|
+
"session before cello_send, or the daemon rejects the send with "
|
|
548
|
+
"session_not_current. If a wake needs no reply, respond with exactly [SILENT] "
|
|
549
|
+
"and make no cello_send call."
|
|
550
|
+
),
|
|
551
|
+
)
|
|
552
|
+
`;
|
|
553
|
+
/** The setup skill — `~/.hermes/skills/cello-bridge-setup/SKILL.md`. */
|
|
554
|
+
export const HERMES_SKILL_MD = `---
|
|
555
|
+
name: cello-bridge-setup
|
|
556
|
+
description: "Install and configure the CELLO agent-to-agent bridge for this Hermes instance."
|
|
557
|
+
version: 1.0.0
|
|
558
|
+
platforms: [linux, macos]
|
|
559
|
+
metadata:
|
|
560
|
+
hermes:
|
|
561
|
+
tags: [cello, messaging, integration, agent-to-agent]
|
|
562
|
+
related_skills: []
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
# CELLO Bridge Setup
|
|
566
|
+
|
|
567
|
+
CELLO is a peer-to-peer identity and trust layer for agent-to-agent communication:
|
|
568
|
+
split-key signing, tamper-evident hash chains, and content-free wake notifications.
|
|
569
|
+
This skill wires the local CELLO daemon into Hermes so this agent can talk to other
|
|
570
|
+
CELLO agents anywhere.
|
|
571
|
+
|
|
572
|
+
Trigger: /cello-bridge-setup, or "install the CELLO bridge".
|
|
573
|
+
|
|
574
|
+
## Steps
|
|
575
|
+
|
|
576
|
+
1. **Check CELLO is set up.** Run \`cello status\`. If the CLI is missing or the daemon
|
|
577
|
+
is not running, walk the user through CELLO onboarding first:
|
|
578
|
+
\`npx --yes @cello-protocol/cli@latest login\`, then \`cello create-agent <name>\`,
|
|
579
|
+
then \`cello register <name> <pre-auth-token>\` (token from the CELLO Operations
|
|
580
|
+
Agent on Telegram). Confirm with \`cello status\`.
|
|
581
|
+
2. **Pick the agent.** Ask the user which registered CELLO agent this Hermes instance
|
|
582
|
+
should bind to (the \`cello status\` output lists them).
|
|
583
|
+
3. **Run the installer** — one command does all the work (plugin scaffold, env binding,
|
|
584
|
+
\`hermes plugins enable cello\`, \`hermes mcp add cello\`):
|
|
585
|
+
|
|
586
|
+
cello install hermes --agent <name>
|
|
587
|
+
|
|
588
|
+
Pass \`--hermes-home <path>\` only if Hermes does not live at ~/.hermes.
|
|
589
|
+
4. **Restart the gateway:** \`hermes gateway restart\`.
|
|
590
|
+
5. **Verify.** Call the \`cello_status\` MCP tool and report the bound agent's state and
|
|
591
|
+
\`standing_receiver_ready\`. The bridge is live when the agent shows online.
|
|
592
|
+
|
|
593
|
+
## How to operate CELLO (after setup)
|
|
594
|
+
|
|
595
|
+
- Wake notices from the CELLO platform are **content-free** — they name a session and a
|
|
596
|
+
counterparty pubkey, never the message. Fetch content with \`cello_check_notifications\`
|
|
597
|
+
and \`cello_receive\`.
|
|
598
|
+
- **Read before you send.** Call \`cello_receive\` on a session before \`cello_send\`; the
|
|
599
|
+
daemon rejects out-of-cursor sends with \`session_not_current\`.
|
|
600
|
+
- If a wake needs no reply, answer with exactly \`[SILENT]\` and make no \`cello_send\` call.
|
|
601
|
+
`;
|
|
602
|
+
//# sourceMappingURL=assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/hermes/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,sEAAsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;CAiBjC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuf9C,CAAC;AAEF,wEAAwE;AACxE,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+C9B,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HERMES-001 — `cello install hermes`: wire the local CELLO daemon into a Hermes
|
|
3
|
+
* Agent installation.
|
|
4
|
+
*
|
|
5
|
+
* What it does (design: trustless-cello 2026-07-09_1915_hermes-agent-integration-plan.md §5):
|
|
6
|
+
* 1. Scaffolds the platform-adapter plugin into $HERMES_HOME/plugins/cello/
|
|
7
|
+
* (plugin.yaml + __init__.py — a pure-stdlib Python IPC client, no subprocess).
|
|
8
|
+
* 2. Drops the cello-bridge-setup skill into $HERMES_HOME/skills/.
|
|
9
|
+
* 3. Binds the agent: writes CELLO_AGENT_NAME=<agent> into $HERMES_HOME/.env
|
|
10
|
+
* (replace-or-append; every other line preserved byte-for-byte).
|
|
11
|
+
* 4. Registers through Hermes' OWN CLI surfaces — `hermes plugins enable cello` and
|
|
12
|
+
* `hermes mcp add cello …` — never by hand-editing config.yaml, so Hermes owns
|
|
13
|
+
* its config format.
|
|
14
|
+
*
|
|
15
|
+
* Never edits anything outside $HERMES_HOME. Idempotent: re-running upgrades the
|
|
16
|
+
* scaffolded files in place and keeps exactly one CELLO_AGENT_NAME line.
|
|
17
|
+
*/
|
|
18
|
+
export interface ExecResult {
|
|
19
|
+
code: number;
|
|
20
|
+
stdout: string;
|
|
21
|
+
stderr: string;
|
|
22
|
+
}
|
|
23
|
+
export type ExecFn = (cmd: string, args: string[]) => Promise<ExecResult>;
|
|
24
|
+
export interface InstallHermesOptions {
|
|
25
|
+
agentName: string;
|
|
26
|
+
/** Defaults to $HERMES_HOME, falling back to ~/.hermes. */
|
|
27
|
+
hermesHome?: string;
|
|
28
|
+
/** Injectable for tests; defaults to a spawn-based runner. */
|
|
29
|
+
exec?: ExecFn;
|
|
30
|
+
}
|
|
31
|
+
export declare function installHermes(opts: InstallHermesOptions): Promise<{
|
|
32
|
+
exitCode: number;
|
|
33
|
+
output: string;
|
|
34
|
+
}>;
|
|
35
|
+
//# sourceMappingURL=install-hermes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-hermes.d.ts","sourceRoot":"","sources":["../../src/hermes/install-hermes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AASH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAE1E,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAoDD,wBAAsB,aAAa,CACjC,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAiG/C"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HERMES-001 — `cello install hermes`: wire the local CELLO daemon into a Hermes
|
|
3
|
+
* Agent installation.
|
|
4
|
+
*
|
|
5
|
+
* What it does (design: trustless-cello 2026-07-09_1915_hermes-agent-integration-plan.md §5):
|
|
6
|
+
* 1. Scaffolds the platform-adapter plugin into $HERMES_HOME/plugins/cello/
|
|
7
|
+
* (plugin.yaml + __init__.py — a pure-stdlib Python IPC client, no subprocess).
|
|
8
|
+
* 2. Drops the cello-bridge-setup skill into $HERMES_HOME/skills/.
|
|
9
|
+
* 3. Binds the agent: writes CELLO_AGENT_NAME=<agent> into $HERMES_HOME/.env
|
|
10
|
+
* (replace-or-append; every other line preserved byte-for-byte).
|
|
11
|
+
* 4. Registers through Hermes' OWN CLI surfaces — `hermes plugins enable cello` and
|
|
12
|
+
* `hermes mcp add cello …` — never by hand-editing config.yaml, so Hermes owns
|
|
13
|
+
* its config format.
|
|
14
|
+
*
|
|
15
|
+
* Never edits anything outside $HERMES_HOME. Idempotent: re-running upgrades the
|
|
16
|
+
* scaffolded files in place and keeps exactly one CELLO_AGENT_NAME line.
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
19
|
+
import { homedir } from "node:os";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
import { spawn } from "node:child_process";
|
|
22
|
+
import { HERMES_PLUGIN_YAML, HERMES_PLUGIN_INIT_PY, HERMES_SKILL_MD } from "./assets.js";
|
|
23
|
+
/** The two Hermes CLI registrations the installer performs (also printed as the
|
|
24
|
+
* manual fallback when the `hermes` binary cannot be run). */
|
|
25
|
+
const HERMES_COMMANDS = [
|
|
26
|
+
["plugins", "enable", "cello"],
|
|
27
|
+
["mcp", "add", "cello", "--command", "npx", "--args", "--yes", "@cello-protocol/connect@latest"],
|
|
28
|
+
];
|
|
29
|
+
const defaultExec = (cmd, args) => new Promise((resolve, reject) => {
|
|
30
|
+
const child = spawn(cmd, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
31
|
+
// `hermes mcp add` is interactive: on a re-run it first asks "... already exists.
|
|
32
|
+
// Overwrite? [y/N]" (default NO), then "Enable all N tools? [Y/n/select]". Feed a
|
|
33
|
+
// few "Y" lines so BOTH prompts are answered yes on the fresh AND the re-run path
|
|
34
|
+
// (extra lines are harmless — the child stops reading once satisfied). A closed
|
|
35
|
+
// stdin would read as EOF → "Cancelled." with exit 0, which is why installHermes
|
|
36
|
+
// also VERIFIES the registration against `hermes mcp list` rather than trusting
|
|
37
|
+
// the exit code.
|
|
38
|
+
child.stdin.write("Y\nY\nY\n");
|
|
39
|
+
child.stdin.end();
|
|
40
|
+
let stdout = "";
|
|
41
|
+
let stderr = "";
|
|
42
|
+
child.stdout.on("data", (d) => (stdout += d.toString("utf-8")));
|
|
43
|
+
child.stderr.on("data", (d) => (stderr += d.toString("utf-8")));
|
|
44
|
+
child.on("error", reject); // e.g. ENOENT — hermes not on PATH
|
|
45
|
+
child.on("close", (code) => resolve({ code: code ?? 1, stdout, stderr }));
|
|
46
|
+
});
|
|
47
|
+
/** Replace-or-append KEY=value in an env file, preserving every other line. */
|
|
48
|
+
function upsertEnvLine(envPath, key, value) {
|
|
49
|
+
const line = `${key}=${value}`;
|
|
50
|
+
let lines = [];
|
|
51
|
+
if (existsSync(envPath)) {
|
|
52
|
+
lines = readFileSync(envPath, "utf-8").split("\n");
|
|
53
|
+
}
|
|
54
|
+
let replaced = false;
|
|
55
|
+
lines = lines.map((l) => {
|
|
56
|
+
if (l.startsWith(`${key}=`)) {
|
|
57
|
+
replaced = true;
|
|
58
|
+
return line;
|
|
59
|
+
}
|
|
60
|
+
return l;
|
|
61
|
+
});
|
|
62
|
+
if (!replaced) {
|
|
63
|
+
// Drop a single trailing empty line before appending so we don't grow blank gaps.
|
|
64
|
+
if (lines.length > 0 && lines[lines.length - 1] === "")
|
|
65
|
+
lines.pop();
|
|
66
|
+
lines.push(line);
|
|
67
|
+
}
|
|
68
|
+
writeFileSync(envPath, lines.join("\n") + (lines[lines.length - 1] === "" ? "" : "\n"));
|
|
69
|
+
}
|
|
70
|
+
export async function installHermes(opts) {
|
|
71
|
+
const agentName = (opts.agentName ?? "").trim();
|
|
72
|
+
if (!agentName) {
|
|
73
|
+
return {
|
|
74
|
+
exitCode: 1,
|
|
75
|
+
output: "Missing agent name. Usage: cello install hermes --agent <name> [--hermes-home <path>]\n" +
|
|
76
|
+
"The agent must be a local CELLO agent (see 'cello status' for the list).",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const hermesHome = opts.hermesHome || process.env.HERMES_HOME || join(homedir(), ".hermes");
|
|
80
|
+
if (!existsSync(hermesHome)) {
|
|
81
|
+
return {
|
|
82
|
+
exitCode: 1,
|
|
83
|
+
output: `Hermes home not found at ${hermesHome}.\n` +
|
|
84
|
+
"Install Hermes Agent first (https://hermes-agent.nousresearch.com), or pass --hermes-home <path> " +
|
|
85
|
+
"if it lives elsewhere.",
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const exec = opts.exec ?? defaultExec;
|
|
89
|
+
const out = [];
|
|
90
|
+
// 1. Plugin package
|
|
91
|
+
const pluginDir = join(hermesHome, "plugins", "cello");
|
|
92
|
+
mkdirSync(pluginDir, { recursive: true });
|
|
93
|
+
writeFileSync(join(pluginDir, "plugin.yaml"), HERMES_PLUGIN_YAML);
|
|
94
|
+
writeFileSync(join(pluginDir, "__init__.py"), HERMES_PLUGIN_INIT_PY);
|
|
95
|
+
out.push(`Wrote plugin: ${pluginDir}/{plugin.yaml,__init__.py}`);
|
|
96
|
+
// 2. Skill
|
|
97
|
+
const skillDir = join(hermesHome, "skills", "cello-bridge-setup");
|
|
98
|
+
mkdirSync(skillDir, { recursive: true });
|
|
99
|
+
writeFileSync(join(skillDir, "SKILL.md"), HERMES_SKILL_MD);
|
|
100
|
+
out.push(`Wrote skill: ${skillDir}/SKILL.md`);
|
|
101
|
+
// 3. Agent binding
|
|
102
|
+
const envPath = join(hermesHome, ".env");
|
|
103
|
+
upsertEnvLine(envPath, "CELLO_AGENT_NAME", agentName);
|
|
104
|
+
out.push(`Bound agent: CELLO_AGENT_NAME=${agentName} (${envPath})`);
|
|
105
|
+
// 4. Register through Hermes' own CLI. A failure here is loud: the files are in
|
|
106
|
+
// place, so we print exactly what remains to be run by hand.
|
|
107
|
+
for (const args of HERMES_COMMANDS) {
|
|
108
|
+
let result;
|
|
109
|
+
try {
|
|
110
|
+
result = await exec("hermes", [...args]);
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
114
|
+
out.push(`\nCould not run the 'hermes' CLI (${msg}).`, "The CELLO files are installed, but Hermes still needs these two commands run manually:", " hermes plugins enable cello", " hermes mcp add cello --command npx --args --yes @cello-protocol/connect@latest", "Then restart the gateway: hermes gateway restart");
|
|
115
|
+
return { exitCode: 1, output: out.join("\n") };
|
|
116
|
+
}
|
|
117
|
+
if (result.code !== 0) {
|
|
118
|
+
out.push(`\n'hermes ${args.join(" ")}' failed (exit ${result.code}):`, (result.stderr || result.stdout).trim(), "Fix the error above and re-run 'cello install hermes' (it is idempotent).");
|
|
119
|
+
return { exitCode: 1, output: out.join("\n") };
|
|
120
|
+
}
|
|
121
|
+
out.push(`Ran: hermes ${args.join(" ")}`);
|
|
122
|
+
}
|
|
123
|
+
// Verify the MCP registration actually landed: `hermes mcp add` can print
|
|
124
|
+
// "Cancelled." and still exit 0 (e.g. when its interactive prompt hits EOF),
|
|
125
|
+
// so the exit code alone proves nothing.
|
|
126
|
+
try {
|
|
127
|
+
const list = await exec("hermes", ["mcp", "list"]);
|
|
128
|
+
if (!list.stdout.includes("cello")) {
|
|
129
|
+
out.push("", "'hermes mcp add' reported success but 'hermes mcp list' does not show a 'cello' server.", "Run this manually and answer Y at the tool prompt:", " hermes mcp add cello --command npx --args --yes @cello-protocol/connect@latest");
|
|
130
|
+
return { exitCode: 1, output: out.join("\n") };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// hermes CLI vanished between calls — vanishingly unlikely; the add above succeeded.
|
|
135
|
+
}
|
|
136
|
+
out.push("Verified: 'cello' present in hermes mcp list");
|
|
137
|
+
out.push("", "CELLO bridge installed. Next step: restart the gateway so Hermes loads the plugin:", " hermes gateway restart", "Then verify from a Hermes chat by calling the cello_status MCP tool.");
|
|
138
|
+
return { exitCode: 0, output: out.join("\n") };
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=install-hermes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-hermes.js","sourceRoot":"","sources":["../../src/hermes/install-hermes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAkBzF;8DAC8D;AAC9D,MAAM,eAAe,GAAyC;IAC5D,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC;IAC9B,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,gCAAgC,CAAC;CACjG,CAAC;AAEF,MAAM,WAAW,GAAW,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CACxC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACpE,kFAAkF;IAClF,kFAAkF;IAClF,kFAAkF;IAClF,gFAAgF;IAChF,iFAAiF;IACjF,gFAAgF;IAChF,iBAAiB;IACjB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC/B,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;IAClB,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACxE,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACxE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,mCAAmC;IAC9D,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAC5E,CAAC,CAAC,CAAC;AAEL,+EAA+E;AAC/E,SAAS,aAAa,CAAC,OAAe,EAAE,GAAW,EAAE,KAAa;IAChE,MAAM,IAAI,GAAG,GAAG,GAAG,IAAI,KAAK,EAAE,CAAC;IAC/B,IAAI,KAAK,GAAa,EAAE,CAAC;IACzB,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACxB,KAAK,GAAG,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC;IACD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACtB,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,CAAC;YAC5B,QAAQ,GAAG,IAAI,CAAC;YAChB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,kFAAkF;QAClF,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE;YAAE,KAAK,CAAC,GAAG,EAAE,CAAC;QACpE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1F,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAA0B;IAE1B,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAChD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO;YACL,QAAQ,EAAE,CAAC;YACX,MAAM,EACJ,yFAAyF;gBACzF,0EAA0E;SAC7E,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAC5F,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO;YACL,QAAQ,EAAE,CAAC;YACX,MAAM,EACJ,4BAA4B,UAAU,KAAK;gBAC3C,mGAAmG;gBACnG,wBAAwB;SAC3B,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,WAAW,CAAC;IACtC,MAAM,GAAG,GAAa,EAAE,CAAC;IAEzB,oBAAoB;IACpB,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACvD,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,kBAAkB,CAAC,CAAC;IAClE,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,qBAAqB,CAAC,CAAC;IACrE,GAAG,CAAC,IAAI,CAAC,iBAAiB,SAAS,4BAA4B,CAAC,CAAC;IAEjE,WAAW;IACX,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,QAAQ,EAAE,oBAAoB,CAAC,CAAC;IAClE,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,eAAe,CAAC,CAAC;IAC3D,GAAG,CAAC,IAAI,CAAC,iBAAiB,QAAQ,WAAW,CAAC,CAAC;IAE/C,mBAAmB;IACnB,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACzC,aAAa,CAAC,OAAO,EAAE,kBAAkB,EAAE,SAAS,CAAC,CAAC;IACtD,GAAG,CAAC,IAAI,CAAC,kCAAkC,SAAS,KAAK,OAAO,GAAG,CAAC,CAAC;IAErE,gFAAgF;IAChF,6DAA6D;IAC7D,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;QACnC,IAAI,MAAkB,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,GAAG,CAAC,IAAI,CACN,qCAAqC,GAAG,IAAI,EAC5C,wFAAwF,EACxF,+BAA+B,EAC/B,kFAAkF,EAClF,kDAAkD,CACnD,CAAC;YACF,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACjD,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACtB,GAAG,CAAC,IAAI,CACN,aAAa,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,IAAI,IAAI,EAC5D,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EACvC,2EAA2E,CAC5E,CAAC;YACF,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACjD,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,wBAAwB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,0EAA0E;IAC1E,6EAA6E;IAC7E,yCAAyC;IACzC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACnC,GAAG,CAAC,IAAI,CACN,EAAE,EACF,yFAAyF,EACzF,oDAAoD,EACpD,kFAAkF,CACnF,CAAC;YACF,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACjD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,qFAAqF;IACvF,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;IAE7D,GAAG,CAAC,IAAI,CACN,EAAE,EACF,oFAAoF,EACpF,0BAA0B,EAC1B,sEAAsE,CACvE,CAAC;IACF,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACjD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cello-protocol/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.34",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"package.json"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@cello-protocol/daemon": "0.0.
|
|
28
|
+
"@cello-protocol/daemon": "0.0.37"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^25.6.2"
|