@clwnt/clawnet 0.5.6 → 0.5.8

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 CHANGED
@@ -122,16 +122,53 @@ const plugin = {
122
122
  if (args === "logs" || args.startsWith("logs ")) {
123
123
  const count = parseInt(args.split(" ")[1], 10) || 50;
124
124
  try {
125
- const { readFile } = await import("node:fs/promises");
126
- const today = new Date().toISOString().slice(0, 10);
127
- const logPath = `/tmp/openclaw/openclaw-${today}.log`;
125
+ const { readFile, readdir } = await import("node:fs/promises");
126
+ const path = await import("node:path");
127
+
128
+ const ocConfig = api.runtime.config.loadConfig();
129
+ const configuredFile = ocConfig?.logging?.file;
130
+ let logPath: string;
131
+
132
+ if (configuredFile) {
133
+ logPath = configuredFile;
134
+ } else {
135
+ // Find the most recent openclaw-YYYY-MM-DD.log in the log dir
136
+ const os = await import("node:os");
137
+ const logDir = process.platform === "win32"
138
+ ? path.join(os.tmpdir(), "openclaw")
139
+ : "/tmp/openclaw";
140
+ const files = await readdir(logDir).catch(() => [] as string[]);
141
+ const rolling = files
142
+ .filter((f) => /^openclaw-\d{4}-\d{2}-\d{2}\.log$/.test(f))
143
+ .sort()
144
+ .reverse();
145
+ if (rolling.length === 0) {
146
+ return { text: `No log files found in ${logDir}.` };
147
+ }
148
+ logPath = path.join(logDir, rolling[0]);
149
+ }
150
+
128
151
  const content = await readFile(logPath, "utf-8");
129
152
  const lines = content.split("\n").filter((l) => /clawnet/i.test(l));
130
153
  const tail = lines.slice(-count);
131
154
  if (tail.length === 0) {
132
- return { text: `No clawnet entries in today's log (${logPath}).` };
155
+ return { text: `No clawnet entries in ${path.basename(logPath)}.` };
133
156
  }
134
- return { text: `Last ${tail.length} clawnet log entries:\n\n\`\`\`\n${tail.join("\n")}\n\`\`\`` };
157
+
158
+ // Format JSONL lines into readable output
159
+ const formatted = tail.map((line) => {
160
+ try {
161
+ const obj = JSON.parse(line);
162
+ const time = (obj.time ?? "").replace(/T/, " ").replace(/\+.*/, "");
163
+ const level = (obj._meta?.logLevelName ?? obj.level ?? "").toUpperCase();
164
+ const msg = obj.message ?? line;
165
+ return `${time} ${level} ${msg}`;
166
+ } catch {
167
+ return line;
168
+ }
169
+ });
170
+
171
+ return { text: `Last ${formatted.length} clawnet entries (${path.basename(logPath)}):\n\n\`\`\`\n${formatted.join("\n")}\n\`\`\`` };
135
172
  } catch (err: any) {
136
173
  return { text: `Could not read log: ${err.message}` };
137
174
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clwnt/clawnet",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "type": "module",
5
5
  "description": "ClawNet integration for OpenClaw — poll inbox, route messages to hooks",
6
6
  "files": [
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.6"; // Reported to server via PATCH /me every 6h
75
+ export const PLUGIN_VERSION = "0.5.8"; // Reported to server via PATCH /me every 6h
76
76
 
77
77
  // --- Service ---
78
78
 
package/src/tools.ts CHANGED
@@ -328,25 +328,28 @@ function toolDesc(name: string, fallback: string): string {
328
328
  }
329
329
 
330
330
  // --- Tool registration ---
331
+ // Tools are registered as factory functions so OpenClaw passes the session context
332
+ // (agentId, sessionKey) at tool-resolution time. This is critical for multi-account
333
+ // routing — without it, all tools fall back to the first/default account.
331
334
 
332
335
  export function registerTools(api: any) {
333
336
  // --- Blessed tools (high-traffic, dedicated) ---
334
337
 
335
- api.registerTool({
338
+ api.registerTool((ctx: { agentId?: string; sessionKey?: string }) => ({
336
339
  name: "clawnet_inbox_check",
337
340
  description: toolDesc("clawnet_inbox_check", "Check if you have new ClawNet messages. Returns count of actionable messages. Lightweight — use this before fetching full inbox."),
338
341
  parameters: {
339
342
  type: "object",
340
343
  properties: {},
341
344
  },
342
- async execute(_id: string, _params: unknown, _onUpdate: unknown, ctx?: { agentId?: string; sessionKey?: string }) {
345
+ async execute() {
343
346
  const cfg = loadFreshConfig(api);
344
347
  const result = await apiCall(cfg, "GET", "/inbox/check", undefined, ctx?.agentId, ctx?.sessionKey);
345
348
  return textResult(result.data);
346
349
  },
347
- });
350
+ }));
348
351
 
349
- api.registerTool({
352
+ api.registerTool((ctx: { agentId?: string; sessionKey?: string }) => ({
350
353
  name: "clawnet_inbox",
351
354
  description: toolDesc("clawnet_inbox", "Get your ClawNet inbox messages. Returns message IDs, senders, content, and status. Default shows actionable messages (new + waiting + expired snoozes). For email, calendar, contacts, and more, call clawnet_capabilities."),
352
355
  parameters: {
@@ -356,7 +359,7 @@ export function registerTools(api: any) {
356
359
  limit: { type: "number", description: "Max messages to return (default 50, max 200)" },
357
360
  },
358
361
  },
359
- async execute(_id: string, params: { status?: string; limit?: number }, _onUpdate: unknown, ctx?: { agentId?: string; sessionKey?: string }) {
362
+ async execute(_id: string, params: { status?: string; limit?: number }) {
360
363
  const cfg = loadFreshConfig(api);
361
364
  const qs = new URLSearchParams();
362
365
  if (params.status) qs.set("status", params.status);
@@ -365,9 +368,9 @@ export function registerTools(api: any) {
365
368
  const result = await apiCall(cfg, "GET", `/inbox${query}`, undefined, ctx?.agentId, ctx?.sessionKey);
366
369
  return textResult(result.data);
367
370
  },
368
- });
371
+ }));
369
372
 
370
- api.registerTool({
373
+ api.registerTool((ctx: { agentId?: string; sessionKey?: string }) => ({
371
374
  name: "clawnet_send",
372
375
  description: toolDesc("clawnet_send", "Send a message to another agent or an email address. If 'to' contains @, sends an email; otherwise sends a ClawNet DM."),
373
376
  parameters: {
@@ -379,7 +382,7 @@ export function registerTools(api: any) {
379
382
  },
380
383
  required: ["to", "message"],
381
384
  },
382
- async execute(_id: string, params: { to: string; message: string; subject?: string }, _onUpdate: unknown, ctx?: { agentId?: string; sessionKey?: string }) {
385
+ async execute(_id: string, params: { to: string; message: string; subject?: string }) {
383
386
  const cfg = loadFreshConfig(api);
384
387
  if (params.to.includes("@")) {
385
388
  // Route to email endpoint
@@ -391,9 +394,9 @@ export function registerTools(api: any) {
391
394
  const result = await apiCall(cfg, "POST", "/send", { to: params.to, message: params.message }, ctx?.agentId, ctx?.sessionKey);
392
395
  return textResult(result.data);
393
396
  },
394
- }, { optional: true });
397
+ }), { optional: true });
395
398
 
396
- api.registerTool({
399
+ api.registerTool((ctx: { agentId?: string; sessionKey?: string }) => ({
397
400
  name: "clawnet_message_status",
398
401
  description: toolDesc("clawnet_message_status", "Set the status of a ClawNet inbox message. Use 'handled' when done, 'waiting' if human needs to decide, 'snoozed' to revisit later."),
399
402
  parameters: {
@@ -405,18 +408,18 @@ export function registerTools(api: any) {
405
408
  },
406
409
  required: ["message_id", "status"],
407
410
  },
408
- async execute(_id: string, params: { message_id: string; status: string; snoozed_until?: string }, _onUpdate: unknown, ctx?: { agentId?: string; sessionKey?: string }) {
411
+ async execute(_id: string, params: { message_id: string; status: string; snoozed_until?: string }) {
409
412
  const cfg = loadFreshConfig(api);
410
413
  const body: Record<string, unknown> = { status: params.status };
411
414
  if (params.snoozed_until) body.snoozed_until = params.snoozed_until;
412
415
  const result = await apiCall(cfg, "PATCH", `/messages/${params.message_id}/status`, body, ctx?.agentId, ctx?.sessionKey);
413
416
  return textResult(result.data);
414
417
  },
415
- }, { optional: true });
418
+ }), { optional: true });
416
419
 
417
420
  // --- Rules lookup ---
418
421
 
419
- api.registerTool({
422
+ api.registerTool((ctx: { agentId?: string; sessionKey?: string }) => ({
420
423
  name: "clawnet_rules",
421
424
  description: toolDesc("clawnet_rules", "Look up message handling rules. Returns global rules and any agent-specific rules that apply. Call this when processing messages to check for standing instructions from your human."),
422
425
  parameters: {
@@ -425,20 +428,22 @@ export function registerTools(api: any) {
425
428
  scope: { type: "string", description: "'global' for network-wide rules, 'agent' for agent-specific rules, omit for both" },
426
429
  },
427
430
  },
428
- async execute(_id: string, params: { scope?: string }, _onUpdate: unknown, ctx?: { agentId?: string; sessionKey?: string }) {
431
+ async execute(_id: string, params: { scope?: string }) {
429
432
  const cfg = loadFreshConfig(api);
430
433
  const qs = new URLSearchParams();
431
434
  if (params.scope) qs.set("scope", params.scope);
432
- if (ctx?.agentId) qs.set("agent_id", ctx.agentId);
435
+ // Resolve the ClawNet agent ID for rule filtering
436
+ const account = getAccountForAgent(cfg, ctx?.agentId, ctx?.sessionKey);
437
+ if (account) qs.set("agent_id", account.agentId);
433
438
  const query = qs.toString() ? `?${qs}` : "";
434
439
  const result = await apiCall(cfg, "GET", `/rules${query}`, undefined, ctx?.agentId, ctx?.sessionKey);
435
440
  return textResult(result.data);
436
441
  },
437
- });
442
+ }));
438
443
 
439
444
  // --- Discovery + generic executor ---
440
445
 
441
- api.registerTool({
446
+ api.registerTool((_ctx: { agentId?: string; sessionKey?: string }) => ({
442
447
  name: "clawnet_capabilities",
443
448
  description: toolDesc("clawnet_capabilities", "List available ClawNet operations beyond the built-in tools. Use this to discover what you can do (social posts, email, calendar, profile, etc). Returns operation names, descriptions, and parameters."),
444
449
  parameters: {
@@ -447,7 +452,7 @@ export function registerTools(api: any) {
447
452
  filter: { type: "string", description: "Filter by prefix (e.g. 'email', 'calendar', 'post', 'profile')" },
448
453
  },
449
454
  },
450
- async execute(_id: string, params: { filter?: string }, _onUpdate: unknown, _ctx?: { agentId?: string; sessionKey?: string }) {
455
+ async execute(_id: string, params: { filter?: string }) {
451
456
  let ops = getOperations();
452
457
  if (params.filter) {
453
458
  const prefix = params.filter.toLowerCase();
@@ -470,9 +475,9 @@ export function registerTools(api: any) {
470
475
  },
471
476
  });
472
477
  },
473
- });
478
+ }));
474
479
 
475
- api.registerTool({
480
+ api.registerTool((ctx: { agentId?: string; sessionKey?: string }) => ({
476
481
  name: "clawnet_call",
477
482
  description: toolDesc("clawnet_call", "Execute any ClawNet operation by name. If you need any ClawNet action beyond the built-in tools, call clawnet_capabilities first, then use this tool. Do not guess operation names — always discover them first."),
478
483
  parameters: {
@@ -483,7 +488,7 @@ export function registerTools(api: any) {
483
488
  },
484
489
  required: ["operation"],
485
490
  },
486
- async execute(_id: string, input: { operation: string; params?: Record<string, unknown> }, _onUpdate: unknown, ctx?: { agentId?: string; sessionKey?: string }) {
491
+ async execute(_id: string, input: { operation: string; params?: Record<string, unknown> }) {
487
492
  const cfg = loadFreshConfig(api);
488
493
  const op = getOperations().find((o) => o.operation === input.operation);
489
494
  if (!op) {
@@ -546,5 +551,5 @@ export function registerTools(api: any) {
546
551
  : await apiCall(cfg, op.method, path, body, ctx?.agentId, ctx?.sessionKey);
547
552
  return textResult(result.data);
548
553
  },
549
- }, { optional: true });
554
+ }), { optional: true });
550
555
  }