@useorgx/openclaw-plugin 0.4.4 → 0.4.6

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.
Files changed (51) hide show
  1. package/README.md +85 -2
  2. package/dashboard/dist/assets/0tOC3wSN.js +214 -0
  3. package/dashboard/dist/assets/Bm8QnMJ_.js +1 -0
  4. package/dashboard/dist/assets/CpJsfbXo.js +9 -0
  5. package/dashboard/dist/assets/CyxZio4Y.js +1 -0
  6. package/dashboard/dist/assets/DaAIOik3.css +1 -0
  7. package/dashboard/dist/index.html +3 -3
  8. package/dist/activity-store.d.ts +28 -0
  9. package/dist/activity-store.js +250 -0
  10. package/dist/agent-context-store.d.ts +19 -0
  11. package/dist/agent-context-store.js +60 -3
  12. package/dist/agent-suite.d.ts +83 -0
  13. package/dist/agent-suite.js +615 -0
  14. package/dist/contracts/client.d.ts +22 -1
  15. package/dist/contracts/client.js +120 -3
  16. package/dist/contracts/types.d.ts +190 -1
  17. package/dist/entity-comment-store.d.ts +29 -0
  18. package/dist/entity-comment-store.js +190 -0
  19. package/dist/hooks/post-reporting-event.mjs +326 -0
  20. package/dist/http-handler.d.ts +7 -1
  21. package/dist/http-handler.js +3619 -585
  22. package/dist/index.js +1039 -80
  23. package/dist/mcp-client-setup.d.ts +30 -0
  24. package/dist/mcp-client-setup.js +347 -0
  25. package/dist/mcp-http-handler.d.ts +55 -0
  26. package/dist/mcp-http-handler.js +395 -0
  27. package/dist/next-up-queue-store.d.ts +31 -0
  28. package/dist/next-up-queue-store.js +169 -0
  29. package/dist/openclaw.plugin.json +1 -1
  30. package/dist/outbox.d.ts +1 -1
  31. package/dist/runtime-instance-store.d.ts +1 -1
  32. package/dist/runtime-instance-store.js +20 -3
  33. package/dist/skill-pack-state.d.ts +69 -0
  34. package/dist/skill-pack-state.js +232 -0
  35. package/dist/worker-supervisor.d.ts +25 -0
  36. package/dist/worker-supervisor.js +62 -0
  37. package/openclaw.plugin.json +1 -1
  38. package/package.json +10 -1
  39. package/skills/orgx-design-agent/SKILL.md +38 -0
  40. package/skills/orgx-engineering-agent/SKILL.md +55 -0
  41. package/skills/orgx-marketing-agent/SKILL.md +40 -0
  42. package/skills/orgx-operations-agent/SKILL.md +40 -0
  43. package/skills/orgx-orchestrator-agent/SKILL.md +45 -0
  44. package/skills/orgx-product-agent/SKILL.md +39 -0
  45. package/skills/orgx-sales-agent/SKILL.md +40 -0
  46. package/skills/ship/SKILL.md +63 -0
  47. package/dashboard/dist/assets/4hvaB0UC.js +0 -9
  48. package/dashboard/dist/assets/BgsfM2lz.js +0 -1
  49. package/dashboard/dist/assets/DCBlK4MX.js +0 -212
  50. package/dashboard/dist/assets/DEuY_RBN.js +0 -1
  51. package/dashboard/dist/assets/jyFhCND-.css +0 -1
@@ -0,0 +1,30 @@
1
+ import type { Logger } from "./mcp-http-handler.js";
2
+ export declare function patchClaudeMcpConfig(input: {
3
+ current: Record<string, unknown>;
4
+ localMcpUrl: string;
5
+ }): {
6
+ updated: boolean;
7
+ next: Record<string, unknown>;
8
+ };
9
+ export declare function patchCursorMcpConfig(input: {
10
+ current: Record<string, unknown>;
11
+ localMcpUrl: string;
12
+ }): {
13
+ updated: boolean;
14
+ next: Record<string, unknown>;
15
+ };
16
+ export declare function patchCodexConfigToml(input: {
17
+ current: string;
18
+ localMcpUrl: string;
19
+ }): {
20
+ updated: boolean;
21
+ next: string;
22
+ };
23
+ export declare function autoConfigureDetectedMcpClients(input: {
24
+ localMcpUrl: string;
25
+ logger?: Logger;
26
+ homeDir?: string;
27
+ }): Promise<{
28
+ updatedPaths: string[];
29
+ skippedPaths: string[];
30
+ }>;
@@ -0,0 +1,347 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { randomUUID } from "node:crypto";
5
+ import { writeFileAtomicSync, writeJsonFileAtomicSync } from "./fs-utils.js";
6
+ const ORGX_LOCAL_MCP_KEY = "orgx-openclaw";
7
+ const ORGX_HOSTED_MCP_URL = "https://mcp.useorgx.com/mcp";
8
+ const ORGX_LOCAL_MCP_SCOPES = [
9
+ "engineering",
10
+ "product",
11
+ "design",
12
+ "marketing",
13
+ "sales",
14
+ "operations",
15
+ "orchestration",
16
+ ];
17
+ function scopedMcpServerKey(scope) {
18
+ return `${ORGX_LOCAL_MCP_KEY}-${scope}`;
19
+ }
20
+ function scopedMcpUrl(localMcpUrl, scope) {
21
+ const base = localMcpUrl.replace(/\/+$/, "");
22
+ return `${base}/${scope}`;
23
+ }
24
+ function escapeRegExp(value) {
25
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26
+ }
27
+ function isRecord(value) {
28
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
29
+ }
30
+ function parseJsonObjectSafe(raw) {
31
+ try {
32
+ const parsed = JSON.parse(raw);
33
+ return isRecord(parsed) ? parsed : null;
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ function fileModeOrDefault(path, fallback) {
40
+ try {
41
+ const stat = statSync(path);
42
+ return stat.mode & 0o777;
43
+ }
44
+ catch {
45
+ return fallback;
46
+ }
47
+ }
48
+ function backupPath(path) {
49
+ return `${path}.bak.${Date.now()}-${randomUUID().slice(0, 8)}`;
50
+ }
51
+ function backupFileSync(path, mode) {
52
+ try {
53
+ const content = readFileSync(path);
54
+ const next = backupPath(path);
55
+ writeFileAtomicSync(next, content.toString("utf8"), { mode, encoding: "utf8" });
56
+ return next;
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ export function patchClaudeMcpConfig(input) {
63
+ const currentServers = isRecord(input.current.mcpServers) ? input.current.mcpServers : {};
64
+ const existingOrgx = isRecord(currentServers.orgx) ? currentServers.orgx : {};
65
+ const existingOrgxUrl = typeof existingOrgx.url === "string" ? existingOrgx.url : "";
66
+ const existingOrgxType = typeof existingOrgx.type === "string" ? existingOrgx.type : "";
67
+ const existing = isRecord(currentServers[ORGX_LOCAL_MCP_KEY]) ? currentServers[ORGX_LOCAL_MCP_KEY] : {};
68
+ const priorUrl = typeof existing.url === "string" ? existing.url : "";
69
+ const priorType = typeof existing.type === "string" ? existing.type : "";
70
+ // Ensure hosted OrgX is available alongside the local proxy. Avoid overwriting
71
+ // custom `orgx` entries unless it's clearly redundant (pointing at the same
72
+ // local proxy URL we install under `orgx-openclaw`).
73
+ const shouldSetHostedOrgx = !isRecord(currentServers.orgx) ||
74
+ (existingOrgxUrl === input.localMcpUrl && existingOrgxType === "http");
75
+ const nextOrgxEntry = {
76
+ ...existingOrgx,
77
+ type: "http",
78
+ url: ORGX_HOSTED_MCP_URL,
79
+ description: typeof existingOrgx.description === "string" && existingOrgx.description.trim().length > 0
80
+ ? existingOrgx.description
81
+ : "OrgX cloud MCP (OAuth)",
82
+ };
83
+ const nextEntry = {
84
+ ...existing,
85
+ type: "http",
86
+ url: input.localMcpUrl,
87
+ description: typeof existing.description === "string" && existing.description.trim().length > 0
88
+ ? existing.description
89
+ : "OrgX platform via local OpenClaw plugin (no OAuth)",
90
+ };
91
+ const updatedScopes = [];
92
+ const scopedEntries = {};
93
+ for (const scope of ORGX_LOCAL_MCP_SCOPES) {
94
+ const key = scopedMcpServerKey(scope);
95
+ const expectedUrl = scopedMcpUrl(input.localMcpUrl, scope);
96
+ const existingScoped = isRecord(currentServers[key]) ? currentServers[key] : {};
97
+ const priorUrl = typeof existingScoped.url === "string" ? existingScoped.url : "";
98
+ const priorType = typeof existingScoped.type === "string" ? existingScoped.type : "";
99
+ const nextScoped = {
100
+ ...existingScoped,
101
+ type: "http",
102
+ url: expectedUrl,
103
+ description: typeof existingScoped.description === "string" && existingScoped.description.trim().length > 0
104
+ ? existingScoped.description
105
+ : `OrgX platform via local OpenClaw plugin (${scope} scope)`,
106
+ };
107
+ scopedEntries[key] = nextScoped;
108
+ if (priorUrl !== expectedUrl || priorType !== "http") {
109
+ updatedScopes.push(scope);
110
+ }
111
+ }
112
+ const nextServers = {
113
+ ...currentServers,
114
+ ...(shouldSetHostedOrgx ? { orgx: nextOrgxEntry } : {}),
115
+ [ORGX_LOCAL_MCP_KEY]: nextEntry,
116
+ ...scopedEntries,
117
+ };
118
+ const next = {
119
+ ...input.current,
120
+ mcpServers: nextServers,
121
+ };
122
+ const updatedLocal = priorUrl !== input.localMcpUrl || priorType !== "http";
123
+ const updatedHosted = shouldSetHostedOrgx &&
124
+ (existingOrgxUrl !== ORGX_HOSTED_MCP_URL || existingOrgxType !== "http");
125
+ const updated = updatedLocal || updatedHosted || updatedScopes.length > 0;
126
+ return { updated, next };
127
+ }
128
+ export function patchCursorMcpConfig(input) {
129
+ const currentServers = isRecord(input.current.mcpServers) ? input.current.mcpServers : {};
130
+ const existing = isRecord(currentServers[ORGX_LOCAL_MCP_KEY]) ? currentServers[ORGX_LOCAL_MCP_KEY] : {};
131
+ const priorUrl = typeof existing.url === "string" ? existing.url : "";
132
+ const nextEntry = {
133
+ ...existing,
134
+ url: input.localMcpUrl,
135
+ };
136
+ const scopedEntries = {};
137
+ let updatedScopes = false;
138
+ for (const scope of ORGX_LOCAL_MCP_SCOPES) {
139
+ const key = scopedMcpServerKey(scope);
140
+ const expectedUrl = scopedMcpUrl(input.localMcpUrl, scope);
141
+ const existingScoped = isRecord(currentServers[key]) ? currentServers[key] : {};
142
+ const priorScopedUrl = typeof existingScoped.url === "string" ? existingScoped.url : "";
143
+ scopedEntries[key] = {
144
+ ...existingScoped,
145
+ url: expectedUrl,
146
+ };
147
+ if (priorScopedUrl !== expectedUrl)
148
+ updatedScopes = true;
149
+ }
150
+ const nextServers = {
151
+ ...currentServers,
152
+ [ORGX_LOCAL_MCP_KEY]: nextEntry,
153
+ ...scopedEntries,
154
+ };
155
+ const next = {
156
+ ...input.current,
157
+ mcpServers: nextServers,
158
+ };
159
+ const updated = priorUrl !== input.localMcpUrl || updatedScopes;
160
+ return { updated, next };
161
+ }
162
+ function upsertCodexMcpServerSection(input) {
163
+ const currentText = input.current;
164
+ const lines = currentText.split(/\r?\n/);
165
+ const escapedKey = escapeRegExp(input.key);
166
+ const headerRegex = new RegExp(`^\\[mcp_servers\\.(?:"${escapedKey}"|${escapedKey})\\]\\s*$`);
167
+ let headerIndex = -1;
168
+ for (let i = 0; i < lines.length; i += 1) {
169
+ if (headerRegex.test(lines[i].trim())) {
170
+ headerIndex = i;
171
+ break;
172
+ }
173
+ }
174
+ const urlLine = `url = "${input.url}"`;
175
+ if (headerIndex === -1) {
176
+ const suffix = ["", `[mcp_servers.\"${input.key}\"]`, urlLine, ""].join("\n");
177
+ const normalized = currentText.endsWith("\n") ? currentText : `${currentText}\n`;
178
+ return { updated: true, next: `${normalized}${suffix}` };
179
+ }
180
+ let sectionEnd = lines.length;
181
+ for (let i = headerIndex + 1; i < lines.length; i += 1) {
182
+ if (lines[i].trim().startsWith("[")) {
183
+ sectionEnd = i;
184
+ break;
185
+ }
186
+ }
187
+ let updated = false;
188
+ let urlIndex = -1;
189
+ for (let i = headerIndex + 1; i < sectionEnd; i += 1) {
190
+ if (/^\s*url\s*=/.test(lines[i])) {
191
+ urlIndex = i;
192
+ break;
193
+ }
194
+ }
195
+ if (urlIndex >= 0) {
196
+ if (lines[urlIndex].trim() !== urlLine) {
197
+ lines[urlIndex] = urlLine;
198
+ updated = true;
199
+ }
200
+ }
201
+ else {
202
+ lines.splice(headerIndex + 1, 0, urlLine);
203
+ updated = true;
204
+ // Recalculate sectionEnd after splice
205
+ sectionEnd = lines.length;
206
+ for (let i = headerIndex + 1; i < lines.length; i += 1) {
207
+ if (lines[i].trim().startsWith("[")) {
208
+ sectionEnd = i;
209
+ break;
210
+ }
211
+ }
212
+ }
213
+ // Strip stale stdio-transport fields that conflict with url-only entries.
214
+ // Codex rejects `url` when `command`/`args` are present (stdio transport).
215
+ const staleFieldRegex = /^\s*(command|args|startup_timeout_sec)\s*=/;
216
+ for (let i = sectionEnd - 1; i > headerIndex; i -= 1) {
217
+ if (staleFieldRegex.test(lines[i])) {
218
+ lines.splice(i, 1);
219
+ updated = true;
220
+ }
221
+ }
222
+ return { updated, next: `${lines.join("\n")}\n` };
223
+ }
224
+ export function patchCodexConfigToml(input) {
225
+ let current = input.current;
226
+ let updatedHosted = false;
227
+ // If the hosted OrgX entry is missing entirely, add a sensible default. This is
228
+ // a no-op if the user already has `orgx` pointed at staging or another URL.
229
+ const hostedHeaderRegex = /^\[mcp_servers\.(?:"orgx"|orgx)\]\s*$/;
230
+ {
231
+ const lines = current.split(/\r?\n/);
232
+ const hasHosted = lines.some((line) => hostedHeaderRegex.test(line.trim()));
233
+ if (!hasHosted) {
234
+ const hostedUrlLine = `url = "${ORGX_HOSTED_MCP_URL}"`;
235
+ const suffix = [
236
+ ...(current.trim().length === 0 ? [] : [""]),
237
+ "[mcp_servers.orgx]",
238
+ hostedUrlLine,
239
+ "",
240
+ ].join("\n");
241
+ const normalized = current.endsWith("\n") ? current : `${current}\n`;
242
+ current = `${normalized}${suffix}`;
243
+ updatedHosted = true;
244
+ }
245
+ }
246
+ let updated = false;
247
+ const base = upsertCodexMcpServerSection({
248
+ current,
249
+ key: ORGX_LOCAL_MCP_KEY,
250
+ url: input.localMcpUrl,
251
+ });
252
+ updated = updated || base.updated;
253
+ current = base.next;
254
+ for (const scope of ORGX_LOCAL_MCP_SCOPES) {
255
+ const next = upsertCodexMcpServerSection({
256
+ current,
257
+ key: scopedMcpServerKey(scope),
258
+ url: scopedMcpUrl(input.localMcpUrl, scope),
259
+ });
260
+ updated = updated || next.updated;
261
+ current = next.next;
262
+ }
263
+ return { updated: updatedHosted || updated, next: current };
264
+ }
265
+ export async function autoConfigureDetectedMcpClients(input) {
266
+ const logger = input.logger ?? {};
267
+ const home = input.homeDir ?? homedir();
268
+ const updatedPaths = [];
269
+ const skippedPaths = [];
270
+ const targets = [
271
+ { kind: "claude", path: join(home, ".claude", "mcp.json") },
272
+ { kind: "cursor", path: join(home, ".cursor", "mcp.json") },
273
+ { kind: "codex", path: join(home, ".codex", "config.toml") },
274
+ ];
275
+ for (const target of targets) {
276
+ if (!existsSync(target.path)) {
277
+ skippedPaths.push(target.path);
278
+ continue;
279
+ }
280
+ const mode = fileModeOrDefault(target.path, 0o600);
281
+ try {
282
+ if (target.kind === "codex") {
283
+ const current = readFileSync(target.path, "utf8");
284
+ const patched = patchCodexConfigToml({ current, localMcpUrl: input.localMcpUrl });
285
+ if (!patched.updated) {
286
+ skippedPaths.push(target.path);
287
+ continue;
288
+ }
289
+ const backup = backupFileSync(target.path, mode);
290
+ if (!backup) {
291
+ logger.warn?.("[orgx] MCP client autoconfig: backup failed; skipping", {
292
+ path: target.path,
293
+ kind: target.kind,
294
+ });
295
+ skippedPaths.push(target.path);
296
+ continue;
297
+ }
298
+ writeFileAtomicSync(target.path, patched.next, { mode, encoding: "utf8" });
299
+ updatedPaths.push(target.path);
300
+ continue;
301
+ }
302
+ const currentText = readFileSync(target.path, "utf8");
303
+ const current = parseJsonObjectSafe(currentText);
304
+ if (!current) {
305
+ logger.warn?.("[orgx] MCP client autoconfig: invalid JSON; skipping", {
306
+ path: target.path,
307
+ kind: target.kind,
308
+ });
309
+ skippedPaths.push(target.path);
310
+ continue;
311
+ }
312
+ const patched = target.kind === "claude"
313
+ ? patchClaudeMcpConfig({ current, localMcpUrl: input.localMcpUrl })
314
+ : patchCursorMcpConfig({ current, localMcpUrl: input.localMcpUrl });
315
+ if (!patched.updated) {
316
+ skippedPaths.push(target.path);
317
+ continue;
318
+ }
319
+ const backup = backupFileSync(target.path, mode);
320
+ if (!backup) {
321
+ logger.warn?.("[orgx] MCP client autoconfig: backup failed; skipping", {
322
+ path: target.path,
323
+ kind: target.kind,
324
+ });
325
+ skippedPaths.push(target.path);
326
+ continue;
327
+ }
328
+ writeJsonFileAtomicSync(target.path, patched.next, mode);
329
+ updatedPaths.push(target.path);
330
+ }
331
+ catch (err) {
332
+ logger.warn?.("[orgx] MCP client autoconfig failed; leaving backup in place", {
333
+ path: target.path,
334
+ kind: target.kind,
335
+ error: err instanceof Error ? err.message : String(err),
336
+ });
337
+ skippedPaths.push(target.path);
338
+ }
339
+ }
340
+ if (updatedPaths.length > 0) {
341
+ logger.info?.("[orgx] MCP client autoconfig applied", {
342
+ localMcpUrl: input.localMcpUrl,
343
+ updated: updatedPaths,
344
+ });
345
+ }
346
+ return { updatedPaths, skippedPaths };
347
+ }
@@ -0,0 +1,55 @@
1
+ export type Logger = {
2
+ info?: (message: string, meta?: Record<string, unknown>) => void;
3
+ warn?: (message: string, meta?: Record<string, unknown>) => void;
4
+ debug?: (message: string, meta?: Record<string, unknown>) => void;
5
+ };
6
+ export interface PluginRequest {
7
+ method?: string;
8
+ url?: string;
9
+ headers: Record<string, string | string[] | undefined>;
10
+ body?: unknown;
11
+ on?: (event: string, listener: (...args: unknown[]) => void) => void;
12
+ once?: (event: string, listener: (...args: unknown[]) => void) => void;
13
+ }
14
+ export interface PluginResponse {
15
+ writeHead(status: number, headers?: Record<string, string>): void;
16
+ end(body?: string | Buffer): void;
17
+ write?(chunk: string | Buffer): boolean | void;
18
+ writableEnded?: boolean;
19
+ }
20
+ export type ToolResult = {
21
+ content: Array<{
22
+ type: "text";
23
+ text: string;
24
+ }>;
25
+ isError?: boolean;
26
+ };
27
+ export type RegisteredTool = {
28
+ name: string;
29
+ description: string;
30
+ parameters: Record<string, unknown>;
31
+ execute: (callId: string, params?: unknown) => Promise<ToolResult>;
32
+ };
33
+ type PromptRole = "system" | "user" | "assistant";
34
+ type PromptMessage = {
35
+ role: PromptRole;
36
+ content: string;
37
+ };
38
+ export type RegisteredPrompt = {
39
+ name: string;
40
+ description?: string;
41
+ arguments?: Array<{
42
+ name: string;
43
+ description?: string;
44
+ required?: boolean;
45
+ }>;
46
+ messages: PromptMessage[];
47
+ };
48
+ export declare function createMcpHttpHandler(input: {
49
+ tools: Map<string, RegisteredTool>;
50
+ prompts?: Map<string, RegisteredPrompt>;
51
+ logger?: Logger;
52
+ serverName: string;
53
+ serverVersion: string;
54
+ }): (req: PluginRequest, res: PluginResponse) => Promise<boolean>;
55
+ export {};