ofiere-openclaw-plugin 1.0.1 → 1.1.0
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 +17 -16
- package/package.json +1 -1
- package/src/tools.ts +113 -23
package/index.ts
CHANGED
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
// index.ts — Ofiere PM Plugin for OpenClaw
|
|
2
|
-
//
|
|
3
|
-
// Pattern: https://docs.openclaw.ai/plugins/building-plugins#quick-start-tool-plugin
|
|
2
|
+
// Matches Composio plugin pattern: plain object export + api.on()
|
|
4
3
|
|
|
5
|
-
import {
|
|
4
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
6
5
|
import { parseOfiereConfig } from "./src/config.js";
|
|
7
6
|
import { getSupabase } from "./src/supabase.js";
|
|
8
|
-
import { registerTools } from "./src/tools.js";
|
|
7
|
+
import { registerTools, probeApiForAgentName } from "./src/tools.js";
|
|
9
8
|
import { getSystemPrompt } from "./src/prompt.js";
|
|
10
9
|
import { registerCli } from "./src/cli.js";
|
|
11
10
|
import { seedAgentCache } from "./src/agent-resolver.js";
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
const ofierePlugin = {
|
|
14
13
|
id: "ofiere",
|
|
15
14
|
name: "Ofiere PM",
|
|
16
15
|
description:
|
|
17
16
|
"Manage Ofiere PM tasks, agents, and projects directly from the agent. " +
|
|
18
17
|
"Create tasks, update progress, assign agents — all synced to the dashboard in real time.",
|
|
19
18
|
|
|
20
|
-
register(api) {
|
|
19
|
+
register(api: OpenClawPluginApi) {
|
|
21
20
|
const config = parseOfiereConfig(api.pluginConfig);
|
|
22
21
|
|
|
23
22
|
// Always register CLI (even if disabled — so user can run `openclaw ofiere setup`)
|
|
24
23
|
registerCli(api);
|
|
25
24
|
|
|
26
25
|
if (!config.enabled) {
|
|
27
|
-
api.logger.debug("[ofiere] Plugin disabled via config");
|
|
26
|
+
api.logger.debug?.("[ofiere] Plugin disabled via config");
|
|
28
27
|
return;
|
|
29
28
|
}
|
|
30
29
|
|
|
@@ -44,7 +43,6 @@ export default definePluginEntry({
|
|
|
44
43
|
|
|
45
44
|
// ── Pre-seed agent cache if OFIERE_AGENT_ID is set (legacy mode) ──────
|
|
46
45
|
if (config.agentId) {
|
|
47
|
-
// Try to extract the calling agent's name from OpenClaw context
|
|
48
46
|
const callerName =
|
|
49
47
|
(api as any)?.agentContext?.accountId ||
|
|
50
48
|
(api as any)?.agentContext?.name ||
|
|
@@ -64,17 +62,18 @@ export default definePluginEntry({
|
|
|
64
62
|
};
|
|
65
63
|
|
|
66
64
|
// ── Hook: inject Ofiere context into every agent prompt ────────────────
|
|
67
|
-
//
|
|
68
|
-
api.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
prependSystemContext: getSystemPrompt(promptState),
|
|
72
|
-
}),
|
|
73
|
-
);
|
|
65
|
+
// Using api.on() — same pattern as Composio
|
|
66
|
+
api.on("before_prompt_build", () => ({
|
|
67
|
+
prependSystemContext: getSystemPrompt(promptState),
|
|
68
|
+
}));
|
|
74
69
|
|
|
75
70
|
// ── Connect to Supabase and register tools ────────────────────────────
|
|
76
71
|
try {
|
|
77
72
|
const supabase = getSupabase(config.supabaseUrl, config.serviceRoleKey);
|
|
73
|
+
|
|
74
|
+
// Probe the api object for any agent identity info (for debugging + fallback)
|
|
75
|
+
probeApiForAgentName(api, api.logger);
|
|
76
|
+
|
|
78
77
|
registerTools(api, supabase, config);
|
|
79
78
|
promptState.toolCount = 5;
|
|
80
79
|
promptState.ready = true;
|
|
@@ -89,4 +88,6 @@ export default definePluginEntry({
|
|
|
89
88
|
api.logger.error(`[ofiere] Failed to initialize: ${msg}`);
|
|
90
89
|
}
|
|
91
90
|
},
|
|
92
|
-
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default ofierePlugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofiere-openclaw-plugin",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenClaw plugin for Ofiere PM — manage tasks, agents, and projects from your agent",
|
|
6
6
|
"keywords": ["openclaw", "ofiere", "project-management", "agents", "plugin"],
|
package/src/tools.ts
CHANGED
|
@@ -30,19 +30,74 @@ function err(message: string): ToolResult {
|
|
|
30
30
|
|
|
31
31
|
// ─── Helper: extract calling agent's accountId from OpenClaw context ─────────
|
|
32
32
|
|
|
33
|
+
// Module-level: set once at registration time from index.ts
|
|
34
|
+
let _registrationAgentName = "";
|
|
35
|
+
export function setRegistrationAgentName(name: string) {
|
|
36
|
+
if (name && !_registrationAgentName) _registrationAgentName = name;
|
|
37
|
+
}
|
|
38
|
+
|
|
33
39
|
function getCallingAgentName(api: any): string {
|
|
34
|
-
// OpenClaw passes agent context in various ways — try
|
|
40
|
+
// OpenClaw passes agent context in various ways — try ALL known paths
|
|
41
|
+
try {
|
|
42
|
+
const candidates = [
|
|
43
|
+
api?.agentContext?.accountId,
|
|
44
|
+
api?.agentContext?.name,
|
|
45
|
+
api?.agentContext?.id,
|
|
46
|
+
api?.currentAgent?.accountId,
|
|
47
|
+
api?.currentAgent?.name,
|
|
48
|
+
api?.currentAgent?.id,
|
|
49
|
+
api?.agent?.accountId,
|
|
50
|
+
api?.agent?.name,
|
|
51
|
+
api?.agent?.id,
|
|
52
|
+
api?.agentId,
|
|
53
|
+
api?.agentName,
|
|
54
|
+
api?.accountId,
|
|
55
|
+
api?.name,
|
|
56
|
+
api?.id,
|
|
57
|
+
api?.metadata?.agentId,
|
|
58
|
+
api?.metadata?.accountId,
|
|
59
|
+
api?.metadata?.agentName,
|
|
60
|
+
api?.context?.agentId,
|
|
61
|
+
api?.context?.accountId,
|
|
62
|
+
api?.context?.agent?.name,
|
|
63
|
+
];
|
|
64
|
+
for (const c of candidates) {
|
|
65
|
+
if (typeof c === "string" && c.trim()) return c.trim();
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
// ignore
|
|
69
|
+
}
|
|
70
|
+
return "";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Probe the API object for any agent identity info. Called once at registration.
|
|
75
|
+
* Logs ALL available keys for debugging.
|
|
76
|
+
*/
|
|
77
|
+
export function probeApiForAgentName(api: any, logger?: any): string {
|
|
78
|
+
// Direct detection
|
|
79
|
+
const name = getCallingAgentName(api);
|
|
80
|
+
if (name) {
|
|
81
|
+
logger?.info?.(`[ofiere] Detected agent from API: "${name}"`);
|
|
82
|
+
setRegistrationAgentName(name);
|
|
83
|
+
return name;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Log all top-level keys for future debugging
|
|
35
87
|
try {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
api
|
|
41
|
-
""
|
|
42
|
-
|
|
88
|
+
const keys = Object.keys(api || {});
|
|
89
|
+
logger?.debug?.(`[ofiere] API object keys: ${JSON.stringify(keys)}`);
|
|
90
|
+
// Check if any key looks like it contains agent info
|
|
91
|
+
for (const key of keys) {
|
|
92
|
+
const val = api[key];
|
|
93
|
+
if (typeof val === "string" && val.length > 0 && val.length < 50) {
|
|
94
|
+
logger?.debug?.(`[ofiere] API.${key} = "${val}"`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
43
97
|
} catch {
|
|
44
|
-
|
|
98
|
+
// ignore
|
|
45
99
|
}
|
|
100
|
+
return "";
|
|
46
101
|
}
|
|
47
102
|
|
|
48
103
|
// ─── Tool Registration ───────────────────────────────────────────────────────
|
|
@@ -57,11 +112,23 @@ export function registerTools(
|
|
|
57
112
|
|
|
58
113
|
/**
|
|
59
114
|
* Resolve the agent ID for the calling agent.
|
|
60
|
-
* Priority: explicit param > runtime context > env var fallback
|
|
115
|
+
* Priority: explicit param > runtime context > registration-time detection > env var > DB fallback
|
|
61
116
|
*/
|
|
62
117
|
async function resolveAgent(explicitId?: string): Promise<string | null> {
|
|
63
|
-
// 1. Explicit agent_id passed by the LLM (e.g. "
|
|
64
|
-
if (explicitId && explicitId.trim())
|
|
118
|
+
// 1. Explicit agent_id passed by the LLM (e.g. "ivy", "daisy", or a UUID)
|
|
119
|
+
if (explicitId && explicitId.trim()) {
|
|
120
|
+
const trimmed = explicitId.trim();
|
|
121
|
+
// If it looks like a UUID or our ID format, use directly
|
|
122
|
+
if (trimmed.match(/^[0-9a-f]{8}-/) || trimmed.match(/^agent-/)) {
|
|
123
|
+
return trimmed;
|
|
124
|
+
}
|
|
125
|
+
// Otherwise treat as a name and resolve to the actual agent ID
|
|
126
|
+
try {
|
|
127
|
+
return await resolveAgentId(trimmed, userId, supabase);
|
|
128
|
+
} catch {
|
|
129
|
+
return trimmed; // fallback: use as-is
|
|
130
|
+
}
|
|
131
|
+
}
|
|
65
132
|
|
|
66
133
|
// 2. Runtime: read calling agent's name from OpenClaw context
|
|
67
134
|
const callerName = getCallingAgentName(api);
|
|
@@ -69,12 +136,37 @@ export function registerTools(
|
|
|
69
136
|
try {
|
|
70
137
|
return await resolveAgentId(callerName, userId, supabase);
|
|
71
138
|
} catch {
|
|
72
|
-
// Fall through
|
|
139
|
+
// Fall through
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 3. Registration-time detection (set when plugin was loaded)
|
|
144
|
+
if (_registrationAgentName) {
|
|
145
|
+
try {
|
|
146
|
+
return await resolveAgentId(_registrationAgentName, userId, supabase);
|
|
147
|
+
} catch {
|
|
148
|
+
// Fall through
|
|
73
149
|
}
|
|
74
150
|
}
|
|
75
151
|
|
|
76
|
-
//
|
|
77
|
-
|
|
152
|
+
// 4. Env var fallback (OFIERE_AGENT_ID — legacy single-agent mode)
|
|
153
|
+
if (fallbackAgentId) return fallbackAgentId;
|
|
154
|
+
|
|
155
|
+
// 5. Nuclear fallback: query the FIRST agent for this user
|
|
156
|
+
try {
|
|
157
|
+
const { data } = await supabase
|
|
158
|
+
.from("agents")
|
|
159
|
+
.select("id")
|
|
160
|
+
.eq("user_id", userId)
|
|
161
|
+
.order("name", { ascending: true })
|
|
162
|
+
.limit(1)
|
|
163
|
+
.single();
|
|
164
|
+
if (data?.id) return data.id;
|
|
165
|
+
} catch {
|
|
166
|
+
// ignore
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return null;
|
|
78
170
|
}
|
|
79
171
|
|
|
80
172
|
// ── OFIERE_LIST_TASKS — Required (read-only, no side effects) ────────
|
|
@@ -134,21 +226,22 @@ export function registerTools(
|
|
|
134
226
|
label: "Create Ofiere Task",
|
|
135
227
|
description:
|
|
136
228
|
"Create a new task in the Ofiere PM dashboard. " +
|
|
137
|
-
"
|
|
229
|
+
"IMPORTANT: You MUST always pass your own name as agent_id (e.g. 'ivy', 'daisy') to assign the task to yourself. " +
|
|
230
|
+
"If you want to assign to a different agent, pass their name instead. " +
|
|
138
231
|
"Pass agent_id as 'none' or 'unassigned' to create an unassigned task. " +
|
|
139
232
|
"The task will appear in the dashboard immediately via real-time sync.",
|
|
140
233
|
parameters: {
|
|
141
234
|
type: "object",
|
|
142
|
-
required: ["title"],
|
|
235
|
+
required: ["title", "agent_id"],
|
|
143
236
|
properties: {
|
|
144
237
|
title: { type: "string", description: "Task title (required)" },
|
|
145
238
|
description: { type: "string", description: "Task description" },
|
|
146
239
|
agent_id: {
|
|
147
240
|
type: "string",
|
|
148
241
|
description:
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
"
|
|
242
|
+
"REQUIRED. Your own agent name (e.g. 'ivy', 'daisy', 'celia') to self-assign, " +
|
|
243
|
+
"or another agent's name to assign to them. " +
|
|
244
|
+
"Pass 'none' or 'unassigned' to create a task with no assignee.",
|
|
152
245
|
},
|
|
153
246
|
status: {
|
|
154
247
|
type: "string",
|
|
@@ -232,7 +325,6 @@ export function registerTools(
|
|
|
232
325
|
}
|
|
233
326
|
},
|
|
234
327
|
},
|
|
235
|
-
{ optional: true },
|
|
236
328
|
);
|
|
237
329
|
|
|
238
330
|
// ── OFIERE_UPDATE_TASK — Optional (has side effects) ─────────────────
|
|
@@ -297,7 +389,6 @@ export function registerTools(
|
|
|
297
389
|
}
|
|
298
390
|
},
|
|
299
391
|
},
|
|
300
|
-
{ optional: true },
|
|
301
392
|
);
|
|
302
393
|
|
|
303
394
|
// ── OFIERE_DELETE_TASK — Optional (destructive side effect) ──────────
|
|
@@ -352,7 +443,6 @@ export function registerTools(
|
|
|
352
443
|
}
|
|
353
444
|
},
|
|
354
445
|
},
|
|
355
|
-
{ optional: true },
|
|
356
446
|
);
|
|
357
447
|
|
|
358
448
|
// ── OFIERE_LIST_AGENTS — Required (read-only, no side effects) ───────
|