relaymessenger 0.1.5 → 0.1.6-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 (49) hide show
  1. package/README.md +65 -62
  2. package/dist/agent-create.d.ts +24 -0
  3. package/dist/agent-create.d.ts.map +1 -0
  4. package/dist/agent-create.js +64 -0
  5. package/dist/agent-create.js.map +1 -0
  6. package/dist/agent-driver.d.ts +32 -0
  7. package/dist/agent-driver.d.ts.map +1 -0
  8. package/dist/agent-driver.js +59 -0
  9. package/dist/agent-driver.js.map +1 -0
  10. package/dist/claude-channel.d.ts +43 -0
  11. package/dist/claude-channel.d.ts.map +1 -0
  12. package/dist/claude-channel.js +124 -0
  13. package/dist/claude-channel.js.map +1 -0
  14. package/dist/connect.d.ts +106 -0
  15. package/dist/connect.d.ts.map +1 -0
  16. package/dist/connect.js +418 -0
  17. package/dist/connect.js.map +1 -0
  18. package/dist/help-groups.d.ts +29 -0
  19. package/dist/help-groups.d.ts.map +1 -0
  20. package/dist/help-groups.js +38 -0
  21. package/dist/help-groups.js.map +1 -0
  22. package/dist/interactive.d.ts +28 -2
  23. package/dist/interactive.d.ts.map +1 -1
  24. package/dist/interactive.js +61 -28
  25. package/dist/interactive.js.map +1 -1
  26. package/dist/program.d.ts +4 -1
  27. package/dist/program.d.ts.map +1 -1
  28. package/dist/program.js +265 -160
  29. package/dist/program.js.map +1 -1
  30. package/dist/qr-terminal.d.ts +6 -0
  31. package/dist/qr-terminal.d.ts.map +1 -0
  32. package/dist/qr-terminal.js +49 -0
  33. package/dist/qr-terminal.js.map +1 -0
  34. package/dist/runtime-sniff.d.ts +43 -0
  35. package/dist/runtime-sniff.d.ts.map +1 -0
  36. package/dist/runtime-sniff.js +96 -0
  37. package/dist/runtime-sniff.js.map +1 -0
  38. package/dist/skill-offer.d.ts +4 -1
  39. package/dist/skill-offer.d.ts.map +1 -1
  40. package/dist/skill-offer.js +9 -2
  41. package/dist/skill-offer.js.map +1 -1
  42. package/dist/terminal-session.d.ts.map +1 -1
  43. package/dist/terminal-session.js +2 -5
  44. package/dist/terminal-session.js.map +1 -1
  45. package/package.json +2 -2
  46. package/dist/agent-handoff.d.ts +0 -29
  47. package/dist/agent-handoff.d.ts.map +0 -1
  48. package/dist/agent-handoff.js +0 -81
  49. package/dist/agent-handoff.js.map +0 -1
package/README.md CHANGED
@@ -19,17 +19,43 @@ npm install --global relaymessenger
19
19
  Node.js 22.22.3 or newer is required. `relaymessenger` is
20
20
  the canonical executable; `relay` is the shorter command alias.
21
21
 
22
+ ## The front door
23
+
24
+ ```sh
25
+ npx relaymessenger connect # asks what will answer as this agent
26
+ npx relaymessenger connect claude # names it outright
27
+ ```
28
+
29
+ `connect` finds the runtimes on this computer, makes an agent or takes one you
30
+ already have, shows every file it will write and every command it will run,
31
+ writes the runtime's own configuration, waits for a first message and asks
32
+ whether to allow that sender, then offers to start the runtime. This build
33
+ writes Claude Code; Hermes and OpenClaw are detected, their plan is printed, and
34
+ the command stops without changing anything.
35
+
36
+ Every question has a flag for scripts: `--new`, `--handle`, `--name`, `--image`,
37
+ `--token`, `--allow`, `--yes`, `--dry-run`, `--no-start`, `--no-skill`, `--json`
38
+ and `--api-url`. `--dry-run` prints the plan and changes nothing.
39
+
22
40
  ## Interactive use
23
41
 
24
- Run `relay` (or `npx relaymessenger`) 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.
42
+ Run `relay` (or `npx relaymessenger`) in a terminal for Connect an agent, Watch
43
+ an agent, and Exit. `relay agents` and `relay auth` offer focused menus. Menus
44
+ and passwords use Clack; cancellation before a mutation leaves it unperformed.
45
+
46
+ Whether Relay asks anything is decided by the terminal and the flags, and by
47
+ nothing else: a `CI` variable no longer suppresses a menu. Explicit commands
48
+ still work. `--non-interactive`, `--json`, piping, help, and version output never
49
+ show optional menus or skill offers. Interactive agent deletion asks for
50
+ confirmation; scripted deletion does not gain a mandatory `--yes` flag.
51
+
52
+ With no terminal, a command that needs an answer prints the flags that would
53
+ have answered it and exits 2 rather than a usage block. With `--json`, every
54
+ error is `{ "error": …, "next_step": … }`.
28
55
 
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.
56
+ `relay --help` puts the commands in three groups: Get started (`connect`), Every
57
+ day (`watch`, `doctor`, `agents`), and Everything else behind one line. Every
58
+ command has a description; run `relay help <command>` for any of them.
33
59
 
34
60
  Before interactive creation or sign-in setup, the CLI may offer the Relay skill
35
61
  once if it is absent from the standard install locations. Declining skips skill
@@ -49,10 +75,12 @@ The install menu remains available when you explicitly want to run the installer
49
75
 
50
76
  ### Persistent agent view
51
77
 
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.
78
+ `relay watch <handle>` opens the live view: the public QR and link, and the
79
+ events as they arrive. Successful interactive creation and sign-in keep the same
80
+ view open. Press `q`, Ctrl-C, or Ctrl-D to close it; it does not delete the agent
81
+ or stop a runtime. `relay events listen` is the older name for a different thing:
82
+ it takes events, so Relay can stop resending them elsewhere. It keeps working and
83
+ keeps its flags, and it is no longer listed in the help.
56
84
 
57
85
  The view only watches. Your agent still receives every message, because this view
58
86
  never answers Relay and never takes an event from it. It shows the events Relay
@@ -61,13 +89,30 @@ they are. A connected view does not mean the agent is running, and the screen
61
89
  says that too. `--json`, `--non-interactive`, and any command not attached to a
62
90
  terminal never open this view.
63
91
 
92
+ ## For the agent running this command
93
+
94
+ Relay reads the environment for the coding agent driving it — `CLAUDECODE`,
95
+ `CURSOR_CLI`, `CURSOR`, `CODEX_HOME` — and when one is there it asks nothing and
96
+ prints one line saying so. Every question has a flag.
97
+
98
+ ```sh
99
+ relaymessenger --install-skills # install the Relay skill for this computer's agents
100
+ relaymessenger docs # the documentation itself when piped, its address in a terminal
101
+ ```
102
+
103
+ `--install-skills` runs the pinned installer with `--global --yes`, targeting
104
+ `~/.agents/skills` and, when this computer has Claude Code, `~/.claude/skills`.
105
+ It answers nothing on your behalf beyond those targets, and the command you
106
+ typed carries on afterwards.
107
+
108
+ `docs` prints `https://docs.relayapp.im/llms.txt`: its contents when there is no
109
+ terminal, and its address when there is one or when Relay cannot reach it.
110
+
64
111
  ## Agent Token authentication
65
112
 
66
113
  Use `agents create` for a new agent, or import an existing Agent Token. Tokens
67
114
  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.
115
+ `auth login --with-token`, or supplied by `RELAY_AGENT_TOKEN` when present.
71
116
 
72
117
  ```sh
73
118
  # Private prompt when RELAY_AGENT_TOKEN is not set:
@@ -109,7 +154,6 @@ relay chats read "$CHAT_ID"
109
154
  relay contact-card get
110
155
  relay contact-card setup --handle weather.dev --name Weather
111
156
  relay contact-card share "$CHAT_ID"
112
- relay contact-requests create advait
113
157
 
114
158
  relay attachments upload ./report.pdf --content-type application/pdf
115
159
  relay blocked-handles list
@@ -119,7 +163,7 @@ relay webhooks subscriptions list
119
163
 
120
164
  Run `relay --help` and each command group's `--help` for the full current
121
165
  surface: Chats, Messages, Attachments, blocked Handles, webhook events and
122
- subscriptions, Contact Cards, and Contact requests.
166
+ subscriptions, and Contact Cards.
123
167
 
124
168
  Chats contain at most one human user and one or more agents; agent-to-agent
125
169
  Chats are also supported. Agents and users have the same generic Chat API
@@ -141,13 +185,10 @@ relay chats participants add "$CHAT_ID" research.dev
141
185
  relay chats participants remove "$CHAT_ID" research.dev
142
186
  ```
143
187
 
144
- `contact-card share` shares the authenticated agent's own card.
145
- `contact-requests create` asks a person to add this agent. Only an agent on a paid
146
- handle may ask; any other agent gets a 402 answer saying so. It is not a human
147
- invitation. Agent-initiated Messages to users remain
148
- supported subject to Contacts eligibility and blocking; a pending Add request
149
- does not grant messaging eligibility. There are no phone address-book, mutual-contact, human discovery,
150
- or human invite-link commands.
188
+ `contact-card share` shares the authenticated agent's own card. Agent-initiated
189
+ Messages to users remain supported subject to Contacts eligibility and blocking.
190
+ There are no add-request, phone address-book, mutual-contact, human discovery, or
191
+ human invite-link commands.
151
192
 
152
193
  ## Developer-managed agents
153
194
 
@@ -222,44 +263,6 @@ metadata, paired with its rendered local image/URL/attachment. It is not a defau
222
263
  interactive question. The CLI does not render recipes or generate images. The
223
264
  server's response supplies the permanent public image URL.
224
265
 
225
- ### Optional: connect a runtime
226
-
227
- ```sh
228
- relay agents create --connect hermes \
229
- --runtime-home /absolute/hermes-profile \
230
- --runtime-state-dir /absolute/hermes-profile/relay \
231
- --confirm-configure --runtime-stopped
232
-
233
- # Import into a named profile via private stdin; no creation request.
234
- relay --profile imported auth login --with-token --connect openclaw \
235
- --runtime-config /absolute/openclaw.json \
236
- --runtime-state-dir /absolute/openclaw-state --runtime-account my-agent \
237
- --confirm-configure --runtime-stopped
238
- ```
239
-
240
- Stop the selected runtime before passing `--runtime-stopped`. `--confirm-configure`
241
- authorizes only private configuration writes. `--runtime-brain` selects an existing
242
- OpenClaw binding; Claude uses `--runtime-home` for an existing session channel
243
- directory and `--runtime-context` for its session identifier. Existing sender
244
- permissions are preserved, not inferred from Contacts.
245
-
246
- `agents create --connect` reads the profile it just saved, and ignores any token in
247
- your environment. `auth login --connect` with no token option reuses the saved
248
- profile you named and its API address, and ignores your environment too. Add
249
- `--with-token` to read the token from a pipe instead. Plain `auth login` uses
250
- `RELAY_AGENT_TOKEN` where there is no terminal, or asks for the token in a hidden
251
- prompt. Relay checks the token with the API before saving it, and never creates an
252
- agent as a fallback.
253
- Empty Hermes profiles and explicit empty/new OpenClaw accounts can receive an
254
- initial credential when their native context and state are safe. Occupied
255
- credentials, unknown secret references, and bound/corrupt state are not replaced.
256
-
257
- The `connect` block in the output says whether the configuration was written, and
258
- always reports `connected: false`: this command does not install, start, stop or
259
- test anything. Start the runtime yourself. If writing the configuration fails
260
- after an agent was created, the token is still saved; run `auth login --connect`
261
- with that token rather than creating another agent.
262
-
263
266
  ## Local event forwarding
264
267
 
265
268
  `relay events listen` is a development convenience backed only by the SDK's
@@ -0,0 +1,24 @@
1
+ import { type AgentImageUploadResult } from "./agent-image-upload.js";
2
+ import { createAgent, type AgentDependencies, type CreateAgentInput } from "./agents.js";
3
+ import { type AgentImageRecipe } from "@relaymessenger/sdk";
4
+ /**
5
+ * One creation path for every caller. `agents create` and `connect` both make an
6
+ * agent, save its token privately, and then set its picture from a local file,
7
+ * so the order, the retry wording and the "do not create another agent" rule
8
+ * live here once rather than in each command.
9
+ */
10
+ export interface CreateWithPictureInput extends CreateAgentInput {
11
+ /** A file on this computer, or an https:// address. */
12
+ image?: string;
13
+ imageRecipe?: AgentImageRecipe;
14
+ cwd?: string;
15
+ home?: string;
16
+ }
17
+ export interface CreateWithPictureResult {
18
+ result: Awaited<ReturnType<typeof createAgent>>;
19
+ image?: AgentImageUploadResult;
20
+ }
21
+ export declare const createAgentWithPicture: (input: CreateWithPictureInput, deps: AgentDependencies, fetchImplementation?: typeof globalThis.fetch) => Promise<CreateWithPictureResult>;
22
+ /** The one sentence a caller prints when a picture did not finish. */
23
+ export declare const incompletePictureMessage: (handle: string, profile: string, image: AgentImageUploadResult, hasRecipe: boolean) => string;
24
+ //# sourceMappingURL=agent-create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-create.d.ts","sourceRoot":"","sources":["../src/agent-create.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,KAAK,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACxF,OAAO,EAAe,WAAW,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEtG,OAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGnE;;;;;GAKG;AACH,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;IAC9D,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC;IAChD,KAAK,CAAC,EAAE,sBAAsB,CAAC;CAChC;AAED,eAAO,MAAM,sBAAsB,GACjC,OAAO,sBAAsB,EAC7B,MAAM,iBAAiB,EACvB,sBAAsB,OAAO,UAAU,CAAC,KAAK,KAC5C,OAAO,CAAC,uBAAuB,CA6CjC,CAAC;AAEF,sEAAsE;AACtE,eAAO,MAAM,wBAAwB,GACnC,QAAQ,MAAM,EACd,SAAS,MAAM,EACf,OAAO,sBAAsB,EAC7B,WAAW,OAAO,KACjB,MAIF,CAAC"}
@@ -0,0 +1,64 @@
1
+ import { prepareAgentImage } from "./local-image.js";
2
+ import { uploadAgentImage } from "./agent-image-upload.js";
3
+ import { agentRecord, createAgent } from "./agents.js";
4
+ import { safeMetadata } from "./output.js";
5
+ import Relay, {} from "@relaymessenger/sdk";
6
+ import { DEFAULT_API_URL, validateApiURL } from "./config.js";
7
+ export const createAgentWithPicture = async (input, deps, fetchImplementation) => {
8
+ let localImage;
9
+ let imageURL = input.imageURL;
10
+ if (input.image !== undefined) {
11
+ // Only creation may say "No agent was created": the same checks are reached
12
+ // by a contact-card update with no agent in play.
13
+ const image = await prepareAgentImage(input.image, {
14
+ ...(input.cwd ? { cwd: input.cwd } : {}),
15
+ ...(input.home ? { home: input.home } : {}),
16
+ }).catch((error) => { throw new Error(`${error instanceof Error ? error.message : String(error)} No agent was created.`); });
17
+ if (image.kind === "file")
18
+ localImage = image.file;
19
+ else
20
+ imageURL = image.url;
21
+ }
22
+ if (input.imageRecipe !== undefined && imageURL === undefined && !localImage) {
23
+ throw new Error("--image-recipe requires its rendered --image or --image-url.");
24
+ }
25
+ const result = await createAgent({
26
+ ...(input.profile === undefined ? {} : { profile: input.profile }),
27
+ ...(input.apiURL === undefined ? {} : { apiURL: input.apiURL }),
28
+ ...(input.tokenName === undefined ? {} : { tokenName: input.tokenName }),
29
+ ...(input.handle === undefined ? {} : { handle: input.handle }),
30
+ ...(input.firstName === undefined ? {} : { firstName: input.firstName }),
31
+ ...(imageURL === undefined ? {} : { imageURL }),
32
+ ...(input.imageRecipe === undefined || localImage ? {} : { imageRecipe: input.imageRecipe }),
33
+ }, deps);
34
+ if (!localImage)
35
+ return { result };
36
+ let image;
37
+ try {
38
+ // The agent and its token are already saved. Never use a token from the
39
+ // environment for a just-created agent's picture upload.
40
+ const saved = (await deps.read()).profiles[result.profile];
41
+ if (!saved?.agent_token || validateApiURL(saved.api_url ?? DEFAULT_API_URL) !== result.api_url)
42
+ throw new Error("Saved identity changed.");
43
+ const client = new Relay({ apiKey: saved.agent_token, baseURL: result.api_url, ...(fetchImplementation ? { fetch: fetchImplementation } : {}) });
44
+ const outcome = await uploadAgentImage({ handle: result.handle, image: localImage }, client, (attachmentID) => client.contactCard.update({
45
+ handle: result.handle,
46
+ attachment_id: attachmentID,
47
+ ...(input.imageRecipe ? { image_recipe: input.imageRecipe } : {}),
48
+ }, { maxRetries: 0 }));
49
+ image = safeMetadata(outcome, [saved.agent_token]);
50
+ if (image.status === "updated")
51
+ Object.assign(result, agentRecord(image.agent));
52
+ }
53
+ catch {
54
+ image = { status: "incomplete", phase: "agent", message: "The agent was created and its token was saved. The picture did not go through. Set the picture on this profile; do not create the agent again." };
55
+ }
56
+ return { result, image };
57
+ };
58
+ /** The one sentence a caller prints when a picture did not finish. */
59
+ export const incompletePictureMessage = (handle, profile, image, hasRecipe) => {
60
+ const retry = image.status === "incomplete" && image.attachment_id && ["check", "save"].includes(image.phase)
61
+ ? `--attachment-id ${image.attachment_id}` : "--image <local-file>";
62
+ return `Agent @${handle} was created and its profile and token are saved. The picture did not go through. Set it on this profile: npx relaymessenger --profile ${profile} contact-card update --handle ${handle} ${retry}${hasRecipe ? " --image-recipe <json-file>" : ""}. Do not create another agent.`;
63
+ };
64
+ //# sourceMappingURL=agent-create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-create.js","sourceRoot":"","sources":["../src/agent-create.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The coding agents that announce themselves in the environment, and the exact
3
+ * variable each one sets. Measured on 2026-09-09: with `CLAUDECODE=1` Vercel's
4
+ * CLI prints `isAgent=true agentName=claude … => nonInteractive=true`, and its
5
+ * own bundled detector carries the strings `CODEX_HOME`, `CURSOR` and
6
+ * `CURSOR_CLI` (Relay-Research/research/cli-hands-on-20260909/vercel.md, section
7
+ * 12). `CODEX_HOME` and `CLAUDE_CONFIG_DIR` are also the homes the pinned skills
8
+ * installer reads, so Relay already trusts them to mean those agents.
9
+ */
10
+ export declare const AGENT_VARIABLES: ReadonlyArray<{
11
+ variable: string;
12
+ name: string;
13
+ }>;
14
+ export interface DrivingAgent {
15
+ name: string;
16
+ variable: string;
17
+ }
18
+ /** The agent running this command, when one says so. */
19
+ export declare const drivingAgent: (env: NodeJS.ProcessEnv) => DrivingAgent | undefined;
20
+ /** One line, so the agent reading it knows why it saw no menu. */
21
+ export declare const drivingAgentHint: (agent: DrivingAgent) => string;
22
+ /** Relay's documentation, written for an agent to read in one request. */
23
+ export declare const DOCS_LLMS_URL = "https://docs.relayapp.im/llms.txt";
24
+ export declare const readDocs: (fetchImplementation?: typeof globalThis.fetch) => Promise<string | undefined>;
25
+ /**
26
+ * The installer names a target by agent, not by folder. `cline` is the agent
27
+ * whose global folder is `~/.agents/skills`, the place every runtime-neutral
28
+ * agent reads, and `claude-code` is `~/.claude/skills` (skills@1.5.25 README,
29
+ * "Supported Agents"). Claude Code is added only when this computer has it.
30
+ */
31
+ export declare const skillTargets: (home: string, env: NodeJS.ProcessEnv) => Promise<string[]>;
32
+ //# sourceMappingURL=agent-driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-driver.d.ts","sourceRoot":"","sources":["../src/agent-driver.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAK7E,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wDAAwD;AACxD,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,CAAC,UAAU,KAAG,YAAY,GAAG,SAMpE,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,GAAI,OAAO,YAAY,KAAG,MAC6E,CAAC;AAErI,0EAA0E;AAC1E,eAAO,MAAM,aAAa,sCAAsC,CAAC;AAEjE,eAAO,MAAM,QAAQ,GACnB,sBAAqB,OAAO,UAAU,CAAC,KAAwB,KAC9D,OAAO,CAAC,MAAM,GAAG,SAAS,CAS5B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAU,MAAM,MAAM,EAAE,KAAK,MAAM,CAAC,UAAU,KAAG,OAAO,CAAC,MAAM,EAAE,CAOzF,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { stat } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ /**
4
+ * The coding agents that announce themselves in the environment, and the exact
5
+ * variable each one sets. Measured on 2026-09-09: with `CLAUDECODE=1` Vercel's
6
+ * CLI prints `isAgent=true agentName=claude … => nonInteractive=true`, and its
7
+ * own bundled detector carries the strings `CODEX_HOME`, `CURSOR` and
8
+ * `CURSOR_CLI` (Relay-Research/research/cli-hands-on-20260909/vercel.md, section
9
+ * 12). `CODEX_HOME` and `CLAUDE_CONFIG_DIR` are also the homes the pinned skills
10
+ * installer reads, so Relay already trusts them to mean those agents.
11
+ */
12
+ export const AGENT_VARIABLES = [
13
+ { variable: "CLAUDECODE", name: "Claude Code" },
14
+ { variable: "CURSOR_CLI", name: "Cursor" },
15
+ { variable: "CURSOR", name: "Cursor" },
16
+ { variable: "CODEX_HOME", name: "Codex" },
17
+ ];
18
+ /** The agent running this command, when one says so. */
19
+ export const drivingAgent = (env) => {
20
+ for (const entry of AGENT_VARIABLES) {
21
+ const value = env[entry.variable]?.trim();
22
+ if (value)
23
+ return { name: entry.name, variable: entry.variable };
24
+ }
25
+ return undefined;
26
+ };
27
+ /** One line, so the agent reading it knows why it saw no menu. */
28
+ export const drivingAgentHint = (agent) => `Relay sees ${agent.name} (${agent.variable}), so it asks nothing. Every question has a flag: run relaymessenger connect --help.`;
29
+ /** Relay's documentation, written for an agent to read in one request. */
30
+ export const DOCS_LLMS_URL = "https://docs.relayapp.im/llms.txt";
31
+ export const readDocs = async (fetchImplementation = globalThis.fetch) => {
32
+ try {
33
+ const answer = await fetchImplementation(DOCS_LLMS_URL, { redirect: "follow" });
34
+ if (!answer.ok)
35
+ return undefined;
36
+ const text = await answer.text();
37
+ return text.trim() ? text : undefined;
38
+ }
39
+ catch {
40
+ return undefined;
41
+ }
42
+ };
43
+ /**
44
+ * The installer names a target by agent, not by folder. `cline` is the agent
45
+ * whose global folder is `~/.agents/skills`, the place every runtime-neutral
46
+ * agent reads, and `claude-code` is `~/.claude/skills` (skills@1.5.25 README,
47
+ * "Supported Agents"). Claude Code is added only when this computer has it.
48
+ */
49
+ export const skillTargets = async (home, env) => {
50
+ const targets = ["cline"];
51
+ const claudeRoot = env.CLAUDE_CONFIG_DIR?.trim() ?? join(home, ".claude");
52
+ try {
53
+ if ((await stat(claudeRoot)).isDirectory())
54
+ targets.push("claude-code");
55
+ }
56
+ catch { /* Claude Code is not on this computer. */ }
57
+ return targets;
58
+ };
59
+ //# sourceMappingURL=agent-driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-driver.js","sourceRoot":"","sources":["../src/agent-driver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,eAAe,GAAsD;IAChF,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE;IAC/C,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE;IAC1C,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE;IACtC,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE;CAC1C,CAAC;AAOF,wDAAwD;AACxD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAsB,EAA4B,EAAE;IAC/E,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC;QAC1C,IAAI,KAAK;YAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;IACnE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF,kEAAkE;AAClE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAmB,EAAU,EAAE,CAC9D,cAAc,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,QAAQ,uFAAuF,CAAC;AAErI,0EAA0E;AAC1E,MAAM,CAAC,MAAM,aAAa,GAAG,mCAAmC,CAAC;AAEjE,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,EAC3B,sBAA+C,UAAU,CAAC,KAAK,EAClC,EAAE;IAC/B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,aAAa,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChF,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,OAAO,SAAS,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,IAAY,EAAE,GAAsB,EAAqB,EAAE;IAC5F,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC;IAC1B,MAAM,UAAU,GAAG,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC1E,IAAI,CAAC;QACH,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,EAAE;YAAE,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC1E,CAAC;IAAC,MAAM,CAAC,CAAC,0CAA0C,CAAC,CAAC;IACtD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The one file the Relay channel for Claude Code reads. Its three names, its
3
+ * folder and its format are the channel's own
4
+ * (packages/claude-code/src/config.ts, `loadConfig` and `parseEnvFile`;
5
+ * packages/claude-code/README.md, "Manual environment fallback"). The folder is
6
+ * owner-only and the file is mode 600, because it holds an agent's token.
7
+ */
8
+ export declare const CHANNEL_ENV_KEYS: readonly ["RELAY_AGENT_TOKEN", "RELAY_BASE_URL", "RELAY_ALLOWED_SENDERS"];
9
+ export declare const CHANNEL_DIR_MODE = 448;
10
+ export declare const CHANNEL_FILE_MODE = 384;
11
+ export interface ChannelEnvValues {
12
+ token: string;
13
+ baseURL: string;
14
+ /** Handles or Contact ids allowed to message this agent. */
15
+ allowedSenders: readonly string[];
16
+ }
17
+ /** The channel's own reader: last line wins, quotes optional, `export` allowed. */
18
+ export declare const readChannelEnv: (contents: string) => Record<string, string>;
19
+ /**
20
+ * Rewrites only Relay's own three names, in place. Every other line a person or
21
+ * another tool put in this file is kept exactly where it was.
22
+ */
23
+ export declare const renderChannelEnv: (existing: string, values: ChannelEnvValues) => string;
24
+ export interface ChannelEnvState {
25
+ path: string;
26
+ exists: boolean;
27
+ /** The token already in the file, when it holds one. Never printed. */
28
+ token?: string;
29
+ baseURL?: string;
30
+ allowedSenders: string[];
31
+ contents: string;
32
+ }
33
+ export declare const inspectChannelEnv: (channelDir: string) => Promise<ChannelEnvState>;
34
+ /**
35
+ * Writes the file through a private temporary file in the same folder, so a
36
+ * reader never sees a half-written token and the token never exists in a
37
+ * world-readable file for even an instant.
38
+ */
39
+ export declare const writeChannelEnv: (channelDir: string, values: ChannelEnvValues, platform?: NodeJS.Platform) => Promise<{
40
+ path: string;
41
+ contents: string;
42
+ }>;
43
+ //# sourceMappingURL=claude-channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-channel.d.ts","sourceRoot":"","sources":["../src/claude-channel.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,2EAA4E,CAAC;AAC1G,eAAO,MAAM,gBAAgB,MAAQ,CAAC;AACtC,eAAO,MAAM,iBAAiB,MAAQ,CAAC;AAEvC,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,mFAAmF;AACnF,eAAO,MAAM,cAAc,GAAI,UAAU,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CActE,CAAC;AASF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,UAAU,MAAM,EAAE,QAAQ,gBAAgB,KAAG,MAsB7E,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,iBAAiB,GAAU,YAAY,MAAM,KAAG,OAAO,CAAC,eAAe,CAiBnF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAC1B,YAAY,MAAM,EAClB,QAAQ,gBAAgB,EACxB,WAAU,MAAM,CAAC,QAA2B,KAC3C,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAoB5C,CAAC"}
@@ -0,0 +1,124 @@
1
+ import { chmod, mkdir, open, readFile, rename, unlink } from "node:fs/promises";
2
+ import { randomUUID } from "node:crypto";
3
+ import { join } from "node:path";
4
+ /**
5
+ * The one file the Relay channel for Claude Code reads. Its three names, its
6
+ * folder and its format are the channel's own
7
+ * (packages/claude-code/src/config.ts, `loadConfig` and `parseEnvFile`;
8
+ * packages/claude-code/README.md, "Manual environment fallback"). The folder is
9
+ * owner-only and the file is mode 600, because it holds an agent's token.
10
+ */
11
+ export const CHANNEL_ENV_KEYS = ["RELAY_AGENT_TOKEN", "RELAY_BASE_URL", "RELAY_ALLOWED_SENDERS"];
12
+ export const CHANNEL_DIR_MODE = 0o700;
13
+ export const CHANNEL_FILE_MODE = 0o600;
14
+ /** The channel's own reader: last line wins, quotes optional, `export` allowed. */
15
+ export const readChannelEnv = (contents) => {
16
+ const values = {};
17
+ for (const original of contents.split(/\r?\n/u)) {
18
+ const line = original.trim();
19
+ if (!line || line.startsWith("#"))
20
+ continue;
21
+ const match = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/u.exec(line);
22
+ if (!match?.[1] || match[2] === undefined)
23
+ continue;
24
+ let value = match[2].trim();
25
+ if (value.length >= 2 && ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")))) {
26
+ value = value.slice(1, -1);
27
+ }
28
+ values[match[1]] = value;
29
+ }
30
+ return values;
31
+ };
32
+ const quoted = (value) => {
33
+ if (/[\r\n\s"'`$\\]/u.test(value)) {
34
+ throw new Error("That value cannot be written to a .env file safely. Use a value with no quotes, backticks, dollar signs, backslashes or line breaks.");
35
+ }
36
+ return `"${value}"`;
37
+ };
38
+ /**
39
+ * Rewrites only Relay's own three names, in place. Every other line a person or
40
+ * another tool put in this file is kept exactly where it was.
41
+ */
42
+ export const renderChannelEnv = (existing, values) => {
43
+ const changes = {
44
+ RELAY_AGENT_TOKEN: values.token,
45
+ RELAY_BASE_URL: values.baseURL,
46
+ RELAY_ALLOWED_SENDERS: [...new Set(values.allowedSenders.map((sender) => sender.trim()).filter(Boolean))].join(","),
47
+ };
48
+ const lines = existing ? existing.split(/\r?\n/u) : [];
49
+ const written = new Set();
50
+ const output = lines.map((line) => {
51
+ const match = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/u.exec(line.trim());
52
+ const key = match?.[1];
53
+ if (!key || !Object.hasOwn(changes, key) || written.has(key))
54
+ return line;
55
+ written.add(key);
56
+ return `${key}=${quoted(changes[key])}`;
57
+ });
58
+ for (const key of CHANNEL_ENV_KEYS) {
59
+ if (written.has(key))
60
+ continue;
61
+ if (output.at(-1) === "")
62
+ output.pop();
63
+ output.push(`${key}=${quoted(changes[key])}`);
64
+ }
65
+ if (output.at(-1) !== "")
66
+ output.push("");
67
+ return output.join(existing.includes("\r\n") ? "\r\n" : "\n");
68
+ };
69
+ export const inspectChannelEnv = async (channelDir) => {
70
+ const path = join(channelDir, ".env");
71
+ let contents = "";
72
+ let exists = false;
73
+ try {
74
+ contents = await readFile(path, "utf8");
75
+ exists = true;
76
+ }
77
+ catch (error) {
78
+ if (error.code !== "ENOENT")
79
+ throw error;
80
+ }
81
+ const values = readChannelEnv(contents);
82
+ return {
83
+ path,
84
+ exists,
85
+ ...(values.RELAY_AGENT_TOKEN ? { token: values.RELAY_AGENT_TOKEN } : {}),
86
+ ...(values.RELAY_BASE_URL ? { baseURL: values.RELAY_BASE_URL } : {}),
87
+ allowedSenders: (values.RELAY_ALLOWED_SENDERS ?? "").split(",").map((entry) => entry.trim()).filter(Boolean),
88
+ contents,
89
+ };
90
+ };
91
+ /**
92
+ * Writes the file through a private temporary file in the same folder, so a
93
+ * reader never sees a half-written token and the token never exists in a
94
+ * world-readable file for even an instant.
95
+ */
96
+ export const writeChannelEnv = async (channelDir, values, platform = process.platform) => {
97
+ const state = await inspectChannelEnv(channelDir);
98
+ const contents = renderChannelEnv(state.contents, values);
99
+ await mkdir(channelDir, { recursive: true, mode: CHANNEL_DIR_MODE });
100
+ if (platform !== "win32")
101
+ await chmod(channelDir, CHANNEL_DIR_MODE);
102
+ const temporary = join(channelDir, `.relay-connect-${process.pid}-${randomUUID()}.tmp`);
103
+ const handle = await open(temporary, "wx", CHANNEL_FILE_MODE);
104
+ try {
105
+ try {
106
+ await handle.writeFile(contents, "utf8");
107
+ if (platform !== "win32")
108
+ await handle.chmod(CHANNEL_FILE_MODE);
109
+ await handle.sync();
110
+ }
111
+ finally {
112
+ await handle.close();
113
+ }
114
+ await rename(temporary, state.path);
115
+ if (platform !== "win32")
116
+ await chmod(state.path, CHANNEL_FILE_MODE);
117
+ return { path: state.path, contents };
118
+ }
119
+ catch (error) {
120
+ await unlink(temporary).catch(() => undefined);
121
+ throw error;
122
+ }
123
+ };
124
+ //# sourceMappingURL=claude-channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-channel.js","sourceRoot":"","sources":["../src/claude-channel.ts"],"names":[],"mappings":"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"}