velaclaw-dev 0.2.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/.gitignore +14 -0
- package/ARCHITECTURE.md +143 -0
- package/README.dev.md +208 -0
- package/README.local-before-remote-sync.md +224 -0
- package/README.md +211 -0
- package/README.public.md +115 -0
- package/RELEASING.md +162 -0
- package/TESTING.md +195 -0
- package/dist/cli.js +213 -0
- package/dist/data.js +2988 -0
- package/dist/server.js +1020 -0
- package/dist/ui.js +1486 -0
- package/members/LAUNCH_CHECKLIST.md +13 -0
- package/members/README.md +17 -0
- package/members/member-template/README.md +9 -0
- package/members/member-template/private-docs/README.md +3 -0
- package/members/member-template/private-memory/README.md +3 -0
- package/members/member-template/private-skills/README.md +4 -0
- package/members/member-template/private-tools/README.md +4 -0
- package/members/member-template/runtime/config/README.md +3 -0
- package/members/member-template/runtime/config/local-plugins/member-quota-guard/index.js +123 -0
- package/members/member-template/runtime/config/local-plugins/member-quota-guard/openclaw.plugin.json +19 -0
- package/members/member-template/runtime/config/local-plugins/member-quota-guard/package.json +10 -0
- package/members/member-template/runtime/config/local-plugins/member-runtime-upgrader/index.js +97 -0
- package/members/member-template/runtime/config/local-plugins/member-runtime-upgrader/openclaw.plugin.json +21 -0
- package/members/member-template/runtime/config/local-plugins/member-runtime-upgrader/package.json +10 -0
- package/members/member-template/runtime/config/local-plugins/shared-asset-injector/index.js +548 -0
- package/members/member-template/runtime/config/local-plugins/shared-asset-injector/openclaw.plugin.json +33 -0
- package/members/member-template/runtime/config/local-plugins/shared-asset-injector/package.json +10 -0
- package/members/member-template/runtime/config/openclaw.json +104 -0
- package/members/member-template/runtime/docker-compose.yml +53 -0
- package/members/member-template/runtime/logs/README.md +3 -0
- package/members/member-template/runtime/secrets/.gitkeep +1 -0
- package/members/member-template/runtime/secrets/README.md +3 -0
- package/members/member-template/runtime/workspace/.gitkeep +1 -0
- package/members/member-template/runtime/workspace/README.md +3 -0
- package/package.json +57 -0
- package/pic/banner.jpg +0 -0
- package/provision-member.md +87 -0
- package/scripts/shared-asset-stack-test.mjs +369 -0
- package/scripts/shared-skill-combo-test.mjs +282 -0
- package/scripts/team-load-test.mjs +358 -0
- package/scripts/verify-install.mjs +44 -0
- package/services/litellm/config.yaml +35 -0
- package/services/litellm/docker-compose.yml +36 -0
- package/services/litellm/litellm.env.example +13 -0
- package/shared-snapshots/README.md +16 -0
- package/shared-snapshots/docs/README.md +3 -0
- package/shared-snapshots/memory/README.md +3 -0
- package/shared-snapshots/skills/README.md +3 -0
- package/shared-snapshots/tools/README.md +4 -0
- package/shared-snapshots/workflows/README.md +3 -0
- package/team-assets/README.md +11 -0
- package/team-assets/policies/README.md +7 -0
- package/team-assets/policies/asset-visibility.md +24 -0
- package/team-assets/policies/high-risk-action-approval.md +18 -0
- package/team-assets/policies/promotion-rules.md +25 -0
- package/team-assets/policies/tool-binding-rules.md +26 -0
- package/team-assets/shared-docs/README.md +3 -0
- package/team-assets/shared-memory/README.md +8 -0
- package/team-assets/shared-skills/README.md +8 -0
- package/team-assets/shared-tools/README.md +8 -0
- package/team-assets/shared-workflows/README.md +9 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Member runtime launch checklist
|
|
2
|
+
|
|
3
|
+
Before launching a member runtime, verify:
|
|
4
|
+
|
|
5
|
+
- [ ] member directory copied from template
|
|
6
|
+
- [ ] `runtime/config/openclaw.json` placeholders replaced
|
|
7
|
+
- [ ] `runtime/secrets/` contains only this member's secrets
|
|
8
|
+
- [ ] `runtime/docker-compose.yml` has unique member ID and port
|
|
9
|
+
- [ ] no Docker socket mount present
|
|
10
|
+
- [ ] no primary workspace mount present
|
|
11
|
+
- [ ] team assets are not mounted writable
|
|
12
|
+
- [ ] high-risk actions remain approval-gated
|
|
13
|
+
- [ ] Telegram allowlist points to the intended member only
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Member runtimes
|
|
2
|
+
|
|
3
|
+
Each member should get a separate directory under this path.
|
|
4
|
+
|
|
5
|
+
Recommended layout per member:
|
|
6
|
+
|
|
7
|
+
- `private-memory/`
|
|
8
|
+
- `private-skills/`
|
|
9
|
+
- `private-tools/`
|
|
10
|
+
- `private-docs/`
|
|
11
|
+
- `runtime/`
|
|
12
|
+
- `config/`
|
|
13
|
+
- `workspace/`
|
|
14
|
+
- `secrets/`
|
|
15
|
+
- `logs/`
|
|
16
|
+
|
|
17
|
+
Duplicate the `member-template/` directory to create a new member runtime.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
4
|
+
|
|
5
|
+
const DEFAULT_POLICY_PATH = "/home/node/.openclaw/team-policy.json";
|
|
6
|
+
const DEFAULT_USAGE_PATH = "/home/node/.openclaw/team-usage.json";
|
|
7
|
+
|
|
8
|
+
async function safeReadJson(filePath) {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(await fs.readFile(filePath, "utf8"));
|
|
11
|
+
} catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function safeWriteJson(filePath, value) {
|
|
17
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
18
|
+
await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function currentPeriods() {
|
|
22
|
+
const now = new Date();
|
|
23
|
+
const day = now.toISOString().slice(0, 10);
|
|
24
|
+
const month = day.slice(0, 7);
|
|
25
|
+
return { day, month };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function defaultUsage(periods) {
|
|
29
|
+
return {
|
|
30
|
+
day: periods.day,
|
|
31
|
+
month: periods.month,
|
|
32
|
+
dailyCount: 0,
|
|
33
|
+
monthlyCount: 0,
|
|
34
|
+
lastSeenAt: null
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function loadPolicyAndUsage(config) {
|
|
39
|
+
const policyPath = config?.policyPath || DEFAULT_POLICY_PATH;
|
|
40
|
+
const usagePath = config?.usagePath || DEFAULT_USAGE_PATH;
|
|
41
|
+
const policy = await safeReadJson(policyPath);
|
|
42
|
+
const periods = currentPeriods();
|
|
43
|
+
const existingUsage = (await safeReadJson(usagePath)) || defaultUsage(periods);
|
|
44
|
+
const usage = {
|
|
45
|
+
...defaultUsage(periods),
|
|
46
|
+
...existingUsage
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (usage.day !== periods.day) {
|
|
50
|
+
usage.day = periods.day;
|
|
51
|
+
usage.dailyCount = 0;
|
|
52
|
+
}
|
|
53
|
+
if (usage.month !== periods.month) {
|
|
54
|
+
usage.month = periods.month;
|
|
55
|
+
usage.monthlyCount = 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { policyPath, usagePath, policy, usage };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isCommandOnly(text) {
|
|
62
|
+
const value = String(text || "").trim();
|
|
63
|
+
return value.startsWith("/");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default definePluginEntry({
|
|
67
|
+
id: "member-quota-guard",
|
|
68
|
+
name: "Member Quota Guard",
|
|
69
|
+
description: "Enforce manager-owned runtime quota and pause policy for member claws",
|
|
70
|
+
register(api) {
|
|
71
|
+
api.registerHook("before_dispatch", async (event) => {
|
|
72
|
+
const body = String(event.body || event.content || "").trim();
|
|
73
|
+
if (!body) return;
|
|
74
|
+
if (isCommandOnly(body)) return;
|
|
75
|
+
|
|
76
|
+
const config = api.pluginConfig || {};
|
|
77
|
+
const { usagePath, policy, usage } = await loadPolicyAndUsage(config);
|
|
78
|
+
const quota = policy?.quota;
|
|
79
|
+
if (!quota) return;
|
|
80
|
+
|
|
81
|
+
if (quota.status === "paused") {
|
|
82
|
+
return {
|
|
83
|
+
handled: true,
|
|
84
|
+
text: "当前这个成员小虾已被 team manager 暂停使用。请联系团队管理员恢复配额。"
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (usage.dailyCount >= quota.dailyMessages) {
|
|
89
|
+
return {
|
|
90
|
+
handled: true,
|
|
91
|
+
text: `今天的消息配额已用完(${quota.dailyMessages}/day)。请联系 team manager 提额或明天再试。`
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (usage.monthlyCount >= quota.monthlyMessages) {
|
|
96
|
+
return {
|
|
97
|
+
handled: true,
|
|
98
|
+
text: `本月的消息配额已用完(${quota.monthlyMessages}/month)。请联系 team manager 提额。`
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
usage.dailyCount += 1;
|
|
103
|
+
usage.monthlyCount += 1;
|
|
104
|
+
usage.lastSeenAt = new Date().toISOString();
|
|
105
|
+
await safeWriteJson(usagePath, usage);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
api.registerHook("before_tool_call", async (event) => {
|
|
109
|
+
if (event.toolName !== "subagents") return;
|
|
110
|
+
|
|
111
|
+
const config = api.pluginConfig || {};
|
|
112
|
+
const { policy } = await loadPolicyAndUsage(config);
|
|
113
|
+
const quota = policy?.quota;
|
|
114
|
+
if (!quota) return;
|
|
115
|
+
if (quota.maxSubagents > 0) return;
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
block: true,
|
|
119
|
+
blockReason: "Subagents are disabled for this member by team manager quota."
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
});
|
package/members/member-template/runtime/config/local-plugins/member-quota-guard/openclaw.plugin.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "member-quota-guard",
|
|
3
|
+
"name": "Member Quota Guard",
|
|
4
|
+
"description": "Enforces manager-owned member runtime policy for pause state and message quotas.",
|
|
5
|
+
"configSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"policyPath": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"default": "/home/node/.openclaw/team-policy.json"
|
|
12
|
+
},
|
|
13
|
+
"usagePath": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"default": "/home/node/.openclaw/team-usage.json"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
let definePluginEntry = (entry) => entry;
|
|
4
|
+
try {
|
|
5
|
+
const runtimeRequire = createRequire("/app/package.json");
|
|
6
|
+
({ definePluginEntry } = runtimeRequire("openclaw/plugin-sdk/plugin-entry"));
|
|
7
|
+
} catch {}
|
|
8
|
+
|
|
9
|
+
const DEFAULT_API_BASE_URL = "http://host.docker.internal:4318";
|
|
10
|
+
|
|
11
|
+
async function fetchJson(url, init = {}) {
|
|
12
|
+
const response = await fetch(url, {
|
|
13
|
+
headers: {
|
|
14
|
+
Accept: "application/json",
|
|
15
|
+
...(init.body ? { "Content-Type": "application/json" } : {}),
|
|
16
|
+
...(init.headers || {})
|
|
17
|
+
},
|
|
18
|
+
...init
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const text = await response.text();
|
|
22
|
+
let payload;
|
|
23
|
+
try {
|
|
24
|
+
payload = text ? JSON.parse(text) : null;
|
|
25
|
+
} catch {
|
|
26
|
+
payload = text;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
const message =
|
|
31
|
+
payload && typeof payload === "object" && typeof payload.error === "string"
|
|
32
|
+
? payload.error
|
|
33
|
+
: typeof payload === "string"
|
|
34
|
+
? payload
|
|
35
|
+
: `${response.status} ${response.statusText}`;
|
|
36
|
+
throw new Error(message);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return payload;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function scheduleUpgrade(config) {
|
|
43
|
+
const apiBaseUrl = config?.apiBaseUrl || DEFAULT_API_BASE_URL;
|
|
44
|
+
const teamSlug = String(config?.teamSlug || "").trim();
|
|
45
|
+
const memberId = String(config?.memberId || "").trim();
|
|
46
|
+
if (!teamSlug || !memberId) {
|
|
47
|
+
throw new Error("upgrade plugin config is incomplete");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return fetchJson(`${apiBaseUrl}/api/teams/${encodeURIComponent(teamSlug)}/members/${encodeURIComponent(memberId)}/upgrade`, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
body: JSON.stringify({
|
|
53
|
+
notifyChannel: config?.notifyChannel || undefined,
|
|
54
|
+
notifyTo: config?.notifyTo ? `${config.notifyChannel}:${config.notifyTo}` : undefined
|
|
55
|
+
})
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function resultText(result) {
|
|
60
|
+
if (result?.status === "already_in_progress") {
|
|
61
|
+
return "当前已有升级任务在进行中,稍等几十秒再试。";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return [
|
|
65
|
+
"已开始升级当前成员 runtime。",
|
|
66
|
+
"我会同步主模板、拉取最新镜像并自动重建容器。",
|
|
67
|
+
"通常 20 到 60 秒恢复,恢复后我会再发一条完成通知。"
|
|
68
|
+
].join("\n");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default definePluginEntry({
|
|
72
|
+
id: "member-runtime-upgrader",
|
|
73
|
+
name: "Member Runtime Upgrader",
|
|
74
|
+
description: "Schedule a self-upgrade for the current member runtime.",
|
|
75
|
+
register(api) {
|
|
76
|
+
api.registerCommand({
|
|
77
|
+
name: "upgrade",
|
|
78
|
+
description: "Upgrade this member runtime from the latest team template and image.",
|
|
79
|
+
acceptsArgs: false,
|
|
80
|
+
nativeNames: { default: "upgrade" },
|
|
81
|
+
handler: async (ctx) => {
|
|
82
|
+
try {
|
|
83
|
+
const result = await scheduleUpgrade({
|
|
84
|
+
...(api.pluginConfig || {}),
|
|
85
|
+
notifyChannel: String(ctx?.channel || "").trim(),
|
|
86
|
+
notifyTo: String(ctx?.senderId || "").trim()
|
|
87
|
+
});
|
|
88
|
+
return { text: resultText(result) };
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return {
|
|
91
|
+
text: `升级失败: ${error instanceof Error ? error.message : String(error)}`
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "member-runtime-upgrader",
|
|
3
|
+
"name": "Member Runtime Upgrader",
|
|
4
|
+
"description": "Lets a member runtime schedule its own template/image upgrade via /upgrade.",
|
|
5
|
+
"configSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"apiBaseUrl": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"default": "http://host.docker.internal:4318"
|
|
12
|
+
},
|
|
13
|
+
"teamSlug": {
|
|
14
|
+
"type": "string"
|
|
15
|
+
},
|
|
16
|
+
"memberId": {
|
|
17
|
+
"type": "string"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|