openclaw-quiubo 0.2.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.
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Quiubo OpenClaw Extension — Entry Point
3
+ *
4
+ * Default export is a function that OpenClaw calls with `api`.
5
+ * We register the channel plugin and optionally start the webhook server.
6
+ */
7
+ import { quiuboPlugin } from './src/channel.js';
8
+ export { QuiuboApiClient } from './src/api.js';
9
+ export { quiuboPlugin, getQuiuboClient } from './src/channel.js';
10
+ export { startWebhookServer } from './src/webhook-handler.js';
11
+ export type { QuiuboAccountConfig, QuiuboGroup, QuiuboMessage, QuiuboIdentity, QuiuboJoinToken, WebhookPayload, WebhookMessageData, WebhookJoinTokenRedeemedData, } from './src/types.js';
12
+ /**
13
+ * OpenClaw plugin entry point.
14
+ *
15
+ * Called by OpenClaw when it loads the extension:
16
+ * import plugin from './index.ts';
17
+ * plugin(api);
18
+ */
19
+ export default function register(api: {
20
+ registerChannel: (opts: {
21
+ plugin: typeof quiuboPlugin;
22
+ }) => void;
23
+ }): void;
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,YAAY,EACV,mBAAmB,EACnB,WAAW,EACX,aAAa,EACb,cAAc,EACd,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,4BAA4B,GAC7B,MAAM,gBAAgB,CAAC;AAExB;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE;IACpC,eAAe,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,YAAY,CAAA;KAAE,KAAK,IAAI,CAAC;CAClE,QAEA"}
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Quiubo OpenClaw Extension — Entry Point
3
+ *
4
+ * Default export is a function that OpenClaw calls with `api`.
5
+ * We register the channel plugin and optionally start the webhook server.
6
+ */
7
+ import { quiuboPlugin } from './src/channel.js';
8
+ // Re-export for consumers
9
+ export { QuiuboApiClient } from './src/api.js';
10
+ export { quiuboPlugin, getQuiuboClient } from './src/channel.js';
11
+ export { startWebhookServer } from './src/webhook-handler.js';
12
+ /**
13
+ * OpenClaw plugin entry point.
14
+ *
15
+ * Called by OpenClaw when it loads the extension:
16
+ * import plugin from './index.ts';
17
+ * plugin(api);
18
+ */
19
+ export default function register(api) {
20
+ api.registerChannel({ plugin: quiuboPlugin });
21
+ }
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,0BAA0B;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAY9D;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAEhC;IACC,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Quiubo REST API Client
3
+ *
4
+ * SDK client for interacting with the Quiubo backend API.
5
+ * Used by the OpenClaw extension to send/receive messages and manage groups.
6
+ */
7
+ import type { QuiuboGroup, QuiuboMessage, QuiuboAuthResponse, QuiuboIdentity, QuiuboJoinToken } from './types.js';
8
+ export declare class QuiuboApiClient {
9
+ private baseUrl;
10
+ private apiKey;
11
+ constructor(baseUrl: string, apiKey: string);
12
+ private request;
13
+ /**
14
+ * Authenticate and verify API key
15
+ */
16
+ authenticate(): Promise<QuiuboAuthResponse>;
17
+ /**
18
+ * Send a plaintext message to an SDK group
19
+ */
20
+ sendMessage(groupId: string, opts: {
21
+ senderIdentityId: string;
22
+ plaintext: string;
23
+ clientMessageId?: string;
24
+ }): Promise<QuiuboMessage>;
25
+ /**
26
+ * List messages from an SDK group with cursor-based pagination
27
+ */
28
+ listMessages(groupId: string, afterMessageId?: string, limit?: number): Promise<{
29
+ messages: QuiuboMessage[];
30
+ hasMore: boolean;
31
+ }>;
32
+ /**
33
+ * Create a new SDK-managed group
34
+ */
35
+ createGroup(opts: {
36
+ externalGroupId: string;
37
+ name: string;
38
+ description?: string;
39
+ }): Promise<QuiuboGroup>;
40
+ /**
41
+ * Add members to an SDK group
42
+ */
43
+ addMembers(groupId: string, identityIds: string[]): Promise<void>;
44
+ /**
45
+ * Remove members from an SDK group
46
+ */
47
+ removeMembers(groupId: string, identityIds: string[]): Promise<void>;
48
+ /**
49
+ * List all SDK-managed groups
50
+ */
51
+ listGroups(): Promise<{
52
+ groups: QuiuboGroup[];
53
+ }>;
54
+ /**
55
+ * Get a specific SDK-managed group
56
+ */
57
+ getGroup(groupId: string): Promise<QuiuboGroup>;
58
+ /**
59
+ * Configure webhook for the SDK app
60
+ */
61
+ configureWebhook(opts: {
62
+ webhookUrl?: string;
63
+ webhookSecret?: string;
64
+ webhookEnabled?: boolean;
65
+ }): Promise<{
66
+ webhookUrl: string | null;
67
+ webhookEnabled: boolean;
68
+ hasSecret: boolean;
69
+ lastFailureAt: string | null;
70
+ lastFailureReason: string | null;
71
+ }>;
72
+ /**
73
+ * Create a service identity owned by this SDK app
74
+ */
75
+ createIdentity(opts: {
76
+ username: string;
77
+ displayName: string;
78
+ avatarUrl?: string;
79
+ }): Promise<QuiuboIdentity>;
80
+ /**
81
+ * List service identities owned by this SDK app
82
+ */
83
+ listIdentities(): Promise<{
84
+ identities: QuiuboIdentity[];
85
+ }>;
86
+ /**
87
+ * Delete (soft-delete) a service identity
88
+ */
89
+ deleteIdentity(identityId: string): Promise<{
90
+ success: boolean;
91
+ }>;
92
+ /**
93
+ * Create a join token for self-service user onboarding
94
+ */
95
+ createJoinToken(opts: {
96
+ botIdentityId: string;
97
+ groupName: string;
98
+ groupDescription?: string;
99
+ externalGroupPrefix?: string;
100
+ isReusable?: boolean;
101
+ maxUses?: number;
102
+ expiresInSeconds?: number;
103
+ metadata?: Record<string, unknown>;
104
+ }): Promise<QuiuboJoinToken>;
105
+ /**
106
+ * List join tokens for this SDK app
107
+ */
108
+ listJoinTokens(): Promise<{
109
+ tokens: QuiuboJoinToken[];
110
+ }>;
111
+ /**
112
+ * Revoke a join token
113
+ */
114
+ deleteJoinToken(tokenId: string): Promise<{
115
+ success: boolean;
116
+ }>;
117
+ }
118
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Quiubo REST API Client
3
+ *
4
+ * SDK client for interacting with the Quiubo backend API.
5
+ * Used by the OpenClaw extension to send/receive messages and manage groups.
6
+ */
7
+ import { randomUUID } from 'crypto';
8
+ export class QuiuboApiClient {
9
+ baseUrl;
10
+ apiKey;
11
+ constructor(baseUrl, apiKey) {
12
+ // Remove trailing slash
13
+ this.baseUrl = baseUrl.replace(/\/$/, '');
14
+ this.apiKey = apiKey;
15
+ }
16
+ async request(method, path, body) {
17
+ const url = `${this.baseUrl}/v1/sdk${path}`;
18
+ const headers = {
19
+ 'X-SDK-API-Key': this.apiKey,
20
+ 'Content-Type': 'application/json',
21
+ };
22
+ const response = await fetch(url, {
23
+ method,
24
+ headers,
25
+ body: body ? JSON.stringify(body) : undefined,
26
+ });
27
+ if (!response.ok) {
28
+ const errorBody = await response.text();
29
+ throw new Error(`Quiubo API error: ${response.status} ${response.statusText} - ${errorBody}`);
30
+ }
31
+ return response.json();
32
+ }
33
+ /**
34
+ * Authenticate and verify API key
35
+ */
36
+ async authenticate() {
37
+ return this.request('POST', '/auth');
38
+ }
39
+ /**
40
+ * Send a plaintext message to an SDK group
41
+ */
42
+ async sendMessage(groupId, opts) {
43
+ return this.request('POST', `/groups/${groupId}/messages`, {
44
+ senderIdentityId: opts.senderIdentityId,
45
+ plaintext: opts.plaintext,
46
+ clientMessageId: opts.clientMessageId ?? randomUUID(),
47
+ });
48
+ }
49
+ /**
50
+ * List messages from an SDK group with cursor-based pagination
51
+ */
52
+ async listMessages(groupId, afterMessageId, limit = 50) {
53
+ const params = new URLSearchParams({ limit: String(limit) });
54
+ if (afterMessageId) {
55
+ params.set('after_message_id', afterMessageId);
56
+ }
57
+ return this.request('GET', `/groups/${groupId}/messages?${params.toString()}`);
58
+ }
59
+ /**
60
+ * Create a new SDK-managed group
61
+ */
62
+ async createGroup(opts) {
63
+ return this.request('POST', '/groups', opts);
64
+ }
65
+ /**
66
+ * Add members to an SDK group
67
+ */
68
+ async addMembers(groupId, identityIds) {
69
+ await this.request('POST', `/groups/${groupId}/members`, {
70
+ identityIds,
71
+ action: 'add',
72
+ });
73
+ }
74
+ /**
75
+ * Remove members from an SDK group
76
+ */
77
+ async removeMembers(groupId, identityIds) {
78
+ await this.request('POST', `/groups/${groupId}/members`, {
79
+ identityIds,
80
+ action: 'remove',
81
+ });
82
+ }
83
+ /**
84
+ * List all SDK-managed groups
85
+ */
86
+ async listGroups() {
87
+ return this.request('GET', '/groups');
88
+ }
89
+ /**
90
+ * Get a specific SDK-managed group
91
+ */
92
+ async getGroup(groupId) {
93
+ return this.request('GET', `/groups/${groupId}`);
94
+ }
95
+ /**
96
+ * Configure webhook for the SDK app
97
+ */
98
+ async configureWebhook(opts) {
99
+ return this.request('PATCH', '/webhook', opts);
100
+ }
101
+ // ========================================================================
102
+ // Identity Management
103
+ // ========================================================================
104
+ /**
105
+ * Create a service identity owned by this SDK app
106
+ */
107
+ async createIdentity(opts) {
108
+ return this.request('POST', '/identities', opts);
109
+ }
110
+ /**
111
+ * List service identities owned by this SDK app
112
+ */
113
+ async listIdentities() {
114
+ return this.request('GET', '/identities');
115
+ }
116
+ /**
117
+ * Delete (soft-delete) a service identity
118
+ */
119
+ async deleteIdentity(identityId) {
120
+ return this.request('DELETE', `/identities/${identityId}`);
121
+ }
122
+ // ========================================================================
123
+ // Join Token Management
124
+ // ========================================================================
125
+ /**
126
+ * Create a join token for self-service user onboarding
127
+ */
128
+ async createJoinToken(opts) {
129
+ return this.request('POST', '/join-tokens', opts);
130
+ }
131
+ /**
132
+ * List join tokens for this SDK app
133
+ */
134
+ async listJoinTokens() {
135
+ return this.request('GET', '/join-tokens');
136
+ }
137
+ /**
138
+ * Revoke a join token
139
+ */
140
+ async deleteJoinToken(tokenId) {
141
+ return this.request('DELETE', `/join-tokens/${tokenId}`);
142
+ }
143
+ }
144
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAMpC,MAAM,OAAO,eAAe;IAClB,OAAO,CAAS;IAChB,MAAM,CAAS;IAEvB,YAAY,OAAe,EAAE,MAAc;QACzC,wBAAwB;QACxB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAEO,KAAK,CAAC,OAAO,CACnB,MAAc,EACd,IAAY,EACZ,IAAc;QAEd,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,UAAU,IAAI,EAAE,CAAC;QAC5C,MAAM,OAAO,GAA2B;YACtC,eAAe,EAAE,IAAI,CAAC,MAAM;YAC5B,cAAc,EAAE,kBAAkB;SACnC,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM;YACN,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;SAC9C,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,MAAM,SAAS,EAAE,CAAC,CAAC;QAChG,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAgB,CAAC;IACvC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY;QAChB,OAAO,IAAI,CAAC,OAAO,CAAqB,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CACf,OAAe,EACf,IAIC;QAED,OAAO,IAAI,CAAC,OAAO,CAAgB,MAAM,EAAE,WAAW,OAAO,WAAW,EAAE;YACxE,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,eAAe,EAAE,IAAI,CAAC,eAAe,IAAI,UAAU,EAAE;SACtD,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAChB,OAAe,EACf,cAAuB,EACvB,KAAK,GAAG,EAAE;QAEV,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC7D,IAAI,cAAc,EAAE,CAAC;YACnB,MAAM,CAAC,GAAG,CAAC,kBAAkB,EAAE,cAAc,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,WAAW,OAAO,aAAa,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACjF,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,IAIjB;QACC,OAAO,IAAI,CAAC,OAAO,CAAc,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IAC5D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,OAAe,EAAE,WAAqB;QACrD,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,OAAO,UAAU,EAAE;YACvD,WAAW;YACX,MAAM,EAAE,KAAK;SACd,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,OAAe,EAAE,WAAqB;QACxD,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,OAAO,UAAU,EAAE;YACvD,WAAW;YACX,MAAM,EAAE,QAAQ;SACjB,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU;QACd,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ,CAAC,OAAe;QAC5B,OAAO,IAAI,CAAC,OAAO,CAAc,KAAK,EAAE,WAAW,OAAO,EAAE,CAAC,CAAC;IAChE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,gBAAgB,CAAC,IAItB;QAOC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;IACjD,CAAC;IAED,2EAA2E;IAC3E,sBAAsB;IACtB,2EAA2E;IAE3E;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,IAIpB;QACC,OAAO,IAAI,CAAC,OAAO,CAAiB,MAAM,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;IACnE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,UAAkB;QACrC,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,eAAe,UAAU,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,2EAA2E;IAC3E,wBAAwB;IACxB,2EAA2E;IAE3E;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,IASrB;QACC,OAAO,IAAI,CAAC,OAAO,CAAkB,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC;IACrE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,OAAe;QACnC,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,gBAAgB,OAAO,EAAE,CAAC,CAAC;IAC3D,CAAC;CACF"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Quiubo Channel Plugin
3
+ *
4
+ * Flat plugin object matching OpenClaw's ChannelPlugin interface.
5
+ *
6
+ * Shape:
7
+ * id, meta, capabilities, config, outbound
8
+ *
9
+ * The gateway (webhook listener) lifecycle is managed outside the plugin
10
+ * object — see index.ts where we wire up the webhook server after
11
+ * the plugin is registered.
12
+ */
13
+ import { QuiuboApiClient } from './api.js';
14
+ import type { QuiuboAccountConfig } from './types.js';
15
+ /**
16
+ * The plugin object passed to api.registerChannel({ plugin }).
17
+ *
18
+ * OpenClaw discovers accounts via config.listAccountIds / resolveAccount,
19
+ * then calls outbound.sendText when the agent needs to respond.
20
+ */
21
+ export declare const quiuboPlugin: {
22
+ id: "quiubo";
23
+ meta: {
24
+ id: string;
25
+ label: string;
26
+ selectionLabel: string;
27
+ blurb: string;
28
+ aliases: string[];
29
+ };
30
+ capabilities: {
31
+ chatTypes: readonly ["group"];
32
+ };
33
+ config: {
34
+ /**
35
+ * Return the list of account IDs from the user's OpenClaw config.
36
+ * Config lives at: channels.quiubo.accounts.<accountId>
37
+ */
38
+ listAccountIds(cfg: Record<string, unknown>): string[];
39
+ /**
40
+ * Resolve an account config into the shape our plugin needs.
41
+ * Returns the raw account object (apiUrl, apiKey, botIdentityId, etc.)
42
+ */
43
+ resolveAccount(cfg: Record<string, unknown>, accountId?: string): QuiuboAccountConfig;
44
+ };
45
+ outbound: {
46
+ deliveryMode: "direct";
47
+ /**
48
+ * Send a text message to the current Quiubo group.
49
+ *
50
+ * OpenClaw calls this with { text, target, ... }.
51
+ * `target` carries routing info — we expect target.groupId to know
52
+ * which Quiubo group to send to.
53
+ */
54
+ sendText({ text, target }: {
55
+ text: string;
56
+ target?: {
57
+ groupId?: string;
58
+ [key: string]: unknown;
59
+ };
60
+ }): Promise<{
61
+ ok: boolean;
62
+ }>;
63
+ };
64
+ };
65
+ /**
66
+ * Get the current API client (for webhook handler and other plumbing).
67
+ * Returns null if no account has been resolved yet.
68
+ */
69
+ export declare function getQuiuboClient(): QuiuboApiClient | null;
70
+ //# sourceMappingURL=channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAatD;;;;;GAKG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;QAerB;;;WAGG;4BACiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE;QAOtD;;;WAGG;4BAEI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,cAChB,MAAM,GACjB,mBAAmB;;;;QAwBtB;;;;;;WAMG;mCAC8B;YAC/B,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,CAAC,EAAE;gBAAE,OAAO,CAAC,EAAE,MAAM,CAAC;gBAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;aAAE,CAAC;SACvD,GAAG,OAAO,CAAC;YAAE,EAAE,EAAE,OAAO,CAAA;SAAE,CAAC;;CA2B/B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,eAAe,IAAI,eAAe,GAAG,IAAI,CAExD"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Quiubo Channel Plugin
3
+ *
4
+ * Flat plugin object matching OpenClaw's ChannelPlugin interface.
5
+ *
6
+ * Shape:
7
+ * id, meta, capabilities, config, outbound
8
+ *
9
+ * The gateway (webhook listener) lifecycle is managed outside the plugin
10
+ * object — see index.ts where we wire up the webhook server after
11
+ * the plugin is registered.
12
+ */
13
+ import { QuiuboApiClient } from './api.js';
14
+ // Module-level client, initialised lazily per-account
15
+ let _client = null;
16
+ let _account = null;
17
+ function getClient() {
18
+ if (!_client || !_account) {
19
+ throw new Error('Quiubo plugin: no account resolved — call resolveAccount first');
20
+ }
21
+ return _client;
22
+ }
23
+ /**
24
+ * The plugin object passed to api.registerChannel({ plugin }).
25
+ *
26
+ * OpenClaw discovers accounts via config.listAccountIds / resolveAccount,
27
+ * then calls outbound.sendText when the agent needs to respond.
28
+ */
29
+ export const quiuboPlugin = {
30
+ id: 'quiubo',
31
+ meta: {
32
+ id: 'quiubo',
33
+ label: 'Quiubo',
34
+ selectionLabel: 'Quiubo (API)',
35
+ blurb: 'Send and receive messages through Quiubo.',
36
+ aliases: ['qb'],
37
+ },
38
+ capabilities: { chatTypes: ['group'] },
39
+ // ── config adapter ──────────────────────────────────────────────
40
+ config: {
41
+ /**
42
+ * Return the list of account IDs from the user's OpenClaw config.
43
+ * Config lives at: channels.quiubo.accounts.<accountId>
44
+ */
45
+ listAccountIds(cfg) {
46
+ const channels = cfg.channels;
47
+ const quiubo = channels?.quiubo;
48
+ const accounts = quiubo?.accounts;
49
+ return Object.keys(accounts ?? {});
50
+ },
51
+ /**
52
+ * Resolve an account config into the shape our plugin needs.
53
+ * Returns the raw account object (apiUrl, apiKey, botIdentityId, etc.)
54
+ */
55
+ resolveAccount(cfg, accountId) {
56
+ const channels = cfg.channels;
57
+ const quiubo = channels?.quiubo;
58
+ const accounts = quiubo?.accounts;
59
+ const id = accountId ?? 'default';
60
+ const account = accounts?.[id];
61
+ if (!account) {
62
+ return { accountId: id };
63
+ }
64
+ // Initialise the API client for this account
65
+ const apiUrl = account.apiUrl ?? 'https://api.quiubo.io';
66
+ _client = new QuiuboApiClient(apiUrl, account.apiKey);
67
+ _account = { ...account, accountId: id };
68
+ return _account;
69
+ },
70
+ },
71
+ // ── outbound adapter ────────────────────────────────────────────
72
+ outbound: {
73
+ deliveryMode: 'direct',
74
+ /**
75
+ * Send a text message to the current Quiubo group.
76
+ *
77
+ * OpenClaw calls this with { text, target, ... }.
78
+ * `target` carries routing info — we expect target.groupId to know
79
+ * which Quiubo group to send to.
80
+ */
81
+ async sendText({ text, target }) {
82
+ const client = getClient();
83
+ const groupId = target?.groupId;
84
+ if (!groupId) {
85
+ console.error('[Quiubo] sendText called without target.groupId');
86
+ return { ok: false };
87
+ }
88
+ const senderId = _account?.botIdentityId;
89
+ if (!senderId) {
90
+ console.error('[Quiubo] sendText called without botIdentityId configured');
91
+ return { ok: false };
92
+ }
93
+ try {
94
+ await client.sendMessage(groupId, {
95
+ senderIdentityId: senderId,
96
+ plaintext: text,
97
+ });
98
+ return { ok: true };
99
+ }
100
+ catch (error) {
101
+ console.error('[Quiubo] sendText failed:', error);
102
+ return { ok: false };
103
+ }
104
+ },
105
+ },
106
+ };
107
+ /**
108
+ * Get the current API client (for webhook handler and other plumbing).
109
+ * Returns null if no account has been resolved yet.
110
+ */
111
+ export function getQuiuboClient() {
112
+ return _client;
113
+ }
114
+ //# sourceMappingURL=channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.js","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Shared types for the Quiubo OpenClaw extension
3
+ */
4
+ /**
5
+ * Account config as stored in OpenClaw config:
6
+ * channels.quiubo.accounts.<accountId>
7
+ */
8
+ export interface QuiuboAccountConfig {
9
+ accountId: string;
10
+ apiUrl: string;
11
+ apiKey: string;
12
+ botIdentityId: string;
13
+ webhookSecret?: string;
14
+ webhookPort?: number;
15
+ }
16
+ export interface QuiuboGroup {
17
+ id: string;
18
+ externalGroupId: string;
19
+ name: string;
20
+ description: string | null;
21
+ retentionDays: number;
22
+ visibility: 'native+sdk' | 'sdk-only';
23
+ memberCount: number;
24
+ createdAt: string;
25
+ }
26
+ export interface QuiuboMessage {
27
+ id: string;
28
+ groupId: string;
29
+ senderIdentityId: string;
30
+ clientMessageId: string;
31
+ ciphertext: string | null;
32
+ plaintext: string | null;
33
+ createdAt: string;
34
+ }
35
+ export interface QuiuboAuthResponse {
36
+ appId: string;
37
+ appName: string;
38
+ authenticated: boolean;
39
+ }
40
+ export interface QuiuboIdentity {
41
+ identityId: string;
42
+ username: string;
43
+ displayName: string;
44
+ avatarUrl: string | null;
45
+ createdAt: string;
46
+ }
47
+ export interface QuiuboJoinToken {
48
+ tokenId: string;
49
+ token: string;
50
+ joinUrl: string;
51
+ qrCodeData: string;
52
+ botIdentityId: string;
53
+ groupName: string;
54
+ isReusable: boolean;
55
+ maxUses: number | null;
56
+ useCount: number;
57
+ expiresAt: string | null;
58
+ isActive: boolean;
59
+ createdAt: string;
60
+ }
61
+ export interface WebhookPayload {
62
+ event: string;
63
+ timestamp: string;
64
+ data: WebhookMessageData | WebhookJoinTokenRedeemedData;
65
+ }
66
+ export interface WebhookMessageData {
67
+ messageId: string;
68
+ groupId: string;
69
+ externalGroupId: string;
70
+ senderIdentityId: string;
71
+ senderUsername: string;
72
+ plaintext: string;
73
+ clientMessageId: string;
74
+ createdAt: string;
75
+ }
76
+ export interface WebhookJoinTokenRedeemedData {
77
+ tokenId: string;
78
+ groupId: string;
79
+ externalGroupId: string;
80
+ groupName: string;
81
+ userIdentityId: string;
82
+ botIdentityId: string;
83
+ isNew: boolean;
84
+ redeemedAt: string;
85
+ }
86
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,YAAY,GAAG,UAAU,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,kBAAkB,GAAG,4BAA4B,CAAC;CACzD;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Shared types for the Quiubo OpenClaw extension
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Webhook Handler
3
+ *
4
+ * Receives inbound webhook events from Quiubo backend.
5
+ * Verifies HMAC-SHA256 signature and routes messages to the OpenClaw agent.
6
+ */
7
+ import { type Server } from 'http';
8
+ import type { WebhookPayload } from './types.js';
9
+ export interface WebhookHandlerOptions {
10
+ port: number;
11
+ webhookSecret?: string;
12
+ botIdentityId: string;
13
+ onMessage: (payload: WebhookPayload) => void | Promise<void>;
14
+ }
15
+ /**
16
+ * Start a webhook HTTP server.
17
+ * Returns the server instance for lifecycle management.
18
+ */
19
+ export declare function startWebhookServer(opts: WebhookHandlerOptions): Promise<Server>;
20
+ //# sourceMappingURL=webhook-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-handler.d.ts","sourceRoot":"","sources":["../../src/webhook-handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAgB,KAAK,MAAM,EAA6C,MAAM,MAAM,CAAC;AAC5F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9D;AA0BD;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwDrF"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Webhook Handler
3
+ *
4
+ * Receives inbound webhook events from Quiubo backend.
5
+ * Verifies HMAC-SHA256 signature and routes messages to the OpenClaw agent.
6
+ */
7
+ import { createHmac, timingSafeEqual } from 'crypto';
8
+ import { createServer } from 'http';
9
+ /**
10
+ * Verify HMAC-SHA256 signature
11
+ */
12
+ function verifySignature(body, signature, secret) {
13
+ const expected = createHmac('sha256', secret).update(body).digest('hex');
14
+ try {
15
+ return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
16
+ }
17
+ catch {
18
+ return false;
19
+ }
20
+ }
21
+ /**
22
+ * Read the full request body
23
+ */
24
+ function readBody(req) {
25
+ return new Promise((resolve, reject) => {
26
+ const chunks = [];
27
+ req.on('data', (chunk) => chunks.push(chunk));
28
+ req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
29
+ req.on('error', reject);
30
+ });
31
+ }
32
+ /**
33
+ * Start a webhook HTTP server.
34
+ * Returns the server instance for lifecycle management.
35
+ */
36
+ export async function startWebhookServer(opts) {
37
+ const { port, webhookSecret, botIdentityId, onMessage } = opts;
38
+ const server = createServer(async (req, res) => {
39
+ // Only accept POST /webhooks/quiubo
40
+ if (req.method !== 'POST' || req.url !== '/webhooks/quiubo') {
41
+ res.writeHead(404);
42
+ res.end('Not found');
43
+ return;
44
+ }
45
+ try {
46
+ const body = await readBody(req);
47
+ // Verify signature if secret is configured
48
+ if (webhookSecret) {
49
+ const signature = req.headers['x-quiubo-signature'];
50
+ if (!signature || !verifySignature(body, signature, webhookSecret)) {
51
+ console.error('[Webhook] Invalid signature');
52
+ res.writeHead(401);
53
+ res.end('Invalid signature');
54
+ return;
55
+ }
56
+ }
57
+ const payload = JSON.parse(body);
58
+ // Skip messages from bot itself (only applies to message events)
59
+ if (payload.event === 'group_message.created' &&
60
+ 'senderIdentityId' in payload.data &&
61
+ payload.data.senderIdentityId === botIdentityId) {
62
+ res.writeHead(200);
63
+ res.end('OK (skipped self)');
64
+ return;
65
+ }
66
+ // Route to handler
67
+ await onMessage(payload);
68
+ res.writeHead(200);
69
+ res.end('OK');
70
+ }
71
+ catch (error) {
72
+ console.error('[Webhook] Handler error:', error);
73
+ res.writeHead(500);
74
+ res.end('Internal error');
75
+ }
76
+ });
77
+ return new Promise((resolve) => {
78
+ server.listen(port, () => {
79
+ console.log(`[Quiubo] Webhook server listening on port ${port}`);
80
+ resolve(server);
81
+ });
82
+ });
83
+ }
84
+ //# sourceMappingURL=webhook-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-handler.js","sourceRoot":"","sources":["../../src/webhook-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ {
2
+ "id": "openclaw-quiubo",
3
+ "name": "Quiubo",
4
+ "version": "0.1.0",
5
+ "configSchema": {
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {}
9
+ }
10
+ }
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "openclaw-quiubo",
3
+ "version": "0.2.1",
4
+ "description": "OpenClaw Quiubo channel plugin — chat with AI assistants through Quiubo",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "openclaw.plugin.json"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "typecheck": "tsc --noEmit",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "openclaw",
25
+ "quiubo",
26
+ "channel",
27
+ "plugin",
28
+ "messaging"
29
+ ],
30
+ "license": "MIT",
31
+ "devDependencies": {
32
+ "@types/node": "^22.10.1",
33
+ "typescript": "^5.7.2"
34
+ },
35
+ "openclaw": {
36
+ "extensions": ["./dist/index.js"]
37
+ }
38
+ }