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.
Files changed (63) hide show
  1. package/.gitignore +14 -0
  2. package/ARCHITECTURE.md +143 -0
  3. package/README.dev.md +208 -0
  4. package/README.local-before-remote-sync.md +224 -0
  5. package/README.md +211 -0
  6. package/README.public.md +115 -0
  7. package/RELEASING.md +162 -0
  8. package/TESTING.md +195 -0
  9. package/dist/cli.js +213 -0
  10. package/dist/data.js +2988 -0
  11. package/dist/server.js +1020 -0
  12. package/dist/ui.js +1486 -0
  13. package/members/LAUNCH_CHECKLIST.md +13 -0
  14. package/members/README.md +17 -0
  15. package/members/member-template/README.md +9 -0
  16. package/members/member-template/private-docs/README.md +3 -0
  17. package/members/member-template/private-memory/README.md +3 -0
  18. package/members/member-template/private-skills/README.md +4 -0
  19. package/members/member-template/private-tools/README.md +4 -0
  20. package/members/member-template/runtime/config/README.md +3 -0
  21. package/members/member-template/runtime/config/local-plugins/member-quota-guard/index.js +123 -0
  22. package/members/member-template/runtime/config/local-plugins/member-quota-guard/openclaw.plugin.json +19 -0
  23. package/members/member-template/runtime/config/local-plugins/member-quota-guard/package.json +10 -0
  24. package/members/member-template/runtime/config/local-plugins/member-runtime-upgrader/index.js +97 -0
  25. package/members/member-template/runtime/config/local-plugins/member-runtime-upgrader/openclaw.plugin.json +21 -0
  26. package/members/member-template/runtime/config/local-plugins/member-runtime-upgrader/package.json +10 -0
  27. package/members/member-template/runtime/config/local-plugins/shared-asset-injector/index.js +548 -0
  28. package/members/member-template/runtime/config/local-plugins/shared-asset-injector/openclaw.plugin.json +33 -0
  29. package/members/member-template/runtime/config/local-plugins/shared-asset-injector/package.json +10 -0
  30. package/members/member-template/runtime/config/openclaw.json +104 -0
  31. package/members/member-template/runtime/docker-compose.yml +53 -0
  32. package/members/member-template/runtime/logs/README.md +3 -0
  33. package/members/member-template/runtime/secrets/.gitkeep +1 -0
  34. package/members/member-template/runtime/secrets/README.md +3 -0
  35. package/members/member-template/runtime/workspace/.gitkeep +1 -0
  36. package/members/member-template/runtime/workspace/README.md +3 -0
  37. package/package.json +57 -0
  38. package/pic/banner.jpg +0 -0
  39. package/provision-member.md +87 -0
  40. package/scripts/shared-asset-stack-test.mjs +369 -0
  41. package/scripts/shared-skill-combo-test.mjs +282 -0
  42. package/scripts/team-load-test.mjs +358 -0
  43. package/scripts/verify-install.mjs +44 -0
  44. package/services/litellm/config.yaml +35 -0
  45. package/services/litellm/docker-compose.yml +36 -0
  46. package/services/litellm/litellm.env.example +13 -0
  47. package/shared-snapshots/README.md +16 -0
  48. package/shared-snapshots/docs/README.md +3 -0
  49. package/shared-snapshots/memory/README.md +3 -0
  50. package/shared-snapshots/skills/README.md +3 -0
  51. package/shared-snapshots/tools/README.md +4 -0
  52. package/shared-snapshots/workflows/README.md +3 -0
  53. package/team-assets/README.md +11 -0
  54. package/team-assets/policies/README.md +7 -0
  55. package/team-assets/policies/asset-visibility.md +24 -0
  56. package/team-assets/policies/high-risk-action-approval.md +18 -0
  57. package/team-assets/policies/promotion-rules.md +25 -0
  58. package/team-assets/policies/tool-binding-rules.md +26 -0
  59. package/team-assets/shared-docs/README.md +3 -0
  60. package/team-assets/shared-memory/README.md +8 -0
  61. package/team-assets/shared-skills/README.md +8 -0
  62. package/team-assets/shared-tools/README.md +8 -0
  63. 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,9 @@
1
+ # Member template
2
+
3
+ Copy this directory to `../<member-id>/` when provisioning a new member runtime.
4
+
5
+ After copying:
6
+ - set the member ID in config
7
+ - add the member's channel token(s)
8
+ - set allowlist user IDs
9
+ - keep all secrets private to that member
@@ -0,0 +1,3 @@
1
+ # Private member docs
2
+
3
+ Keep drafts, notes, and member-private references here.
@@ -0,0 +1,3 @@
1
+ # Private member memory
2
+
3
+ Store only this member's private memory here.
@@ -0,0 +1,4 @@
1
+ # Private member skills
2
+
3
+ Member-specific skills live here.
4
+ If a skill becomes broadly useful, publish a reviewed copy into team shared skills.
@@ -0,0 +1,4 @@
1
+ # Private member tools
2
+
3
+ Keep only member-private tool bindings or metadata here.
4
+ Do not place team-wide shared credentials here.
@@ -0,0 +1,3 @@
1
+ # Runtime config directory
2
+
3
+ Put the member runtime's OpenClaw config here.
@@ -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
+ });
@@ -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,10 @@
1
+ {
2
+ "name": "@local/member-quota-guard",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "openclaw": {
6
+ "extensions": [
7
+ "./index.js"
8
+ ]
9
+ }
10
+ }
@@ -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
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@local/member-runtime-upgrader",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "openclaw": {
6
+ "extensions": [
7
+ "./index.js"
8
+ ]
9
+ }
10
+ }