relaymessenger 0.0.0 → 0.1.0-staging.1

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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +313 -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/agent-image-upload.d.ts +23 -0
  8. package/dist/agent-image-upload.d.ts.map +1 -0
  9. package/dist/agent-image-upload.js +45 -0
  10. package/dist/agent-image-upload.js.map +1 -0
  11. package/dist/agent-session.d.ts +23 -0
  12. package/dist/agent-session.d.ts.map +1 -0
  13. package/dist/agent-session.js +37 -0
  14. package/dist/agent-session.js.map +1 -0
  15. package/dist/agents.d.ts +52 -0
  16. package/dist/agents.d.ts.map +1 -0
  17. package/dist/agents.js +194 -0
  18. package/dist/agents.js.map +1 -0
  19. package/dist/cli.d.ts +3 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +4 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/client.d.ts +8 -0
  24. package/dist/client.d.ts.map +1 -0
  25. package/dist/client.js +13 -0
  26. package/dist/client.js.map +1 -0
  27. package/dist/config.d.ts +46 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +374 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/doctor.d.ts +21 -0
  32. package/dist/doctor.d.ts.map +1 -0
  33. package/dist/doctor.js +92 -0
  34. package/dist/doctor.js.map +1 -0
  35. package/dist/event-listen.d.ts +11 -0
  36. package/dist/event-listen.d.ts.map +1 -0
  37. package/dist/event-listen.js +37 -0
  38. package/dist/event-listen.js.map +1 -0
  39. package/dist/interactive.d.ts +23 -0
  40. package/dist/interactive.d.ts.map +1 -0
  41. package/dist/interactive.js +113 -0
  42. package/dist/interactive.js.map +1 -0
  43. package/dist/local-image.d.ts +23 -0
  44. package/dist/local-image.d.ts.map +1 -0
  45. package/dist/local-image.js +91 -0
  46. package/dist/local-image.js.map +1 -0
  47. package/dist/output.d.ts +6 -0
  48. package/dist/output.d.ts.map +1 -0
  49. package/dist/output.js +37 -0
  50. package/dist/output.js.map +1 -0
  51. package/dist/program.d.ts +30 -0
  52. package/dist/program.d.ts.map +1 -0
  53. package/dist/program.js +966 -0
  54. package/dist/program.js.map +1 -0
  55. package/dist/runtime-connect/implementation.d.ts +4 -0
  56. package/dist/runtime-connect/implementation.d.ts.map +1 -0
  57. package/dist/runtime-connect/implementation.js +487 -0
  58. package/dist/runtime-connect/implementation.js.map +1 -0
  59. package/dist/runtime-connect/windows-acl.d.ts +15 -0
  60. package/dist/runtime-connect/windows-acl.d.ts.map +1 -0
  61. package/dist/runtime-connect/windows-acl.js +65 -0
  62. package/dist/runtime-connect/windows-acl.js.map +1 -0
  63. package/dist/runtime-connect.d.ts +49 -0
  64. package/dist/runtime-connect.d.ts.map +1 -0
  65. package/dist/runtime-connect.js +2 -0
  66. package/dist/runtime-connect.js.map +1 -0
  67. package/dist/secret-input.d.ts +4 -0
  68. package/dist/secret-input.d.ts.map +1 -0
  69. package/dist/secret-input.js +68 -0
  70. package/dist/secret-input.js.map +1 -0
  71. package/dist/skill-offer.d.ts +6 -0
  72. package/dist/skill-offer.d.ts.map +1 -0
  73. package/dist/skill-offer.js +205 -0
  74. package/dist/skill-offer.js.map +1 -0
  75. package/dist/terminal-session.d.ts +54 -0
  76. package/dist/terminal-session.d.ts.map +1 -0
  77. package/dist/terminal-session.js +204 -0
  78. package/dist/terminal-session.js.map +1 -0
  79. package/dist/terminal-watch.d.ts +28 -0
  80. package/dist/terminal-watch.d.ts.map +1 -0
  81. package/dist/terminal-watch.js +73 -0
  82. package/dist/terminal-watch.js.map +1 -0
  83. 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,313 @@
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
+ Before interactive creation or sign-in setup, the CLI may offer the Relay skill
35
+ once if it is absent from the standard install locations. Declining skips skill
36
+ installation; cancelling stops setup before any identity is created. 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 are reported before setup proceeds and never repeat
45
+ an agent creation. 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
+ ### Persistent agent view
51
+
52
+ Successful interactive creation and sign-in keep the public QR/link and event
53
+ view open. Reopen an existing saved identity with
54
+ `relay --profile <saved-profile> auth status`. Press `q`, Ctrl-C, or Ctrl-D to
55
+ close the view; it does not delete the agent or stop a selected runtime.
56
+
57
+ The view uses the SDK's explicit `observe: true` WebSocket mode and requires the
58
+ server's `observational: true` ready confirmation. It sends no event ACK or
59
+ FULL-sync completion and never falls back to a consuming listener. Events are
60
+ best-effort and may have retention gaps; event-view connectivity is not model
61
+ readiness. Unavailable observation is displayed without claiming a connected
62
+ runtime. JSON, non-interactive, and non-TTY commands do not open this session.
63
+
64
+ ## Agent Token authentication
65
+
66
+ Use `agents create` for a new agent, or import an existing Agent Token. Tokens
67
+ can be entered through the private `auth login` prompt, read from stdin with
68
+ `auth login --with-token`, supplied by `RELAY_AGENT_TOKEN` when present, or reused
69
+ from the selected saved profile with `--connect`;
70
+ there is deliberately no token command-line option.
71
+
72
+ ```sh
73
+ # Private prompt when RELAY_AGENT_TOKEN is not set:
74
+ relay auth login --api-url https://api.staging.relayapp.im
75
+ # Headless stdin:
76
+ printf '%s' "$RELAY_AGENT_TOKEN" | relay auth login --with-token --api-url https://api.staging.relayapp.im
77
+ relay auth status
78
+ relay doctor
79
+ ```
80
+
81
+ Profiles live in `${XDG_CONFIG_HOME:-~/.config}/relay/config.json`. The
82
+ directory is mode `0700` and the file is mode `0600` on POSIX systems.
83
+
84
+ ```sh
85
+ relay profiles add staging --api-url https://api.staging.relayapp.im
86
+ relay profiles use staging
87
+ printf '%s' "$STAGING_RELAY_AGENT_TOKEN" |
88
+ relay auth login --profile staging --with-token
89
+ relay profiles list
90
+ ```
91
+
92
+ Resource-command token resolution order is:
93
+
94
+ 1. `RELAY_AGENT_TOKEN`, `RELAY_API_URL`, and `RELAY_PROFILE`;
95
+ 2. the selected local profile;
96
+ 3. `https://api.relayapp.im` as the API URL.
97
+
98
+ Plain HTTP API URLs are rejected except for loopback development origins.
99
+
100
+ ## Resource commands
101
+
102
+ Resource commands below print JSON; agent creation also offers a human-readable
103
+ share link and QR unless `--json` is selected.
104
+
105
+ ```sh
106
+ relay chats list --limit 20
107
+ relay chats get "$CHAT_ID"
108
+ relay chats messages list "$CHAT_ID" --limit 50 --order desc # newest first; omit --order for oldest first
109
+ relay chats messages send "$CHAT_ID" --text "Hello" \
110
+ --idempotency-key "$(uuidgen)"
111
+ relay messages send --to advait --text "Hello" \
112
+ --idempotency-key "$(uuidgen)"
113
+ relay messages react "$MESSAGE_ID" --operation add --type love
114
+ relay chats typing start "$CHAT_ID"
115
+ relay chats read "$CHAT_ID"
116
+
117
+ relay contact-card get
118
+ relay contact-card setup --handle weather.acme --first-name Weather
119
+ relay contact-card share "$CHAT_ID"
120
+ relay contact-requests create advait
121
+
122
+ relay attachments upload ./report.pdf --content-type application/pdf
123
+ relay blocked-handles list
124
+ relay webhooks events
125
+ relay webhooks subscriptions list
126
+ ```
127
+
128
+ Run `relay --help` and each command group's `--help` for the full current
129
+ surface: Chats, Messages, Attachments, blocked Handles, webhook events and
130
+ subscriptions, Contact Cards, and Contact requests.
131
+
132
+ Chats contain at most one human user and one or more agents; agent-to-agent
133
+ Chats are also supported. Agents and users have the same generic Chat API
134
+ permissions. Creating or reusing a user-containing Chat requires every agent
135
+ to be that user's added, unblocked Contact, including an agent sender. Adding
136
+ an agent checks the new target and any acting agent; an agent removing others
137
+ must still be the user's added, unblocked Contact. Self-leave keeps existing
138
+ rules. This is admission eligibility, not a new membership-history or un-add
139
+ revocation lifecycle: removing a Contact does not imply removal from all groups.
140
+ It does not require conversational approval or company-policy tables. Agent-only
141
+ messaging keeps its existing behavior, without a new per-agent mutual-Add rule.
142
+ Chats allow at most 7 total participants including the sender, so `--to`
143
+ accepts at most 6 recipient Handles.
144
+
145
+ Participant commands keep their generic names; add an eligible agent by its Handle:
146
+
147
+ ```sh
148
+ relay chats participants add "$CHAT_ID" research.agent
149
+ relay chats participants remove "$CHAT_ID" research.agent
150
+ ```
151
+
152
+ `contact-card share` shares the authenticated agent's own card.
153
+ `contact-requests create` asks a user to add the authenticated Premium Handle
154
+ agent; it is not a human invitation. Agent-initiated Messages to users remain
155
+ supported subject to Contacts eligibility and blocking; a pending Add request
156
+ does not grant messaging eligibility. There are no phone address-book, mutual-contact, human discovery,
157
+ or human invite-link commands.
158
+
159
+ ## Developer-managed agents
160
+
161
+ ```sh
162
+ relay agents create --api-url https://api.staging.relayapp.im
163
+ relay agents create --api-url https://api.staging.relayapp.im --json
164
+ relay agents list --json
165
+ relay --profile brave_cangoo.dev agents delete brave_cangoo.dev
166
+ ```
167
+
168
+ Creation stores the one-time Agent Token in a new named profile and prints only
169
+ public metadata, a share link, and a terminal QR. JSON output includes
170
+ `token: "stored"`, never the secret. Use an explicit `--profile <new-name>` to
171
+ choose a new profile name; existing profiles and the current profile selection
172
+ are preserved. `--token-name` labels the token, not a machine identity.
173
+
174
+ Listing is local inventory, not a global account API. Each saved credential reads
175
+ its current Contact Card using its saved API origin; environment token/origin
176
+ overrides are not applied across the inventory. Tokenless profiles remain in `profiles list`, not agent inventory.
177
+ Unavailable Contact Cards are reported without exposing error bodies.
178
+
179
+ Deletion honors explicit profile/ENV selection. Otherwise it selects one saved
180
+ credential by its authenticated Contact Card, refusing unavailable or ambiguous
181
+ matches (including the same handle on multiple origins). It only clears that
182
+ profile's matching saved credential after confirmed HTTP 204. Errors and uncertain
183
+ responses retain credentials; unrelated environment/profile credentials are not
184
+ removed. New creation defaults to the staging API when this package has a staging prerelease
185
+ version; it never inherits an empty legacy production profile. Explicit `--api-url`
186
+ or `RELAY_API_URL` overrides remain authoritative, and existing profile origins
187
+ are unchanged. Creation is never automatically retried. If creation succeeds but local
188
+ storage fails, the command reports the safely assigned handle and whether local
189
+ storage is present, absent, or unverified, without printing the secret. Private
190
+ config write/ACL preflight runs before the POST and never overwrites existing
191
+ credentials. It is not a reservation or a durable recovery mechanism.
192
+
193
+ ### Optional identity and picture
194
+
195
+ ```sh
196
+ relay agents create --api-url https://api.staging.relayapp.im \
197
+ --handle my_helper.dev --name "My Helper" \
198
+ --image-url https://images.example.com/helper.png
199
+ ```
200
+
201
+ Omit any option to keep the server's assigned handle/readable bird name/default
202
+ image. Custom handles are full lowercase `.dev` handles; a collision is an error,
203
+ never a request for a random replacement. Interactive creation asks `Handle (optional)`, `Name (optional)`, and `Image
204
+ (optional)` with a single help line; blank answers preserve defaults. Selecting
205
+ Create already expresses intent, so no second create confirmation is shown.
206
+ Recipe files remain an advanced `--image-recipe` flag, not another setup question.
207
+
208
+ `--image <path-or-url>` accepts a local supported image or public HTTPS URL;
209
+ `--image-url` remains a URL alias. The CLI checks a local file's readability,
210
+ size, and image signature before creation. Once the new token is privately saved,
211
+ it allocates/uploads through the existing Attachments API, checks completion,
212
+ and updates the Contact Card using the completed `attachment_id`.
213
+
214
+ If image upload/promotion is not confirmed, the new identity and saved profile
215
+ are retained, and the command reports the incomplete image phase. Retry the
216
+ image on that existing identity—do not run `agents create` again:
217
+
218
+ ```sh
219
+ relay --profile my_helper.dev contact-card update --handle my_helper.dev --image ./helper.png
220
+ # If upload completed but promotion failed, reuse the returned attachment ID:
221
+ relay --profile my_helper.dev contact-card update --handle my_helper.dev --attachment-id <completed-id>
222
+ ```
223
+
224
+ `--image-recipe <json-file>` remains an advanced flag for existing Relay avatar
225
+ metadata, paired with its rendered local image/URL/attachment. It is not a default
226
+ interactive question. The CLI does not render recipes or generate images. The
227
+ server's response supplies the permanent public image URL.
228
+
229
+ ### Optional native runtime handoff
230
+
231
+ ```sh
232
+ relay agents create --api-url https://api.staging.relayapp.im --connect hermes \
233
+ --runtime-home /absolute/hermes-profile \
234
+ --runtime-state-dir /absolute/hermes-profile/relay \
235
+ --confirm-configure --runtime-stopped
236
+
237
+ # Import into a staging profile via private stdin; no creation request.
238
+ relay --profile staging auth login --with-token --api-url https://api.staging.relayapp.im --connect openclaw \
239
+ --runtime-config /absolute/openclaw.json \
240
+ --runtime-state-dir /absolute/openclaw-state --runtime-account my-agent \
241
+ --confirm-configure --runtime-stopped
242
+ ```
243
+
244
+ Stop the selected runtime before passing `--runtime-stopped`. `--confirm-configure`
245
+ authorizes only private configuration writes. `--runtime-brain` selects an existing
246
+ OpenClaw binding; Claude uses `--runtime-home` for an existing session channel
247
+ directory and `--runtime-context` for its session identifier. Existing sender
248
+ permissions are preserved, not inferred from Contacts.
249
+
250
+ Creation handoff reads the newly saved profile directly, ignoring unrelated ENV
251
+ credentials. `auth login --connect` without token-input flags reuses the selected saved profile
252
+ and its origin, ignoring unrelated ENV credentials. Add `--with-token` to select stdin explicitly. Plain `auth login` uses
253
+ `RELAY_AGENT_TOKEN` in headless environments or a hidden terminal prompt. Handoff validates the credential before saving
254
+ an import and never falls back to creation.
255
+ Empty Hermes profiles and explicit empty/new OpenClaw accounts can receive an
256
+ initial credential when their native context and state are safe. Occupied
257
+ credentials, unknown secret references, and bound/corrupt state are not replaced.
258
+
259
+ Handoff output reports configuration status and `connected: false`: this command
260
+ does not install, launch, stop, or test-connect a runtime. Start it using its native
261
+ workflow. If handoff fails after creation, the token remains stored; use `auth login --connect` with the existing token rather than creating another identity.
262
+
263
+ ## Local event forwarding
264
+
265
+ `relay events listen` is a development convenience backed only by the SDK's
266
+ source-backed Agent WebSocket. It refuses Relay's production API, requires an
267
+ explicit profile, and requires confirmation that the profile belongs to a
268
+ dedicated non-production Agent whose durable checkpoint may advance:
269
+
270
+ ```sh
271
+ relay --profile staging events listen --acknowledge-events
272
+ relay --profile staging events listen --acknowledge-events \
273
+ --forward-to http://127.0.0.1:3000/relay-events
274
+ ```
275
+
276
+ Forward destinations must be loopback HTTP(S). Forwarded bodies are the
277
+ original Relay event envelopes but are **unsigned** and carry
278
+ `x-relay-dev-forwarded: 1`; this is not a substitute for testing Standard
279
+ Webhooks signature verification. A non-2xx local response is not acknowledged,
280
+ so Relay can redeliver it. Local receivers must deduplicate by `event_id`.
281
+
282
+ The listener refuses a FULL-sync request rather than falsely claiming it
283
+ rebuilt durable state. It also cannot run while the Agent has webhook
284
+ subscriptions because Relay makes those delivery modes exclusive. Never point
285
+ it at an Agent whose checkpoint is owned by another consumer.
286
+
287
+ ## Doctor
288
+
289
+ `relay doctor` checks the Node runtime, API URL, token resolution, local file
290
+ permissions, SDK contract availability, and a read-only API request.
291
+ `relay doctor --offline` skips only the network request and is suitable for
292
+ package-install checks.
293
+
294
+ ## Security
295
+
296
+ - Keep Agent Tokens out of source, URLs, shell arguments, and logs.
297
+ - Prefer secret-manager injection through `RELAY_AGENT_TOKEN` in automation.
298
+ - Output and error paths redact every locally resolvable token.
299
+ - This package has no coding-agent runtime, pairing flow, or hidden private
300
+ API client.
301
+
302
+ ## Development
303
+
304
+ All Linux execution happens in a fresh Daytona sandbox:
305
+
306
+ ```sh
307
+ npm ci
308
+ npm run validate
309
+ ```
310
+
311
+ `validate` performs type checking, unit and negative tests, the pinned SDK
312
+ operation-hash check, boundary checks, package packing, isolated tarball
313
+ 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,23 @@
1
+ import type Relay from "@relaymessenger/sdk";
2
+ import type { ContactCardItem } from "@relaymessenger/sdk";
3
+ import type { LocalAgentImage } from "./local-image.js";
4
+ export type AgentImageUploadPhase = "identity" | "allocation" | "upload" | "completion" | "promotion";
5
+ export type AgentImageUploadResult = {
6
+ status: "updated";
7
+ attachment_id: string;
8
+ agent: ContactCardItem;
9
+ } | {
10
+ status: "incomplete";
11
+ phase: AgentImageUploadPhase;
12
+ attachment_id?: string;
13
+ message: string;
14
+ };
15
+ /** Existing authenticated attachment lifecycle only. The caller supplies the
16
+ * agreed SDK contact-card promotion operation; no route or HTTP implementation
17
+ * is duplicated here. An uncertain operation is never automatically repeated. */
18
+ export declare function uploadAgentImage(input: {
19
+ handle: string;
20
+ image?: LocalAgentImage;
21
+ attachmentID?: string;
22
+ }, client: Pick<Relay, "attachments" | "contactCard">, promote: (attachmentID: string) => Promise<ContactCardItem>): Promise<AgentImageUploadResult>;
23
+ //# sourceMappingURL=agent-image-upload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-image-upload.d.ts","sourceRoot":"","sources":["../src/agent-image-upload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,qBAAqB,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,MAAM,qBAAqB,GAAG,UAAU,GAAG,YAAY,GAAG,QAAQ,GAAG,YAAY,GAAG,WAAW,CAAC;AACtG,MAAM,MAAM,sBAAsB,GAC9B;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,eAAe,CAAA;CAAE,GACpE;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,qBAAqB,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpG;;iFAEiF;AACjF,wBAAsB,gBAAgB,CACpC,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,eAAe,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,EACzE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,CAAC,EAClD,OAAO,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,eAAe,CAAC,GAC1D,OAAO,CAAC,sBAAsB,CAAC,CAmCjC"}
@@ -0,0 +1,45 @@
1
+ /** Existing authenticated attachment lifecycle only. The caller supplies the
2
+ * agreed SDK contact-card promotion operation; no route or HTTP implementation
3
+ * is duplicated here. An uncertain operation is never automatically repeated. */
4
+ export async function uploadAgentImage(input, client, promote) {
5
+ let phase = "identity";
6
+ let attachmentID = input.attachmentID;
7
+ try {
8
+ const cards = await client.contactCard.retrieve({}, { maxRetries: 0 });
9
+ if (cards.contact_cards.length !== 1 || cards.contact_cards[0]?.handle !== input.handle || cards.contact_cards[0].kind !== "agent" || !cards.contact_cards[0].is_active) {
10
+ throw new Error("Selected token does not match the intended agent.");
11
+ }
12
+ if (input.image && input.attachmentID)
13
+ throw new Error("Choose a new image or existing attachment.");
14
+ if (!attachmentID) {
15
+ if (!input.image)
16
+ throw new Error("Image file required.");
17
+ phase = "allocation";
18
+ const allocation = await client.attachments.create({
19
+ filename: input.image.filename, content_type: input.image.contentType, size_bytes: input.image.size,
20
+ }, { maxRetries: 0 });
21
+ attachmentID = allocation.attachment_id;
22
+ phase = "upload";
23
+ await client.attachments.upload(allocation, new Blob([Uint8Array.from(input.image.data)], { type: input.image.contentType }), { maxRetries: 0, timeout: 120_000, signal: AbortSignal.timeout(120_000) });
24
+ }
25
+ phase = "completion";
26
+ const attachment = await client.attachments.retrieve(attachmentID, { maxRetries: 0 });
27
+ if (attachment.status !== "complete")
28
+ throw new Error("Attachment not complete.");
29
+ phase = "promotion";
30
+ const agent = await promote(attachmentID);
31
+ if (agent.handle !== input.handle || agent.kind !== "agent")
32
+ throw new Error("Promotion response does not match the intended agent.");
33
+ return { status: "updated", attachment_id: attachmentID, agent };
34
+ }
35
+ catch {
36
+ // Never return server/upload response text or exception details: either may
37
+ // reflect the private token or temporary upload capability.
38
+ return {
39
+ status: "incomplete", phase,
40
+ ...(attachmentID ? { attachment_id: attachmentID } : {}),
41
+ message: "Agent identity and saved token are retained. Image update was not confirmed; retry the image on this existing identity, not agent creation.",
42
+ };
43
+ }
44
+ }
45
+ //# sourceMappingURL=agent-image-upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-image-upload.js","sourceRoot":"","sources":["../src/agent-image-upload.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import Relay from "@relaymessenger/sdk";
2
+ import type { AgentDependencies } from "./agents.js";
3
+ import { type TerminalSessionIO, type TerminalSessionOptions, type TerminalSessionResult, type TerminalRuntimeState } from "./terminal-session.js";
4
+ export interface AgentSessionInput {
5
+ profile: string;
6
+ handle?: string;
7
+ apiURL?: string;
8
+ shareURL?: string;
9
+ runtime?: TerminalRuntimeState;
10
+ }
11
+ export interface AgentSessionDependencies {
12
+ agents: Pick<AgentDependencies, "read">;
13
+ fetch?: typeof globalThis.fetch;
14
+ session?: (options: TerminalSessionOptions, io?: TerminalSessionIO) => Promise<TerminalSessionResult>;
15
+ io?: TerminalSessionIO;
16
+ client?: (token: string, origin: string) => Pick<Relay, "contactCard" | "websocket">;
17
+ }
18
+ /** New identities use the exact server share URL. For older imported profiles,
19
+ * only known Relay environments have a source-backed public-link mapping. */
20
+ export declare function savedAgentShareURL(origin: string, handle: string): string;
21
+ /** Uses the actual saved profile, never an unrelated ENV token or origin. */
22
+ export declare function openSavedAgentSession(input: AgentSessionInput, dependencies: AgentSessionDependencies): Promise<TerminalSessionResult>;
23
+ //# sourceMappingURL=agent-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-session.d.ts","sourceRoot":"","sources":["../src/agent-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,qBAAqB,CAAC;AACxC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAsB,KAAK,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,KAAK,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAGvK,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC;AACD,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;IACxC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,EAAE,EAAE,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACtG,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,WAAW,CAAC,CAAC;CACtF;AACD;6EAC6E;AAC7E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAIzE;AAED,6EAA6E;AAC7E,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,wBAAwB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAmB5I"}
@@ -0,0 +1,37 @@
1
+ import Relay from "@relaymessenger/sdk";
2
+ import { DEFAULT_API_URL, STAGING_API_URL, validateApiURL, validateToken } from "./config.js";
3
+ import { runTerminalSession } from "./terminal-session.js";
4
+ import { sdkTerminalObserver } from "./terminal-watch.js";
5
+ /** New identities use the exact server share URL. For older imported profiles,
6
+ * only known Relay environments have a source-backed public-link mapping. */
7
+ export function savedAgentShareURL(origin, handle) {
8
+ const publicOrigin = origin === STAGING_API_URL ? "https://staging.relayapp.im"
9
+ : origin === DEFAULT_API_URL ? "https://go.relayapp.im" : undefined;
10
+ return publicOrigin ? new URL(`/@${encodeURIComponent(handle)}`, publicOrigin).href : "";
11
+ }
12
+ /** Uses the actual saved profile, never an unrelated ENV token or origin. */
13
+ export async function openSavedAgentSession(input, dependencies) {
14
+ const config = await dependencies.agents.read();
15
+ const selected = config.profiles[input.profile];
16
+ if (!selected?.agent_token)
17
+ throw new Error("Selected profile has no saved credential for viewing.");
18
+ const origin = validateApiURL(selected.api_url ?? DEFAULT_API_URL);
19
+ if (input.apiURL && input.apiURL !== origin)
20
+ throw new Error("Selected profile origin changed; no observer opened.");
21
+ const token = validateToken(selected.agent_token);
22
+ const client = dependencies.client?.(token, origin) ?? new Relay({ apiKey: token, baseURL: origin, ...(dependencies.fetch ? { fetch: dependencies.fetch } : {}) });
23
+ const cards = await client.contactCard.retrieve({}, { maxRetries: 0 });
24
+ if (cards.contact_cards.length !== 1 || cards.contact_cards[0]?.kind !== "agent" || !cards.contact_cards[0].is_active)
25
+ throw new Error("An active saved agent is required for the terminal view.");
26
+ const card = cards.contact_cards[0];
27
+ if (input.handle && input.handle !== card.handle)
28
+ throw new Error("Selected profile identity changed; no observer opened.");
29
+ return (dependencies.session ?? runTerminalSession)({
30
+ interactive: true,
31
+ agent: { handle: card.handle, name: card.first_name, profile: input.profile, shareUrl: input.shareURL ?? savedAgentShareURL(origin, card.handle) },
32
+ runtime: input.runtime ?? { ownership: "unknown", connection: "unknown" },
33
+ observer: sdkTerminalObserver(client),
34
+ secrets: [token],
35
+ }, dependencies.io);
36
+ }
37
+ //# sourceMappingURL=agent-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-session.js","sourceRoot":"","sources":["../src/agent-session.ts"],"names":[],"mappings":"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"}