openclaw-quiubo 2.6.21 → 2.6.24
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 +6 -0
- package/dist/index.js +88 -21
- package/dist/index.js.map +3 -3
- package/dist/src/api.d.ts +6 -0
- package/dist/src/api.d.ts.map +1 -1
- package/dist/src/channel.d.ts.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -8952,11 +8952,20 @@ function getQuiuboRuntime() {
|
|
|
8952
8952
|
}
|
|
8953
8953
|
|
|
8954
8954
|
// src/channel.ts
|
|
8955
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
8956
|
-
import { basename } from "path";
|
|
8955
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
8956
|
+
import { basename, join as join2 } from "path";
|
|
8957
8957
|
|
|
8958
8958
|
// src/api.ts
|
|
8959
8959
|
import { randomUUID } from "crypto";
|
|
8960
|
+
var QuiuboApiError = class extends Error {
|
|
8961
|
+
constructor(status, statusText, body) {
|
|
8962
|
+
super(`Quiubo API error: ${status} ${statusText} - ${body}`);
|
|
8963
|
+
this.status = status;
|
|
8964
|
+
this.statusText = statusText;
|
|
8965
|
+
this.body = body;
|
|
8966
|
+
this.name = "QuiuboApiError";
|
|
8967
|
+
}
|
|
8968
|
+
};
|
|
8960
8969
|
var QuiuboApiClient = class {
|
|
8961
8970
|
baseUrl;
|
|
8962
8971
|
apiKey;
|
|
@@ -8977,7 +8986,7 @@ var QuiuboApiClient = class {
|
|
|
8977
8986
|
});
|
|
8978
8987
|
if (!response.ok) {
|
|
8979
8988
|
const errorBody = await response.text();
|
|
8980
|
-
throw new
|
|
8989
|
+
throw new QuiuboApiError(response.status, response.statusText, errorBody);
|
|
8981
8990
|
}
|
|
8982
8991
|
if (response.status === 204) {
|
|
8983
8992
|
return void 0;
|
|
@@ -13256,14 +13265,36 @@ var AgentKeyManager = class {
|
|
|
13256
13265
|
};
|
|
13257
13266
|
|
|
13258
13267
|
// src/channel.ts
|
|
13259
|
-
|
|
13268
|
+
var KEYS_DIR = join2(process.env.HOME ?? process.env.USERPROFILE ?? "", ".openclaw", "cron");
|
|
13269
|
+
async function loadOrGenerateKeys(cfg, accountId) {
|
|
13270
|
+
const seedFile = join2(KEYS_DIR, `quiubo-keys-${accountId}.json`);
|
|
13271
|
+
try {
|
|
13272
|
+
const raw = await readFile2(seedFile, "utf-8");
|
|
13273
|
+
const { seed: seedB642 } = JSON.parse(raw);
|
|
13274
|
+
if (seedB642) {
|
|
13275
|
+
const seed = new Uint8Array(Buffer.from(seedB642, "base64"));
|
|
13276
|
+
return { ...deriveKeypairFromSeed(seed), freshlyGenerated: false };
|
|
13277
|
+
}
|
|
13278
|
+
} catch {
|
|
13279
|
+
}
|
|
13260
13280
|
if (cfg.keys?.seed) {
|
|
13261
13281
|
const seed = new Uint8Array(Buffer.from(cfg.keys.seed, "base64"));
|
|
13262
|
-
|
|
13282
|
+
const keys2 = deriveKeypairFromSeed(seed);
|
|
13283
|
+
await persistSeed(seedFile, cfg.keys.seed);
|
|
13284
|
+
return { ...keys2, freshlyGenerated: false };
|
|
13263
13285
|
}
|
|
13264
13286
|
const keys = generateAgentKeypair();
|
|
13265
|
-
|
|
13266
|
-
|
|
13287
|
+
const seedB64 = Buffer.from(keys.seed).toString("base64");
|
|
13288
|
+
cfg.keys = { seed: seedB64 };
|
|
13289
|
+
await persistSeed(seedFile, seedB64);
|
|
13290
|
+
return { ...keys, freshlyGenerated: true };
|
|
13291
|
+
}
|
|
13292
|
+
async function persistSeed(seedFile, seedB64) {
|
|
13293
|
+
try {
|
|
13294
|
+
await mkdir2(KEYS_DIR, { recursive: true });
|
|
13295
|
+
await writeFile2(seedFile, JSON.stringify({ seed: seedB64 }), "utf-8");
|
|
13296
|
+
} catch {
|
|
13297
|
+
}
|
|
13267
13298
|
}
|
|
13268
13299
|
async function readMdAttachments(mediaUrls, source, log, accountId) {
|
|
13269
13300
|
const attachments = [];
|
|
@@ -13623,8 +13654,12 @@ var quiuboPlugin = {
|
|
|
13623
13654
|
log?.info?.(`[${accountId}] [outbound:sendText] sent to group ${groupId} (realtime=${apiResp?.realtimeDelivered})`);
|
|
13624
13655
|
return { ok: true };
|
|
13625
13656
|
} catch (error) {
|
|
13657
|
+
if (error instanceof QuiuboApiError) {
|
|
13658
|
+
log?.error?.(`[${accountId}] [outbound:sendText] SEND FAILED [${error.status}] group=${groupId} \u2014 ${error.body}`);
|
|
13659
|
+
return { ok: false, error: `${error.status}: ${error.body}` };
|
|
13660
|
+
}
|
|
13626
13661
|
const msg = error instanceof Error ? error.message : String(error);
|
|
13627
|
-
log?.error?.(`[${accountId}] [outbound:sendText]
|
|
13662
|
+
log?.error?.(`[${accountId}] [outbound:sendText] SEND FAILED [network] group=${groupId} \u2014 ${msg}`);
|
|
13628
13663
|
return { ok: false, error: msg };
|
|
13629
13664
|
}
|
|
13630
13665
|
},
|
|
@@ -13675,8 +13710,12 @@ var quiuboPlugin = {
|
|
|
13675
13710
|
log?.info?.(`[${accountId}] [outbound:sendMedia] sent to group ${groupId} (realtime=${apiResp?.realtimeDelivered})`);
|
|
13676
13711
|
return { ok: true };
|
|
13677
13712
|
} catch (error) {
|
|
13713
|
+
if (error instanceof QuiuboApiError) {
|
|
13714
|
+
log?.error?.(`[${accountId}] [outbound:sendMedia] SEND FAILED [${error.status}] group=${groupId} \u2014 ${error.body}`);
|
|
13715
|
+
return { ok: false, error: `${error.status}: ${error.body}` };
|
|
13716
|
+
}
|
|
13678
13717
|
const msg = error instanceof Error ? error.message : String(error);
|
|
13679
|
-
log?.error?.(`[${accountId}] [outbound:sendMedia]
|
|
13718
|
+
log?.error?.(`[${accountId}] [outbound:sendMedia] SEND FAILED [network] group=${groupId} \u2014 ${msg}`);
|
|
13680
13719
|
return { ok: false, error: msg };
|
|
13681
13720
|
}
|
|
13682
13721
|
}
|
|
@@ -13692,8 +13731,15 @@ var quiuboPlugin = {
|
|
|
13692
13731
|
startAccount: async (ctx) => {
|
|
13693
13732
|
const { cfg, accountId, abortSignal, log } = ctx;
|
|
13694
13733
|
const quiuboConfig = getChannelConfig(cfg)?.accounts?.[accountId];
|
|
13695
|
-
if (!quiuboConfig
|
|
13696
|
-
log?.
|
|
13734
|
+
if (!quiuboConfig) {
|
|
13735
|
+
log?.error?.(`[${accountId}] Quiubo: [FATAL] STARTUP FAILED \u2014 no config found for account "${accountId}". Check channels.quiubo.accounts.${accountId} in your OpenClaw config.`);
|
|
13736
|
+
return;
|
|
13737
|
+
}
|
|
13738
|
+
const missing = [];
|
|
13739
|
+
if (!quiuboConfig.apiKey) missing.push("apiKey");
|
|
13740
|
+
if (!quiuboConfig.botIdentityId) missing.push("botIdentityId");
|
|
13741
|
+
if (missing.length > 0) {
|
|
13742
|
+
log?.error?.(`[${accountId}] Quiubo: [FATAL] STARTUP FAILED \u2014 missing required config: ${missing.join(", ")}. Run "openclaw accounts configure quiubo" to set them.`);
|
|
13697
13743
|
return;
|
|
13698
13744
|
}
|
|
13699
13745
|
if (quiuboConfig.enabled === false) {
|
|
@@ -13725,7 +13771,17 @@ var quiuboPlugin = {
|
|
|
13725
13771
|
log?.info?.(`[${accountId}] Quiubo: Pusher not configured \u2014 using polling`);
|
|
13726
13772
|
}
|
|
13727
13773
|
} catch (error) {
|
|
13728
|
-
|
|
13774
|
+
if (error instanceof QuiuboApiError) {
|
|
13775
|
+
if (error.status === 401) {
|
|
13776
|
+
log?.error?.(`[${accountId}] Quiubo: [FATAL] STARTUP FAILED \u2014 API key is invalid or expired (401). Regenerate your SDK API key in the Quiubo dashboard.`);
|
|
13777
|
+
} else if (error.status === 403) {
|
|
13778
|
+
log?.error?.(`[${accountId}] Quiubo: [FATAL] STARTUP FAILED \u2014 API key lacks permissions (403). Check your SDK app tier and permissions.`);
|
|
13779
|
+
} else {
|
|
13780
|
+
log?.error?.(`[${accountId}] Quiubo: [FATAL] STARTUP FAILED \u2014 authentication error (${error.status}): ${error.body}`);
|
|
13781
|
+
}
|
|
13782
|
+
} else {
|
|
13783
|
+
log?.error?.(`[${accountId}] Quiubo: [FATAL] STARTUP FAILED \u2014 cannot reach API at ${apiUrl}: ${error instanceof Error ? error.message : error}`);
|
|
13784
|
+
}
|
|
13729
13785
|
return;
|
|
13730
13786
|
}
|
|
13731
13787
|
try {
|
|
@@ -13814,9 +13870,9 @@ var quiuboPlugin = {
|
|
|
13814
13870
|
resolvedAgentName = match.name;
|
|
13815
13871
|
log?.info?.(`[${accountId}] Quiubo: resolved agent ${match.name} (${match.id}) for heartbeat, displayName=${resolvedAgentDisplayName}`);
|
|
13816
13872
|
try {
|
|
13817
|
-
const keys = loadOrGenerateKeys(quiuboConfig);
|
|
13818
|
-
if (!match.e2eeConfigured) {
|
|
13819
|
-
log?.info?.(`[${accountId}] Quiubo: enrolling E2EE keys for agent ${match.id}`);
|
|
13873
|
+
const keys = await loadOrGenerateKeys(quiuboConfig, accountId);
|
|
13874
|
+
if (!match.e2eeConfigured || keys.freshlyGenerated) {
|
|
13875
|
+
log?.info?.(`[${accountId}] Quiubo: ${keys.freshlyGenerated ? "re-" : ""}enrolling E2EE keys for agent ${match.id}`);
|
|
13820
13876
|
const { challengeId, challenge } = await client.requestKeyChallenge(match.id, {
|
|
13821
13877
|
signingPublicKey: toBase64(keys.signingPublicKey),
|
|
13822
13878
|
encryptionPublicKey: toBase64(keys.encryptionPublicKey),
|
|
@@ -14036,7 +14092,7 @@ var quiuboPlugin = {
|
|
|
14036
14092
|
}
|
|
14037
14093
|
await gateway.loadCursors();
|
|
14038
14094
|
await gateway.start();
|
|
14039
|
-
log?.info?.(`[${accountId}] Quiubo:
|
|
14095
|
+
log?.info?.(`[${accountId}] Quiubo: [OK] STARTUP COMPLETE \u2014 realtime=${pusherConfig ? "pusher" : "polling"}, agent=${resolvedAgentId ? resolvedAgentName : "none"}, e2ee=${keyManager ? "ready" : "off"}`);
|
|
14040
14096
|
return new Promise((resolve) => {
|
|
14041
14097
|
const cleanup = () => {
|
|
14042
14098
|
log?.info?.(`[${accountId}] Quiubo: gateway stopping`);
|
|
@@ -14081,9 +14137,9 @@ function resolveOutboundGroupId(ctx) {
|
|
|
14081
14137
|
async function resolveAnnounceGroupId(accountId, log) {
|
|
14082
14138
|
try {
|
|
14083
14139
|
const { readFile: readFile3 } = await import("node:fs/promises");
|
|
14084
|
-
const { join:
|
|
14140
|
+
const { join: join3 } = await import("node:path");
|
|
14085
14141
|
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
14086
|
-
const cronPath =
|
|
14142
|
+
const cronPath = join3(homeDir, ".openclaw", "cron", "jobs.json");
|
|
14087
14143
|
const raw = await readFile3(cronPath, "utf-8");
|
|
14088
14144
|
const parsed = JSON.parse(raw);
|
|
14089
14145
|
const jobs = parsed?.jobs ?? [];
|
|
@@ -14105,9 +14161,9 @@ async function resolveAnnounceGroupId(accountId, log) {
|
|
|
14105
14161
|
async function getActivityData(runtime2, log, agentId) {
|
|
14106
14162
|
try {
|
|
14107
14163
|
const { readFile: readFile3 } = await import("node:fs/promises");
|
|
14108
|
-
const { join:
|
|
14164
|
+
const { join: join3 } = await import("node:path");
|
|
14109
14165
|
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
14110
|
-
const cronPath =
|
|
14166
|
+
const cronPath = join3(homeDir, ".openclaw", "cron", "jobs.json");
|
|
14111
14167
|
log?.info?.(`getActivityData: reading ${cronPath} (agentId=${agentId})`);
|
|
14112
14168
|
const raw = await readFile3(cronPath, "utf-8");
|
|
14113
14169
|
const parsed = JSON.parse(raw);
|
|
@@ -14253,6 +14309,13 @@ async function routeInboundMessage(opts) {
|
|
|
14253
14309
|
log?.warn?.(`[${accountId}] Quiubo: skipping outbound \u2014 agent lacks send_messages scope for group ${groupId}`);
|
|
14254
14310
|
return;
|
|
14255
14311
|
}
|
|
14312
|
+
if (payload.text) {
|
|
14313
|
+
payload.text = payload.text.replace(/\n*Reasoning:\n_[^_]*_\n*/g, "").trim();
|
|
14314
|
+
}
|
|
14315
|
+
if (!payload.text && mdAttachments.length === 0) {
|
|
14316
|
+
log?.debug?.(`[${accountId}] Quiubo: skipping empty reply after reasoning strip`);
|
|
14317
|
+
return;
|
|
14318
|
+
}
|
|
14256
14319
|
log?.info?.(`[${accountId}] Quiubo: delivering ${info.kind} reply [${payload.text?.length ?? 0} chars, ${mdAttachments.length} attachments]`);
|
|
14257
14320
|
try {
|
|
14258
14321
|
let ciphertextOut;
|
|
@@ -14282,7 +14345,11 @@ async function routeInboundMessage(opts) {
|
|
|
14282
14345
|
log?.info?.(`[${accountId}] Quiubo: reply sent to group ${groupId} (realtime=${apiResp?.realtimeDelivered})`);
|
|
14283
14346
|
}
|
|
14284
14347
|
} catch (error) {
|
|
14285
|
-
|
|
14348
|
+
if (error instanceof QuiuboApiError) {
|
|
14349
|
+
log?.error?.(`[${accountId}] Quiubo: SEND FAILED [${error.status}] group=${groupId} \u2014 ${error.body}`);
|
|
14350
|
+
} else {
|
|
14351
|
+
log?.error?.(`[${accountId}] Quiubo: SEND FAILED [network] group=${groupId} \u2014 ${error instanceof Error ? error.message : error}`);
|
|
14352
|
+
}
|
|
14286
14353
|
}
|
|
14287
14354
|
},
|
|
14288
14355
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|