@useorgx/openclaw-plugin 0.4.5 → 0.4.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/README.md +333 -26
- package/dashboard/dist/assets/B3ziCA02.js +8 -0
- package/dashboard/dist/assets/BNeJ0kpF.js +1 -0
- package/dashboard/dist/assets/BzkiMPmM.js +215 -0
- package/dashboard/dist/assets/CUV9IHHi.js +1 -0
- package/dashboard/dist/assets/CpJsfbXo.js +9 -0
- package/dashboard/dist/assets/Ie7d9Iq2.css +1 -0
- package/dashboard/dist/assets/sAhvFnpk.js +4 -0
- package/dashboard/dist/index.html +5 -5
- package/dist/activity-actor-fields.d.ts +3 -0
- package/dist/activity-actor-fields.js +128 -0
- package/dist/activity-store.d.ts +28 -0
- package/dist/activity-store.js +257 -0
- package/dist/agent-context-store.d.ts +19 -0
- package/dist/agent-context-store.js +60 -3
- package/dist/agent-suite.d.ts +83 -0
- package/dist/agent-suite.js +615 -0
- package/dist/artifacts/register-artifact.d.ts +47 -0
- package/dist/artifacts/register-artifact.js +271 -0
- package/dist/auth-store.js +8 -13
- package/dist/contracts/client.d.ts +23 -1
- package/dist/contracts/client.js +127 -8
- package/dist/contracts/types.d.ts +194 -1
- package/dist/entity-comment-store.d.ts +29 -0
- package/dist/entity-comment-store.js +190 -0
- package/dist/hooks/post-reporting-event.mjs +326 -0
- package/dist/http-handler.d.ts +7 -1
- package/dist/http-handler.js +4500 -534
- package/dist/index.js +1078 -68
- package/dist/local-openclaw.js +8 -0
- package/dist/mcp-client-setup.js +145 -28
- package/dist/mcp-http-handler.d.ts +17 -0
- package/dist/mcp-http-handler.js +144 -3
- package/dist/next-up-queue-store.d.ts +31 -0
- package/dist/next-up-queue-store.js +169 -0
- package/dist/openclaw.plugin.json +1 -1
- package/dist/outbox.d.ts +1 -1
- package/dist/runtime-instance-store.d.ts +1 -1
- package/dist/runtime-instance-store.js +19 -2
- package/dist/skill-pack-state.d.ts +69 -0
- package/dist/skill-pack-state.js +232 -0
- package/dist/worker-supervisor.d.ts +25 -0
- package/dist/worker-supervisor.js +77 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +15 -1
- package/skills/orgx-design-agent/SKILL.md +38 -0
- package/skills/orgx-engineering-agent/SKILL.md +55 -0
- package/skills/orgx-marketing-agent/SKILL.md +40 -0
- package/skills/orgx-operations-agent/SKILL.md +40 -0
- package/skills/orgx-orchestrator-agent/SKILL.md +45 -0
- package/skills/orgx-product-agent/SKILL.md +39 -0
- package/skills/orgx-sales-agent/SKILL.md +40 -0
- package/skills/ship/SKILL.md +63 -0
- package/dashboard/dist/assets/B68j2crt.js +0 -1
- package/dashboard/dist/assets/BZZ-fiJx.js +0 -32
- package/dashboard/dist/assets/BoXlCHKa.js +0 -9
- package/dashboard/dist/assets/Bq9x_Xyh.css +0 -1
- package/dashboard/dist/assets/DBhrRVdp.js +0 -1
- package/dashboard/dist/assets/DD1jv1Hd.js +0 -8
- package/dashboard/dist/assets/DNjbmawF.js +0 -214
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, } from "node:fs";
|
|
2
|
+
import { getOrgxPluginConfigDir, getOrgxPluginConfigPath } from "./paths.js";
|
|
3
|
+
import { backupCorruptFileSync, writeJsonFileAtomicSync } from "./fs-utils.js";
|
|
4
|
+
const MAX_PINS = 240;
|
|
5
|
+
function storeDir() {
|
|
6
|
+
return getOrgxPluginConfigDir();
|
|
7
|
+
}
|
|
8
|
+
function storeFile() {
|
|
9
|
+
return getOrgxPluginConfigPath("next-up-queue.json");
|
|
10
|
+
}
|
|
11
|
+
function ensureStoreDir() {
|
|
12
|
+
const dir = storeDir();
|
|
13
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
14
|
+
try {
|
|
15
|
+
chmodSync(dir, 0o700);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// best effort
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function parseJson(value) {
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(value);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function normalizeNullableString(value) {
|
|
30
|
+
if (typeof value !== "string")
|
|
31
|
+
return null;
|
|
32
|
+
const trimmed = value.trim();
|
|
33
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
34
|
+
}
|
|
35
|
+
function normalizeEntry(input) {
|
|
36
|
+
return {
|
|
37
|
+
initiativeId: input.initiativeId.trim(),
|
|
38
|
+
workstreamId: input.workstreamId.trim(),
|
|
39
|
+
preferredTaskId: normalizeNullableString(input.preferredTaskId),
|
|
40
|
+
preferredMilestoneId: normalizeNullableString(input.preferredMilestoneId),
|
|
41
|
+
createdAt: input.createdAt,
|
|
42
|
+
updatedAt: input.updatedAt,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function readNextUpQueuePins() {
|
|
46
|
+
const file = storeFile();
|
|
47
|
+
try {
|
|
48
|
+
if (!existsSync(file)) {
|
|
49
|
+
return { version: 1, updatedAt: new Date().toISOString(), pins: [] };
|
|
50
|
+
}
|
|
51
|
+
const raw = readFileSync(file, "utf8");
|
|
52
|
+
const parsed = parseJson(raw);
|
|
53
|
+
if (!parsed || typeof parsed !== "object") {
|
|
54
|
+
backupCorruptFileSync(file);
|
|
55
|
+
return { version: 1, updatedAt: new Date().toISOString(), pins: [] };
|
|
56
|
+
}
|
|
57
|
+
const pins = Array.isArray(parsed.pins) ? parsed.pins : [];
|
|
58
|
+
return {
|
|
59
|
+
version: 1,
|
|
60
|
+
updatedAt: typeof parsed.updatedAt === "string"
|
|
61
|
+
? parsed.updatedAt
|
|
62
|
+
: new Date().toISOString(),
|
|
63
|
+
pins: pins
|
|
64
|
+
.filter((entry) => Boolean(entry && typeof entry === "object"))
|
|
65
|
+
.map((entry) => normalizeEntry(entry)),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return { version: 1, updatedAt: new Date().toISOString(), pins: [] };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export function upsertNextUpQueuePin(input) {
|
|
73
|
+
const initiativeId = input.initiativeId.trim();
|
|
74
|
+
const workstreamId = input.workstreamId.trim();
|
|
75
|
+
if (!initiativeId || !workstreamId) {
|
|
76
|
+
return readNextUpQueuePins();
|
|
77
|
+
}
|
|
78
|
+
ensureStoreDir();
|
|
79
|
+
const now = new Date().toISOString();
|
|
80
|
+
const next = readNextUpQueuePins();
|
|
81
|
+
const key = `${initiativeId}:${workstreamId}`;
|
|
82
|
+
const existing = next.pins.find((pin) => `${pin.initiativeId}:${pin.workstreamId}` === key);
|
|
83
|
+
const updated = normalizeEntry({
|
|
84
|
+
initiativeId,
|
|
85
|
+
workstreamId,
|
|
86
|
+
preferredTaskId: input.preferredTaskId ?? existing?.preferredTaskId ?? null,
|
|
87
|
+
preferredMilestoneId: input.preferredMilestoneId ?? existing?.preferredMilestoneId ?? null,
|
|
88
|
+
createdAt: existing?.createdAt ?? now,
|
|
89
|
+
updatedAt: now,
|
|
90
|
+
});
|
|
91
|
+
next.pins = [updated, ...next.pins.filter((pin) => `${pin.initiativeId}:${pin.workstreamId}` !== key)].slice(0, MAX_PINS);
|
|
92
|
+
next.updatedAt = now;
|
|
93
|
+
try {
|
|
94
|
+
writeJsonFileAtomicSync(storeFile(), next, 0o600);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// best effort
|
|
98
|
+
}
|
|
99
|
+
return next;
|
|
100
|
+
}
|
|
101
|
+
export function removeNextUpQueuePin(input) {
|
|
102
|
+
const initiativeId = input.initiativeId.trim();
|
|
103
|
+
const workstreamId = input.workstreamId.trim();
|
|
104
|
+
if (!initiativeId || !workstreamId) {
|
|
105
|
+
return readNextUpQueuePins();
|
|
106
|
+
}
|
|
107
|
+
ensureStoreDir();
|
|
108
|
+
const next = readNextUpQueuePins();
|
|
109
|
+
const key = `${initiativeId}:${workstreamId}`;
|
|
110
|
+
const filtered = next.pins.filter((pin) => `${pin.initiativeId}:${pin.workstreamId}` !== key);
|
|
111
|
+
if (filtered.length === next.pins.length)
|
|
112
|
+
return next;
|
|
113
|
+
next.pins = filtered;
|
|
114
|
+
next.updatedAt = new Date().toISOString();
|
|
115
|
+
try {
|
|
116
|
+
writeJsonFileAtomicSync(storeFile(), next, 0o600);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// best effort
|
|
120
|
+
}
|
|
121
|
+
return next;
|
|
122
|
+
}
|
|
123
|
+
export function setNextUpQueuePinOrder(input) {
|
|
124
|
+
ensureStoreDir();
|
|
125
|
+
const next = readNextUpQueuePins();
|
|
126
|
+
const now = new Date().toISOString();
|
|
127
|
+
const byKey = new Map(next.pins.map((pin) => [`${pin.initiativeId}:${pin.workstreamId}`, pin]));
|
|
128
|
+
const ordered = [];
|
|
129
|
+
const seen = new Set();
|
|
130
|
+
for (const entry of input.order) {
|
|
131
|
+
const initiativeId = (entry.initiativeId ?? "").trim();
|
|
132
|
+
const workstreamId = (entry.workstreamId ?? "").trim();
|
|
133
|
+
if (!initiativeId || !workstreamId)
|
|
134
|
+
continue;
|
|
135
|
+
const key = `${initiativeId}:${workstreamId}`;
|
|
136
|
+
if (seen.has(key))
|
|
137
|
+
continue;
|
|
138
|
+
seen.add(key);
|
|
139
|
+
const pin = byKey.get(key);
|
|
140
|
+
if (pin) {
|
|
141
|
+
ordered.push(pin);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
ordered.push({
|
|
145
|
+
initiativeId,
|
|
146
|
+
workstreamId,
|
|
147
|
+
preferredTaskId: null,
|
|
148
|
+
preferredMilestoneId: null,
|
|
149
|
+
createdAt: now,
|
|
150
|
+
updatedAt: now,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
for (const pin of next.pins) {
|
|
155
|
+
const key = `${pin.initiativeId}:${pin.workstreamId}`;
|
|
156
|
+
if (seen.has(key))
|
|
157
|
+
continue;
|
|
158
|
+
ordered.push(pin);
|
|
159
|
+
}
|
|
160
|
+
next.pins = ordered.slice(0, MAX_PINS);
|
|
161
|
+
next.updatedAt = now;
|
|
162
|
+
try {
|
|
163
|
+
writeJsonFileAtomicSync(storeFile(), next, 0o600);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
// best effort
|
|
167
|
+
}
|
|
168
|
+
return next;
|
|
169
|
+
}
|
package/dist/outbox.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import type { LiveActivityItem } from "./types.js";
|
|
7
7
|
export interface OutboxEvent {
|
|
8
8
|
id: string;
|
|
9
|
-
type: "progress" | "decision" | "artifact" | "changeset";
|
|
9
|
+
type: "progress" | "decision" | "artifact" | "changeset" | "retro" | "outcome";
|
|
10
10
|
timestamp: string;
|
|
11
11
|
payload: Record<string, unknown>;
|
|
12
12
|
/** Converted to a LiveActivityItem for dashboard display. */
|
|
@@ -21,7 +21,7 @@ export type RuntimeInstanceRecord = {
|
|
|
21
21
|
id: string;
|
|
22
22
|
sourceClient: RuntimeSourceClient;
|
|
23
23
|
displayName: string;
|
|
24
|
-
providerLogo: "openai" | "anthropic" | "openclaw" | "orgx" | "unknown";
|
|
24
|
+
providerLogo: "codex" | "openai" | "anthropic" | "openclaw" | "orgx" | "unknown";
|
|
25
25
|
state: RuntimeInstanceState;
|
|
26
26
|
event: RuntimeHookEvent;
|
|
27
27
|
runId: string | null;
|
|
@@ -94,6 +94,22 @@ function toProviderLogo(sourceClient) {
|
|
|
94
94
|
return "orgx";
|
|
95
95
|
return "unknown";
|
|
96
96
|
}
|
|
97
|
+
function normalizeProviderLogo(value, sourceClient) {
|
|
98
|
+
const normalized = normalizeNullableString(value)?.toLowerCase();
|
|
99
|
+
if (normalized === "codex")
|
|
100
|
+
return sourceClient === "codex" ? "openai" : "codex";
|
|
101
|
+
if (normalized === "openai")
|
|
102
|
+
return "openai";
|
|
103
|
+
if (normalized === "anthropic")
|
|
104
|
+
return "anthropic";
|
|
105
|
+
if (normalized === "openclaw")
|
|
106
|
+
return "openclaw";
|
|
107
|
+
if (normalized === "orgx")
|
|
108
|
+
return "orgx";
|
|
109
|
+
if (normalized === "unknown")
|
|
110
|
+
return "unknown";
|
|
111
|
+
return toProviderLogo(sourceClient);
|
|
112
|
+
}
|
|
97
113
|
function toDisplayName(sourceClient) {
|
|
98
114
|
if (sourceClient === "codex")
|
|
99
115
|
return "Codex";
|
|
@@ -152,11 +168,12 @@ function normalizeProgress(value) {
|
|
|
152
168
|
return Math.max(0, Math.min(100, Math.round(value)));
|
|
153
169
|
}
|
|
154
170
|
function normalizeRecord(input) {
|
|
171
|
+
const sourceClient = normalizeSourceClient(input.sourceClient);
|
|
155
172
|
return {
|
|
156
173
|
id: normalizeNullableString(input.id) ?? input.id,
|
|
157
|
-
sourceClient
|
|
174
|
+
sourceClient,
|
|
158
175
|
displayName: normalizeNullableString(input.displayName) ?? "Runtime",
|
|
159
|
-
providerLogo: input.providerLogo,
|
|
176
|
+
providerLogo: normalizeProviderLogo(input.providerLogo, sourceClient),
|
|
160
177
|
state: normalizeState(input.state),
|
|
161
178
|
event: normalizeHookEvent(input.event),
|
|
162
179
|
runId: normalizeNullableString(input.runId),
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { SkillPack } from "./contracts/types.js";
|
|
2
|
+
import type { OrgxSkillPackOverrides } from "./agent-suite.js";
|
|
3
|
+
export type SkillPackState = {
|
|
4
|
+
version: 1;
|
|
5
|
+
updatedAt: string;
|
|
6
|
+
lastCheckedAt: string | null;
|
|
7
|
+
lastError: string | null;
|
|
8
|
+
etag: string | null;
|
|
9
|
+
policy: {
|
|
10
|
+
frozen: boolean;
|
|
11
|
+
pinnedChecksum: string | null;
|
|
12
|
+
};
|
|
13
|
+
pack: {
|
|
14
|
+
name: string;
|
|
15
|
+
version: string;
|
|
16
|
+
checksum: string;
|
|
17
|
+
updated_at: string | null;
|
|
18
|
+
} | null;
|
|
19
|
+
remote: {
|
|
20
|
+
name: string;
|
|
21
|
+
version: string;
|
|
22
|
+
checksum: string;
|
|
23
|
+
updated_at: string | null;
|
|
24
|
+
} | null;
|
|
25
|
+
overrides: OrgxSkillPackOverrides | null;
|
|
26
|
+
};
|
|
27
|
+
export declare function readSkillPackState(input?: {
|
|
28
|
+
openclawDir?: string;
|
|
29
|
+
}): SkillPackState;
|
|
30
|
+
export declare function writeSkillPackState(state: SkillPackState, input?: {
|
|
31
|
+
openclawDir?: string;
|
|
32
|
+
}): void;
|
|
33
|
+
export declare function updateSkillPackPolicy(input: {
|
|
34
|
+
openclawDir?: string;
|
|
35
|
+
frozen?: boolean;
|
|
36
|
+
pinnedChecksum?: string | null;
|
|
37
|
+
pinToCurrent?: boolean;
|
|
38
|
+
clearPin?: boolean;
|
|
39
|
+
}): SkillPackState;
|
|
40
|
+
export declare function toOrgxSkillPackOverrides(input: {
|
|
41
|
+
pack: SkillPack;
|
|
42
|
+
etag: string | null;
|
|
43
|
+
}): OrgxSkillPackOverrides;
|
|
44
|
+
export declare function refreshSkillPackState(input: {
|
|
45
|
+
getSkillPack: (args: {
|
|
46
|
+
name?: string;
|
|
47
|
+
ifNoneMatch?: string | null;
|
|
48
|
+
}) => Promise<{
|
|
49
|
+
ok: true;
|
|
50
|
+
notModified: true;
|
|
51
|
+
etag: string | null;
|
|
52
|
+
pack: null;
|
|
53
|
+
} | {
|
|
54
|
+
ok: true;
|
|
55
|
+
notModified: false;
|
|
56
|
+
etag: string | null;
|
|
57
|
+
pack: SkillPack;
|
|
58
|
+
} | {
|
|
59
|
+
ok: false;
|
|
60
|
+
status: number;
|
|
61
|
+
error: string;
|
|
62
|
+
}>;
|
|
63
|
+
packName?: string;
|
|
64
|
+
openclawDir?: string;
|
|
65
|
+
force?: boolean;
|
|
66
|
+
}): Promise<{
|
|
67
|
+
state: SkillPackState;
|
|
68
|
+
changed: boolean;
|
|
69
|
+
}>;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { writeFileAtomicSync } from "./fs-utils.js";
|
|
4
|
+
import { getOpenClawDir } from "./paths.js";
|
|
5
|
+
const STORE_VERSION = 1;
|
|
6
|
+
const STATE_FILENAME = "orgx-skill-pack-state.json";
|
|
7
|
+
function nowIso() {
|
|
8
|
+
return new Date().toISOString();
|
|
9
|
+
}
|
|
10
|
+
function isRecord(value) {
|
|
11
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
12
|
+
}
|
|
13
|
+
function coerceString(value) {
|
|
14
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
15
|
+
}
|
|
16
|
+
function statePath(openclawDir) {
|
|
17
|
+
return join(openclawDir, STATE_FILENAME);
|
|
18
|
+
}
|
|
19
|
+
export function readSkillPackState(input) {
|
|
20
|
+
const openclawDir = input?.openclawDir ?? getOpenClawDir();
|
|
21
|
+
const path = statePath(openclawDir);
|
|
22
|
+
const empty = {
|
|
23
|
+
version: STORE_VERSION,
|
|
24
|
+
updatedAt: nowIso(),
|
|
25
|
+
lastCheckedAt: null,
|
|
26
|
+
lastError: null,
|
|
27
|
+
etag: null,
|
|
28
|
+
policy: { frozen: false, pinnedChecksum: null },
|
|
29
|
+
pack: null,
|
|
30
|
+
remote: null,
|
|
31
|
+
overrides: null,
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
if (!existsSync(path))
|
|
35
|
+
return empty;
|
|
36
|
+
const raw = readFileSync(path, "utf8");
|
|
37
|
+
const parsed = JSON.parse(raw);
|
|
38
|
+
if (!isRecord(parsed))
|
|
39
|
+
return empty;
|
|
40
|
+
if (parsed.version !== STORE_VERSION)
|
|
41
|
+
return empty;
|
|
42
|
+
const policy = isRecord(parsed.policy) ? parsed.policy : null;
|
|
43
|
+
const pack = isRecord(parsed.pack) ? parsed.pack : null;
|
|
44
|
+
const remote = isRecord(parsed.remote) ? parsed.remote : null;
|
|
45
|
+
const overrides = isRecord(parsed.overrides) ? parsed.overrides : null;
|
|
46
|
+
return {
|
|
47
|
+
version: STORE_VERSION,
|
|
48
|
+
updatedAt: coerceString(parsed.updatedAt) ?? nowIso(),
|
|
49
|
+
lastCheckedAt: coerceString(parsed.lastCheckedAt),
|
|
50
|
+
lastError: coerceString(parsed.lastError),
|
|
51
|
+
etag: coerceString(parsed.etag),
|
|
52
|
+
policy: {
|
|
53
|
+
frozen: Boolean(policy?.frozen),
|
|
54
|
+
pinnedChecksum: coerceString(policy?.pinnedChecksum),
|
|
55
|
+
},
|
|
56
|
+
pack: pack
|
|
57
|
+
? {
|
|
58
|
+
name: coerceString(pack.name) ?? "",
|
|
59
|
+
version: coerceString(pack.version) ?? "",
|
|
60
|
+
checksum: coerceString(pack.checksum) ?? "",
|
|
61
|
+
updated_at: coerceString(pack.updated_at),
|
|
62
|
+
}
|
|
63
|
+
: null,
|
|
64
|
+
remote: remote
|
|
65
|
+
? {
|
|
66
|
+
name: coerceString(remote.name) ?? "",
|
|
67
|
+
version: coerceString(remote.version) ?? "",
|
|
68
|
+
checksum: coerceString(remote.checksum) ?? "",
|
|
69
|
+
updated_at: coerceString(remote.updated_at),
|
|
70
|
+
}
|
|
71
|
+
: null,
|
|
72
|
+
overrides: overrides
|
|
73
|
+
? overrides
|
|
74
|
+
: null,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return empty;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function writeSkillPackState(state, input) {
|
|
82
|
+
const openclawDir = input?.openclawDir ?? getOpenClawDir();
|
|
83
|
+
const path = statePath(openclawDir);
|
|
84
|
+
writeFileAtomicSync(path, `${JSON.stringify(state, null, 2)}\n`, {
|
|
85
|
+
mode: 0o600,
|
|
86
|
+
encoding: "utf8",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
export function updateSkillPackPolicy(input) {
|
|
90
|
+
const prev = readSkillPackState({ openclawDir: input.openclawDir });
|
|
91
|
+
const nextPolicy = { ...prev.policy };
|
|
92
|
+
if (typeof input.frozen === "boolean") {
|
|
93
|
+
nextPolicy.frozen = input.frozen;
|
|
94
|
+
}
|
|
95
|
+
if (input.clearPin) {
|
|
96
|
+
nextPolicy.pinnedChecksum = null;
|
|
97
|
+
}
|
|
98
|
+
else if (input.pinToCurrent) {
|
|
99
|
+
nextPolicy.pinnedChecksum = prev.pack?.checksum ?? prev.remote?.checksum ?? null;
|
|
100
|
+
}
|
|
101
|
+
else if (typeof input.pinnedChecksum === "string") {
|
|
102
|
+
nextPolicy.pinnedChecksum = input.pinnedChecksum.trim() || null;
|
|
103
|
+
}
|
|
104
|
+
else if (input.pinnedChecksum === null) {
|
|
105
|
+
nextPolicy.pinnedChecksum = null;
|
|
106
|
+
}
|
|
107
|
+
const next = {
|
|
108
|
+
...prev,
|
|
109
|
+
updatedAt: nowIso(),
|
|
110
|
+
policy: nextPolicy,
|
|
111
|
+
};
|
|
112
|
+
writeSkillPackState(next, { openclawDir: input.openclawDir });
|
|
113
|
+
return next;
|
|
114
|
+
}
|
|
115
|
+
function asRecord(value) {
|
|
116
|
+
return isRecord(value) ? value : null;
|
|
117
|
+
}
|
|
118
|
+
function parseOpenclawSkillOverridesFromManifest(manifest) {
|
|
119
|
+
const root = manifest ?? {};
|
|
120
|
+
const candidates = [
|
|
121
|
+
asRecord(root.openclaw_skills),
|
|
122
|
+
asRecord(root.openclawSkills),
|
|
123
|
+
asRecord(asRecord(root.openclaw)?.skills),
|
|
124
|
+
].filter(Boolean);
|
|
125
|
+
const out = {};
|
|
126
|
+
for (const candidate of candidates) {
|
|
127
|
+
for (const [k, v] of Object.entries(candidate)) {
|
|
128
|
+
if (typeof v !== "string")
|
|
129
|
+
continue;
|
|
130
|
+
const key = k.trim().toLowerCase();
|
|
131
|
+
if (!key)
|
|
132
|
+
continue;
|
|
133
|
+
out[key] = v;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
export function toOrgxSkillPackOverrides(input) {
|
|
139
|
+
const manifest = asRecord(input.pack.manifest) ?? {};
|
|
140
|
+
const rawOverrides = parseOpenclawSkillOverridesFromManifest(manifest);
|
|
141
|
+
const openclaw_skills = {};
|
|
142
|
+
for (const [k, v] of Object.entries(rawOverrides)) {
|
|
143
|
+
// Domains are normalized to lowercase keys in the manifest.
|
|
144
|
+
openclaw_skills[k] = v;
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
source: "server",
|
|
148
|
+
name: input.pack.name,
|
|
149
|
+
version: input.pack.version,
|
|
150
|
+
checksum: input.pack.checksum,
|
|
151
|
+
etag: input.etag,
|
|
152
|
+
updated_at: input.pack.updated_at ?? null,
|
|
153
|
+
openclaw_skills,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
export async function refreshSkillPackState(input) {
|
|
157
|
+
const packName = (input.packName ?? "").trim() || "orgx-agent-suite";
|
|
158
|
+
const prev = readSkillPackState({ openclawDir: input.openclawDir });
|
|
159
|
+
if (!input.force && prev.policy.frozen) {
|
|
160
|
+
const next = {
|
|
161
|
+
...prev,
|
|
162
|
+
updatedAt: nowIso(),
|
|
163
|
+
lastCheckedAt: nowIso(),
|
|
164
|
+
lastError: null,
|
|
165
|
+
};
|
|
166
|
+
writeSkillPackState(next, { openclawDir: input.openclawDir });
|
|
167
|
+
return { state: next, changed: false };
|
|
168
|
+
}
|
|
169
|
+
const result = await input.getSkillPack({
|
|
170
|
+
name: packName,
|
|
171
|
+
ifNoneMatch: input.force ? null : prev.etag,
|
|
172
|
+
});
|
|
173
|
+
if (result.ok && result.notModified) {
|
|
174
|
+
const next = {
|
|
175
|
+
...prev,
|
|
176
|
+
updatedAt: nowIso(),
|
|
177
|
+
lastCheckedAt: nowIso(),
|
|
178
|
+
lastError: null,
|
|
179
|
+
etag: result.etag ?? prev.etag,
|
|
180
|
+
};
|
|
181
|
+
writeSkillPackState(next, { openclawDir: input.openclawDir });
|
|
182
|
+
return { state: next, changed: false };
|
|
183
|
+
}
|
|
184
|
+
if (result.ok && !result.notModified && result.pack) {
|
|
185
|
+
const remoteMeta = {
|
|
186
|
+
name: result.pack.name,
|
|
187
|
+
version: result.pack.version,
|
|
188
|
+
checksum: result.pack.checksum,
|
|
189
|
+
updated_at: result.pack.updated_at ?? null,
|
|
190
|
+
};
|
|
191
|
+
if (prev.policy.pinnedChecksum &&
|
|
192
|
+
prev.policy.pinnedChecksum !== result.pack.checksum) {
|
|
193
|
+
const next = {
|
|
194
|
+
...prev,
|
|
195
|
+
updatedAt: nowIso(),
|
|
196
|
+
lastCheckedAt: nowIso(),
|
|
197
|
+
lastError: null,
|
|
198
|
+
etag: result.etag ?? prev.etag,
|
|
199
|
+
remote: remoteMeta,
|
|
200
|
+
};
|
|
201
|
+
writeSkillPackState(next, { openclawDir: input.openclawDir });
|
|
202
|
+
return { state: next, changed: false };
|
|
203
|
+
}
|
|
204
|
+
const overrides = toOrgxSkillPackOverrides({ pack: result.pack, etag: result.etag ?? null });
|
|
205
|
+
const next = {
|
|
206
|
+
version: STORE_VERSION,
|
|
207
|
+
updatedAt: nowIso(),
|
|
208
|
+
lastCheckedAt: nowIso(),
|
|
209
|
+
lastError: null,
|
|
210
|
+
etag: result.etag ?? null,
|
|
211
|
+
policy: prev.policy,
|
|
212
|
+
pack: {
|
|
213
|
+
name: result.pack.name,
|
|
214
|
+
version: result.pack.version,
|
|
215
|
+
checksum: result.pack.checksum,
|
|
216
|
+
updated_at: result.pack.updated_at ?? null,
|
|
217
|
+
},
|
|
218
|
+
remote: remoteMeta,
|
|
219
|
+
overrides,
|
|
220
|
+
};
|
|
221
|
+
writeSkillPackState(next, { openclawDir: input.openclawDir });
|
|
222
|
+
return { state: next, changed: prev.pack?.checksum !== next.pack?.checksum };
|
|
223
|
+
}
|
|
224
|
+
const next = {
|
|
225
|
+
...prev,
|
|
226
|
+
updatedAt: nowIso(),
|
|
227
|
+
lastCheckedAt: nowIso(),
|
|
228
|
+
lastError: !result.ok ? result.error : prev.lastError,
|
|
229
|
+
};
|
|
230
|
+
writeSkillPackState(next, { openclawDir: input.openclawDir });
|
|
231
|
+
return { state: next, changed: false };
|
|
232
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type WorkerKillDecision = {
|
|
2
|
+
kill: false;
|
|
3
|
+
elapsedMs: number;
|
|
4
|
+
idleMs: number;
|
|
5
|
+
} | {
|
|
6
|
+
kill: true;
|
|
7
|
+
kind: "timeout" | "log_stall";
|
|
8
|
+
reason: string;
|
|
9
|
+
elapsedMs: number;
|
|
10
|
+
idleMs: number;
|
|
11
|
+
};
|
|
12
|
+
export type McpHandshakeFailure = {
|
|
13
|
+
kind: "mcp_handshake";
|
|
14
|
+
server: string | null;
|
|
15
|
+
line: string | null;
|
|
16
|
+
};
|
|
17
|
+
export declare function detectMcpHandshakeFailure(logText: unknown): McpHandshakeFailure | null;
|
|
18
|
+
export declare function shouldKillWorker(input: {
|
|
19
|
+
nowEpochMs: number;
|
|
20
|
+
startedAtEpochMs: number;
|
|
21
|
+
logUpdatedAtEpochMs: number;
|
|
22
|
+
}, limits: {
|
|
23
|
+
timeoutMs: number;
|
|
24
|
+
stallMs: number;
|
|
25
|
+
}): WorkerKillDecision;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
function pickString(value) {
|
|
2
|
+
if (typeof value !== "string")
|
|
3
|
+
return null;
|
|
4
|
+
const trimmed = value.trim();
|
|
5
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
6
|
+
}
|
|
7
|
+
function parseIgnoredMcpServers(raw) {
|
|
8
|
+
if (typeof raw !== "string")
|
|
9
|
+
return new Set(["codex_apps"]);
|
|
10
|
+
const trimmed = raw.trim();
|
|
11
|
+
if (!trimmed || trimmed.toLowerCase() === "none")
|
|
12
|
+
return new Set();
|
|
13
|
+
return new Set(trimmed
|
|
14
|
+
.split(",")
|
|
15
|
+
.map((entry) => entry.trim().toLowerCase())
|
|
16
|
+
.filter(Boolean));
|
|
17
|
+
}
|
|
18
|
+
export function detectMcpHandshakeFailure(logText) {
|
|
19
|
+
const text = String(logText ?? "");
|
|
20
|
+
const lower = text.toLowerCase();
|
|
21
|
+
const handshakeSignals = [
|
|
22
|
+
"mcp startup failed",
|
|
23
|
+
"handshaking with mcp server failed",
|
|
24
|
+
"initialize response",
|
|
25
|
+
"send message error transport",
|
|
26
|
+
];
|
|
27
|
+
if (!handshakeSignals.some((needle) => lower.includes(needle))) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const lines = text
|
|
31
|
+
.split("\n")
|
|
32
|
+
.map((line) => line.trim())
|
|
33
|
+
.filter(Boolean);
|
|
34
|
+
const signalLine = lines.find((line) => /mcp startup failed|handshaking with mcp server failed/i.test(line)) ??
|
|
35
|
+
lines.find((line) => /initialize response|send message error transport/i.test(line)) ??
|
|
36
|
+
null;
|
|
37
|
+
const serverMatch = signalLine?.match(/mcp(?:\s*:\s*)?\s*([a-z0-9_-]+)\s+failed:/i) ??
|
|
38
|
+
signalLine?.match(/mcp client for\s+`?([^`]+)`?\s+failed to start/i) ??
|
|
39
|
+
signalLine?.match(/mcp client for\s+\[?([^\]]+)\]?\s+failed to start/i) ??
|
|
40
|
+
null;
|
|
41
|
+
const server = serverMatch ? pickString(serverMatch[1]) ?? null : null;
|
|
42
|
+
const ignoredServers = parseIgnoredMcpServers(process.env.ORGX_AUTOPILOT_MCP_HANDSHAKE_IGNORE_SERVERS);
|
|
43
|
+
if (server && ignoredServers.has(server.toLowerCase())) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
kind: "mcp_handshake",
|
|
48
|
+
server,
|
|
49
|
+
line: signalLine,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function shouldKillWorker(input, limits) {
|
|
53
|
+
const now = Number(input.nowEpochMs) || Date.now();
|
|
54
|
+
const startedAt = Number(input.startedAtEpochMs) || now;
|
|
55
|
+
const logUpdatedAt = Number(input.logUpdatedAtEpochMs) || startedAt;
|
|
56
|
+
const elapsedMs = Math.max(0, now - startedAt);
|
|
57
|
+
const idleMs = Math.max(0, now - logUpdatedAt);
|
|
58
|
+
if (Number.isFinite(limits.timeoutMs) && limits.timeoutMs > 0 && elapsedMs > limits.timeoutMs) {
|
|
59
|
+
return {
|
|
60
|
+
kill: true,
|
|
61
|
+
kind: "timeout",
|
|
62
|
+
reason: `Worker exceeded timeout (${Math.round(limits.timeoutMs / 1_000)}s)`,
|
|
63
|
+
elapsedMs,
|
|
64
|
+
idleMs,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (Number.isFinite(limits.stallMs) && limits.stallMs > 0 && idleMs > limits.stallMs) {
|
|
68
|
+
return {
|
|
69
|
+
kill: true,
|
|
70
|
+
kind: "log_stall",
|
|
71
|
+
reason: `Worker log stalled (${Math.round(limits.stallMs / 1_000)}s)`,
|
|
72
|
+
elapsedMs,
|
|
73
|
+
idleMs,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return { kill: false, elapsedMs, idleMs };
|
|
77
|
+
}
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@useorgx/openclaw-plugin",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.8",
|
|
4
4
|
"description": "OrgX plugin for OpenClaw — agent orchestration, quality gates, model routing, and live dashboard",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -35,10 +35,24 @@
|
|
|
35
35
|
"typecheck": "tsc --noEmit",
|
|
36
36
|
"test:hooks": "npm run build:core && node --test --test-concurrency=1 tests/*.test.mjs tests/**/*.test.mjs",
|
|
37
37
|
"job:dispatch": "npm run build:core && node ./scripts/run-codex-dispatch-job.mjs",
|
|
38
|
+
"agents:refresh": "node ./scripts/daily-agents-refresh.mjs",
|
|
39
|
+
"agents:install": "node ./scripts/install-daily-agents-refresh.mjs",
|
|
38
40
|
"verify:clean-install": "node ./scripts/verify-clean-install.mjs",
|
|
41
|
+
"verify:agent-suite": "npm run build:core && node ./scripts/verify-agent-suite-install.mjs",
|
|
39
42
|
"verify:billing": "npm run build:core && node ./scripts/verify-billing-scenarios.mjs",
|
|
43
|
+
"verify:autopilot-e2e:local": "npm run build:core && node ./scripts/verify-autopilot-e2e-local.mjs",
|
|
44
|
+
"verify:autopilot-e2e:matrix": "npm run build:core && node ./scripts/verify-autopilot-e2e-matrix.mjs",
|
|
45
|
+
"verify:autopilot-e2e:entities": "npm run build:core && node ./scripts/verify-autopilot-e2e-entities.mjs",
|
|
46
|
+
"verify:autopilot-e2e:entities-matrix": "npm run build:core && node ./scripts/verify-autopilot-e2e-entities-matrix.mjs",
|
|
47
|
+
"verify:iwmt-cascade": "npm run build:core && node ./scripts/verify-iwmt-cascade-e2e.mjs",
|
|
48
|
+
"e2e:auto-continue": "node ./scripts/e2e-auto-continue.mjs",
|
|
49
|
+
"e2e:agent-suite": "npm run build:core && node ./scripts/e2e-agent-suite-kickoff-3x.mjs",
|
|
50
|
+
"demo:record": "node ./scripts/record-demo.mjs",
|
|
40
51
|
"qa:capture": "node ./scripts/capture-qa-evidence.mjs",
|
|
52
|
+
"ship": "node ./scripts/ship.mjs",
|
|
41
53
|
"ops:launch-checklist": "node ./scripts/run-launch-checklist.mjs",
|
|
54
|
+
"seo:plan": "node ./scripts/apply-seo-plan-v1.mjs",
|
|
55
|
+
"seo:run": "node ./scripts/seo/run.mjs",
|
|
42
56
|
"install:dashboard": "npm --prefix dashboard ci",
|
|
43
57
|
"build:dashboard": "npm run install:dashboard && npm --prefix dashboard run build",
|
|
44
58
|
"build:core": "rm -rf dist && tsc && node ./scripts/copy-manifest.mjs",
|