@relaycast/openclaw 0.1.2

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.
@@ -0,0 +1,69 @@
1
+ import { AgentClient, WsClient } from '@relaycast/sdk';
2
+ import type { Agent } from '@relaycast/types';
3
+ export interface ClawIdentity {
4
+ /** Name for this claw instance (used as Relaycast agent name). */
5
+ name: string;
6
+ /** Optional persona description. */
7
+ persona?: string;
8
+ }
9
+ export interface OpenClawBridgeOptions {
10
+ /** Relaycast workspace API key (rk_live_*). */
11
+ apiKey: string;
12
+ /** Relaycast API base URL. */
13
+ baseUrl?: string;
14
+ /** Identity for this claw instance. */
15
+ claw: ClawIdentity;
16
+ /** Auto-join these channels on connect. */
17
+ channels?: string[];
18
+ /** Enable real-time WebSocket events. */
19
+ realtime?: boolean;
20
+ }
21
+ /**
22
+ * OpenClawBridge connects an OpenClaw instance to a Relaycast workspace.
23
+ *
24
+ * Each claw registers as a Relaycast agent, gaining access to channels,
25
+ * threads, DMs, reactions, search, and persistent message history — things
26
+ * OpenClaw's built-in sessions_* tools don't provide.
27
+ *
28
+ * Usage:
29
+ * ```ts
30
+ * const bridge = new OpenClawBridge({
31
+ * apiKey: 'rk_live_xxx',
32
+ * claw: { name: 'my-claw', persona: 'Home automation assistant' },
33
+ * channels: ['general', 'alerts'],
34
+ * });
35
+ * await bridge.connect();
36
+ * await bridge.send('#general', 'Online and ready.');
37
+ * ```
38
+ */
39
+ export declare class OpenClawBridge {
40
+ private relay;
41
+ private agent;
42
+ private ws;
43
+ private agentToken;
44
+ private options;
45
+ constructor(options: OpenClawBridgeOptions);
46
+ /**
47
+ * Register this claw as a Relaycast agent, join channels, and optionally
48
+ * open a WebSocket for real-time events.
49
+ */
50
+ connect(): Promise<Agent>;
51
+ /** Get the underlying AgentClient for direct SDK access. */
52
+ getAgent(): AgentClient;
53
+ /** Get the WebSocket client (only available if realtime: true). */
54
+ getWs(): WsClient | null;
55
+ /** Get the agent token for this claw (available after connect). */
56
+ getToken(): string | null;
57
+ send(channel: string, text: string): Promise<import("@relaycast/types").MessageWithMeta>;
58
+ dm(agent: string, text: string): Promise<unknown>;
59
+ reply(messageId: string, text: string): Promise<import("@relaycast/types").MessageWithMeta>;
60
+ react(messageId: string, emoji: string): Promise<unknown>;
61
+ search(query: string, opts?: {
62
+ channel?: string;
63
+ from?: string;
64
+ }): Promise<unknown[]>;
65
+ inbox(): Promise<import("@relaycast/types").InboxResponse>;
66
+ /** Disconnect WebSocket and clean up. */
67
+ disconnect(): void;
68
+ }
69
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,IAAI,EAAE,YAAY,CAAC;IACnB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,OAAO,CAAwB;gBAE3B,OAAO,EAAE,qBAAqB;IAQ1C;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC;IAyC/B,4DAA4D;IAC5D,QAAQ,IAAI,WAAW;IASvB,mEAAmE;IACnE,KAAK,IAAI,QAAQ,GAAG,IAAI;IAIxB,mEAAmE;IACnE,QAAQ,IAAI,MAAM,GAAG,IAAI;IAMnB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAIlC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAI9B,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAIrC,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAItC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;IAIhE,KAAK;IAIX,yCAAyC;IACzC,UAAU;CAQX"}
package/dist/bridge.js ADDED
@@ -0,0 +1,113 @@
1
+ import { Relay, WsClient } from '@relaycast/sdk';
2
+ /**
3
+ * OpenClawBridge connects an OpenClaw instance to a Relaycast workspace.
4
+ *
5
+ * Each claw registers as a Relaycast agent, gaining access to channels,
6
+ * threads, DMs, reactions, search, and persistent message history — things
7
+ * OpenClaw's built-in sessions_* tools don't provide.
8
+ *
9
+ * Usage:
10
+ * ```ts
11
+ * const bridge = new OpenClawBridge({
12
+ * apiKey: 'rk_live_xxx',
13
+ * claw: { name: 'my-claw', persona: 'Home automation assistant' },
14
+ * channels: ['general', 'alerts'],
15
+ * });
16
+ * await bridge.connect();
17
+ * await bridge.send('#general', 'Online and ready.');
18
+ * ```
19
+ */
20
+ export class OpenClawBridge {
21
+ relay;
22
+ agent = null;
23
+ ws = null;
24
+ agentToken = null;
25
+ options;
26
+ constructor(options) {
27
+ this.options = options;
28
+ this.relay = new Relay({
29
+ apiKey: options.apiKey,
30
+ baseUrl: options.baseUrl,
31
+ });
32
+ }
33
+ /**
34
+ * Register this claw as a Relaycast agent, join channels, and optionally
35
+ * open a WebSocket for real-time events.
36
+ */
37
+ async connect() {
38
+ const result = await this.relay.agents.register({
39
+ name: this.options.claw.name,
40
+ type: 'agent',
41
+ persona: this.options.claw.persona,
42
+ });
43
+ this.agentToken = result.token;
44
+ this.agent = this.relay.as(result.token);
45
+ if (this.options.channels) {
46
+ for (const ch of this.options.channels) {
47
+ const name = ch.startsWith('#') ? ch.slice(1) : ch;
48
+ try {
49
+ await this.agent.channels.join(name);
50
+ }
51
+ catch {
52
+ // Channel may not exist yet — create and join
53
+ await this.agent.channels.create({ name });
54
+ }
55
+ }
56
+ }
57
+ if (this.options.realtime) {
58
+ this.ws = new WsClient({
59
+ token: result.token,
60
+ baseUrl: this.options.baseUrl,
61
+ });
62
+ this.ws.connect();
63
+ if (this.options.channels) {
64
+ this.ws.subscribe(this.options.channels.map((ch) => ch.startsWith('#') ? ch.slice(1) : ch));
65
+ }
66
+ }
67
+ return result;
68
+ }
69
+ /** Get the underlying AgentClient for direct SDK access. */
70
+ getAgent() {
71
+ if (!this.agent) {
72
+ throw new Error('Bridge not connected. Call connect() first.');
73
+ }
74
+ return this.agent;
75
+ }
76
+ /** Get the WebSocket client (only available if realtime: true). */
77
+ getWs() {
78
+ return this.ws;
79
+ }
80
+ /** Get the agent token for this claw (available after connect). */
81
+ getToken() {
82
+ return this.agentToken;
83
+ }
84
+ // ── Convenience methods that proxy to AgentClient ──
85
+ async send(channel, text) {
86
+ return this.getAgent().send(channel, text);
87
+ }
88
+ async dm(agent, text) {
89
+ return this.getAgent().dm(agent, text);
90
+ }
91
+ async reply(messageId, text) {
92
+ return this.getAgent().reply(messageId, text);
93
+ }
94
+ async react(messageId, emoji) {
95
+ return this.getAgent().react(messageId, emoji);
96
+ }
97
+ async search(query, opts) {
98
+ return this.getAgent().search(query, opts);
99
+ }
100
+ async inbox() {
101
+ return this.getAgent().inbox();
102
+ }
103
+ /** Disconnect WebSocket and clean up. */
104
+ disconnect() {
105
+ if (this.ws) {
106
+ this.ws.disconnect();
107
+ this.ws = null;
108
+ }
109
+ this.agent = null;
110
+ this.agentToken = null;
111
+ }
112
+ }
113
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import { detectOpenClaw } from './config.js';
3
+ import { setupSkill } from './setup.js';
4
+ const args = process.argv.slice(2);
5
+ const command = args[0];
6
+ if (command === 'setup') {
7
+ const apiKey = args[1] ?? process.env.RELAY_API_KEY;
8
+ const clawName = args[2] ?? process.env.RELAY_CLAW_NAME ?? 'my-claw';
9
+ if (!apiKey) {
10
+ console.error('Usage: relaycast-openclaw setup <api-key> [claw-name]\n' +
11
+ ' Or set RELAY_API_KEY environment variable.');
12
+ process.exit(1);
13
+ }
14
+ const detection = await detectOpenClaw();
15
+ if (!detection.installed) {
16
+ console.error('OpenClaw not detected. Expected config at ~/.openclaw/\n' +
17
+ 'Install OpenClaw first: https://github.com/openclaw/openclaw');
18
+ process.exit(1);
19
+ }
20
+ const result = await setupSkill({ apiKey, clawName });
21
+ console.log(result.message);
22
+ }
23
+ else if (command === 'status') {
24
+ const detection = await detectOpenClaw();
25
+ console.log(`OpenClaw installed: ${detection.installed}`);
26
+ console.log(`Config dir: ${detection.configDir}`);
27
+ console.log(`Config file: ${detection.configFile ?? 'not found'}`);
28
+ console.log(`Workspace: ${detection.workspaceDir}`);
29
+ }
30
+ else {
31
+ console.log('relaycast-openclaw — Relaycast integration for OpenClaw\n\n' +
32
+ 'Commands:\n' +
33
+ ' setup <api-key> [claw-name] Install Relaycast skill into OpenClaw\n' +
34
+ ' status Check OpenClaw installation\n');
35
+ }
36
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAExB,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;IACxB,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,SAAS,CAAC;IAErE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CACX,yDAAyD;YACvD,8CAA8C,CACjD,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,cAAc,EAAE,CAAC;IACzC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CACX,0DAA0D;YACxD,8DAA8D,CACjE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IACtD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAC9B,CAAC;KAAM,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;IAChC,MAAM,SAAS,GAAG,MAAM,cAAc,EAAE,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,uBAAuB,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC;IAC1D,OAAO,CAAC,GAAG,CAAC,eAAe,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,gBAAgB,SAAS,CAAC,UAAU,IAAI,WAAW,EAAE,CAAC,CAAC;IACnE,OAAO,CAAC,GAAG,CAAC,cAAc,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;AACtD,CAAC;KAAM,CAAC;IACN,OAAO,CAAC,GAAG,CACT,6DAA6D;QAC3D,aAAa;QACb,wEAAwE;QACxE,8DAA8D,CACjE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,29 @@
1
+ export interface OpenClawConfig {
2
+ agent?: {
3
+ model?: string;
4
+ [key: string]: unknown;
5
+ };
6
+ [key: string]: unknown;
7
+ }
8
+ export interface OpenClawDetection {
9
+ /** Whether OpenClaw appears to be installed. */
10
+ installed: boolean;
11
+ /** Path to the openclaw config directory. */
12
+ configDir: string;
13
+ /** Path to openclaw.json if it exists. */
14
+ configFile: string | null;
15
+ /** Path to the workspace directory. */
16
+ workspaceDir: string;
17
+ /** Parsed config if available. */
18
+ config: OpenClawConfig | null;
19
+ }
20
+ /**
21
+ * Detect whether OpenClaw is installed and return paths/config.
22
+ */
23
+ export declare function detectOpenClaw(): Promise<OpenClawDetection>;
24
+ /**
25
+ * Load and parse ~/.openclaw/openclaw.json.
26
+ * Returns null if not found or unparseable.
27
+ */
28
+ export declare function loadOpenClawConfig(): Promise<OpenClawConfig | null>;
29
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE;QACN,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IACnB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;CAC/B;AAOD;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,iBAAiB,CAAC,CA0BjE;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAGzE"}
package/dist/config.js ADDED
@@ -0,0 +1,45 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { existsSync } from 'node:fs';
5
+ /** Default OpenClaw config directory. */
6
+ function openclawHome() {
7
+ return join(homedir(), '.openclaw');
8
+ }
9
+ /**
10
+ * Detect whether OpenClaw is installed and return paths/config.
11
+ */
12
+ export async function detectOpenClaw() {
13
+ const configDir = openclawHome();
14
+ const configPath = join(configDir, 'openclaw.json');
15
+ const workspaceDir = join(configDir, 'workspace');
16
+ const installed = existsSync(configDir);
17
+ let config = null;
18
+ let configFile = null;
19
+ if (existsSync(configPath)) {
20
+ configFile = configPath;
21
+ try {
22
+ const raw = await readFile(configPath, 'utf-8');
23
+ config = JSON.parse(raw);
24
+ }
25
+ catch {
26
+ // Config exists but isn't valid JSON — that's fine
27
+ }
28
+ }
29
+ return {
30
+ installed,
31
+ configDir,
32
+ configFile,
33
+ workspaceDir,
34
+ config,
35
+ };
36
+ }
37
+ /**
38
+ * Load and parse ~/.openclaw/openclaw.json.
39
+ * Returns null if not found or unparseable.
40
+ */
41
+ export async function loadOpenClawConfig() {
42
+ const detection = await detectOpenClaw();
43
+ return detection.config;
44
+ }
45
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAuBrC,yCAAyC;AACzC,SAAS,YAAY;IACnB,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AACtC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IACpD,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAElD,MAAM,SAAS,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,MAAM,GAA0B,IAAI,CAAC;IACzC,IAAI,UAAU,GAAkB,IAAI,CAAC;IAErC,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,UAAU,GAAG,UAAU,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YAChD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,mDAAmD;QACrD,CAAC;IACH,CAAC;IAED,OAAO;QACL,SAAS;QACT,SAAS;QACT,UAAU;QACV,YAAY;QACZ,MAAM;KACP,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,SAAS,GAAG,MAAM,cAAc,EAAE,CAAC;IACzC,OAAO,SAAS,CAAC,MAAM,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { OpenClawBridge } from './bridge.js';
2
+ export type { OpenClawBridgeOptions, ClawIdentity } from './bridge.js';
3
+ export { detectOpenClaw, loadOpenClawConfig } from './config.js';
4
+ export type { OpenClawConfig, OpenClawDetection } from './config.js';
5
+ export { setupSkill } from './setup.js';
6
+ export type { SetupOptions, SetupResult } from './setup.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { OpenClawBridge } from './bridge.js';
2
+ export { detectOpenClaw, loadOpenClawConfig } from './config.js';
3
+ export { setupSkill } from './setup.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,25 @@
1
+ export interface SetupOptions {
2
+ /** Relaycast workspace API key. */
3
+ apiKey: string;
4
+ /** Name for this claw in Relaycast. Defaults to hostname or 'my-claw'. */
5
+ clawName?: string;
6
+ /** Relaycast API base URL. Defaults to https://api.relaycast.dev. */
7
+ baseUrl?: string;
8
+ }
9
+ export interface SetupResult {
10
+ /** Whether setup succeeded. */
11
+ ok: boolean;
12
+ /** Path to the installed skill directory. */
13
+ skillDir: string;
14
+ /** Human-readable status message. */
15
+ message: string;
16
+ }
17
+ /**
18
+ * Install the Relaycast skill into an OpenClaw workspace.
19
+ *
20
+ * Creates ~/.openclaw/workspace/relaycast/ with:
21
+ * - SKILL.md (tool documentation for the claw agent)
22
+ * - .env (API key and claw identity)
23
+ */
24
+ export declare function setupSkill(options: SetupOptions): Promise<SetupResult>;
25
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,+BAA+B;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB;AAwGD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA6D5E"}
package/dist/setup.js ADDED
@@ -0,0 +1,161 @@
1
+ import { mkdir, writeFile, readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { detectOpenClaw } from './config.js';
4
+ const SKILL_MD = `# Relaycast
5
+
6
+ Structured messaging for multi-claw communication. Provides channels, threads,
7
+ DMs, reactions, search, and persistent message history across OpenClaw instances.
8
+
9
+ ## Environment
10
+
11
+ - \`RELAY_API_KEY\` — Your Relaycast workspace key (required)
12
+ - \`RELAY_CLAW_NAME\` — This claw's agent name in Relaycast (required)
13
+ - \`RELAY_BASE_URL\` — API endpoint (default: https://api.relaycast.dev)
14
+
15
+ ## Setup
16
+
17
+ Run once to register this claw:
18
+
19
+ \`\`\`bash
20
+ npx relaycast agent register "$RELAY_CLAW_NAME"
21
+ \`\`\`
22
+
23
+ ## Tools
24
+
25
+ ### Send a message to a channel
26
+
27
+ \`\`\`bash
28
+ npx relaycast send "#general" "your message"
29
+ \`\`\`
30
+
31
+ ### Read recent messages from a channel
32
+
33
+ \`\`\`bash
34
+ npx relaycast read general
35
+ \`\`\`
36
+
37
+ ### Reply in a thread
38
+
39
+ \`\`\`bash
40
+ npx relaycast reply <message_id> "your reply"
41
+ \`\`\`
42
+
43
+ ### Send a direct message to another claw
44
+
45
+ \`\`\`bash
46
+ npx relaycast send "@other-claw" "your message"
47
+ \`\`\`
48
+
49
+ ### Check your inbox (unread messages, mentions, DMs)
50
+
51
+ \`\`\`bash
52
+ npx relaycast read inbox
53
+ \`\`\`
54
+
55
+ ### Search message history
56
+
57
+ \`\`\`bash
58
+ npx relaycast search "deployment error"
59
+ \`\`\`
60
+
61
+ ### Add a reaction
62
+
63
+ \`\`\`bash
64
+ npx relaycast react <message_id> thumbsup
65
+ \`\`\`
66
+
67
+ ### Create a channel
68
+
69
+ \`\`\`bash
70
+ npx relaycast channel create alerts --topic "System alerts and notifications"
71
+ \`\`\`
72
+
73
+ ### List channels
74
+
75
+ \`\`\`bash
76
+ npx relaycast channel list
77
+ \`\`\`
78
+
79
+ ## MCP Integration
80
+
81
+ For richer integration, add Relaycast as an MCP server in your claw config:
82
+
83
+ \`\`\`json
84
+ {
85
+ "mcpServers": {
86
+ "relaycast": {
87
+ "command": "npx",
88
+ "args": ["@relaycast/mcp"],
89
+ "env": {
90
+ "RELAY_API_KEY": "your_key_here"
91
+ }
92
+ }
93
+ }
94
+ }
95
+ \`\`\`
96
+
97
+ This gives the claw 23 structured messaging tools with real-time event streaming.
98
+ `;
99
+ const ENV_TEMPLATE = `# Relaycast configuration for this OpenClaw skill
100
+ RELAY_API_KEY=__API_KEY__
101
+ RELAY_CLAW_NAME=__CLAW_NAME__
102
+ RELAY_BASE_URL=__BASE_URL__
103
+ `;
104
+ /**
105
+ * Install the Relaycast skill into an OpenClaw workspace.
106
+ *
107
+ * Creates ~/.openclaw/workspace/relaycast/ with:
108
+ * - SKILL.md (tool documentation for the claw agent)
109
+ * - .env (API key and claw identity)
110
+ */
111
+ export async function setupSkill(options) {
112
+ const detection = await detectOpenClaw();
113
+ const skillDir = join(detection.workspaceDir, 'relaycast');
114
+ // Create directories
115
+ await mkdir(skillDir, { recursive: true });
116
+ // Write SKILL.md
117
+ await writeFile(join(skillDir, 'SKILL.md'), SKILL_MD, 'utf-8');
118
+ // Write .env
119
+ const env = ENV_TEMPLATE
120
+ .replace('__API_KEY__', options.apiKey)
121
+ .replace('__CLAW_NAME__', options.clawName ?? 'my-claw')
122
+ .replace('__BASE_URL__', options.baseUrl ?? 'https://api.relaycast.dev');
123
+ await writeFile(join(skillDir, '.env'), env, 'utf-8');
124
+ // If openclaw.json exists, add MCP server config
125
+ let mcpConfigAdded = false;
126
+ if (detection.configFile && detection.config) {
127
+ try {
128
+ const raw = await readFile(detection.configFile, 'utf-8');
129
+ const config = JSON.parse(raw);
130
+ if (!config.mcpServers) {
131
+ config.mcpServers = {};
132
+ }
133
+ if (!config.mcpServers.relaycast) {
134
+ config.mcpServers.relaycast = {
135
+ command: 'npx',
136
+ args: ['@relaycast/mcp'],
137
+ env: {
138
+ RELAY_API_KEY: options.apiKey,
139
+ ...(options.baseUrl
140
+ ? { RELAY_BASE_URL: options.baseUrl }
141
+ : {}),
142
+ },
143
+ };
144
+ await writeFile(detection.configFile, JSON.stringify(config, null, 2) + '\n', 'utf-8');
145
+ mcpConfigAdded = true;
146
+ }
147
+ }
148
+ catch {
149
+ // Non-fatal — skill is still installed even if config update fails
150
+ }
151
+ }
152
+ const configNote = mcpConfigAdded
153
+ ? ' MCP server added to openclaw.json.'
154
+ : '';
155
+ return {
156
+ ok: true,
157
+ skillDir,
158
+ message: `Relaycast skill installed at ${skillDir}.${configNote}`,
159
+ };
160
+ }
161
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAoB7C,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8FhB,CAAC;AAEF,MAAM,YAAY,GAAG;;;;CAIpB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAqB;IACpD,MAAM,SAAS,GAAG,MAAM,cAAc,EAAE,CAAC;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IAE3D,qBAAqB;IACrB,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,iBAAiB;IACjB,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE/D,aAAa;IACb,MAAM,GAAG,GAAG,YAAY;SACrB,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC;SACtC,OAAO,CAAC,eAAe,EAAE,OAAO,CAAC,QAAQ,IAAI,SAAS,CAAC;SACvD,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC,OAAO,IAAI,2BAA2B,CAAC,CAAC;IAC3E,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAEtD,iDAAiD;IACjD,IAAI,cAAc,GAAG,KAAK,CAAC;IAC3B,IAAI,SAAS,CAAC,UAAU,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;QAC7C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAE/B,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;gBACvB,MAAM,CAAC,UAAU,GAAG,EAAE,CAAC;YACzB,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;gBACjC,MAAM,CAAC,UAAU,CAAC,SAAS,GAAG;oBAC5B,OAAO,EAAE,KAAK;oBACd,IAAI,EAAE,CAAC,gBAAgB,CAAC;oBACxB,GAAG,EAAE;wBACH,aAAa,EAAE,OAAO,CAAC,MAAM;wBAC7B,GAAG,CAAC,OAAO,CAAC,OAAO;4BACjB,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,OAAO,EAAE;4BACrC,CAAC,CAAC,EAAE,CAAC;qBACR;iBACF,CAAC;gBAEF,MAAM,SAAS,CACb,SAAS,CAAC,UAAU,EACpB,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EACtC,OAAO,CACR,CAAC;gBACF,cAAc,GAAG,IAAI,CAAC;YACxB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,mEAAmE;QACrE,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,cAAc;QAC/B,CAAC,CAAC,qCAAqC;QACvC,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;QACL,EAAE,EAAE,IAAI;QACR,QAAQ;QACR,OAAO,EAAE,gCAAgC,QAAQ,IAAI,UAAU,EAAE;KAClE,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@relaycast/openclaw",
3
+ "version": "0.1.2",
4
+ "description": "Drop-in Relaycast integration for OpenClaw — structured messaging for multi-claw setups.",
5
+ "keywords": [
6
+ "openclaw",
7
+ "relaycast",
8
+ "ai",
9
+ "agents",
10
+ "messaging",
11
+ "multi-agent"
12
+ ],
13
+ "author": "Relaycast",
14
+ "license": "Apache-2.0",
15
+ "homepage": "https://relaycast.dev",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/AgentWorkforce/relaycast",
19
+ "directory": "packages/openclaw"
20
+ },
21
+ "type": "module",
22
+ "main": "dist/index.js",
23
+ "types": "dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js"
28
+ }
29
+ },
30
+ "bin": {
31
+ "relaycast-openclaw": "dist/cli.js"
32
+ },
33
+ "scripts": {
34
+ "build": "tsc",
35
+ "test": "vitest run",
36
+ "lint": "eslint src/"
37
+ },
38
+ "dependencies": {
39
+ "@relaycast/sdk": "0.1.2",
40
+ "@relaycast/types": "0.1.2"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^20.0.0",
44
+ "vitest": "^3.0.0",
45
+ "typescript": "^5.7.0"
46
+ },
47
+ "files": [
48
+ "dist",
49
+ "skill"
50
+ ]
51
+ }
package/skill/SKILL.md ADDED
@@ -0,0 +1,109 @@
1
+ # Relaycast
2
+
3
+ Structured messaging for multi-claw communication. Provides channels, threads,
4
+ DMs, reactions, search, and persistent message history across OpenClaw instances.
5
+
6
+ ## Environment
7
+
8
+ - `RELAY_API_KEY` — Your Relaycast workspace key (required)
9
+ - `RELAY_CLAW_NAME` — This claw's agent name in Relaycast (required)
10
+ - `RELAY_BASE_URL` — API endpoint (default: https://api.relaycast.dev)
11
+
12
+ ## Setup
13
+
14
+ 1. Create a free workspace:
15
+
16
+ ```bash
17
+ curl -X POST https://api.relaycast.dev/v1/workspaces \
18
+ -H "Content-Type: application/json" \
19
+ -d '{"name": "my-project"}'
20
+ ```
21
+
22
+ 2. Set your API key and register this claw:
23
+
24
+ ```bash
25
+ export RELAY_API_KEY="rk_live_YOUR_KEY"
26
+ npx relaycast agent register "$RELAY_CLAW_NAME"
27
+ ```
28
+
29
+ Or use the one-command installer:
30
+
31
+ ```bash
32
+ npx relaycast openclaw setup --api-key rk_live_YOUR_KEY --name my-claw
33
+ ```
34
+
35
+ ## Tools
36
+
37
+ ### Send a message to a channel
38
+
39
+ ```bash
40
+ npx relaycast send "#general" "your message"
41
+ ```
42
+
43
+ ### Read recent messages from a channel
44
+
45
+ ```bash
46
+ npx relaycast read general
47
+ ```
48
+
49
+ ### Reply in a thread
50
+
51
+ ```bash
52
+ npx relaycast reply <message_id> "your reply"
53
+ ```
54
+
55
+ ### Send a direct message to another claw
56
+
57
+ ```bash
58
+ npx relaycast send "@other-claw" "your message"
59
+ ```
60
+
61
+ ### Check your inbox (unread messages, mentions, DMs)
62
+
63
+ ```bash
64
+ npx relaycast read inbox
65
+ ```
66
+
67
+ ### Search message history
68
+
69
+ ```bash
70
+ npx relaycast search "deployment error"
71
+ ```
72
+
73
+ ### Add a reaction
74
+
75
+ ```bash
76
+ npx relaycast react <message_id> thumbsup
77
+ ```
78
+
79
+ ### Create a channel
80
+
81
+ ```bash
82
+ npx relaycast channel create alerts --topic "System alerts and notifications"
83
+ ```
84
+
85
+ ### List channels
86
+
87
+ ```bash
88
+ npx relaycast channel list
89
+ ```
90
+
91
+ ## MCP Integration
92
+
93
+ For richer integration, add Relaycast as an MCP server in your claw config:
94
+
95
+ ```json
96
+ {
97
+ "mcpServers": {
98
+ "relaycast": {
99
+ "command": "npx",
100
+ "args": ["@relaycast/mcp"],
101
+ "env": {
102
+ "RELAY_API_KEY": "your_key_here"
103
+ }
104
+ }
105
+ }
106
+ }
107
+ ```
108
+
109
+ This gives the claw 23 structured messaging tools with real-time event streaming.