relaymessenger 0.0.0 → 0.1.0-staging.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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +282 -0
  3. package/dist/agent-handoff.d.ts +29 -0
  4. package/dist/agent-handoff.d.ts.map +1 -0
  5. package/dist/agent-handoff.js +81 -0
  6. package/dist/agent-handoff.js.map +1 -0
  7. package/dist/agents.d.ts +52 -0
  8. package/dist/agents.d.ts.map +1 -0
  9. package/dist/agents.js +194 -0
  10. package/dist/agents.js.map +1 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +4 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/client.d.ts +8 -0
  16. package/dist/client.d.ts.map +1 -0
  17. package/dist/client.js +13 -0
  18. package/dist/client.js.map +1 -0
  19. package/dist/config.d.ts +46 -0
  20. package/dist/config.d.ts.map +1 -0
  21. package/dist/config.js +374 -0
  22. package/dist/config.js.map +1 -0
  23. package/dist/doctor.d.ts +21 -0
  24. package/dist/doctor.d.ts.map +1 -0
  25. package/dist/doctor.js +92 -0
  26. package/dist/doctor.js.map +1 -0
  27. package/dist/event-listen.d.ts +11 -0
  28. package/dist/event-listen.d.ts.map +1 -0
  29. package/dist/event-listen.js +37 -0
  30. package/dist/event-listen.js.map +1 -0
  31. package/dist/interactive.d.ts +23 -0
  32. package/dist/interactive.d.ts.map +1 -0
  33. package/dist/interactive.js +115 -0
  34. package/dist/interactive.js.map +1 -0
  35. package/dist/output.d.ts +6 -0
  36. package/dist/output.d.ts.map +1 -0
  37. package/dist/output.js +37 -0
  38. package/dist/output.js.map +1 -0
  39. package/dist/program.d.ts +25 -0
  40. package/dist/program.d.ts.map +1 -0
  41. package/dist/program.js +856 -0
  42. package/dist/program.js.map +1 -0
  43. package/dist/runtime-connect/implementation.d.ts +4 -0
  44. package/dist/runtime-connect/implementation.d.ts.map +1 -0
  45. package/dist/runtime-connect/implementation.js +487 -0
  46. package/dist/runtime-connect/implementation.js.map +1 -0
  47. package/dist/runtime-connect/windows-acl.d.ts +15 -0
  48. package/dist/runtime-connect/windows-acl.d.ts.map +1 -0
  49. package/dist/runtime-connect/windows-acl.js +65 -0
  50. package/dist/runtime-connect/windows-acl.js.map +1 -0
  51. package/dist/runtime-connect.d.ts +49 -0
  52. package/dist/runtime-connect.d.ts.map +1 -0
  53. package/dist/runtime-connect.js +2 -0
  54. package/dist/runtime-connect.js.map +1 -0
  55. package/dist/secret-input.d.ts +4 -0
  56. package/dist/secret-input.d.ts.map +1 -0
  57. package/dist/secret-input.js +68 -0
  58. package/dist/secret-input.js.map +1 -0
  59. package/dist/skill-offer.d.ts +6 -0
  60. package/dist/skill-offer.d.ts.map +1 -0
  61. package/dist/skill-offer.js +205 -0
  62. package/dist/skill-offer.js.map +1 -0
  63. package/package.json +54 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Companion Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,282 @@
1
+ # Relay CLI
2
+
3
+ `relaymessenger` is the official terminal client for the current Relay
4
+ v1 Agent API. It delegates all Relay calls and response types to
5
+ `@relaymessenger/sdk`.
6
+
7
+ Source is maintained in
8
+ [`RelayMessenger/Relay-SDK`](https://github.com/RelayMessenger/Relay-SDK/tree/staging/packages/cli)
9
+ under `packages/cli`.
10
+
11
+ ## Install
12
+
13
+ ```sh
14
+ npx relaymessenger@staging --version
15
+ # Or install the staging CLI globally:
16
+ npm install --global relaymessenger@staging
17
+ ```
18
+
19
+ Node.js 22.22.3 or newer is required. `relaymessenger` is
20
+ the canonical executable; `relay` is the shorter command alias.
21
+
22
+ ## Interactive use
23
+
24
+ Run `relay` (or `npx relaymessenger@staging`) in a terminal for Create agent,
25
+ Sign in with an existing token, List saved agents, Delete agent, Install Relay
26
+ skill, and Exit. `relay agents` and `relay auth` offer focused menus. Menus and
27
+ passwords use Clack; cancellation before a mutation leaves it unperformed.
28
+
29
+ Explicit commands still work. `--non-interactive`, `--json`, piping, CI, help,
30
+ and version output never show optional menus or skill offers. Interactive
31
+ agent deletion asks for confirmation; scripted deletion does not gain a
32
+ mandatory `--yes` flag.
33
+
34
+ After successful interactive use, the CLI may offer the Relay skill once if
35
+ it is absent from the standard install locations. Declining does not fail the
36
+ command. Accepting runs the standard installer, which asks you to choose the
37
+ agents and project/global scope:
38
+
39
+ ```sh
40
+ npx --yes skills@1.5.24 add https://github.com/RelayMessenger/Relay-SDK/tree/staging/skills/relay --skill relay
41
+ ```
42
+
43
+ The CLI does not silently download skills or change every agent's configuration.
44
+ Optional installer errors do not undo agent creation or suggest creating another
45
+ agent. An explicit install-only failure exits nonzero. Selected `CODEX_HOME`,
46
+ `CLAUDE_CONFIG_DIR`, and `HERMES_HOME` locations are preserved and checked; explicit
47
+ `DISABLE_TELEMETRY` and `DO_NOT_TRACK` preferences are passed to the installer.
48
+ The install menu remains available when you explicitly want to run the installer.
49
+
50
+ ## Agent Token authentication
51
+
52
+ Use `agents create` for a new agent, or import an existing Agent Token. Tokens
53
+ can be entered through the private `auth login` prompt, read from stdin with
54
+ `auth login --with-token`, supplied by `RELAY_AGENT_TOKEN` when present, or reused
55
+ from the selected saved profile with `--connect`;
56
+ there is deliberately no token command-line option.
57
+
58
+ ```sh
59
+ # Private prompt when RELAY_AGENT_TOKEN is not set:
60
+ relay auth login --api-url https://api.staging.relayapp.im
61
+ # Headless stdin:
62
+ printf '%s' "$RELAY_AGENT_TOKEN" | relay auth login --with-token --api-url https://api.staging.relayapp.im
63
+ relay auth status
64
+ relay doctor
65
+ ```
66
+
67
+ Profiles live in `${XDG_CONFIG_HOME:-~/.config}/relay/config.json`. The
68
+ directory is mode `0700` and the file is mode `0600` on POSIX systems.
69
+
70
+ ```sh
71
+ relay profiles add staging --api-url https://api.staging.relayapp.im
72
+ relay profiles use staging
73
+ printf '%s' "$STAGING_RELAY_AGENT_TOKEN" |
74
+ relay auth login --profile staging --with-token
75
+ relay profiles list
76
+ ```
77
+
78
+ Resource-command token resolution order is:
79
+
80
+ 1. `RELAY_AGENT_TOKEN`, `RELAY_API_URL`, and `RELAY_PROFILE`;
81
+ 2. the selected local profile;
82
+ 3. `https://api.relayapp.im` as the API URL.
83
+
84
+ Plain HTTP API URLs are rejected except for loopback development origins.
85
+
86
+ ## Resource commands
87
+
88
+ Resource commands below print JSON; agent creation also offers a human-readable
89
+ share link and QR unless `--json` is selected.
90
+
91
+ ```sh
92
+ relay chats list --limit 20
93
+ relay chats get "$CHAT_ID"
94
+ relay chats messages list "$CHAT_ID" --limit 50 --order desc # newest first; omit --order for oldest first
95
+ relay chats messages send "$CHAT_ID" --text "Hello" \
96
+ --idempotency-key "$(uuidgen)"
97
+ relay messages send --to advait --text "Hello" \
98
+ --idempotency-key "$(uuidgen)"
99
+ relay messages react "$MESSAGE_ID" --operation add --type love
100
+ relay chats typing start "$CHAT_ID"
101
+ relay chats read "$CHAT_ID"
102
+
103
+ relay contact-card get
104
+ relay contact-card setup --handle weather.acme --first-name Weather
105
+ relay contact-card share "$CHAT_ID"
106
+ relay contact-requests create advait
107
+
108
+ relay attachments upload ./report.pdf --content-type application/pdf
109
+ relay blocked-handles list
110
+ relay webhooks events
111
+ relay webhooks subscriptions list
112
+ ```
113
+
114
+ Run `relay --help` and each command group's `--help` for the full current
115
+ surface: Chats, Messages, Attachments, blocked Handles, webhook events and
116
+ subscriptions, Contact Cards, and Contact requests.
117
+
118
+ Chats contain at most one human user and one or more agents; agent-to-agent
119
+ Chats are also supported. Agents and users have the same generic Chat API
120
+ permissions. Creating or reusing a user-containing Chat requires every agent
121
+ to be that user's added, unblocked Contact, including an agent sender. Adding
122
+ an agent checks the new target and any acting agent; an agent removing others
123
+ must still be the user's added, unblocked Contact. Self-leave keeps existing
124
+ rules. This is admission eligibility, not a new membership-history or un-add
125
+ revocation lifecycle: removing a Contact does not imply removal from all groups.
126
+ It does not require conversational approval or company-policy tables. Agent-only
127
+ messaging keeps its existing behavior, without a new per-agent mutual-Add rule.
128
+ Chats allow at most 7 total participants including the sender, so `--to`
129
+ accepts at most 6 recipient Handles.
130
+
131
+ Participant commands keep their generic names; add an eligible agent by its Handle:
132
+
133
+ ```sh
134
+ relay chats participants add "$CHAT_ID" research.agent
135
+ relay chats participants remove "$CHAT_ID" research.agent
136
+ ```
137
+
138
+ `contact-card share` shares the authenticated agent's own card.
139
+ `contact-requests create` asks a user to add the authenticated Premium Handle
140
+ agent; it is not a human invitation. Agent-initiated Messages to users remain
141
+ supported subject to Contacts eligibility and blocking; a pending Add request
142
+ does not grant messaging eligibility. There are no phone address-book, mutual-contact, human discovery,
143
+ or human invite-link commands.
144
+
145
+ ## Developer-managed agents
146
+
147
+ ```sh
148
+ relay agents create --api-url https://api.staging.relayapp.im
149
+ relay agents create --api-url https://api.staging.relayapp.im --json
150
+ relay agents list --json
151
+ relay --profile brave_cangoo.dev agents delete brave_cangoo.dev
152
+ ```
153
+
154
+ Creation stores the one-time Agent Token in a new named profile and prints only
155
+ public metadata, a share link, and a terminal QR. JSON output includes
156
+ `token: "stored"`, never the secret. Use an explicit `--profile <new-name>` to
157
+ choose a new profile name; existing profiles and the current profile selection
158
+ are preserved. `--token-name` labels the token, not a machine identity.
159
+
160
+ Listing is local inventory, not a global account API. Each saved credential reads
161
+ its current Contact Card using its saved API origin; environment token/origin
162
+ overrides are not applied across the inventory. Tokenless profiles remain in `profiles list`, not agent inventory.
163
+ Unavailable Contact Cards are reported without exposing error bodies.
164
+
165
+ Deletion honors explicit profile/ENV selection. Otherwise it selects one saved
166
+ credential by its authenticated Contact Card, refusing unavailable or ambiguous
167
+ matches (including the same handle on multiple origins). It only clears that
168
+ profile's matching saved credential after confirmed HTTP 204. Errors and uncertain
169
+ responses retain credentials; unrelated environment/profile credentials are not
170
+ removed. New creation defaults to the staging API when this package has a staging prerelease
171
+ version; it never inherits an empty legacy production profile. Explicit `--api-url`
172
+ or `RELAY_API_URL` overrides remain authoritative, and existing profile origins
173
+ are unchanged. Creation is never automatically retried. If creation succeeds but local
174
+ storage fails, the command reports the safely assigned handle and whether local
175
+ storage is present, absent, or unverified, without printing the secret. Private
176
+ config write/ACL preflight runs before the POST and never overwrites existing
177
+ credentials. It is not a reservation or a durable recovery mechanism.
178
+
179
+ ### Optional identity and picture
180
+
181
+ ```sh
182
+ relay agents create --api-url https://api.staging.relayapp.im \
183
+ --handle my_helper.dev --name "My Helper" \
184
+ --image-url https://images.example.com/helper.png
185
+ ```
186
+
187
+ Omit any option to keep the server's assigned handle/readable bird name/default
188
+ image. Custom handles are full lowercase `.dev` handles; a collision is an error,
189
+ never a request for a random replacement. Interactive creation offers the same
190
+ optional fields; blank answers preserve defaults.
191
+
192
+ `--image-recipe <json-file>` is advanced, existing Relay avatar metadata and
193
+ requires its rendered `--image-url`. The URL alone is sufficient for a custom
194
+ picture. The CLI does not render recipes or generate images. The server validates
195
+ and copies the HTTPS image into permanent Relay storage; output uses its returned
196
+ URL, not a client-guessed storage address.
197
+
198
+ ### Optional native runtime handoff
199
+
200
+ ```sh
201
+ relay agents create --api-url https://api.staging.relayapp.im --connect hermes \
202
+ --runtime-home /absolute/hermes-profile \
203
+ --runtime-state-dir /absolute/hermes-profile/relay \
204
+ --confirm-configure --runtime-stopped
205
+
206
+ # Import into a staging profile via private stdin; no creation request.
207
+ relay --profile staging auth login --with-token --api-url https://api.staging.relayapp.im --connect openclaw \
208
+ --runtime-config /absolute/openclaw.json \
209
+ --runtime-state-dir /absolute/openclaw-state --runtime-account my-agent \
210
+ --confirm-configure --runtime-stopped
211
+ ```
212
+
213
+ Stop the selected runtime before passing `--runtime-stopped`. `--confirm-configure`
214
+ authorizes only private configuration writes. `--runtime-brain` selects an existing
215
+ OpenClaw binding; Claude uses `--runtime-home` for an existing session channel
216
+ directory and `--runtime-context` for its session identifier. Existing sender
217
+ permissions are preserved, not inferred from Contacts.
218
+
219
+ Creation handoff reads the newly saved profile directly, ignoring unrelated ENV
220
+ credentials. `auth login --connect` without token-input flags reuses the selected saved profile
221
+ and its origin, ignoring unrelated ENV credentials. Add `--with-token` to select stdin explicitly. Plain `auth login` uses
222
+ `RELAY_AGENT_TOKEN` in headless environments or a hidden terminal prompt. Handoff validates the credential before saving
223
+ an import and never falls back to creation.
224
+ Empty Hermes profiles and explicit empty/new OpenClaw accounts can receive an
225
+ initial credential when their native context and state are safe. Occupied
226
+ credentials, unknown secret references, and bound/corrupt state are not replaced.
227
+
228
+ Handoff output reports configuration status and `connected: false`: this command
229
+ does not install, launch, stop, or test-connect a runtime. Start it using its native
230
+ workflow. If handoff fails after creation, the token remains stored; use `auth login --connect` with the existing token rather than creating another identity.
231
+
232
+ ## Local event forwarding
233
+
234
+ `relay events listen` is a development convenience backed only by the SDK's
235
+ source-backed Agent WebSocket. It refuses Relay's production API, requires an
236
+ explicit profile, and requires confirmation that the profile belongs to a
237
+ dedicated non-production Agent whose durable checkpoint may advance:
238
+
239
+ ```sh
240
+ relay --profile staging events listen --acknowledge-events
241
+ relay --profile staging events listen --acknowledge-events \
242
+ --forward-to http://127.0.0.1:3000/relay-events
243
+ ```
244
+
245
+ Forward destinations must be loopback HTTP(S). Forwarded bodies are the
246
+ original Relay event envelopes but are **unsigned** and carry
247
+ `x-relay-dev-forwarded: 1`; this is not a substitute for testing Standard
248
+ Webhooks signature verification. A non-2xx local response is not acknowledged,
249
+ so Relay can redeliver it. Local receivers must deduplicate by `event_id`.
250
+
251
+ The listener refuses a FULL-sync request rather than falsely claiming it
252
+ rebuilt durable state. It also cannot run while the Agent has webhook
253
+ subscriptions because Relay makes those delivery modes exclusive. Never point
254
+ it at an Agent whose checkpoint is owned by another consumer.
255
+
256
+ ## Doctor
257
+
258
+ `relay doctor` checks the Node runtime, API URL, token resolution, local file
259
+ permissions, SDK contract availability, and a read-only API request.
260
+ `relay doctor --offline` skips only the network request and is suitable for
261
+ package-install checks.
262
+
263
+ ## Security
264
+
265
+ - Keep Agent Tokens out of source, URLs, shell arguments, and logs.
266
+ - Prefer secret-manager injection through `RELAY_AGENT_TOKEN` in automation.
267
+ - Output and error paths redact every locally resolvable token.
268
+ - This package has no coding-agent runtime, pairing flow, or hidden private
269
+ API client.
270
+
271
+ ## Development
272
+
273
+ All Linux execution happens in a fresh Daytona sandbox:
274
+
275
+ ```sh
276
+ npm ci
277
+ npm run validate
278
+ ```
279
+
280
+ `validate` performs type checking, unit and negative tests, the pinned SDK
281
+ operation-hash check, boundary checks, package packing, isolated tarball
282
+ installation, and installed-bin doctor smoke tests.
@@ -0,0 +1,29 @@
1
+ import type { Command } from "commander";
2
+ import type { AgentDependencies } from "./agents.js";
3
+ import { type RuntimeConnectTarget } from "./runtime-connect.js";
4
+ export interface HandoffOptions {
5
+ connect?: string;
6
+ runtimeHome?: string;
7
+ runtimeConfig?: string;
8
+ runtimeStateDir?: string;
9
+ runtimeAccount?: string;
10
+ runtimeBrain?: string;
11
+ runtimeContext?: string;
12
+ confirmConfigure?: boolean;
13
+ runtimeStopped?: boolean;
14
+ }
15
+ export declare function handoffOptions(command: Command): Command;
16
+ export declare function handoffTarget(options: HandoffOptions): Promise<RuntimeConnectTarget | undefined>;
17
+ export declare function handoffAgent(target: RuntimeConnectTarget, profile: string | undefined, deps: AgentDependencies, confirmation: {
18
+ consent: boolean;
19
+ runtimeStopped: boolean;
20
+ }, savedProfileOnly?: boolean): Promise<{
21
+ profile: string;
22
+ handle: string;
23
+ runtime: "openclaw" | "hermes" | "claude-code";
24
+ status: "conflict" | "required-action" | "configured" | "ready";
25
+ code: string;
26
+ message: string;
27
+ connected: boolean;
28
+ }>;
29
+ //# sourceMappingURL=agent-handoff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-handoff.d.ts","sourceRoot":"","sources":["../src/agent-handoff.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAA2C,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE1G,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AACD,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAUxD;AAWD,wBAAsB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAsBtG;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,iBAAiB,EAAE,YAAY,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,cAAc,EAAE,OAAO,CAAA;CAAE,EAAE,gBAAgB,UAAQ;;;;;;;;GAiB3M"}
@@ -0,0 +1,81 @@
1
+ import { safeMetadata } from "./output.js";
2
+ import { lstat, realpath } from "node:fs/promises";
3
+ import { basename, dirname, isAbsolute, join } from "node:path";
4
+ import { DEFAULT_API_URL, validateApiURL, validateToken } from "./config.js";
5
+ import { applyRuntimeConnect, planRuntimeConnect } from "./runtime-connect.js";
6
+ export function handoffOptions(command) {
7
+ return command.option("--connect <runtime>", "configure openclaw, hermes, or claude-code; does not launch it")
8
+ .option("--runtime-home <path>", "absolute Hermes profile home or Claude session channel directory")
9
+ .option("--runtime-config <path>", "absolute existing OpenClaw config file")
10
+ .option("--runtime-state-dir <path>", "absolute selected runtime state directory")
11
+ .option("--runtime-account <name>", "explicit OpenClaw Relay account")
12
+ .option("--runtime-brain <id>", "existing OpenClaw brain binding")
13
+ .option("--runtime-context <id>", "explicit Claude session context")
14
+ .option("--confirm-configure", "consent to writing only the selected runtime credential config")
15
+ .option("--runtime-stopped", "confirm you have stopped the selected runtime before configuration");
16
+ }
17
+ async function nativePath(value, name, file = false) {
18
+ if (!value || !isAbsolute(value))
19
+ throw new Error(`${name} must select an absolute native path.`);
20
+ // The user selects the context; resolve directory aliases, never linked config files.
21
+ return file ? join(await realpath(dirname(value)), basename(value)) : await realpath(value);
22
+ }
23
+ async function requiredNativeFile(path) {
24
+ const info = await lstat(path);
25
+ if (!info.isFile() || info.isSymbolicLink())
26
+ throw new Error("Select an existing regular native runtime configuration file.");
27
+ return path;
28
+ }
29
+ export async function handoffTarget(options) {
30
+ if (!options.connect) {
31
+ if (Object.entries(options).some(([key, value]) => (key.startsWith("runtime") || key === "confirmConfigure") && value !== undefined))
32
+ throw new Error("Runtime options require --connect.");
33
+ return undefined;
34
+ }
35
+ if (!options.confirmConfigure || !options.runtimeStopped)
36
+ throw new Error("Handoff requires --confirm-configure and --runtime-stopped after you stop the selected runtime.");
37
+ if (options.connect === "openclaw") {
38
+ if (!options.runtimeAccount)
39
+ throw new Error("Select --runtime-account explicitly.");
40
+ return { runtime: "openclaw", configPath: await requiredNativeFile(await nativePath(options.runtimeConfig, "--runtime-config", true)), stateDir: await nativePath(options.runtimeStateDir, "--runtime-state-dir", true), account: options.runtimeAccount, ...(options.runtimeBrain ? { brain: options.runtimeBrain } : {}) };
41
+ }
42
+ if (options.connect === "hermes") {
43
+ const profileHome = await nativePath(options.runtimeHome, "--runtime-home");
44
+ await requiredNativeFile(join(profileHome, "config.yaml"));
45
+ return { runtime: "hermes", profileHome, stateDir: await nativePath(options.runtimeStateDir, "--runtime-state-dir", true) };
46
+ }
47
+ if (options.connect === "claude-code") {
48
+ if (!options.runtimeContext?.trim())
49
+ throw new Error("Select --runtime-context explicitly.");
50
+ const channelDir = await nativePath(options.runtimeHome, "--runtime-home");
51
+ await requiredNativeFile(join(channelDir, ".env"));
52
+ return { runtime: "claude-code", channelDir, context: options.runtimeContext };
53
+ }
54
+ throw new Error("--connect must be openclaw, hermes, or claude-code.");
55
+ }
56
+ export async function handoffAgent(target, profile, deps, confirmation, savedProfileOnly = false) {
57
+ if (confirmation.consent !== true || confirmation.runtimeStopped !== true)
58
+ throw new Error("Explicit handoff consent and runtime stop confirmation are required.");
59
+ // Read the just-created/imported saved credential; ENV must not substitute another agent.
60
+ const saved = savedProfileOnly ? (await deps.read()).profiles[profile] : undefined;
61
+ if (savedProfileOnly && !saved?.agent_token)
62
+ throw new Error("Saved profile credential is unavailable; no runtime config was changed.");
63
+ const auth = savedProfileOnly
64
+ ? { token: validateToken(saved.agent_token), apiURL: validateApiURL(saved.api_url ?? DEFAULT_API_URL), profile: profile }
65
+ : await deps.auth(profile);
66
+ let cards;
67
+ try {
68
+ cards = await deps.client(auth.token, auth.apiURL).contactCard.retrieve();
69
+ }
70
+ catch {
71
+ throw new Error("Existing credential validation failed; no runtime config changed and no new agent was created.");
72
+ }
73
+ const agents = cards.contact_cards.filter((card) => card.kind === "agent" && card.is_active);
74
+ if (agents.length !== 1)
75
+ throw new Error("Select a credential with exactly one active agent Contact Card before handoff.");
76
+ const plan = await planRuntimeConnect({ agent: { token: auth.token, origin: auth.apiURL, handle: agents[0].handle }, target });
77
+ const result = plan.status === "ready" ? await applyRuntimeConnect(plan, { consent: confirmation.consent, runtimeStopped: confirmation.runtimeStopped }) : plan;
78
+ // Do not serialize private input, plan internals, rollback capabilities, or errors.
79
+ return safeMetadata({ profile: auth.profile, handle: agents[0].handle, runtime: target.runtime, status: result.status, code: result.code, message: result.message, connected: false }, [auth.token]);
80
+ }
81
+ //# sourceMappingURL=agent-handoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-handoff.js","sourceRoot":"","sources":["../src/agent-handoff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import Relay, { type AgentImageRecipe, type ContactCardItem } from "@relaymessenger/sdk";
2
+ import type { ConfigContext, RelayConfig, ResolvedAuth } from "./config.js";
3
+ /** Injected SDK and persistence boundaries keep command logic independently testable. */
4
+ export interface AgentDependencies {
5
+ read: () => Promise<RelayConfig>;
6
+ preflight: () => Promise<void>;
7
+ update: <T>(change: (config: RelayConfig) => T) => Promise<T>;
8
+ bootstrap: typeof Relay.createAgent;
9
+ client: (token: string, apiURL: string) => Pick<Relay, "contactCard" | "agents">;
10
+ auth: (profile?: string) => Promise<ResolvedAuth>;
11
+ env: NodeJS.ProcessEnv;
12
+ }
13
+ export declare const agentDependencies: (context?: ConfigContext, fetch?: typeof globalThis.fetch) => AgentDependencies;
14
+ export interface CreateAgentInput {
15
+ profile?: string;
16
+ apiURL?: string;
17
+ tokenName?: string;
18
+ handle?: string;
19
+ firstName?: string;
20
+ imageURL?: string;
21
+ imageRecipe?: AgentImageRecipe;
22
+ }
23
+ export declare function createAgent(input: CreateAgentInput, deps: AgentDependencies): Promise<{
24
+ profile: string;
25
+ api_url: string;
26
+ agent: ContactCardItem;
27
+ share_url: string;
28
+ token: "stored";
29
+ }>;
30
+ export declare function listAgents(deps: AgentDependencies): Promise<{
31
+ agents: ({
32
+ profile: string;
33
+ api_url: string;
34
+ token: string;
35
+ contact_cards: ContactCardItem[];
36
+ error?: never;
37
+ } | {
38
+ profile: string;
39
+ api_url: string;
40
+ token: string;
41
+ error: string;
42
+ contact_cards?: never;
43
+ })[];
44
+ }>;
45
+ export declare function selectAgentAuth(handle: string, profile: string | undefined, deps: AgentDependencies): Promise<ResolvedAuth>;
46
+ export declare function deleteAgent(handle: string, profile: string | undefined, deps: AgentDependencies): Promise<{
47
+ ok: boolean;
48
+ handle: string;
49
+ profile: string;
50
+ token: string;
51
+ }>;
52
+ //# sourceMappingURL=agents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAyC,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAChI,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG5E,yFAAyF;AACzF,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;IACjC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAC9D,SAAS,EAAE,OAAO,KAAK,CAAC,WAAW,CAAC;IACpC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,QAAQ,CAAC,CAAC;IACjF,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAClD,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,eAAO,MAAM,iBAAiB,GAAI,UAAS,aAAkB,EAAE,QAAQ,OAAO,UAAU,CAAC,KAAK,KAAG,iBAQ/F,CAAC;AAQH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAChC;AAYD,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,iBAAiB;;;;;;GAuEjF;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,iBAAiB;;;;;;;;;;;;;;GAevD;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC,CA0BjI;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,iBAAiB;;;;;GAsBrG"}