@relaymessenger/openclaw-plugin 0.3.4 → 0.4.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.
- package/LICENSE +1 -1
- package/README.md +162 -123
- package/contracts/relay-sdk-0.3.0-staging.5.registry.json +69 -0
- package/contracts/relay-v1.lock.json +77 -0
- package/dist/index.js +2 -2
- package/dist/setup-entry.js +1 -2
- package/dist/src/accounts.js +63 -34
- package/dist/src/channel.js +144 -533
- package/dist/src/dispatch.js +257 -0
- package/dist/src/full-sync.js +24 -0
- package/dist/src/gateway.js +171 -0
- package/dist/src/inbound.js +54 -85
- package/dist/src/ingress.js +64 -0
- package/dist/src/outbound.js +48 -111
- package/dist/src/runtime.js +2 -3
- package/dist/src/state.js +492 -0
- package/dist/src/types.js +1 -3
- package/index.ts +1 -2
- package/openclaw.plugin.json +15 -18
- package/package.json +114 -40
- package/setup-entry.ts +0 -2
- package/src/accounts.ts +95 -51
- package/src/channel.ts +271 -646
- package/src/dispatch.ts +324 -0
- package/src/full-sync.ts +47 -0
- package/src/gateway.ts +216 -0
- package/src/inbound.ts +71 -122
- package/src/ingress.ts +123 -0
- package/src/outbound.ts +70 -149
- package/src/runtime.ts +4 -4
- package/src/state.ts +609 -0
- package/src/types.ts +51 -162
- package/dist/src/account-lock.js +0 -91
- package/dist/src/client.js +0 -13
- package/dist/src/cursor-store.js +0 -136
- package/dist/src/inbound-dedupe.js +0 -175
- package/dist/src/invocations.js +0 -47
- package/dist/src/lifecycle.js +0 -35
- package/dist/src/poll-loop.js +0 -137
- package/dist/src/responding.js +0 -36
- package/dist/src/security.js +0 -26
- package/dist/src/state-files.js +0 -243
- package/dist/src/vendor/relay-sdk/client.js +0 -163
- package/dist/src/vendor/relay-sdk/errors.js +0 -45
- package/dist/src/vendor/relay-sdk/types.js +0 -2
- package/dist/src/vendor/relay-sdk/url.js +0 -39
- package/src/account-lock.ts +0 -108
- package/src/client.ts +0 -51
- package/src/cursor-store.ts +0 -186
- package/src/inbound-dedupe.ts +0 -241
- package/src/invocations.ts +0 -58
- package/src/lifecycle.ts +0 -42
- package/src/poll-loop.ts +0 -173
- package/src/responding.ts +0 -52
- package/src/security.ts +0 -36
- package/src/state-files.ts +0 -298
- package/src/vendor/relay-sdk/README.md +0 -28
- package/src/vendor/relay-sdk/client.ts +0 -293
- package/src/vendor/relay-sdk/errors.ts +0 -61
- package/src/vendor/relay-sdk/types.ts +0 -82
- package/src/vendor/relay-sdk/url.ts +0 -43
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,145 +1,184 @@
|
|
|
1
|
-
# Relay
|
|
1
|
+
# Relay for OpenClaw
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
you text like a friend.
|
|
3
|
+
`@relaymessenger/openclaw-plugin` is the native Relay channel for OpenClaw
|
|
4
|
+
`2026.8.1`.
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
Source is maintained in
|
|
7
|
+
[`RelayMessenger/Relay-SDK`](https://github.com/RelayMessenger/Relay-SDK/tree/main/packages/openclaw)
|
|
8
|
+
under `packages/openclaw`.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
one. The one thing an older core cannot do is separate two byte-identical
|
|
14
|
-
chunks of a single reply, which arrive as one message.
|
|
10
|
+
It connects an OpenClaw gateway to Relay with a Relay Agent Token. Relay
|
|
11
|
+
delivers events over its v1 WebSocket, and the plugin sends replies through
|
|
12
|
+
the Relay v1 REST Message API. The plugin imports `@relaymessenger/sdk`; it
|
|
13
|
+
does not contain a copied Relay client or protocol implementation.
|
|
15
14
|
|
|
16
15
|
## Install
|
|
17
16
|
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
relaymessenger pair
|
|
21
|
-
relaymessenger install-openclaw
|
|
17
|
+
```bash
|
|
18
|
+
openclaw plugins install @relaymessenger/openclaw-plugin
|
|
22
19
|
```
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
package, persists that archive in the paired account's private Relay runtime,
|
|
26
|
-
and invokes OpenClaw's managed `npm-pack:` installer on the stable copy so
|
|
27
|
-
declared runtime dependencies are installed with the plugin. It surgically
|
|
28
|
-
adds Relay to `~/.openclaw/openclaw.json`, preserves unrelated configuration,
|
|
29
|
-
writes the token to an owner-only file, and never prints it. It refuses to
|
|
30
|
-
replace a different configured Relay identity.
|
|
21
|
+
Configure the default account:
|
|
31
22
|
|
|
32
|
-
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"channels": {
|
|
26
|
+
"relay": {
|
|
27
|
+
"enabled": true,
|
|
28
|
+
"tokenFile": "/run/secrets/relay-agent-token"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
For a single account, `RELAY_AGENT_TOKEN` is also supported. Use
|
|
35
|
+
`RELAY_BASE_URL` only when the Agent Token belongs to a non-production Relay
|
|
36
|
+
environment.
|
|
37
|
+
|
|
38
|
+
One OpenClaw gateway can back multiple Relay Contacts:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"channels": {
|
|
43
|
+
"relay": {
|
|
44
|
+
"defaultAccount": "default",
|
|
45
|
+
"accounts": {
|
|
46
|
+
"default": {
|
|
47
|
+
"tokenFile": "/run/secrets/relay-default"
|
|
48
|
+
},
|
|
49
|
+
"support": {
|
|
50
|
+
"tokenFile": "/run/secrets/relay-support"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
39
56
|
```
|
|
40
57
|
|
|
41
|
-
|
|
58
|
+
Do not reuse one Agent Token in two configured accounts. Channel-level
|
|
59
|
+
`token`, `tokenFile`, and `RELAY_AGENT_TOKEN` credentials belong only to the
|
|
60
|
+
default account. Every named account must set its own inline token or
|
|
61
|
+
`tokenFile`; named accounts never inherit default credentials.
|
|
62
|
+
|
|
63
|
+
## Relay vocabulary and scope
|
|
64
|
+
|
|
65
|
+
- A **Contact** is a Relay user or agent profile.
|
|
66
|
+
- Each Contact owns a public **Handle**.
|
|
67
|
+
- A **Chat** is direct or group.
|
|
68
|
+
- A **Message** belongs to one Chat and contains ordered parts.
|
|
69
|
+
|
|
70
|
+
The plugin starts OpenClaw turns for every inbound user-authored
|
|
71
|
+
`message.received` event in a direct Chat. In a group Chat, it starts a turn
|
|
72
|
+
only when a text part's canonical `mention` Handle matches the canonical
|
|
73
|
+
`chat.owner_handle`, or when `reply_to.message_id` resolves through Relay to a
|
|
74
|
+
Message authored by this agent in the same Chat. Visible `@handle` text is not
|
|
75
|
+
parsed as a mention. Unmentioned group traffic, agent-authored Messages,
|
|
76
|
+
reactions, typing events, receipts, and membership events are durably accepted
|
|
77
|
+
without starting a turn.
|
|
78
|
+
|
|
79
|
+
Text, link, and media parts are rendered into agent-visible text. Media stays
|
|
80
|
+
a labeled signed URL; the plugin does not upload or send media.
|
|
81
|
+
|
|
82
|
+
Outbound support is deliberately limited to text Messages and Message reply
|
|
83
|
+
references. OpenClaw splits text at Relay's current 10,000-character text-part
|
|
84
|
+
limit. Reactions, edit, unsend, native threads, rich cards, and outbound media
|
|
85
|
+
are not declared.
|
|
86
|
+
|
|
87
|
+
`allowFrom` optionally limits inbound turns to exact Relay Contact IDs or
|
|
88
|
+
Handles:
|
|
42
89
|
|
|
43
90
|
```json
|
|
44
91
|
{
|
|
45
|
-
"plugins": {
|
|
46
|
-
"allow": ["relay"],
|
|
47
|
-
"entries": { "relay": { "enabled": true } }
|
|
48
|
-
},
|
|
49
92
|
"channels": {
|
|
50
93
|
"relay": {
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
|
|
94
|
+
"tokenFile": "/run/secrets/relay-agent-token",
|
|
95
|
+
"allowFrom": [
|
|
96
|
+
"alice",
|
|
97
|
+
"00000000-0000-7000-8000-000000000001"
|
|
98
|
+
]
|
|
54
99
|
}
|
|
55
100
|
}
|
|
56
101
|
}
|
|
57
102
|
```
|
|
58
103
|
|
|
59
|
-
`
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
##
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
voice memos render as a labeled fetchable capability URL (the URL is itself
|
|
141
|
-
the authorization, so no Agent Token is needed to fetch the bytes) rather
|
|
142
|
-
than the agent seeing the file inline; reactions are observe-only; receipts
|
|
143
|
-
(`message.delivered`/`message.read`) never start a turn. Final agent replies
|
|
144
|
-
are delivered durably (chunked to Relay's 8 KiB per-part cap,
|
|
145
|
-
idempotency-keyed, retry-safe).
|
|
104
|
+
Without `allowFrom`, any user Contact whose Message Relay delivers to this
|
|
105
|
+
agent can start a direct turn, while the group activation rules above still
|
|
106
|
+
apply.
|
|
107
|
+
|
|
108
|
+
## Durable delivery
|
|
109
|
+
|
|
110
|
+
For every WebSocket event, the plugin:
|
|
111
|
+
|
|
112
|
+
1. inserts `event_id` into OpenClaw's channel ingress queue, or its private
|
|
113
|
+
SQLite queue when a local/npm-pack install does not have trusted host state;
|
|
114
|
+
2. returns from the SDK callback only after that transaction commits;
|
|
115
|
+
3. lets the SDK send the cumulative WebSocket ACK;
|
|
116
|
+
4. dispatches a stored event through OpenClaw's ingress lifecycle.
|
|
117
|
+
|
|
118
|
+
A replayed `event_id` reaches the existing pending, completed, or failed row
|
|
119
|
+
and does not repeat model or tool work. The SDK owns sequence validation,
|
|
120
|
+
cumulative ACKs, reconnect replay, and JSON heartbeat ping/pong.
|
|
121
|
+
|
|
122
|
+
When Relay requires `full_sync`, the plugin pages through every visible Chat
|
|
123
|
+
and every visible Message, atomically replaces its local snapshot, and returns
|
|
124
|
+
only after the snapshot commits. The SDK then sends
|
|
125
|
+
`full_sync_complete`.
|
|
126
|
+
|
|
127
|
+
Relay Webhooks and the WebSocket are exclusive. Startup checks that the Agent
|
|
128
|
+
has no saved Webhook subscriptions. The SDK also treats WebSocket HTTP `409`
|
|
129
|
+
and close code `4410` as terminal `RelayWebhookConfiguredError` failures.
|
|
130
|
+
|
|
131
|
+
Final OpenClaw replies use the native durable outbound Message adapter. Each
|
|
132
|
+
REST send carries a stable idempotency key derived from OpenClaw's delivery
|
|
133
|
+
queue ID and part index. Unknown-send reconciliation repeats the exact text
|
|
134
|
+
chunks with the same keys, so Relay returns the original Message or commits
|
|
135
|
+
it once.
|
|
136
|
+
|
|
137
|
+
## Development
|
|
138
|
+
|
|
139
|
+
Use Node.js `22.22.3` or newer. Build and test on Linux:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npm install
|
|
143
|
+
npm run validate
|
|
144
|
+
npm run pack:smoke
|
|
145
|
+
npm run gateway:harness
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`npm run release:validate` runs all three commands. CI and the guarded manual
|
|
149
|
+
staging workflow use that full sequence. The staging workflow accepts only an
|
|
150
|
+
exact SHA selected from the `staging` branch, the matching
|
|
151
|
+
`x.y.z-staging.n` package version, and the `staging` npm tag. It retains the
|
|
152
|
+
validated tarball and publishes that same digest with npm provenance; its
|
|
153
|
+
publish job is also bound to the `staging` GitHub environment.
|
|
154
|
+
|
|
155
|
+
`gateway:harness` packs the plugin, installs the tarball with OpenClaw
|
|
156
|
+
`2026.8.1`, inspects the managed installation, starts a real OpenClaw gateway,
|
|
157
|
+
connects to a loopback Relay WebSocket, receives one Message, and proves the
|
|
158
|
+
durable ACK and idempotent REST reply.
|
|
159
|
+
|
|
160
|
+
## Contract lock
|
|
161
|
+
|
|
162
|
+
`contracts/relay-v1.lock.json` records the compatibility boundary used by this
|
|
163
|
+
release: Relay Server
|
|
164
|
+
`9b4d5bb32cc749c6fd271969948c385300d404d6`, OpenAPI SHA-256
|
|
165
|
+
`f62f431fc0daa48500926bf87753f81c3fdda25ab463b130ca97f2896367e0a5`,
|
|
166
|
+
and the exact `@relaymessenger/sdk@0.3.0-staging.5` registry integrity, source
|
|
167
|
+
commit `8570546ce30ab39d702409893006dbccd351f2b4`, REST operations, and WebSocket
|
|
168
|
+
frames consumed by the plugin.
|
|
169
|
+
|
|
170
|
+
Public CI hashes the checked-in `contracts/relay-openapi.yaml` fixture and
|
|
171
|
+
requires the locked digest above. The retained private release receipt also
|
|
172
|
+
sets `RELAY_SERVER_SOURCE_DIR` to an exact checkout of Server `9b4d5bb32cc7`
|
|
173
|
+
and proves that the fixture bytes match that commit before packaging. The
|
|
174
|
+
public Relay-SDK monorepo does not require credentials for the private Server
|
|
175
|
+
source and does not overstate what npm metadata can attest.
|
|
176
|
+
|
|
177
|
+
`contracts/relay-sdk-0.3.0-staging.5.registry.json` records immutable npm
|
|
178
|
+
publication metadata and tarball digests. Validation downloads the registry
|
|
179
|
+
tarball, verifies SHA-1, SHA-256, and SHA-512 integrity, checks the installed
|
|
180
|
+
SDK, and compares its package metadata with exact SDK commit `8570546`.
|
|
181
|
+
The npm metadata has no `gitHead` or attestation for this version, so the
|
|
182
|
+
receipt deliberately does not claim cryptographic source-to-tarball
|
|
183
|
+
provenance. These are local artifact compatibility checks, not a claim of a
|
|
184
|
+
hosted Relay deployment test.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "relay-sdk-registry-receipt/v1",
|
|
3
|
+
"source": {
|
|
4
|
+
"repository": "RelayMessenger/Relay-SDK",
|
|
5
|
+
"commit": "776a9a7873f41c0c9947439c44444674a7d55c5d",
|
|
6
|
+
"packageJsonPath": "packages/sdk/package.json",
|
|
7
|
+
"packageJsonSha256": "2cd3b01e65725ab55d5b520ded6c1235e1f4c74af0ff07478d3ba029d67eda1a",
|
|
8
|
+
"contractPath": "contracts/relay-v1-openapi.yaml",
|
|
9
|
+
"contractSha256": "f62f431fc0daa48500926bf87753f81c3fdda25ab463b130ca97f2896367e0a5"
|
|
10
|
+
},
|
|
11
|
+
"registry": {
|
|
12
|
+
"packumentUrl": "https://registry.npmjs.org/@relaymessenger%2fsdk",
|
|
13
|
+
"package": "@relaymessenger/sdk",
|
|
14
|
+
"version": "0.3.0-staging.5",
|
|
15
|
+
"publishedAt": "2026-09-01T18:08:51.723Z",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/RelayMessenger/Relay-SDK.git",
|
|
19
|
+
"directory": "packages/sdk"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public",
|
|
23
|
+
"registry": "https://registry.npmjs.org/",
|
|
24
|
+
"tag": "staging"
|
|
25
|
+
},
|
|
26
|
+
"distTagsObserved": {
|
|
27
|
+
"latest": "0.3.0-staging.4",
|
|
28
|
+
"staging": "0.3.0-staging.5",
|
|
29
|
+
"mutable": true
|
|
30
|
+
},
|
|
31
|
+
"dist": {
|
|
32
|
+
"tarball": "https://registry.npmjs.org/@relaymessenger/sdk/-/sdk-0.3.0-staging.5.tgz",
|
|
33
|
+
"integrity": "sha512-mVtK8Tv8WVKCkZLzZMbAbazlrf8VrVtu6UcQE4wY685UPLrHqlYm/2XoQstyTP6unik9xiVQC9L5CwR4lxP70w==",
|
|
34
|
+
"shasum": "7549747779b301a0dc60259fa64729e9e7d7db6a",
|
|
35
|
+
"sha256": "ac9336350a82590a0c0d9960ef971858e5ad77413078fe7e82ad5f99a1a3cc9e",
|
|
36
|
+
"bytes": 36545,
|
|
37
|
+
"fileCount": 37,
|
|
38
|
+
"unpackedSize": 167814,
|
|
39
|
+
"signatureMetadata": [
|
|
40
|
+
{
|
|
41
|
+
"keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U",
|
|
42
|
+
"sig": "MEYCIQC/HnUppjEuFAkql79k8scZSDgECdY5clQ19ufRu6WkXQIhAK1iXKe+bjI3+us+VmA2j/KMpXbwkbaYzlKh4yzLwCZ6"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"attestations": {
|
|
46
|
+
"url": "https://registry.npmjs.org/-/npm/v1/attestations/@relaymessenger%2fsdk@0.3.0-staging.5",
|
|
47
|
+
"provenance": {
|
|
48
|
+
"predicateType": "https://slsa.dev/provenance/v1"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"installedArtifact": {
|
|
53
|
+
"packageJsonSha256": "2cd3b01e65725ab55d5b520ded6c1235e1f4c74af0ff07478d3ba029d67eda1a",
|
|
54
|
+
"typesSha256": "efa163145777ad4df93263d87605794ca099dd8db6fe1b36cc71d0873377a653"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"provenanceBoundary": {
|
|
58
|
+
"registryGitHead": null,
|
|
59
|
+
"registryAttestations": {
|
|
60
|
+
"url": "https://registry.npmjs.org/-/npm/v1/attestations/@relaymessenger%2fsdk@0.3.0-staging.5",
|
|
61
|
+
"provenance": {
|
|
62
|
+
"predicateType": "https://slsa.dev/provenance/v1"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"attestationEndpoint": "https://registry.npmjs.org/-/npm/v1/attestations/@relaymessenger%2fsdk@0.3.0-staging.5",
|
|
66
|
+
"attestationEndpointObservedStatus": 200,
|
|
67
|
+
"claim": "The exact registry tarball, npm signature, and SLSA provenance metadata are recorded. The package.json bytes match the Relay-SDK 776a9a7 release commit; the receipt does not claim that the registry attestation independently proves the source commit."
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"relayServer": {
|
|
3
|
+
"repository": "RelayMessenger/Relay-Server",
|
|
4
|
+
"commit": "9b4d5bb32cc749c6fd271969948c385300d404d6",
|
|
5
|
+
"openapiPath": "contracts/developer/openapi.yaml",
|
|
6
|
+
"sha256": "f62f431fc0daa48500926bf87753f81c3fdda25ab463b130ca97f2896367e0a5"
|
|
7
|
+
},
|
|
8
|
+
"relaySdk": {
|
|
9
|
+
"package": "@relaymessenger/sdk",
|
|
10
|
+
"version": "0.3.0-staging.5",
|
|
11
|
+
"integrity": "sha512-mVtK8Tv8WVKCkZLzZMbAbazlrf8VrVtu6UcQE4wY685UPLrHqlYm/2XoQstyTP6unik9xiVQC9L5CwR4lxP70w==",
|
|
12
|
+
"operationsSha256": "8429a056605fa9555fcb34067bbb34f7e364ed18c17ec7786b2c1621044012f1",
|
|
13
|
+
"registryReceipt": "contracts/relay-sdk-0.3.0-staging.5.registry.json",
|
|
14
|
+
"source": {
|
|
15
|
+
"repository": "RelayMessenger/Relay-SDK",
|
|
16
|
+
"commit": "776a9a7873f41c0c9947439c44444674a7d55c5d",
|
|
17
|
+
"carriedOpenapiSha256": "f62f431fc0daa48500926bf87753f81c3fdda25ab463b130ca97f2896367e0a5"
|
|
18
|
+
},
|
|
19
|
+
"usedOperations": [
|
|
20
|
+
{
|
|
21
|
+
"method": "GET",
|
|
22
|
+
"path": "/v1/chats",
|
|
23
|
+
"operationId": "listChats"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"method": "GET",
|
|
27
|
+
"path": "/v1/chats/{chatId}/messages",
|
|
28
|
+
"operationId": "getMessages"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"method": "GET",
|
|
32
|
+
"path": "/v1/messages/{messageId}",
|
|
33
|
+
"operationId": "getMessage"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"method": "POST",
|
|
37
|
+
"path": "/v1/chats/{chatId}/messages",
|
|
38
|
+
"operationId": "sendMessageToChat"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"method": "POST",
|
|
42
|
+
"path": "/v1/chats/{chatId}/read",
|
|
43
|
+
"operationId": "markChatAsRead"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"method": "POST",
|
|
47
|
+
"path": "/v1/chats/{chatId}/typing",
|
|
48
|
+
"operationId": "startTyping"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"method": "DELETE",
|
|
52
|
+
"path": "/v1/chats/{chatId}/typing",
|
|
53
|
+
"operationId": "stopTyping"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"method": "GET",
|
|
57
|
+
"path": "/v1/webhook-subscriptions",
|
|
58
|
+
"operationId": "listWebhookSubscriptions"
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"usedWebSocketFrames": {
|
|
62
|
+
"serverToSdk": [
|
|
63
|
+
"ready",
|
|
64
|
+
"event",
|
|
65
|
+
"full_sync",
|
|
66
|
+
"ping",
|
|
67
|
+
"error",
|
|
68
|
+
"disconnect"
|
|
69
|
+
],
|
|
70
|
+
"sdkToServer": [
|
|
71
|
+
"ack",
|
|
72
|
+
"full_sync_complete",
|
|
73
|
+
"pong"
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// Relay channel plugin entrypoint registers the OpenClaw integration.
|
|
2
1
|
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
|
|
3
2
|
import { relayChannelPlugin } from "./src/channel.js";
|
|
4
3
|
import { setRelayRuntime } from "./src/runtime.js";
|
|
5
4
|
export default defineChannelPluginEntry({
|
|
6
5
|
id: "relay",
|
|
7
6
|
name: "Relay",
|
|
8
|
-
description: "Relay channel plugin
|
|
7
|
+
description: "Native Relay channel plugin for OpenClaw.",
|
|
9
8
|
plugin: relayChannelPlugin,
|
|
10
9
|
setRuntime: setRelayRuntime,
|
|
11
10
|
});
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
package/dist/setup-entry.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
// Lightweight setup entry: loaded instead of the full entry while the channel
|
|
2
|
-
// is disabled/unconfigured, so status/config surfaces avoid runtime imports.
|
|
3
1
|
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
|
|
4
2
|
import { relayChannelPlugin } from "./src/channel.js";
|
|
5
3
|
export default defineSetupPluginEntry(relayChannelPlugin);
|
|
4
|
+
//# sourceMappingURL=setup-entry.js.map
|
package/dist/src/accounts.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
// Multi-account resolution: channels.relay.accounts.<id> with a
|
|
2
|
-
// default-account fallback, so one OpenClaw can back several Relay contacts
|
|
3
|
-
// (one Agent Token each). Env vars cover the single-account quickstart.
|
|
4
1
|
import { createAccountListHelpers, resolveMergedAccountConfig, } from "openclaw/plugin-sdk/account-helpers";
|
|
5
|
-
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
2
|
+
import { DEFAULT_ACCOUNT_ID, normalizeAccountId, } from "openclaw/plugin-sdk/account-id";
|
|
6
3
|
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/channel-core";
|
|
7
|
-
import { DEFAULT_RELAY_BASE_URL, normalizeRelayBaseUrl } from "./client.js";
|
|
8
4
|
export const RELAY_TOKEN_ENV_VAR = "RELAY_AGENT_TOKEN";
|
|
9
5
|
export const RELAY_BASE_URL_ENV_VAR = "RELAY_BASE_URL";
|
|
10
|
-
const
|
|
6
|
+
export const DEFAULT_RELAY_BASE_URL = "https://api.relayapp.im";
|
|
11
7
|
const { listAccountIds, resolveDefaultAccountId } = createAccountListHelpers("relay", {
|
|
12
8
|
normalizeAccountId,
|
|
13
9
|
implicitDefaultAccount: {
|
|
@@ -15,53 +11,86 @@ const { listAccountIds, resolveDefaultAccountId } = createAccountListHelpers("re
|
|
|
15
11
|
envVars: [RELAY_TOKEN_ENV_VAR],
|
|
16
12
|
},
|
|
17
13
|
});
|
|
18
|
-
export { listAccountIds as listRelayAccountIds, resolveDefaultAccountId as resolveDefaultRelayAccountId,
|
|
19
|
-
function
|
|
14
|
+
export { DEFAULT_ACCOUNT_ID, listAccountIds as listRelayAccountIds, resolveDefaultAccountId as resolveDefaultRelayAccountId, };
|
|
15
|
+
function mergedAccountConfig(cfg, accountId) {
|
|
20
16
|
return resolveMergedAccountConfig({
|
|
21
17
|
channelConfig: cfg.channels?.relay,
|
|
22
18
|
accounts: cfg.channels?.relay?.accounts,
|
|
23
19
|
accountId,
|
|
24
|
-
|
|
20
|
+
// A channel-level token or tokenFile belongs only to the implicit/default
|
|
21
|
+
// account. Named accounts may inherit non-credential defaults, but must
|
|
22
|
+
// opt into their own credential source.
|
|
23
|
+
omitKeys: accountId === DEFAULT_ACCOUNT_ID
|
|
24
|
+
? ["defaultAccount"]
|
|
25
|
+
: ["defaultAccount", "token", "tokenFile"],
|
|
25
26
|
normalizeAccountId,
|
|
26
27
|
});
|
|
27
28
|
}
|
|
28
29
|
function resolveToken(params) {
|
|
29
|
-
const
|
|
30
|
-
if (
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
? tryReadSecretFileSync(params.merged.tokenFile, "relay tokenFile")?.trim()
|
|
30
|
+
const inline = params.config.token?.trim();
|
|
31
|
+
if (inline)
|
|
32
|
+
return inline;
|
|
33
|
+
const fromFile = params.config.tokenFile
|
|
34
|
+
? tryReadSecretFileSync(params.config.tokenFile, "relay tokenFile")?.trim()
|
|
35
35
|
: undefined;
|
|
36
|
-
if (fromFile)
|
|
36
|
+
if (fromFile)
|
|
37
37
|
return fromFile;
|
|
38
|
+
return params.accountId === DEFAULT_ACCOUNT_ID
|
|
39
|
+
? (params.env[RELAY_TOKEN_ENV_VAR]?.trim() ?? "")
|
|
40
|
+
: "";
|
|
41
|
+
}
|
|
42
|
+
function isLoopbackHostname(hostname) {
|
|
43
|
+
return (hostname === "localhost" ||
|
|
44
|
+
hostname === "127.0.0.1" ||
|
|
45
|
+
hostname === "::1" ||
|
|
46
|
+
hostname.endsWith(".localhost"));
|
|
47
|
+
}
|
|
48
|
+
export function normalizeRelayBaseUrl(value) {
|
|
49
|
+
let parsed;
|
|
50
|
+
try {
|
|
51
|
+
parsed = new URL(value?.trim() || DEFAULT_RELAY_BASE_URL);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
throw new Error("relay: baseUrl must be an absolute Relay API origin");
|
|
38
55
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (params.accountId === DEFAULT_ACCOUNT_ID) {
|
|
42
|
-
return params.env[RELAY_TOKEN_ENV_VAR]?.trim() ?? "";
|
|
56
|
+
if (parsed.username || parsed.password || parsed.search || parsed.hash) {
|
|
57
|
+
throw new Error("relay: baseUrl must not contain credentials, query, or fragment");
|
|
43
58
|
}
|
|
44
|
-
|
|
59
|
+
if (parsed.pathname !== "/" && parsed.pathname !== "") {
|
|
60
|
+
throw new Error("relay: baseUrl must be an origin without a path");
|
|
61
|
+
}
|
|
62
|
+
if (parsed.protocol !== "https:" &&
|
|
63
|
+
!(parsed.protocol === "http:" && isLoopbackHostname(parsed.hostname))) {
|
|
64
|
+
throw new Error("relay: baseUrl must use HTTPS (HTTP is allowed only for loopback tests)");
|
|
65
|
+
}
|
|
66
|
+
return parsed.origin;
|
|
67
|
+
}
|
|
68
|
+
function normalizeAllowFrom(values) {
|
|
69
|
+
return [
|
|
70
|
+
...new Set((values ?? [])
|
|
71
|
+
.map((value) => value.trim())
|
|
72
|
+
.filter((value) => value.length > 0)),
|
|
73
|
+
];
|
|
45
74
|
}
|
|
46
75
|
export function resolveRelayAccount(params) {
|
|
47
76
|
const env = params.env ?? process.env;
|
|
48
77
|
const accountId = normalizeAccountId(params.accountId);
|
|
49
|
-
const
|
|
78
|
+
const config = mergedAccountConfig(params.cfg, accountId);
|
|
50
79
|
const baseEnabled = params.cfg.channels?.relay?.enabled !== false;
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const pollTimeoutSeconds = Math.min(Math.max(merged.pollTimeoutSeconds ?? DEFAULT_POLL_TIMEOUT_SECONDS, 1), 30);
|
|
80
|
+
const token = resolveToken({ config, accountId, env });
|
|
81
|
+
const configuredBaseUrl = config.baseUrl?.trim() ||
|
|
82
|
+
(accountId === DEFAULT_ACCOUNT_ID
|
|
83
|
+
? env[RELAY_BASE_URL_ENV_VAR]?.trim()
|
|
84
|
+
: undefined);
|
|
57
85
|
return {
|
|
58
86
|
accountId,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
87
|
+
...(config.name?.trim() ? { name: config.name.trim() } : {}),
|
|
88
|
+
enabled: baseEnabled && config.enabled !== false,
|
|
89
|
+
configured: token.length > 0,
|
|
62
90
|
token,
|
|
63
|
-
baseUrl,
|
|
64
|
-
|
|
65
|
-
config
|
|
91
|
+
baseUrl: normalizeRelayBaseUrl(configuredBaseUrl),
|
|
92
|
+
allowFrom: normalizeAllowFrom(config.allowFrom),
|
|
93
|
+
config,
|
|
66
94
|
};
|
|
67
95
|
}
|
|
96
|
+
//# sourceMappingURL=accounts.js.map
|