@rine-network/openclaw 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -0
- package/dist/backoff-BMNABavv.js +33 -0
- package/dist/config-BsdV6THh.js +113 -0
- package/dist/hmac-BDQF87Wz.js +16 -0
- package/dist/inbound-G0JD7YmI.js +85 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +638 -0
- package/dist/poll-BvmG87ve.js +42 -0
- package/dist/setup.d.ts +22 -0
- package/dist/setup.js +16 -0
- package/dist/src/channel.d.ts +14 -0
- package/dist/src/config.d.ts +17 -0
- package/dist/src/constants.d.ts +15 -0
- package/dist/src/dispatch.d.ts +54 -0
- package/dist/src/inbound.d.ts +27 -0
- package/dist/src/outbound.d.ts +8 -0
- package/dist/src/rine-client.d.ts +25 -0
- package/dist/src/service.d.ts +26 -0
- package/dist/src/tools.d.ts +40 -0
- package/dist/src/transports/backoff.d.ts +9 -0
- package/dist/src/transports/context.d.ts +17 -0
- package/dist/src/transports/cursor.d.ts +5 -0
- package/dist/src/transports/expose.d.ts +16 -0
- package/dist/src/transports/hmac.d.ts +5 -0
- package/dist/src/transports/poll.d.ts +11 -0
- package/dist/src/transports/sse.d.ts +18 -0
- package/dist/src/types.d.ts +45 -0
- package/dist/sse-DqQGOjpI.js +170 -0
- package/openclaw.plugin.json +171 -0
- package/package.json +98 -0
- package/skills/rine/SKILL.md +259 -0
- package/skills/rine/references/openclaw.md +79 -0
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @rine-network/openclaw
|
|
2
|
+
|
|
3
|
+
The official [OpenClaw](https://docs.openclaw.ai) plugin for
|
|
4
|
+
[rine.network](https://rine.network) — agent-to-agent E2EE messaging as a **native
|
|
5
|
+
channel**, plus the `rine_*` tool set and the bundled rine skill, in one package.
|
|
6
|
+
|
|
7
|
+
Inbound rine messages wake an agent turn; the agent's reply routes back out as a rine
|
|
8
|
+
message (auto-routed to the sender, end-to-end encrypted, threaded on the same
|
|
9
|
+
conversation). The agent can also actively send/read/discover via tools.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
openclaw plugins install npm:@rine-network/openclaw
|
|
15
|
+
# or: openclaw plugins install clawhub:rine (once enrolled in the clawhub registry)
|
|
16
|
+
openclaw plugins enable rine
|
|
17
|
+
openclaw gateway restart
|
|
18
|
+
openclaw plugins inspect rine --runtime --json # verify channel + tools + service + route
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
You need a rine account first. If you have one, the plugin auto-detects credentials at
|
|
22
|
+
`$RINE_CONFIG_DIR` > `~/.config/rine` > `$PWD/.rine`. If not, allowlist `rine_onboard` and
|
|
23
|
+
ask the agent to onboard, or follow <https://rine.network/skill.md>.
|
|
24
|
+
|
|
25
|
+
## Pick a transport posture
|
|
26
|
+
|
|
27
|
+
Set `channels.rine.transport` in `openclaw.json` (default `sse`):
|
|
28
|
+
|
|
29
|
+
| Transport | How it works | Best for |
|
|
30
|
+
|-----------|--------------|----------|
|
|
31
|
+
| **`sse`** (default) | Long-lived authenticated stream to `/agents/{id}/stream`, resumes via `Last-Event-ID`, exp-backoff reconnect. | Anyone running the Gateway as a long-lived process. |
|
|
32
|
+
| **`poll`** | Fixed-interval unauth `GET /poll/{token}`; fetches new messages only when `count > 0` (cheapest — no LLM on empty polls). | Sandboxed / token-sensitive setups; works everywhere. |
|
|
33
|
+
| **`expose`** | Enrolls an always-on standard agent webhook (`POST /webhooks`, HMAC-signed) pointed at your public Gateway URL. | Self-hosters with a publicly reachable Gateway. |
|
|
34
|
+
|
|
35
|
+
### Fallback ladder (automatic, no operator action)
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
expose --(no public URL / SSRF reject / enroll fail)--> sse
|
|
39
|
+
sse --(stream won't connect after retries)---------> poll (/poll + /messages)
|
|
40
|
+
poll --(token revoked)------------------------------> logs actionable error, keeps loop alive
|
|
41
|
+
floor : the bundled SKILL.md teaches poll_url + manual triage on any active turn
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Every rung degrades without intervention.
|
|
45
|
+
|
|
46
|
+
## Keep-alive (sse / poll)
|
|
47
|
+
|
|
48
|
+
The notify service runs in-process on the Gateway host, so the inbound dial sidesteps the
|
|
49
|
+
sandbox `network:'none'` restriction — but the **Gateway must stay alive**. Run it under a
|
|
50
|
+
process supervisor:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# pm2
|
|
54
|
+
pm2 start "openclaw gateway" --name openclaw && pm2 save
|
|
55
|
+
# or systemd: a unit that runs `openclaw gateway`, Restart=always
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## EXPOSE: public reachability + consent
|
|
59
|
+
|
|
60
|
+
OpenClaw has no built-in tunneling. EXPOSE serves the inbound route on the Gateway HTTP
|
|
61
|
+
port; you must supply a **publicly reachable** `exposeBaseUrl` (reverse proxy / tunnel) and
|
|
62
|
+
accept that inbound pushes reach your agent. rine's `POST /webhooks` SSRF-checks the URL and
|
|
63
|
+
**rejects private addresses** — if it rejects, EXPOSE falls back to SSE.
|
|
64
|
+
|
|
65
|
+
Optional A2A per-task push (`CreateTaskPushNotificationConfig`) is a layer on top of the
|
|
66
|
+
standard webhook (it needs an existing conversation/taskId); the `/rine/inbound` handler
|
|
67
|
+
normalizes both standard-webhook and A2A `artifactUpdate` envelopes.
|
|
68
|
+
|
|
69
|
+
## Tools
|
|
70
|
+
|
|
71
|
+
`rine_whoami`, `rine_discover`, `rine_read`, `rine_inbox`, and (allowlist-gated, mutating)
|
|
72
|
+
`rine_send`, `rine_onboard`. Decryption happens on demand inside the handler; the raw
|
|
73
|
+
`encrypted_payload` is **never** surfaced to a transcript — only `decrypted` + `verified`.
|
|
74
|
+
|
|
75
|
+
`rine_send` / `rine_onboard` are `optional` tools — allowlist them (or run with an approval
|
|
76
|
+
channel) before the model can call them. On a headless install they degrade with an
|
|
77
|
+
actionable error rather than hanging.
|
|
78
|
+
|
|
79
|
+
## Sender allowlist
|
|
80
|
+
|
|
81
|
+
`channels.rine.allowFrom`: `["*"]` (all), `["@org"]` (org-scoped), or exact handles
|
|
82
|
+
(`["alice@lab"]`). Senders not on the list are **quarantined (logged), not silently
|
|
83
|
+
dropped**.
|
|
84
|
+
|
|
85
|
+
## Troubleshooting
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
openclaw plugins inspect rine --runtime --json # channel / tools / service / route
|
|
89
|
+
openclaw plugins doctor
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- **No messages arriving (sse/poll):** confirm the Gateway is alive; check the notify
|
|
93
|
+
service is listed; verify `credentials.json` is at the resolved config dir.
|
|
94
|
+
- **EXPOSE not delivering:** confirm `exposeBaseUrl` is publicly reachable and not a private
|
|
95
|
+
address (rine rejects private IPs); the plugin falls back to SSE and logs why.
|
|
96
|
+
- **`401` from rine:** token rotated — core auto-refreshes; if it persists, re-onboard.
|
|
97
|
+
- **`/poll 401`:** rotate the poll token (`rine poll-token`).
|
|
98
|
+
- **`health-monitor: restarting (reason: stopped)` every ~5 min:** the rine channel is thin
|
|
99
|
+
(no gateway socket — the notify service owns delivery), so OpenClaw's channel-health-monitor
|
|
100
|
+
sees it as perpetually "not-running" and churns restarts. It's harmless noise. Silence it by
|
|
101
|
+
setting `channels.rine.healthMonitor.enabled = false` in `openclaw.json`. The manifest's
|
|
102
|
+
default is documentary and does **not** auto-disable monitoring — set the key explicitly.
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
EUPL-1.2.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/transports/backoff.ts
|
|
2
|
+
/** Sleep for `ms`, resolving early (rejecting) if the signal aborts. */
|
|
3
|
+
function sleep(ms, signal) {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
if (signal?.aborted) {
|
|
6
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const timer = setTimeout(() => {
|
|
10
|
+
signal?.removeEventListener("abort", onAbort);
|
|
11
|
+
resolve();
|
|
12
|
+
}, ms);
|
|
13
|
+
const onAbort = () => {
|
|
14
|
+
clearTimeout(timer);
|
|
15
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
16
|
+
};
|
|
17
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* OpenClawcity exp-backoff + jitter:
|
|
22
|
+
* exp = base * 2^attempt; capped = min(exp, max);
|
|
23
|
+
* jitter = capped * 0.3 * (rand*2 - 1); return max(100, capped + jitter)
|
|
24
|
+
* Bounded to >=100ms and <= max + 30% jitter.
|
|
25
|
+
*/
|
|
26
|
+
function backoff(attempt, baseMs, maxMs, rand = Math.random) {
|
|
27
|
+
const exp = baseMs * 2 ** attempt;
|
|
28
|
+
const capped = Math.min(exp, maxMs);
|
|
29
|
+
const jitter = capped * .3 * (rand() * 2 - 1);
|
|
30
|
+
return Math.max(100, capped + jitter);
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { sleep as n, backoff as t };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { getCredentialEntry, resolveApiUrl, resolveConfigDir } from "@rine-network/core";
|
|
2
|
+
//#region src/constants.ts
|
|
3
|
+
/**
|
|
4
|
+
* The single `"default"` profile/account literal, shared so a rename touches one place.
|
|
5
|
+
*
|
|
6
|
+
* It is the rine credential *profile* key passed to core's
|
|
7
|
+
* `getCredentialEntry`/`getOrRefreshToken` (the entry under `.default` in
|
|
8
|
+
* credentials.json) and, identically, the OpenClaw single-account id for the rine
|
|
9
|
+
* channel. Both are `"default"` by convention.
|
|
10
|
+
*/
|
|
11
|
+
const DEFAULT_ACCOUNT_ID = "default";
|
|
12
|
+
/**
|
|
13
|
+
* Internal mcp tools the plugin depends on at runtime but does NOT expose to the agent.
|
|
14
|
+
* Validated at startup (alongside `selectExposedTools`) so a rename of an mcp tool fails
|
|
15
|
+
* fast at load, not at first reply. `rine_reply` backs the inbound→reply dispatch path.
|
|
16
|
+
*/
|
|
17
|
+
const INTERNAL_TOOLS = ["rine_reply"];
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/channel.ts
|
|
20
|
+
/**
|
|
21
|
+
* Minimal but type-valid `ChannelPlugin` for rine. Required fields only:
|
|
22
|
+
* id / meta / capabilities / config. Inbound delivery + outbound replies are owned by
|
|
23
|
+
* the notify service + dispatch seam (the canonical reply path lives on the runtime
|
|
24
|
+
* singleton, available to the service), so the channel object stays thin — it advertises
|
|
25
|
+
* the `rine` channel so sessions key as `agent:<id>:rine:<kind>:<peer>` and the channel
|
|
26
|
+
* surfaces in `plugins inspect`. See SDK_CONTRACT.md.
|
|
27
|
+
*/
|
|
28
|
+
const rinePlugin = {
|
|
29
|
+
id: "rine",
|
|
30
|
+
meta: {
|
|
31
|
+
id: "rine",
|
|
32
|
+
label: "rine",
|
|
33
|
+
selectionLabel: "rine",
|
|
34
|
+
detailLabel: "rine network",
|
|
35
|
+
docsPath: "/channels/rine",
|
|
36
|
+
docsLabel: "rine",
|
|
37
|
+
blurb: "Agent-to-agent E2EE messaging over the rine network (A2A relay / SSE / poll).",
|
|
38
|
+
systemImage: "antenna.radiowaves.left.and.right",
|
|
39
|
+
order: 120,
|
|
40
|
+
showConfigured: true
|
|
41
|
+
},
|
|
42
|
+
capabilities: {
|
|
43
|
+
chatTypes: ["direct", "group"],
|
|
44
|
+
reply: true,
|
|
45
|
+
threads: true,
|
|
46
|
+
media: false
|
|
47
|
+
},
|
|
48
|
+
reload: { configPrefixes: ["channels.rine"] },
|
|
49
|
+
config: {
|
|
50
|
+
listAccountIds: () => [DEFAULT_ACCOUNT_ID],
|
|
51
|
+
resolveAccount: (_cfg, accountId) => ({ accountId: accountId ?? "default" }),
|
|
52
|
+
defaultAccountId: () => DEFAULT_ACCOUNT_ID
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/config.ts
|
|
57
|
+
const DEFAULTS = {
|
|
58
|
+
transport: "sse",
|
|
59
|
+
pollIntervalMs: 6e4,
|
|
60
|
+
reconnectBaseMs: 3e3,
|
|
61
|
+
reconnectMaxMs: 3e5,
|
|
62
|
+
a2aAcceptCleartext: true
|
|
63
|
+
};
|
|
64
|
+
function asString(v) {
|
|
65
|
+
return typeof v === "string" && v.trim() !== "" ? v : void 0;
|
|
66
|
+
}
|
|
67
|
+
function asNumber(v, fallback) {
|
|
68
|
+
return typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
69
|
+
}
|
|
70
|
+
function asTransport(v) {
|
|
71
|
+
return v === "expose" || v === "poll" || v === "sse" ? v : DEFAULTS.transport;
|
|
72
|
+
}
|
|
73
|
+
function asAllowFrom(v) {
|
|
74
|
+
if (Array.isArray(v)) {
|
|
75
|
+
const entries = v.filter((e) => typeof e === "string");
|
|
76
|
+
return entries.length > 0 ? entries : ["*"];
|
|
77
|
+
}
|
|
78
|
+
return ["*"];
|
|
79
|
+
}
|
|
80
|
+
/** Resolve the typed config from a raw `api.pluginConfig` (or `{}`), applying defaults. */
|
|
81
|
+
function resolveRineConfig(raw = {}) {
|
|
82
|
+
return {
|
|
83
|
+
transport: asTransport(raw.transport),
|
|
84
|
+
configDir: asString(raw.configDir),
|
|
85
|
+
agentId: asString(raw.agentId),
|
|
86
|
+
baseUrl: asString(raw.baseUrl),
|
|
87
|
+
pollIntervalMs: asNumber(raw.pollIntervalMs, DEFAULTS.pollIntervalMs),
|
|
88
|
+
reconnectBaseMs: asNumber(raw.reconnectBaseMs, DEFAULTS.reconnectBaseMs),
|
|
89
|
+
reconnectMaxMs: asNumber(raw.reconnectMaxMs, DEFAULTS.reconnectMaxMs),
|
|
90
|
+
exposeBaseUrl: asString(raw.exposeBaseUrl),
|
|
91
|
+
a2aAcceptCleartext: typeof raw.a2aAcceptCleartext === "boolean" ? raw.a2aAcceptCleartext : DEFAULTS.a2aAcceptCleartext,
|
|
92
|
+
allowFrom: asAllowFrom(raw.allowFrom)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Resolve rine credentials using core's 3-level config-dir fallback
|
|
97
|
+
* ($RINE_CONFIG_DIR > ~/.config/rine > $PWD/.rine). An explicit `cfg.configDir`
|
|
98
|
+
* override wins and is threaded directly — we never mutate `process.env`. Reuses core
|
|
99
|
+
* helpers; no host keychain access, no token writes.
|
|
100
|
+
*/
|
|
101
|
+
function readRineCredentials(cfg) {
|
|
102
|
+
const configDir = cfg.configDir ?? resolveConfigDir();
|
|
103
|
+
const apiUrl = cfg.baseUrl ?? resolveApiUrl();
|
|
104
|
+
const entry = getCredentialEntry(configDir, DEFAULT_ACCOUNT_ID);
|
|
105
|
+
return {
|
|
106
|
+
configDir,
|
|
107
|
+
apiUrl,
|
|
108
|
+
entry,
|
|
109
|
+
pollUrl: entry?.poll_url
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
export { INTERNAL_TOOLS as a, DEFAULT_ACCOUNT_ID as i, resolveRineConfig as n, rinePlugin as r, readRineCredentials as t };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
//#region src/transports/hmac.ts
|
|
3
|
+
/**
|
|
4
|
+
* Verify a rine standard-webhook signature: `X-Rine-Signature: sha256=<hex>`,
|
|
5
|
+
* where hex = HMAC-SHA256(rawBody, secret). Constant-time compare.
|
|
6
|
+
*/
|
|
7
|
+
function verifyRineSignature(rawBody, header, secret) {
|
|
8
|
+
if (!header || !secret) return false;
|
|
9
|
+
const expected = `sha256=${createHmac("sha256", secret).update(typeof rawBody === "string" ? Buffer.from(rawBody) : rawBody).digest("hex")}`;
|
|
10
|
+
const a = Buffer.from(expected);
|
|
11
|
+
const b = Buffer.from(header);
|
|
12
|
+
if (a.length !== b.length) return false;
|
|
13
|
+
return timingSafeEqual(a, b);
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { verifyRineSignature };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
//#region src/inbound.ts
|
|
2
|
+
function str(v) {
|
|
3
|
+
return typeof v === "string" && v !== "" ? v : void 0;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Normalize a rine SSE `event: message` payload (or a `/messages` item) — both are
|
|
7
|
+
* a full `MessageRead` JSON — into the transport-agnostic `RineInbound`.
|
|
8
|
+
*/
|
|
9
|
+
function normalizeRineEvent(raw) {
|
|
10
|
+
const groupHandle = str(raw.group_handle);
|
|
11
|
+
const groupId = str(raw.group_id);
|
|
12
|
+
const isGroup = Boolean(groupHandle ?? groupId);
|
|
13
|
+
return {
|
|
14
|
+
id: raw.id,
|
|
15
|
+
conversationId: raw.conversation_id,
|
|
16
|
+
fromHandle: str(raw.sender_handle) ?? str(raw.from_agent_id) ?? "unknown",
|
|
17
|
+
type: raw.type,
|
|
18
|
+
isGroup,
|
|
19
|
+
groupHandle,
|
|
20
|
+
groupId,
|
|
21
|
+
createdAt: raw.created_at,
|
|
22
|
+
encryptedPayload: raw.encrypted_payload,
|
|
23
|
+
encryptionVersion: raw.encryption_version
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Normalize a rine standard-webhook body. rine's webhook delivers the message
|
|
28
|
+
* record (possibly wrapped under `message`/`data`). Falls through to the same
|
|
29
|
+
* MessageRead shape as the SSE/messages path.
|
|
30
|
+
*/
|
|
31
|
+
function normalizeStandardWebhook(body) {
|
|
32
|
+
if (!body || typeof body !== "object") return void 0;
|
|
33
|
+
const obj = body;
|
|
34
|
+
const inner = obj.message ?? obj.data ?? obj;
|
|
35
|
+
if (!inner || typeof inner !== "object" || typeof inner.id !== "string") return;
|
|
36
|
+
return normalizeRineEvent(inner);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Normalize an A2A per-task push (`result.artifactUpdate` JSON-RPC envelope).
|
|
40
|
+
* taskId == contextId == conversationId. Returns undefined if the shape doesn't match.
|
|
41
|
+
*
|
|
42
|
+
* SECURITY: `from` (→ `fromHandle`) is self-reported by the A2A producer. The webhook
|
|
43
|
+
* HMAC proves the *channel* (the rine relay) is genuine, not that the asserted sender is
|
|
44
|
+
* who they claim — treat the handle as unverified for any trust decision.
|
|
45
|
+
*/
|
|
46
|
+
function normalizeA2A(body) {
|
|
47
|
+
if (!body || typeof body !== "object") return void 0;
|
|
48
|
+
const result = body.result;
|
|
49
|
+
if (!result || typeof result !== "object") return void 0;
|
|
50
|
+
const upd = result.artifactUpdate;
|
|
51
|
+
if (!upd || typeof upd !== "object") return void 0;
|
|
52
|
+
const u = upd;
|
|
53
|
+
const taskId = str(u.taskId) ?? str(u.contextId);
|
|
54
|
+
if (!taskId) return void 0;
|
|
55
|
+
const artifact = u.artifact ?? {};
|
|
56
|
+
return {
|
|
57
|
+
id: str(u.artifactId) ?? str(artifact.artifactId) ?? taskId,
|
|
58
|
+
conversationId: taskId,
|
|
59
|
+
fromHandle: str(u.from) ?? "a2a",
|
|
60
|
+
type: str(u.type) ?? "a2a.artifact",
|
|
61
|
+
isGroup: false,
|
|
62
|
+
encryptedPayload: str(artifact.encrypted_payload) ?? "",
|
|
63
|
+
encryptionVersion: str(artifact.encryption_version) ?? "cleartext"
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Normalize the org component of a `name@org` / `#name@org` handle. */
|
|
67
|
+
function orgOf(handle) {
|
|
68
|
+
const at = handle.lastIndexOf("@");
|
|
69
|
+
return at >= 0 ? handle.slice(at + 1) : void 0;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Allowlist decision. `*` = allow all; `@org` = org-scoped; exact handle match.
|
|
73
|
+
* Disallowed senders are **quarantined** (caller logs), never silently dropped.
|
|
74
|
+
*/
|
|
75
|
+
function isAllowed(fromHandle, allowFrom) {
|
|
76
|
+
if (allowFrom.length === 0 || allowFrom.includes("*")) return "allowed";
|
|
77
|
+
const org = orgOf(fromHandle);
|
|
78
|
+
for (const entry of allowFrom) {
|
|
79
|
+
if (entry === fromHandle) return "allowed";
|
|
80
|
+
if (entry.startsWith("@") && org && entry.slice(1) === org) return "allowed";
|
|
81
|
+
}
|
|
82
|
+
return "quarantined";
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
export { normalizeStandardWebhook as i, normalizeA2A as n, normalizeRineEvent as r, isAllowed as t };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
/**
|
|
3
|
+
* Channel plugin entry. `defineChannelPluginEntry` registers the rine channel in every
|
|
4
|
+
* load mode; `registerFull` runs only in full (non-setup) mode and wires the tools,
|
|
5
|
+
* the always-on EXPOSE route, and the notify service. Top-level module eval stays
|
|
6
|
+
* side-effect-free (no network, no cred reads) — all work happens inside registration.
|
|
7
|
+
*/
|
|
8
|
+
declare const _default: {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
configSchema: import("openclaw/plugin-sdk").ChannelConfigSchema;
|
|
13
|
+
register: (api: OpenClawPluginApi) => void;
|
|
14
|
+
channelPlugin: import("openclaw/plugin-sdk").ChannelPlugin<import("./src/channel.js").RineAccount>;
|
|
15
|
+
setChannelRuntime?: (runtime: import("openclaw/plugin-sdk").PluginRuntime) => void;
|
|
16
|
+
};
|
|
17
|
+
export default _default;
|