openclaw-rumi 0.1.1 → 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.
- package/README.md +10 -3
- package/dist/index.d.ts +33 -0
- package/dist/index.js +2 -0
- package/dist/src/client.d.ts +2 -1
- package/dist/src/client.js +3 -0
- package/dist/src/tools/health-check.d.ts +35 -0
- package/dist/src/tools/health-check.js +46 -0
- package/dist/src/types.d.ts +14 -0
- package/index.ts +2 -0
- package/package.json +24 -3
- package/skills/SKILL.md +6 -5
- package/src/client.ts +5 -0
- package/src/tools/health-check.ts +60 -0
- package/src/types.ts +15 -0
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Rumi — OpenClaw Plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Talk to real people, not just AI.** Rumi matches you with humans who share your interests — by what you want to talk about, not by photos.
|
|
4
|
+
|
|
5
|
+
Your OpenClaw agent finds the right person in the background and connects you when a match is found. Chat right inside OpenClaw or on the Rumi website.
|
|
4
6
|
|
|
5
7
|
## Quick Start
|
|
6
8
|
|
|
@@ -48,20 +50,25 @@ If you prefer to set up manually:
|
|
|
48
50
|
|
|
49
51
|
| Tool | Description |
|
|
50
52
|
|------|-------------|
|
|
53
|
+
| `rumi_health_check` | Verify your token, check quota, and see active sessions |
|
|
51
54
|
| `rumi_find_partner` | Find someone to chat with based on your interests |
|
|
52
55
|
| `rumi_check_status` | Check if a pending match has been found |
|
|
53
56
|
| `rumi_send_message` | Send a message to your matched partner |
|
|
54
57
|
| `rumi_get_messages` | Get recent messages from a conversation |
|
|
55
58
|
|
|
56
|
-
## Usage
|
|
59
|
+
## Usage Examples
|
|
57
60
|
|
|
58
61
|
Just tell your OpenClaw agent:
|
|
59
62
|
|
|
60
63
|
> "I want to find someone to talk about hiking with"
|
|
61
64
|
|
|
65
|
+
> "Find me someone who's into TypeScript and has done large-scale migrations"
|
|
66
|
+
|
|
67
|
+
> "I'm bored, connect me with someone interesting"
|
|
68
|
+
|
|
62
69
|
The agent will use Rumi to find a compatible person. When matched, you can chat directly through OpenClaw or visit the Rumi website.
|
|
63
70
|
|
|
64
|
-
The agent can also **proactively** find matches when it detects you might enjoy talking to a real person — you'll only be notified when someone is found.
|
|
71
|
+
The agent can also **proactively** find matches when it detects you might enjoy talking to a real person — like when you're deep into a hobby topic, working solo late at night, or hitting the limits of what AI can offer. You'll only be notified when someone is found.
|
|
65
72
|
|
|
66
73
|
## Development
|
|
67
74
|
|
package/dist/index.d.ts
CHANGED
|
@@ -125,6 +125,39 @@ declare const _default: {
|
|
|
125
125
|
messageCount?: undefined;
|
|
126
126
|
messages?: undefined;
|
|
127
127
|
}>;
|
|
128
|
+
} | {
|
|
129
|
+
name: string;
|
|
130
|
+
description: string;
|
|
131
|
+
parameters: {};
|
|
132
|
+
execute: () => Promise<{
|
|
133
|
+
status: string;
|
|
134
|
+
weeklyUsage: {
|
|
135
|
+
used: number;
|
|
136
|
+
limit: number;
|
|
137
|
+
remaining: number;
|
|
138
|
+
};
|
|
139
|
+
activeSession: {
|
|
140
|
+
sessionId: string;
|
|
141
|
+
status: string;
|
|
142
|
+
} | null;
|
|
143
|
+
tokenExpiresAt: string | null;
|
|
144
|
+
message: string;
|
|
145
|
+
setupUrl?: undefined;
|
|
146
|
+
} | {
|
|
147
|
+
status: string;
|
|
148
|
+
setupUrl: string;
|
|
149
|
+
message: string;
|
|
150
|
+
weeklyUsage?: undefined;
|
|
151
|
+
activeSession?: undefined;
|
|
152
|
+
tokenExpiresAt?: undefined;
|
|
153
|
+
} | {
|
|
154
|
+
status: string;
|
|
155
|
+
message: string;
|
|
156
|
+
weeklyUsage?: undefined;
|
|
157
|
+
activeSession?: undefined;
|
|
158
|
+
tokenExpiresAt?: undefined;
|
|
159
|
+
setupUrl?: undefined;
|
|
160
|
+
}>;
|
|
128
161
|
})[];
|
|
129
162
|
}>;
|
|
130
163
|
};
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { createFindPartnerTool } from "./src/tools/find-partner";
|
|
|
4
4
|
import { createCheckStatusTool } from "./src/tools/check-status";
|
|
5
5
|
import { createSendMessageTool } from "./src/tools/send-message";
|
|
6
6
|
import { createGetMessagesTool } from "./src/tools/get-messages";
|
|
7
|
+
import { createHealthCheckTool } from "./src/tools/health-check";
|
|
7
8
|
export default {
|
|
8
9
|
id: "openclaw-rumi",
|
|
9
10
|
slot: "tool",
|
|
@@ -16,6 +17,7 @@ export default {
|
|
|
16
17
|
const client = new RumiClient(config);
|
|
17
18
|
return {
|
|
18
19
|
tools: [
|
|
20
|
+
createHealthCheckTool(client),
|
|
19
21
|
createFindPartnerTool(client),
|
|
20
22
|
createCheckStatusTool(client),
|
|
21
23
|
createSendMessageTool(client),
|
package/dist/src/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RumiConfig, FindMatchResponse, SessionStatusResponse, MessagesResponse, SendMessageResponse } from "./types";
|
|
1
|
+
import type { RumiConfig, FindMatchResponse, SessionStatusResponse, MessagesResponse, SendMessageResponse, HealthResponse } from "./types";
|
|
2
2
|
/**
|
|
3
3
|
* HTTP client for Rumi Partner API
|
|
4
4
|
*/
|
|
@@ -11,5 +11,6 @@ export declare class RumiClient {
|
|
|
11
11
|
getSessionStatus(sessionId: string): Promise<SessionStatusResponse>;
|
|
12
12
|
getMessages(conversationId: string, after?: string, limit?: number): Promise<MessagesResponse>;
|
|
13
13
|
getConnectUrl(): string;
|
|
14
|
+
healthCheck(): Promise<HealthResponse>;
|
|
14
15
|
sendMessage(conversationId: string, content: string): Promise<SendMessageResponse>;
|
|
15
16
|
}
|
package/dist/src/client.js
CHANGED
|
@@ -54,6 +54,9 @@ export class RumiClient {
|
|
|
54
54
|
getConnectUrl() {
|
|
55
55
|
return `${this.baseUrl}/connect?partner=openclaw`;
|
|
56
56
|
}
|
|
57
|
+
async healthCheck() {
|
|
58
|
+
return this.request("/api/partner/health");
|
|
59
|
+
}
|
|
57
60
|
async sendMessage(conversationId, content) {
|
|
58
61
|
return this.request(`/api/conversations/${encodeURIComponent(conversationId)}/messages`, {
|
|
59
62
|
method: "POST",
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { RumiClient } from "../client";
|
|
2
|
+
export declare function createHealthCheckTool(client: RumiClient): {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {};
|
|
6
|
+
execute: () => Promise<{
|
|
7
|
+
status: string;
|
|
8
|
+
weeklyUsage: {
|
|
9
|
+
used: number;
|
|
10
|
+
limit: number;
|
|
11
|
+
remaining: number;
|
|
12
|
+
};
|
|
13
|
+
activeSession: {
|
|
14
|
+
sessionId: string;
|
|
15
|
+
status: string;
|
|
16
|
+
} | null;
|
|
17
|
+
tokenExpiresAt: string | null;
|
|
18
|
+
message: string;
|
|
19
|
+
setupUrl?: undefined;
|
|
20
|
+
} | {
|
|
21
|
+
status: string;
|
|
22
|
+
setupUrl: string;
|
|
23
|
+
message: string;
|
|
24
|
+
weeklyUsage?: undefined;
|
|
25
|
+
activeSession?: undefined;
|
|
26
|
+
tokenExpiresAt?: undefined;
|
|
27
|
+
} | {
|
|
28
|
+
status: string;
|
|
29
|
+
message: string;
|
|
30
|
+
weeklyUsage?: undefined;
|
|
31
|
+
activeSession?: undefined;
|
|
32
|
+
tokenExpiresAt?: undefined;
|
|
33
|
+
setupUrl?: undefined;
|
|
34
|
+
}>;
|
|
35
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export function createHealthCheckTool(client) {
|
|
2
|
+
return {
|
|
3
|
+
name: "rumi_health_check",
|
|
4
|
+
description: "Check if Rumi is properly configured: token validity, weekly usage quota, and any active matching sessions. Use this before rumi_find_partner to verify the setup is working.",
|
|
5
|
+
parameters: {},
|
|
6
|
+
execute: async () => {
|
|
7
|
+
try {
|
|
8
|
+
const result = await client.healthCheck();
|
|
9
|
+
const parts = [
|
|
10
|
+
`Status: ${result.status}`,
|
|
11
|
+
`Weekly usage: ${result.weeklyUsage.used}/${result.weeklyUsage.limit} (${result.weeklyUsage.remaining} remaining)`,
|
|
12
|
+
];
|
|
13
|
+
if (result.tokenExpiresAt) {
|
|
14
|
+
const expires = new Date(result.tokenExpiresAt);
|
|
15
|
+
const daysLeft = Math.ceil((expires.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
|
|
16
|
+
parts.push(`Token expires in ${daysLeft} days`);
|
|
17
|
+
}
|
|
18
|
+
if (result.activeSession) {
|
|
19
|
+
parts.push(`Active session: ${result.activeSession.sessionId} (${result.activeSession.status})`);
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
status: result.status,
|
|
23
|
+
weeklyUsage: result.weeklyUsage,
|
|
24
|
+
activeSession: result.activeSession,
|
|
25
|
+
tokenExpiresAt: result.tokenExpiresAt,
|
|
26
|
+
message: parts.join("\n"),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
31
|
+
if (message.includes("401") || message.includes("UNAUTHORIZED")) {
|
|
32
|
+
return {
|
|
33
|
+
status: "setup_required",
|
|
34
|
+
setupUrl: client.getConnectUrl(),
|
|
35
|
+
message: "Rumi is not set up or the token has expired. Open this URL to set up: " +
|
|
36
|
+
client.getConnectUrl(),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
status: "error",
|
|
41
|
+
message: `Health check failed: ${message}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -38,6 +38,20 @@ export interface SendMessageResponse {
|
|
|
38
38
|
id: string;
|
|
39
39
|
created_at: string;
|
|
40
40
|
}
|
|
41
|
+
export interface HealthResponse {
|
|
42
|
+
status: string;
|
|
43
|
+
userId: string;
|
|
44
|
+
tokenExpiresAt: string | null;
|
|
45
|
+
weeklyUsage: {
|
|
46
|
+
used: number;
|
|
47
|
+
limit: number;
|
|
48
|
+
remaining: number;
|
|
49
|
+
};
|
|
50
|
+
activeSession: {
|
|
51
|
+
sessionId: string;
|
|
52
|
+
status: string;
|
|
53
|
+
} | null;
|
|
54
|
+
}
|
|
41
55
|
export declare const FindPartnerInput: import("@sinclair/typebox").TObject<{
|
|
42
56
|
description: import("@sinclair/typebox").TString;
|
|
43
57
|
locale: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"zh-TW">, import("@sinclair/typebox").TLiteral<"en">, import("@sinclair/typebox").TLiteral<"ja">, import("@sinclair/typebox").TLiteral<"ko">]>>;
|
package/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { createFindPartnerTool } from "./src/tools/find-partner";
|
|
|
4
4
|
import { createCheckStatusTool } from "./src/tools/check-status";
|
|
5
5
|
import { createSendMessageTool } from "./src/tools/send-message";
|
|
6
6
|
import { createGetMessagesTool } from "./src/tools/get-messages";
|
|
7
|
+
import { createHealthCheckTool } from "./src/tools/health-check";
|
|
7
8
|
|
|
8
9
|
export default {
|
|
9
10
|
id: "openclaw-rumi",
|
|
@@ -19,6 +20,7 @@ export default {
|
|
|
19
20
|
|
|
20
21
|
return {
|
|
21
22
|
tools: [
|
|
23
|
+
createHealthCheckTool(client),
|
|
22
24
|
createFindPartnerTool(client),
|
|
23
25
|
createCheckStatusTool(client),
|
|
24
26
|
createSendMessageTool(client),
|
package/package.json
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-rumi",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "OpenClaw plugin to find real people to chat with — AI-powered interest matching connects you with compatible humans for genuine conversations",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"homepage": "https://rumi.app",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/maggiehu/Rumi",
|
|
11
|
+
"directory": "openclaw-plugin"
|
|
12
|
+
},
|
|
7
13
|
"scripts": {
|
|
8
14
|
"build": "tsc",
|
|
9
15
|
"dev": "tsc --watch"
|
|
@@ -11,7 +17,22 @@
|
|
|
11
17
|
"openclaw": {
|
|
12
18
|
"extensions": "./dist/index.js"
|
|
13
19
|
},
|
|
14
|
-
"keywords": [
|
|
20
|
+
"keywords": [
|
|
21
|
+
"openclaw",
|
|
22
|
+
"openclaw-plugin",
|
|
23
|
+
"rumi",
|
|
24
|
+
"social",
|
|
25
|
+
"matching",
|
|
26
|
+
"chat",
|
|
27
|
+
"human-connection",
|
|
28
|
+
"interest-matching",
|
|
29
|
+
"conversation",
|
|
30
|
+
"ai-agent",
|
|
31
|
+
"find-people",
|
|
32
|
+
"real-people",
|
|
33
|
+
"networking",
|
|
34
|
+
"penpal"
|
|
35
|
+
],
|
|
15
36
|
"license": "MIT",
|
|
16
37
|
"dependencies": {
|
|
17
38
|
"@sinclair/typebox": "^0.32.0"
|
package/skills/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rumi
|
|
3
|
-
description:
|
|
3
|
+
description: Match with real humans who share your interests — topic-driven matching, not photo swiping. Chat through OpenClaw or on the web.
|
|
4
4
|
user-invocable: true
|
|
5
5
|
metadata: {"openclaw":{"emoji":"🤝"}}
|
|
6
6
|
---
|
|
@@ -66,10 +66,11 @@ You can and SHOULD proactively search for a match when you detect these patterns
|
|
|
66
66
|
|
|
67
67
|
### If set up (apiToken configured):
|
|
68
68
|
|
|
69
|
-
1.
|
|
70
|
-
2.
|
|
71
|
-
3.
|
|
72
|
-
4.
|
|
69
|
+
1. Call `rumi_health_check` first to verify token and check quota
|
|
70
|
+
2. Gather context about what the user wants to talk about (or infer from conversation)
|
|
71
|
+
3. Call `rumi_find_partner` with a rich description — include interests, mood, what kind of person they want
|
|
72
|
+
4. If status is `searching` — check back with `rumi_check_status` every few minutes
|
|
73
|
+
5. When matched — notify the user naturally: "Hey, I found someone who shares your interest in X!"
|
|
73
74
|
|
|
74
75
|
## Handling Results
|
|
75
76
|
|
package/src/client.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
SessionStatusResponse,
|
|
5
5
|
MessagesResponse,
|
|
6
6
|
SendMessageResponse,
|
|
7
|
+
HealthResponse,
|
|
7
8
|
} from "./types";
|
|
8
9
|
|
|
9
10
|
const REQUEST_TIMEOUT_MS = 60000;
|
|
@@ -86,6 +87,10 @@ export class RumiClient {
|
|
|
86
87
|
return `${this.baseUrl}/connect?partner=openclaw`;
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
async healthCheck(): Promise<HealthResponse> {
|
|
91
|
+
return this.request<HealthResponse>("/api/partner/health");
|
|
92
|
+
}
|
|
93
|
+
|
|
89
94
|
async sendMessage(
|
|
90
95
|
conversationId: string,
|
|
91
96
|
content: string
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { RumiClient } from "../client";
|
|
2
|
+
|
|
3
|
+
export function createHealthCheckTool(client: RumiClient) {
|
|
4
|
+
return {
|
|
5
|
+
name: "rumi_health_check",
|
|
6
|
+
description:
|
|
7
|
+
"Check if Rumi is properly configured: token validity, weekly usage quota, and any active matching sessions. Use this before rumi_find_partner to verify the setup is working.",
|
|
8
|
+
parameters: {},
|
|
9
|
+
execute: async () => {
|
|
10
|
+
try {
|
|
11
|
+
const result = await client.healthCheck();
|
|
12
|
+
|
|
13
|
+
const parts = [
|
|
14
|
+
`Status: ${result.status}`,
|
|
15
|
+
`Weekly usage: ${result.weeklyUsage.used}/${result.weeklyUsage.limit} (${result.weeklyUsage.remaining} remaining)`,
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
if (result.tokenExpiresAt) {
|
|
19
|
+
const expires = new Date(result.tokenExpiresAt);
|
|
20
|
+
const daysLeft = Math.ceil(
|
|
21
|
+
(expires.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
|
|
22
|
+
);
|
|
23
|
+
parts.push(`Token expires in ${daysLeft} days`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (result.activeSession) {
|
|
27
|
+
parts.push(
|
|
28
|
+
`Active session: ${result.activeSession.sessionId} (${result.activeSession.status})`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
status: result.status,
|
|
34
|
+
weeklyUsage: result.weeklyUsage,
|
|
35
|
+
activeSession: result.activeSession,
|
|
36
|
+
tokenExpiresAt: result.tokenExpiresAt,
|
|
37
|
+
message: parts.join("\n"),
|
|
38
|
+
};
|
|
39
|
+
} catch (error) {
|
|
40
|
+
const message =
|
|
41
|
+
error instanceof Error ? error.message : String(error);
|
|
42
|
+
|
|
43
|
+
if (message.includes("401") || message.includes("UNAUTHORIZED")) {
|
|
44
|
+
return {
|
|
45
|
+
status: "setup_required",
|
|
46
|
+
setupUrl: client.getConnectUrl(),
|
|
47
|
+
message:
|
|
48
|
+
"Rumi is not set up or the token has expired. Open this URL to set up: " +
|
|
49
|
+
client.getConnectUrl(),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
status: "error",
|
|
55
|
+
message: `Health check failed: ${message}`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -55,6 +55,21 @@ export interface SendMessageResponse {
|
|
|
55
55
|
created_at: string;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
export interface HealthResponse {
|
|
59
|
+
status: string;
|
|
60
|
+
userId: string;
|
|
61
|
+
tokenExpiresAt: string | null;
|
|
62
|
+
weeklyUsage: {
|
|
63
|
+
used: number;
|
|
64
|
+
limit: number;
|
|
65
|
+
remaining: number;
|
|
66
|
+
};
|
|
67
|
+
activeSession: {
|
|
68
|
+
sessionId: string;
|
|
69
|
+
status: string;
|
|
70
|
+
} | null;
|
|
71
|
+
}
|
|
72
|
+
|
|
58
73
|
// Tool input schemas
|
|
59
74
|
export const FindPartnerInput = Type.Object({
|
|
60
75
|
description: Type.String({
|