@clwnt/clawnet 0.5.3 → 0.5.5
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/index.ts +1 -1
- package/package.json +1 -1
- package/src/cli.ts +61 -12
- package/src/service.ts +1 -1
- package/src/tools.ts +52 -23
package/index.ts
CHANGED
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -29,9 +29,9 @@ function sleep(ms: number): Promise<void> {
|
|
|
29
29
|
// --- Hook mapping builder (from spec) ---
|
|
30
30
|
|
|
31
31
|
const DEFAULT_HOOK_TEMPLATE =
|
|
32
|
-
"You have {{count}} new ClawNet message(s).\n\n" +
|
|
33
|
-
"
|
|
34
|
-
"{{context}}";
|
|
32
|
+
"You have {{count}} new ClawNet message(s). Process ONLY the new messages below — the conversation history is provided for context only.\n\n" +
|
|
33
|
+
"New messages (action required):\n{{messages}}\n\n" +
|
|
34
|
+
"Prior conversation history (for context only — do NOT re-process these):\n{{context}}";
|
|
35
35
|
|
|
36
36
|
let cachedHookTemplate: string | null = null;
|
|
37
37
|
|
|
@@ -687,11 +687,11 @@ export function registerClawnetCli(params: { program: Command; api: any; cfg: Cl
|
|
|
687
687
|
console.log("");
|
|
688
688
|
});
|
|
689
689
|
|
|
690
|
-
// ---
|
|
690
|
+
// --- disable ---
|
|
691
691
|
root
|
|
692
|
-
.command("
|
|
693
|
-
.option("--purge", "
|
|
694
|
-
.description("Disable ClawNet plugin and remove hook
|
|
692
|
+
.command("disable")
|
|
693
|
+
.option("--purge", "Also remove account config (tokens, accounts)")
|
|
694
|
+
.description("Disable ClawNet plugin and remove hook mappings")
|
|
695
695
|
.action(async (opts) => {
|
|
696
696
|
const currentConfig = api.runtime.config.loadConfig();
|
|
697
697
|
const cfg = { ...currentConfig };
|
|
@@ -716,20 +716,69 @@ export function registerClawnetCli(params: { program: Command; api: any; cfg: Cl
|
|
|
716
716
|
}
|
|
717
717
|
const removedCount = beforeCount - (cfg.hooks?.mappings?.length ?? 0);
|
|
718
718
|
|
|
719
|
-
console.log("\n ClawNet
|
|
720
|
-
console.log(" - Plugin disabled");
|
|
719
|
+
console.log("\n ClawNet disabled.\n");
|
|
720
|
+
console.log(" - Plugin disabled (polling stopped)");
|
|
721
|
+
console.log(" - Hook mappings removed");
|
|
721
722
|
if (removedCount > 0) {
|
|
722
|
-
console.log(`
|
|
723
|
+
console.log(` (${removedCount} mapping(s) cleaned up)`);
|
|
723
724
|
}
|
|
725
|
+
console.log("");
|
|
726
|
+
console.log(" To re-enable: openclaw clawnet setup");
|
|
727
|
+
console.log(" To fully remove: openclaw plugins uninstall clawnet");
|
|
724
728
|
|
|
725
729
|
// Do NOT touch: hooks.enabled, hooks.token, allowedSessionKeyPrefixes, allowedAgentIds
|
|
726
730
|
|
|
727
731
|
await api.runtime.config.writeConfigFile(cfg);
|
|
728
732
|
|
|
729
|
-
console.log(" - hooks.enabled, hooks.token left untouched");
|
|
730
733
|
if (opts.purge) {
|
|
731
|
-
console.log(" -
|
|
734
|
+
console.log(" - Account config purged");
|
|
735
|
+
}
|
|
736
|
+
console.log("\n Restart the Gateway to apply: openclaw gateway restart\n");
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
// --- enable ---
|
|
740
|
+
root
|
|
741
|
+
.command("enable")
|
|
742
|
+
.description("Re-enable a previously disabled ClawNet plugin")
|
|
743
|
+
.action(async () => {
|
|
744
|
+
const currentConfig = api.runtime.config.loadConfig();
|
|
745
|
+
const cfg = structuredClone(currentConfig);
|
|
746
|
+
|
|
747
|
+
const pluginEntry = cfg.plugins?.entries?.clawnet;
|
|
748
|
+
if (!pluginEntry) {
|
|
749
|
+
console.log("\n No ClawNet config found. Run `openclaw clawnet setup` first.\n");
|
|
750
|
+
return;
|
|
732
751
|
}
|
|
752
|
+
|
|
753
|
+
if (pluginEntry.enabled) {
|
|
754
|
+
console.log("\n ClawNet is already enabled.\n");
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
pluginEntry.enabled = true;
|
|
759
|
+
|
|
760
|
+
// Restore hook mappings from accounts
|
|
761
|
+
const accounts: any[] = pluginEntry.config?.accounts ?? [];
|
|
762
|
+
const enabled = accounts.filter((a: any) => a.enabled !== false);
|
|
763
|
+
if (enabled.length > 0) {
|
|
764
|
+
cfg.hooks ??= {};
|
|
765
|
+
let mappings = cfg.hooks.mappings ?? [];
|
|
766
|
+
for (const account of enabled) {
|
|
767
|
+
const channel = pluginEntry.config?.deliver?.channel ?? "last";
|
|
768
|
+
mappings = upsertMapping(mappings, buildClawnetMapping(
|
|
769
|
+
account.id,
|
|
770
|
+
channel,
|
|
771
|
+
account.openclawAgentId ?? account.id,
|
|
772
|
+
));
|
|
773
|
+
}
|
|
774
|
+
cfg.hooks.mappings = mappings;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
await api.runtime.config.writeConfigFile(cfg);
|
|
778
|
+
|
|
779
|
+
console.log("\n ClawNet enabled.\n");
|
|
780
|
+
console.log(` - Plugin enabled (${enabled.length} account(s))`);
|
|
781
|
+
console.log(` - Hook mappings restored`);
|
|
733
782
|
console.log("\n Restart the Gateway to apply: openclaw gateway restart\n");
|
|
734
783
|
});
|
|
735
784
|
}
|
package/src/service.ts
CHANGED
|
@@ -72,7 +72,7 @@ async function reloadOnboardingMessage(): Promise<void> {
|
|
|
72
72
|
|
|
73
73
|
const SKILL_UPDATE_INTERVAL_MS = 6 * 60 * 60 * 1000; // 6 hours
|
|
74
74
|
const SKILL_FILES = ["skill.json", "api-reference.md", "inbox-handler.md", "capabilities.json", "hook-template.txt", "tool-descriptions.json", "onboarding-message.txt"];
|
|
75
|
-
export const PLUGIN_VERSION = "0.5.
|
|
75
|
+
export const PLUGIN_VERSION = "0.5.5"; // Reported to server via PATCH /me every 6h
|
|
76
76
|
|
|
77
77
|
// --- Service ---
|
|
78
78
|
|
package/src/tools.ts
CHANGED
|
@@ -1,10 +1,32 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { resolveToken } from "./config.js";
|
|
1
|
+
import { type ClawnetConfig, parseConfig, resolveToken } from "./config.js";
|
|
3
2
|
|
|
4
3
|
// --- Helpers ---
|
|
5
4
|
|
|
6
|
-
function
|
|
7
|
-
|
|
5
|
+
function loadFreshConfig(api: any): ClawnetConfig {
|
|
6
|
+
const raw = api.runtime.config.loadConfig()?.plugins?.entries?.clawnet?.config ?? {};
|
|
7
|
+
return parseConfig(raw as Record<string, unknown>);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Extract ClawNet account ID from session key (e.g. "hook:clawnet:tom:inbox" -> "tom").
|
|
12
|
+
*/
|
|
13
|
+
function accountIdFromSessionKey(sessionKey?: string): string | null {
|
|
14
|
+
if (!sessionKey) return null;
|
|
15
|
+
const match = sessionKey.match(/^hook:clawnet:([^:]+):/);
|
|
16
|
+
return match ? match[1] : null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getAccountForAgent(cfg: ClawnetConfig, openclawAgentId?: string, sessionKey?: string) {
|
|
20
|
+
// Best match: extract account ID from session key (multi-account safe)
|
|
21
|
+
const sessionAccountId = accountIdFromSessionKey(sessionKey);
|
|
22
|
+
if (sessionAccountId) {
|
|
23
|
+
const match = cfg.accounts.find((a) => a.enabled && a.id === sessionAccountId);
|
|
24
|
+
if (match) {
|
|
25
|
+
const token = resolveToken(match.token);
|
|
26
|
+
if (token) return { ...match, resolvedToken: token };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Match by OpenClaw agent ID if provided (single-account or non-hook context)
|
|
8
30
|
if (openclawAgentId) {
|
|
9
31
|
const match = cfg.accounts.find((a) => a.enabled && a.openclawAgentId === openclawAgentId);
|
|
10
32
|
if (match) {
|
|
@@ -35,8 +57,9 @@ async function apiCall(
|
|
|
35
57
|
path: string,
|
|
36
58
|
body?: unknown,
|
|
37
59
|
openclawAgentId?: string,
|
|
60
|
+
sessionKey?: string,
|
|
38
61
|
): Promise<{ ok: boolean; status: number; data: any }> {
|
|
39
|
-
const account = getAccountForAgent(cfg, openclawAgentId);
|
|
62
|
+
const account = getAccountForAgent(cfg, openclawAgentId, sessionKey);
|
|
40
63
|
if (!account) {
|
|
41
64
|
return { ok: false, status: 0, data: { error: "no_account", message: "No ClawNet account configured. Run: openclaw clawnet setup" } };
|
|
42
65
|
}
|
|
@@ -55,8 +78,9 @@ async function apiCallRaw(
|
|
|
55
78
|
path: string,
|
|
56
79
|
rawBody: string,
|
|
57
80
|
openclawAgentId?: string,
|
|
81
|
+
sessionKey?: string,
|
|
58
82
|
): Promise<{ ok: boolean; status: number; data: any }> {
|
|
59
|
-
const account = getAccountForAgent(cfg, openclawAgentId);
|
|
83
|
+
const account = getAccountForAgent(cfg, openclawAgentId, sessionKey);
|
|
60
84
|
if (!account) {
|
|
61
85
|
return { ok: false, status: 0, data: { error: "no_account", message: "No ClawNet account configured. Run: openclaw clawnet setup" } };
|
|
62
86
|
}
|
|
@@ -299,8 +323,7 @@ function toolDesc(name: string, fallback: string): string {
|
|
|
299
323
|
|
|
300
324
|
// --- Tool registration ---
|
|
301
325
|
|
|
302
|
-
export function registerTools(api: any
|
|
303
|
-
// Load cached descriptions synchronously-safe (already loaded by service start)
|
|
326
|
+
export function registerTools(api: any) {
|
|
304
327
|
// --- Blessed tools (high-traffic, dedicated) ---
|
|
305
328
|
|
|
306
329
|
api.registerTool({
|
|
@@ -310,8 +333,9 @@ export function registerTools(api: any, cfg: ClawnetConfig) {
|
|
|
310
333
|
type: "object",
|
|
311
334
|
properties: {},
|
|
312
335
|
},
|
|
313
|
-
async execute(_id: string, _params: unknown, _onUpdate: unknown, ctx?: { agentId?: string }) {
|
|
314
|
-
const
|
|
336
|
+
async execute(_id: string, _params: unknown, _onUpdate: unknown, ctx?: { agentId?: string; sessionKey?: string }) {
|
|
337
|
+
const cfg = loadFreshConfig(api);
|
|
338
|
+
const result = await apiCall(cfg, "GET", "/inbox/check", undefined, ctx?.agentId, ctx?.sessionKey);
|
|
315
339
|
return textResult(result.data);
|
|
316
340
|
},
|
|
317
341
|
});
|
|
@@ -326,12 +350,13 @@ export function registerTools(api: any, cfg: ClawnetConfig) {
|
|
|
326
350
|
limit: { type: "number", description: "Max messages to return (default 50, max 200)" },
|
|
327
351
|
},
|
|
328
352
|
},
|
|
329
|
-
async execute(_id: string, params: { status?: string; limit?: number }, _onUpdate: unknown, ctx?: { agentId?: string }) {
|
|
353
|
+
async execute(_id: string, params: { status?: string; limit?: number }, _onUpdate: unknown, ctx?: { agentId?: string; sessionKey?: string }) {
|
|
354
|
+
const cfg = loadFreshConfig(api);
|
|
330
355
|
const qs = new URLSearchParams();
|
|
331
356
|
if (params.status) qs.set("status", params.status);
|
|
332
357
|
if (params.limit) qs.set("limit", String(params.limit));
|
|
333
358
|
const query = qs.toString() ? `?${qs}` : "";
|
|
334
|
-
const result = await apiCall(cfg, "GET", `/inbox${query}`, undefined, ctx?.agentId);
|
|
359
|
+
const result = await apiCall(cfg, "GET", `/inbox${query}`, undefined, ctx?.agentId, ctx?.sessionKey);
|
|
335
360
|
return textResult(result.data);
|
|
336
361
|
},
|
|
337
362
|
});
|
|
@@ -348,15 +373,16 @@ export function registerTools(api: any, cfg: ClawnetConfig) {
|
|
|
348
373
|
},
|
|
349
374
|
required: ["to", "message"],
|
|
350
375
|
},
|
|
351
|
-
async execute(_id: string, params: { to: string; message: string; subject?: string }, _onUpdate: unknown, ctx?: { agentId?: string }) {
|
|
376
|
+
async execute(_id: string, params: { to: string; message: string; subject?: string }, _onUpdate: unknown, ctx?: { agentId?: string; sessionKey?: string }) {
|
|
377
|
+
const cfg = loadFreshConfig(api);
|
|
352
378
|
if (params.to.includes("@")) {
|
|
353
379
|
// Route to email endpoint
|
|
354
380
|
const body: Record<string, string> = { to: params.to, body: params.message };
|
|
355
381
|
if (params.subject) body.subject = params.subject;
|
|
356
|
-
const result = await apiCall(cfg, "POST", "/email/send", body, ctx?.agentId);
|
|
382
|
+
const result = await apiCall(cfg, "POST", "/email/send", body, ctx?.agentId, ctx?.sessionKey);
|
|
357
383
|
return textResult(result.data);
|
|
358
384
|
}
|
|
359
|
-
const result = await apiCall(cfg, "POST", "/send", { to: params.to, message: params.message }, ctx?.agentId);
|
|
385
|
+
const result = await apiCall(cfg, "POST", "/send", { to: params.to, message: params.message }, ctx?.agentId, ctx?.sessionKey);
|
|
360
386
|
return textResult(result.data);
|
|
361
387
|
},
|
|
362
388
|
}, { optional: true });
|
|
@@ -373,10 +399,11 @@ export function registerTools(api: any, cfg: ClawnetConfig) {
|
|
|
373
399
|
},
|
|
374
400
|
required: ["message_id", "status"],
|
|
375
401
|
},
|
|
376
|
-
async execute(_id: string, params: { message_id: string; status: string; snoozed_until?: string }, _onUpdate: unknown, ctx?: { agentId?: string }) {
|
|
402
|
+
async execute(_id: string, params: { message_id: string; status: string; snoozed_until?: string }, _onUpdate: unknown, ctx?: { agentId?: string; sessionKey?: string }) {
|
|
403
|
+
const cfg = loadFreshConfig(api);
|
|
377
404
|
const body: Record<string, unknown> = { status: params.status };
|
|
378
405
|
if (params.snoozed_until) body.snoozed_until = params.snoozed_until;
|
|
379
|
-
const result = await apiCall(cfg, "PATCH", `/messages/${params.message_id}/status`, body, ctx?.agentId);
|
|
406
|
+
const result = await apiCall(cfg, "PATCH", `/messages/${params.message_id}/status`, body, ctx?.agentId, ctx?.sessionKey);
|
|
380
407
|
return textResult(result.data);
|
|
381
408
|
},
|
|
382
409
|
}, { optional: true });
|
|
@@ -392,12 +419,13 @@ export function registerTools(api: any, cfg: ClawnetConfig) {
|
|
|
392
419
|
scope: { type: "string", description: "'global' for network-wide rules, 'agent' for agent-specific rules, omit for both" },
|
|
393
420
|
},
|
|
394
421
|
},
|
|
395
|
-
async execute(_id: string, params: { scope?: string }, _onUpdate: unknown, ctx?: { agentId?: string }) {
|
|
422
|
+
async execute(_id: string, params: { scope?: string }, _onUpdate: unknown, ctx?: { agentId?: string; sessionKey?: string }) {
|
|
423
|
+
const cfg = loadFreshConfig(api);
|
|
396
424
|
const qs = new URLSearchParams();
|
|
397
425
|
if (params.scope) qs.set("scope", params.scope);
|
|
398
426
|
if (ctx?.agentId) qs.set("agent_id", ctx.agentId);
|
|
399
427
|
const query = qs.toString() ? `?${qs}` : "";
|
|
400
|
-
const result = await apiCall(cfg, "GET", `/rules${query}`, undefined, ctx?.agentId);
|
|
428
|
+
const result = await apiCall(cfg, "GET", `/rules${query}`, undefined, ctx?.agentId, ctx?.sessionKey);
|
|
401
429
|
return textResult(result.data);
|
|
402
430
|
},
|
|
403
431
|
});
|
|
@@ -413,7 +441,7 @@ export function registerTools(api: any, cfg: ClawnetConfig) {
|
|
|
413
441
|
filter: { type: "string", description: "Filter by prefix (e.g. 'email', 'calendar', 'post', 'profile')" },
|
|
414
442
|
},
|
|
415
443
|
},
|
|
416
|
-
async execute(_id: string, params: { filter?: string }, _onUpdate: unknown, _ctx?: { agentId?: string }) {
|
|
444
|
+
async execute(_id: string, params: { filter?: string }, _onUpdate: unknown, _ctx?: { agentId?: string; sessionKey?: string }) {
|
|
417
445
|
let ops = getOperations();
|
|
418
446
|
if (params.filter) {
|
|
419
447
|
const prefix = params.filter.toLowerCase();
|
|
@@ -449,7 +477,8 @@ export function registerTools(api: any, cfg: ClawnetConfig) {
|
|
|
449
477
|
},
|
|
450
478
|
required: ["operation"],
|
|
451
479
|
},
|
|
452
|
-
async execute(_id: string, input: { operation: string; params?: Record<string, unknown> }, _onUpdate: unknown, ctx?: { agentId?: string }) {
|
|
480
|
+
async execute(_id: string, input: { operation: string; params?: Record<string, unknown> }, _onUpdate: unknown, ctx?: { agentId?: string; sessionKey?: string }) {
|
|
481
|
+
const cfg = loadFreshConfig(api);
|
|
453
482
|
const op = getOperations().find((o) => o.operation === input.operation);
|
|
454
483
|
if (!op) {
|
|
455
484
|
return textResult({ error: "unknown_operation", message: `Unknown operation: ${input.operation}. Call clawnet_capabilities to see available operations.` });
|
|
@@ -507,8 +536,8 @@ export function registerTools(api: any, cfg: ClawnetConfig) {
|
|
|
507
536
|
}
|
|
508
537
|
|
|
509
538
|
const result = rawBody !== undefined
|
|
510
|
-
? await apiCallRaw(cfg, op.method, path, rawBody, ctx?.agentId)
|
|
511
|
-
: await apiCall(cfg, op.method, path, body, ctx?.agentId);
|
|
539
|
+
? await apiCallRaw(cfg, op.method, path, rawBody, ctx?.agentId, ctx?.sessionKey)
|
|
540
|
+
: await apiCall(cfg, op.method, path, body, ctx?.agentId, ctx?.sessionKey);
|
|
512
541
|
return textResult(result.data);
|
|
513
542
|
},
|
|
514
543
|
}, { optional: true });
|