ofiere-openclaw-plugin 4.55.0 → 4.56.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.
- package/README.md +104 -104
- package/dist/src/gateOps.js +169 -0
- package/dist/src/planExecute.js +145 -0
- package/dist/src/prompt.js +130 -130
- package/dist/src/tools.js +32 -60
- package/index.ts +105 -105
- package/openclaw.plugin.json +2 -1
- package/package.json +2 -2
- package/src/agent-resolver.ts +90 -90
- package/src/agent-tier.ts +192 -192
- package/src/attach-token.ts +106 -106
- package/src/attachments.ts +601 -601
- package/src/cli.ts +247 -247
- package/src/config.ts +78 -78
- package/src/gateOps.ts +177 -0
- package/src/planExecute.ts +197 -0
- package/src/prompt.ts +267 -267
- package/src/sop-render.ts +216 -216
- package/src/supabase.ts +13 -13
- package/src/tools.ts +30 -59
- package/src/types/openclaw.d.ts +8 -8
- package/src/types.ts +10 -10
package/src/cli.ts
CHANGED
|
@@ -1,247 +1,247 @@
|
|
|
1
|
-
// src/cli.ts — CLI registration for Ofiere PM plugin
|
|
2
|
-
// Uses api.registerCli with descriptors as documented:
|
|
3
|
-
// https://docs.openclaw.ai/plugins/sdk-overview#cli-registration-metadata
|
|
4
|
-
//
|
|
5
|
-
// The descriptors array enables lazy plugin CLI registration and root help text.
|
|
6
|
-
// The async ({ program }) => {} pattern is shown in the official Matrix plugin example.
|
|
7
|
-
|
|
8
|
-
import * as fs from "node:fs";
|
|
9
|
-
import * as os from "node:os";
|
|
10
|
-
import * as path from "node:path";
|
|
11
|
-
import * as readline from "node:readline";
|
|
12
|
-
import { createClient } from "@supabase/supabase-js";
|
|
13
|
-
|
|
14
|
-
// ── Config paths ─────────────────────────────────────────────────────────────
|
|
15
|
-
|
|
16
|
-
/** Auto-detect OpenClaw home for env file */
|
|
17
|
-
function getOpenClawHome(): string {
|
|
18
|
-
if (fs.existsSync("/data/.openclaw")) return "/data/.openclaw";
|
|
19
|
-
return path.join(os.homedir(), ".openclaw");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function getEnvFile(): string {
|
|
23
|
-
return path.join(getOpenClawHome(), ".env");
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** Read env vars from the .env file */
|
|
27
|
-
function readEnvFile(): Record<string, string> {
|
|
28
|
-
const envPath = getEnvFile();
|
|
29
|
-
const result: Record<string, string> = {};
|
|
30
|
-
try {
|
|
31
|
-
const content = fs.readFileSync(envPath, "utf-8");
|
|
32
|
-
for (const line of content.split("\n")) {
|
|
33
|
-
const match = line.match(/^([^#=]+)=(.*)$/);
|
|
34
|
-
if (match) {
|
|
35
|
-
result[match[1].trim()] = match[2].trim();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
} catch { /* file doesn't exist yet */ }
|
|
39
|
-
return result;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Append or update env vars in the .env file (idempotent) */
|
|
43
|
-
function setEnvVars(vars: Record<string, string>): void {
|
|
44
|
-
const envPath = getEnvFile();
|
|
45
|
-
const dir = path.dirname(envPath);
|
|
46
|
-
if (!fs.existsSync(dir)) {
|
|
47
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
let content = "";
|
|
51
|
-
try {
|
|
52
|
-
content = fs.readFileSync(envPath, "utf-8");
|
|
53
|
-
} catch { /* file doesn't exist yet */ }
|
|
54
|
-
|
|
55
|
-
for (const [key, value] of Object.entries(vars)) {
|
|
56
|
-
const regex = new RegExp(`^${key}=.*$`, "m");
|
|
57
|
-
if (regex.test(content)) {
|
|
58
|
-
content = content.replace(regex, `${key}=${value}`);
|
|
59
|
-
} else {
|
|
60
|
-
content = content.trimEnd() + `\n${key}=${value}`;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
fs.writeFileSync(envPath, content.trim() + "\n");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function getPluginConfig(): {
|
|
68
|
-
supabaseUrl?: string;
|
|
69
|
-
serviceRoleKey?: string;
|
|
70
|
-
userId?: string;
|
|
71
|
-
agentId?: string;
|
|
72
|
-
enabled?: boolean;
|
|
73
|
-
} {
|
|
74
|
-
const env = readEnvFile();
|
|
75
|
-
return {
|
|
76
|
-
supabaseUrl: process.env.OFIERE_SUPABASE_URL || env.OFIERE_SUPABASE_URL || undefined,
|
|
77
|
-
serviceRoleKey: process.env.OFIERE_SERVICE_ROLE_KEY || env.OFIERE_SERVICE_ROLE_KEY || undefined,
|
|
78
|
-
userId: process.env.OFIERE_USER_ID || env.OFIERE_USER_ID || undefined,
|
|
79
|
-
agentId: process.env.OFIERE_AGENT_ID || env.OFIERE_AGENT_ID || undefined,
|
|
80
|
-
enabled: true,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function ask(rl: readline.Interface, question: string): Promise<string> {
|
|
85
|
-
return new Promise((resolve) => rl.question(question, resolve));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// ── CLI Registration ─────────────────────────────────────────────────────────
|
|
89
|
-
|
|
90
|
-
export function registerCli(api: any): void {
|
|
91
|
-
api.registerCli(
|
|
92
|
-
// Async registrar — follows the pattern from the official Matrix plugin example
|
|
93
|
-
async ({ program }: { program: any }) => {
|
|
94
|
-
const cmd = program
|
|
95
|
-
.command("ofiere")
|
|
96
|
-
.description("Ofiere PM dashboard integration");
|
|
97
|
-
|
|
98
|
-
// ── setup ──────────────────────────────────────────────────────────
|
|
99
|
-
cmd
|
|
100
|
-
.command("setup")
|
|
101
|
-
.description("Configure Ofiere PM connection (writes to .env, never touches openclaw.json)")
|
|
102
|
-
.option("--supabase-url <url>", "Supabase project URL")
|
|
103
|
-
.option("--service-key <key>", "Supabase service role key")
|
|
104
|
-
.option("--user-id <id>", "Ofiere user UUID")
|
|
105
|
-
.action(async (opts: {
|
|
106
|
-
supabaseUrl?: string;
|
|
107
|
-
serviceKey?: string;
|
|
108
|
-
userId?: string;
|
|
109
|
-
}) => {
|
|
110
|
-
let supabaseUrl = opts.supabaseUrl?.trim();
|
|
111
|
-
let serviceKey = opts.serviceKey?.trim();
|
|
112
|
-
let userId = opts.userId?.trim();
|
|
113
|
-
|
|
114
|
-
// Interactive mode if any field is missing
|
|
115
|
-
if (!supabaseUrl || !serviceKey || !userId) {
|
|
116
|
-
console.log("\nOfiere PM Setup\n");
|
|
117
|
-
const rl = readline.createInterface({
|
|
118
|
-
input: process.stdin,
|
|
119
|
-
output: process.stdout,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
if (!supabaseUrl) {
|
|
123
|
-
supabaseUrl = (await ask(rl, "Supabase URL (https://xxx.supabase.co): ")).trim();
|
|
124
|
-
}
|
|
125
|
-
if (!serviceKey) {
|
|
126
|
-
serviceKey = (await ask(rl, "Service role key: ")).trim();
|
|
127
|
-
}
|
|
128
|
-
if (!userId) {
|
|
129
|
-
userId = (await ask(rl, "User ID (UUID): ")).trim();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
rl.close();
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (!supabaseUrl || !serviceKey || !userId) {
|
|
136
|
-
console.log("\nAll fields are required. Setup cancelled.");
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Write to .env file (NEVER touches openclaw.json)
|
|
141
|
-
setEnvVars({
|
|
142
|
-
OFIERE_SUPABASE_URL: supabaseUrl,
|
|
143
|
-
OFIERE_SERVICE_ROLE_KEY: serviceKey,
|
|
144
|
-
OFIERE_USER_ID: userId,
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
const envFile = getEnvFile();
|
|
148
|
-
console.log(`\nDone. Saved to ${envFile}`);
|
|
149
|
-
console.log(` Supabase URL: ${supabaseUrl}`);
|
|
150
|
-
console.log(` User ID: ${userId}`);
|
|
151
|
-
console.log(` Plugin: enabled (global — all agents)`);
|
|
152
|
-
console.log("\nRestart to apply: openclaw gateway restart\n");
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
// ── status ─────────────────────────────────────────────────────────
|
|
156
|
-
cmd
|
|
157
|
-
.command("status")
|
|
158
|
-
.description("Show Ofiere PM plugin configuration")
|
|
159
|
-
.action(async () => {
|
|
160
|
-
const cfg = getPluginConfig();
|
|
161
|
-
console.log("\nOfiere PM Status\n");
|
|
162
|
-
|
|
163
|
-
if (!cfg.supabaseUrl) {
|
|
164
|
-
console.log(" Not configured. Run: openclaw ofiere setup\n");
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const maskedKey = cfg.serviceRoleKey
|
|
169
|
-
? `${cfg.serviceRoleKey.slice(0, 10)}...${cfg.serviceRoleKey.slice(-4)}`
|
|
170
|
-
: "not set";
|
|
171
|
-
|
|
172
|
-
console.log(` Supabase URL: ${cfg.supabaseUrl}`);
|
|
173
|
-
console.log(` Service Key: ${maskedKey}`);
|
|
174
|
-
console.log(` User ID: ${cfg.userId || "not set"}`);
|
|
175
|
-
console.log(` Agent ID: ${cfg.agentId || "(auto-detect — all agents)"}`);
|
|
176
|
-
console.log(` Config Source: ${getEnvFile()}`);
|
|
177
|
-
console.log("");
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// ── doctor ─────────────────────────────────────────────────────────
|
|
181
|
-
cmd
|
|
182
|
-
.command("doctor")
|
|
183
|
-
.description("Test Ofiere PM connection")
|
|
184
|
-
.action(async () => {
|
|
185
|
-
const cfg = getPluginConfig();
|
|
186
|
-
console.log("\nOfiere PM Doctor\n");
|
|
187
|
-
|
|
188
|
-
if (!cfg.supabaseUrl || !cfg.serviceRoleKey || !cfg.userId) {
|
|
189
|
-
console.log(" Not configured. Run: openclaw ofiere setup\n");
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
console.log(` Supabase URL: ${cfg.supabaseUrl}`);
|
|
194
|
-
console.log(` Agent Mode: ${cfg.agentId ? `fixed (${cfg.agentId})` : "auto-detect (all agents)"}`);
|
|
195
|
-
console.log("\n Testing connection...");
|
|
196
|
-
|
|
197
|
-
try {
|
|
198
|
-
const sb = createClient(cfg.supabaseUrl, cfg.serviceRoleKey, {
|
|
199
|
-
auth: { persistSession: false },
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
const { data, error } = await sb
|
|
203
|
-
.from("agents")
|
|
204
|
-
.select("id, name, status")
|
|
205
|
-
.eq("user_id", cfg.userId)
|
|
206
|
-
.order("name");
|
|
207
|
-
|
|
208
|
-
if (error) {
|
|
209
|
-
console.log(`\n ✗ Connection failed: ${error.message}\n`);
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
console.log(` ✓ Found ${(data || []).length} agents\n`);
|
|
214
|
-
for (const agent of data || []) {
|
|
215
|
-
const marker = agent.id === cfg.agentId ? " ← PINNED" : "";
|
|
216
|
-
console.log(` ${agent.id} — ${agent.name} (${agent.status})${marker}`);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const { count } = await sb
|
|
220
|
-
.from("tasks")
|
|
221
|
-
.select("id", { count: "exact", head: true })
|
|
222
|
-
.eq("user_id", cfg.userId);
|
|
223
|
-
|
|
224
|
-
console.log(`\n Tasks accessible: ${count ?? 0}`);
|
|
225
|
-
console.log("\n Status: healthy ✓\n");
|
|
226
|
-
} catch (e) {
|
|
227
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
228
|
-
console.log(`\n ✗ Connection failed: ${msg}`);
|
|
229
|
-
console.log("\n Possible causes:");
|
|
230
|
-
console.log(" - Invalid Supabase URL or service key");
|
|
231
|
-
console.log(" - Network issue reaching Supabase");
|
|
232
|
-
console.log(" - Invalid user ID\n");
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
},
|
|
236
|
-
// Descriptors enable lazy registration and root help text
|
|
237
|
-
{
|
|
238
|
-
descriptors: [
|
|
239
|
-
{
|
|
240
|
-
name: "ofiere",
|
|
241
|
-
description: "Manage Ofiere PM dashboard integration (setup, status, diagnostics)",
|
|
242
|
-
hasSubcommands: true,
|
|
243
|
-
},
|
|
244
|
-
],
|
|
245
|
-
},
|
|
246
|
-
);
|
|
247
|
-
}
|
|
1
|
+
// src/cli.ts — CLI registration for Ofiere PM plugin
|
|
2
|
+
// Uses api.registerCli with descriptors as documented:
|
|
3
|
+
// https://docs.openclaw.ai/plugins/sdk-overview#cli-registration-metadata
|
|
4
|
+
//
|
|
5
|
+
// The descriptors array enables lazy plugin CLI registration and root help text.
|
|
6
|
+
// The async ({ program }) => {} pattern is shown in the official Matrix plugin example.
|
|
7
|
+
|
|
8
|
+
import * as fs from "node:fs";
|
|
9
|
+
import * as os from "node:os";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
import * as readline from "node:readline";
|
|
12
|
+
import { createClient } from "@supabase/supabase-js";
|
|
13
|
+
|
|
14
|
+
// ── Config paths ─────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
/** Auto-detect OpenClaw home for env file */
|
|
17
|
+
function getOpenClawHome(): string {
|
|
18
|
+
if (fs.existsSync("/data/.openclaw")) return "/data/.openclaw";
|
|
19
|
+
return path.join(os.homedir(), ".openclaw");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getEnvFile(): string {
|
|
23
|
+
return path.join(getOpenClawHome(), ".env");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Read env vars from the .env file */
|
|
27
|
+
function readEnvFile(): Record<string, string> {
|
|
28
|
+
const envPath = getEnvFile();
|
|
29
|
+
const result: Record<string, string> = {};
|
|
30
|
+
try {
|
|
31
|
+
const content = fs.readFileSync(envPath, "utf-8");
|
|
32
|
+
for (const line of content.split("\n")) {
|
|
33
|
+
const match = line.match(/^([^#=]+)=(.*)$/);
|
|
34
|
+
if (match) {
|
|
35
|
+
result[match[1].trim()] = match[2].trim();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} catch { /* file doesn't exist yet */ }
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Append or update env vars in the .env file (idempotent) */
|
|
43
|
+
function setEnvVars(vars: Record<string, string>): void {
|
|
44
|
+
const envPath = getEnvFile();
|
|
45
|
+
const dir = path.dirname(envPath);
|
|
46
|
+
if (!fs.existsSync(dir)) {
|
|
47
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let content = "";
|
|
51
|
+
try {
|
|
52
|
+
content = fs.readFileSync(envPath, "utf-8");
|
|
53
|
+
} catch { /* file doesn't exist yet */ }
|
|
54
|
+
|
|
55
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
56
|
+
const regex = new RegExp(`^${key}=.*$`, "m");
|
|
57
|
+
if (regex.test(content)) {
|
|
58
|
+
content = content.replace(regex, `${key}=${value}`);
|
|
59
|
+
} else {
|
|
60
|
+
content = content.trimEnd() + `\n${key}=${value}`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fs.writeFileSync(envPath, content.trim() + "\n");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getPluginConfig(): {
|
|
68
|
+
supabaseUrl?: string;
|
|
69
|
+
serviceRoleKey?: string;
|
|
70
|
+
userId?: string;
|
|
71
|
+
agentId?: string;
|
|
72
|
+
enabled?: boolean;
|
|
73
|
+
} {
|
|
74
|
+
const env = readEnvFile();
|
|
75
|
+
return {
|
|
76
|
+
supabaseUrl: process.env.OFIERE_SUPABASE_URL || env.OFIERE_SUPABASE_URL || undefined,
|
|
77
|
+
serviceRoleKey: process.env.OFIERE_SERVICE_ROLE_KEY || env.OFIERE_SERVICE_ROLE_KEY || undefined,
|
|
78
|
+
userId: process.env.OFIERE_USER_ID || env.OFIERE_USER_ID || undefined,
|
|
79
|
+
agentId: process.env.OFIERE_AGENT_ID || env.OFIERE_AGENT_ID || undefined,
|
|
80
|
+
enabled: true,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function ask(rl: readline.Interface, question: string): Promise<string> {
|
|
85
|
+
return new Promise((resolve) => rl.question(question, resolve));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ── CLI Registration ─────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
export function registerCli(api: any): void {
|
|
91
|
+
api.registerCli(
|
|
92
|
+
// Async registrar — follows the pattern from the official Matrix plugin example
|
|
93
|
+
async ({ program }: { program: any }) => {
|
|
94
|
+
const cmd = program
|
|
95
|
+
.command("ofiere")
|
|
96
|
+
.description("Ofiere PM dashboard integration");
|
|
97
|
+
|
|
98
|
+
// ── setup ──────────────────────────────────────────────────────────
|
|
99
|
+
cmd
|
|
100
|
+
.command("setup")
|
|
101
|
+
.description("Configure Ofiere PM connection (writes to .env, never touches openclaw.json)")
|
|
102
|
+
.option("--supabase-url <url>", "Supabase project URL")
|
|
103
|
+
.option("--service-key <key>", "Supabase service role key")
|
|
104
|
+
.option("--user-id <id>", "Ofiere user UUID")
|
|
105
|
+
.action(async (opts: {
|
|
106
|
+
supabaseUrl?: string;
|
|
107
|
+
serviceKey?: string;
|
|
108
|
+
userId?: string;
|
|
109
|
+
}) => {
|
|
110
|
+
let supabaseUrl = opts.supabaseUrl?.trim();
|
|
111
|
+
let serviceKey = opts.serviceKey?.trim();
|
|
112
|
+
let userId = opts.userId?.trim();
|
|
113
|
+
|
|
114
|
+
// Interactive mode if any field is missing
|
|
115
|
+
if (!supabaseUrl || !serviceKey || !userId) {
|
|
116
|
+
console.log("\nOfiere PM Setup\n");
|
|
117
|
+
const rl = readline.createInterface({
|
|
118
|
+
input: process.stdin,
|
|
119
|
+
output: process.stdout,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (!supabaseUrl) {
|
|
123
|
+
supabaseUrl = (await ask(rl, "Supabase URL (https://xxx.supabase.co): ")).trim();
|
|
124
|
+
}
|
|
125
|
+
if (!serviceKey) {
|
|
126
|
+
serviceKey = (await ask(rl, "Service role key: ")).trim();
|
|
127
|
+
}
|
|
128
|
+
if (!userId) {
|
|
129
|
+
userId = (await ask(rl, "User ID (UUID): ")).trim();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
rl.close();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!supabaseUrl || !serviceKey || !userId) {
|
|
136
|
+
console.log("\nAll fields are required. Setup cancelled.");
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Write to .env file (NEVER touches openclaw.json)
|
|
141
|
+
setEnvVars({
|
|
142
|
+
OFIERE_SUPABASE_URL: supabaseUrl,
|
|
143
|
+
OFIERE_SERVICE_ROLE_KEY: serviceKey,
|
|
144
|
+
OFIERE_USER_ID: userId,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const envFile = getEnvFile();
|
|
148
|
+
console.log(`\nDone. Saved to ${envFile}`);
|
|
149
|
+
console.log(` Supabase URL: ${supabaseUrl}`);
|
|
150
|
+
console.log(` User ID: ${userId}`);
|
|
151
|
+
console.log(` Plugin: enabled (global — all agents)`);
|
|
152
|
+
console.log("\nRestart to apply: openclaw gateway restart\n");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// ── status ─────────────────────────────────────────────────────────
|
|
156
|
+
cmd
|
|
157
|
+
.command("status")
|
|
158
|
+
.description("Show Ofiere PM plugin configuration")
|
|
159
|
+
.action(async () => {
|
|
160
|
+
const cfg = getPluginConfig();
|
|
161
|
+
console.log("\nOfiere PM Status\n");
|
|
162
|
+
|
|
163
|
+
if (!cfg.supabaseUrl) {
|
|
164
|
+
console.log(" Not configured. Run: openclaw ofiere setup\n");
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const maskedKey = cfg.serviceRoleKey
|
|
169
|
+
? `${cfg.serviceRoleKey.slice(0, 10)}...${cfg.serviceRoleKey.slice(-4)}`
|
|
170
|
+
: "not set";
|
|
171
|
+
|
|
172
|
+
console.log(` Supabase URL: ${cfg.supabaseUrl}`);
|
|
173
|
+
console.log(` Service Key: ${maskedKey}`);
|
|
174
|
+
console.log(` User ID: ${cfg.userId || "not set"}`);
|
|
175
|
+
console.log(` Agent ID: ${cfg.agentId || "(auto-detect — all agents)"}`);
|
|
176
|
+
console.log(` Config Source: ${getEnvFile()}`);
|
|
177
|
+
console.log("");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// ── doctor ─────────────────────────────────────────────────────────
|
|
181
|
+
cmd
|
|
182
|
+
.command("doctor")
|
|
183
|
+
.description("Test Ofiere PM connection")
|
|
184
|
+
.action(async () => {
|
|
185
|
+
const cfg = getPluginConfig();
|
|
186
|
+
console.log("\nOfiere PM Doctor\n");
|
|
187
|
+
|
|
188
|
+
if (!cfg.supabaseUrl || !cfg.serviceRoleKey || !cfg.userId) {
|
|
189
|
+
console.log(" Not configured. Run: openclaw ofiere setup\n");
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
console.log(` Supabase URL: ${cfg.supabaseUrl}`);
|
|
194
|
+
console.log(` Agent Mode: ${cfg.agentId ? `fixed (${cfg.agentId})` : "auto-detect (all agents)"}`);
|
|
195
|
+
console.log("\n Testing connection...");
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const sb = createClient(cfg.supabaseUrl, cfg.serviceRoleKey, {
|
|
199
|
+
auth: { persistSession: false },
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const { data, error } = await sb
|
|
203
|
+
.from("agents")
|
|
204
|
+
.select("id, name, status")
|
|
205
|
+
.eq("user_id", cfg.userId)
|
|
206
|
+
.order("name");
|
|
207
|
+
|
|
208
|
+
if (error) {
|
|
209
|
+
console.log(`\n ✗ Connection failed: ${error.message}\n`);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
console.log(` ✓ Found ${(data || []).length} agents\n`);
|
|
214
|
+
for (const agent of data || []) {
|
|
215
|
+
const marker = agent.id === cfg.agentId ? " ← PINNED" : "";
|
|
216
|
+
console.log(` ${agent.id} — ${agent.name} (${agent.status})${marker}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const { count } = await sb
|
|
220
|
+
.from("tasks")
|
|
221
|
+
.select("id", { count: "exact", head: true })
|
|
222
|
+
.eq("user_id", cfg.userId);
|
|
223
|
+
|
|
224
|
+
console.log(`\n Tasks accessible: ${count ?? 0}`);
|
|
225
|
+
console.log("\n Status: healthy ✓\n");
|
|
226
|
+
} catch (e) {
|
|
227
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
228
|
+
console.log(`\n ✗ Connection failed: ${msg}`);
|
|
229
|
+
console.log("\n Possible causes:");
|
|
230
|
+
console.log(" - Invalid Supabase URL or service key");
|
|
231
|
+
console.log(" - Network issue reaching Supabase");
|
|
232
|
+
console.log(" - Invalid user ID\n");
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
// Descriptors enable lazy registration and root help text
|
|
237
|
+
{
|
|
238
|
+
descriptors: [
|
|
239
|
+
{
|
|
240
|
+
name: "ofiere",
|
|
241
|
+
description: "Manage Ofiere PM dashboard integration (setup, status, diagnostics)",
|
|
242
|
+
hasSubcommands: true,
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
},
|
|
246
|
+
);
|
|
247
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import type { OfiereConfig } from "./types.js";
|
|
3
|
-
|
|
4
|
-
export const OfiereConfigSchema = z.object({
|
|
5
|
-
enabled: z.boolean().default(true),
|
|
6
|
-
supabaseUrl: z.string().default(""),
|
|
7
|
-
serviceRoleKey: z.string().default(""),
|
|
8
|
-
userId: z.string().default(""),
|
|
9
|
-
agentId: z.string().default(""),
|
|
10
|
-
timezone: z.string().default("Asia/Jakarta"),
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
// Cache the first successful config so per-agent re-registrations don't lose it
|
|
14
|
-
let _cachedConfig: OfiereConfig | null = null;
|
|
15
|
-
|
|
16
|
-
export function parseOfiereConfig(value: unknown): OfiereConfig {
|
|
17
|
-
const raw =
|
|
18
|
-
value && typeof value === "object" && !Array.isArray(value)
|
|
19
|
-
? (value as Record<string, unknown>)
|
|
20
|
-
: {};
|
|
21
|
-
|
|
22
|
-
const configObj = raw.config as Record<string, unknown> | undefined;
|
|
23
|
-
|
|
24
|
-
// Support both nested (config.supabaseUrl) and flat (supabaseUrl) access
|
|
25
|
-
// Also fall back to env vars, then to cached config
|
|
26
|
-
const supabaseUrl =
|
|
27
|
-
(typeof configObj?.supabaseUrl === "string" && configObj.supabaseUrl.trim()) ||
|
|
28
|
-
(typeof raw.supabaseUrl === "string" && raw.supabaseUrl.trim()) ||
|
|
29
|
-
process.env.OFIERE_SUPABASE_URL ||
|
|
30
|
-
process.env.SUPABASE_URL ||
|
|
31
|
-
_cachedConfig?.supabaseUrl ||
|
|
32
|
-
"";
|
|
33
|
-
|
|
34
|
-
const serviceRoleKey =
|
|
35
|
-
(typeof configObj?.serviceRoleKey === "string" && configObj.serviceRoleKey.trim()) ||
|
|
36
|
-
(typeof raw.serviceRoleKey === "string" && raw.serviceRoleKey.trim()) ||
|
|
37
|
-
process.env.OFIERE_SERVICE_ROLE_KEY ||
|
|
38
|
-
process.env.SUPABASE_SERVICE_ROLE_KEY ||
|
|
39
|
-
_cachedConfig?.serviceRoleKey ||
|
|
40
|
-
"";
|
|
41
|
-
|
|
42
|
-
const userId =
|
|
43
|
-
(typeof configObj?.userId === "string" && configObj.userId.trim()) ||
|
|
44
|
-
(typeof raw.userId === "string" && raw.userId.trim()) ||
|
|
45
|
-
process.env.OFIERE_USER_ID ||
|
|
46
|
-
_cachedConfig?.userId ||
|
|
47
|
-
"";
|
|
48
|
-
|
|
49
|
-
const agentId =
|
|
50
|
-
(typeof configObj?.agentId === "string" && configObj.agentId.trim()) ||
|
|
51
|
-
(typeof raw.agentId === "string" && raw.agentId.trim()) ||
|
|
52
|
-
process.env.OFIERE_AGENT_ID ||
|
|
53
|
-
_cachedConfig?.agentId ||
|
|
54
|
-
"";
|
|
55
|
-
|
|
56
|
-
const timezone =
|
|
57
|
-
(typeof configObj?.timezone === "string" && configObj.timezone.trim()) ||
|
|
58
|
-
(typeof raw.timezone === "string" && raw.timezone.trim()) ||
|
|
59
|
-
process.env.OFIERE_TIMEZONE ||
|
|
60
|
-
_cachedConfig?.timezone ||
|
|
61
|
-
"Asia/Jakarta";
|
|
62
|
-
|
|
63
|
-
const parsed = OfiereConfigSchema.parse({
|
|
64
|
-
...raw,
|
|
65
|
-
supabaseUrl,
|
|
66
|
-
serviceRoleKey,
|
|
67
|
-
userId,
|
|
68
|
-
agentId,
|
|
69
|
-
timezone,
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Cache if this parse yielded a complete config
|
|
73
|
-
if (parsed.supabaseUrl && parsed.serviceRoleKey && parsed.userId) {
|
|
74
|
-
_cachedConfig = parsed;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return parsed;
|
|
78
|
-
}
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { OfiereConfig } from "./types.js";
|
|
3
|
+
|
|
4
|
+
export const OfiereConfigSchema = z.object({
|
|
5
|
+
enabled: z.boolean().default(true),
|
|
6
|
+
supabaseUrl: z.string().default(""),
|
|
7
|
+
serviceRoleKey: z.string().default(""),
|
|
8
|
+
userId: z.string().default(""),
|
|
9
|
+
agentId: z.string().default(""),
|
|
10
|
+
timezone: z.string().default("Asia/Jakarta"),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
// Cache the first successful config so per-agent re-registrations don't lose it
|
|
14
|
+
let _cachedConfig: OfiereConfig | null = null;
|
|
15
|
+
|
|
16
|
+
export function parseOfiereConfig(value: unknown): OfiereConfig {
|
|
17
|
+
const raw =
|
|
18
|
+
value && typeof value === "object" && !Array.isArray(value)
|
|
19
|
+
? (value as Record<string, unknown>)
|
|
20
|
+
: {};
|
|
21
|
+
|
|
22
|
+
const configObj = raw.config as Record<string, unknown> | undefined;
|
|
23
|
+
|
|
24
|
+
// Support both nested (config.supabaseUrl) and flat (supabaseUrl) access
|
|
25
|
+
// Also fall back to env vars, then to cached config
|
|
26
|
+
const supabaseUrl =
|
|
27
|
+
(typeof configObj?.supabaseUrl === "string" && configObj.supabaseUrl.trim()) ||
|
|
28
|
+
(typeof raw.supabaseUrl === "string" && raw.supabaseUrl.trim()) ||
|
|
29
|
+
process.env.OFIERE_SUPABASE_URL ||
|
|
30
|
+
process.env.SUPABASE_URL ||
|
|
31
|
+
_cachedConfig?.supabaseUrl ||
|
|
32
|
+
"";
|
|
33
|
+
|
|
34
|
+
const serviceRoleKey =
|
|
35
|
+
(typeof configObj?.serviceRoleKey === "string" && configObj.serviceRoleKey.trim()) ||
|
|
36
|
+
(typeof raw.serviceRoleKey === "string" && raw.serviceRoleKey.trim()) ||
|
|
37
|
+
process.env.OFIERE_SERVICE_ROLE_KEY ||
|
|
38
|
+
process.env.SUPABASE_SERVICE_ROLE_KEY ||
|
|
39
|
+
_cachedConfig?.serviceRoleKey ||
|
|
40
|
+
"";
|
|
41
|
+
|
|
42
|
+
const userId =
|
|
43
|
+
(typeof configObj?.userId === "string" && configObj.userId.trim()) ||
|
|
44
|
+
(typeof raw.userId === "string" && raw.userId.trim()) ||
|
|
45
|
+
process.env.OFIERE_USER_ID ||
|
|
46
|
+
_cachedConfig?.userId ||
|
|
47
|
+
"";
|
|
48
|
+
|
|
49
|
+
const agentId =
|
|
50
|
+
(typeof configObj?.agentId === "string" && configObj.agentId.trim()) ||
|
|
51
|
+
(typeof raw.agentId === "string" && raw.agentId.trim()) ||
|
|
52
|
+
process.env.OFIERE_AGENT_ID ||
|
|
53
|
+
_cachedConfig?.agentId ||
|
|
54
|
+
"";
|
|
55
|
+
|
|
56
|
+
const timezone =
|
|
57
|
+
(typeof configObj?.timezone === "string" && configObj.timezone.trim()) ||
|
|
58
|
+
(typeof raw.timezone === "string" && raw.timezone.trim()) ||
|
|
59
|
+
process.env.OFIERE_TIMEZONE ||
|
|
60
|
+
_cachedConfig?.timezone ||
|
|
61
|
+
"Asia/Jakarta";
|
|
62
|
+
|
|
63
|
+
const parsed = OfiereConfigSchema.parse({
|
|
64
|
+
...raw,
|
|
65
|
+
supabaseUrl,
|
|
66
|
+
serviceRoleKey,
|
|
67
|
+
userId,
|
|
68
|
+
agentId,
|
|
69
|
+
timezone,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Cache if this parse yielded a complete config
|
|
73
|
+
if (parsed.supabaseUrl && parsed.serviceRoleKey && parsed.userId) {
|
|
74
|
+
_cachedConfig = parsed;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return parsed;
|
|
78
|
+
}
|