@grwnd/openclaw-governance 2.0.0 → 3.0.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 +3 -3
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@grwnd/openclaw-governance)
|
|
4
4
|
[](../../LICENSE)
|
|
5
5
|
|
|
6
|
-
OpenClaw identity bridge plugin for [@grwnd/pi-governance](https://github.com/
|
|
6
|
+
OpenClaw identity bridge plugin for [@grwnd/pi-governance](https://github.com/dtmirizzi/pi-governance).
|
|
7
7
|
|
|
8
8
|
Parses OpenClaw session keys (WhatsApp, Discord, Slack, Telegram) and maps channel users to governance roles — so pi-governance enforces the right RBAC policy per user without any manual env var setup.
|
|
9
9
|
|
|
@@ -14,7 +14,7 @@ OpenClaw session_start
|
|
|
14
14
|
→ @grwnd/openclaw-governance plugin
|
|
15
15
|
→ parse sessionKey "agent:<id>:whatsapp:dm:+15550123"
|
|
16
16
|
→ lookup "whatsapp:+15550123" in openclaw-users.yaml
|
|
17
|
-
→ write process.env.
|
|
17
|
+
→ write process.env.PI_GOV_USER, PI_GOV_ROLE, PI_GOV_ORG_UNIT
|
|
18
18
|
→ @grwnd/pi-governance Pi extension
|
|
19
19
|
→ EnvIdentityProvider reads the env vars
|
|
20
20
|
→ governance enforced with correct role
|
|
@@ -78,7 +78,7 @@ If `users_file` is omitted, it defaults to `./openclaw-users.yaml` in the curren
|
|
|
78
78
|
|
|
79
79
|
### 4. Set up governance rules
|
|
80
80
|
|
|
81
|
-
Create your pi-governance config and rules as normal — see the [pi-governance docs](https://
|
|
81
|
+
Create your pi-governance config and rules as normal — see the [pi-governance docs](https://dtmirizzi.github.io/pi-governance/guide/quickstart). The roles you assign in `openclaw-users.yaml` must match roles defined in `governance-rules.yaml`.
|
|
82
82
|
|
|
83
83
|
### 5. Verify
|
|
84
84
|
|
package/dist/index.cjs
CHANGED
|
@@ -68,10 +68,10 @@ function applyIdentity(usersConfig, sessionKey) {
|
|
|
68
68
|
if (!parsed) return;
|
|
69
69
|
const user = usersConfig.users[`${parsed.channel}:${parsed.peerId}`] ?? usersConfig.default;
|
|
70
70
|
if (!user) return;
|
|
71
|
-
process.env.
|
|
72
|
-
process.env.
|
|
71
|
+
process.env.PI_GOV_USER = `${parsed.channel}:${parsed.peerId}`;
|
|
72
|
+
process.env.PI_GOV_ROLE = user.role;
|
|
73
73
|
if (user.org_unit) {
|
|
74
|
-
process.env.
|
|
74
|
+
process.env.PI_GOV_ORG_UNIT = user.org_unit;
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
var plugin = {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/parse-session-key.ts","../src/load-users.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { parseSessionKey } from './parse-session-key.js';\nimport { loadUsers, type UsersConfig } from './load-users.js';\n\nexport { parseSessionKey, type ParsedSessionKey } from './parse-session-key.js';\nexport { loadUsers, lookupUser, type UserEntry, type UsersConfig } from './load-users.js';\n\ninterface PluginConfig {\n users_file?: string;\n}\n\ninterface PluginApi {\n on(event: string, handler: (ctx: { sessionKey?: string }) => void): void;\n}\n\nfunction applyIdentity(usersConfig: UsersConfig, sessionKey: string | undefined): void {\n if (!sessionKey) return;\n\n const parsed = parseSessionKey(sessionKey);\n if (!parsed) return;\n\n const user = usersConfig.users[`${parsed.channel}:${parsed.peerId}`] ?? usersConfig.default;\n if (!user) return;\n\n process.env.
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/parse-session-key.ts","../src/load-users.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { parseSessionKey } from './parse-session-key.js';\nimport { loadUsers, type UsersConfig } from './load-users.js';\n\nexport { parseSessionKey, type ParsedSessionKey } from './parse-session-key.js';\nexport { loadUsers, lookupUser, type UserEntry, type UsersConfig } from './load-users.js';\n\ninterface PluginConfig {\n users_file?: string;\n}\n\ninterface PluginApi {\n on(event: string, handler: (ctx: { sessionKey?: string }) => void): void;\n}\n\nfunction applyIdentity(usersConfig: UsersConfig, sessionKey: string | undefined): void {\n if (!sessionKey) return;\n\n const parsed = parseSessionKey(sessionKey);\n if (!parsed) return;\n\n const user = usersConfig.users[`${parsed.channel}:${parsed.peerId}`] ?? usersConfig.default;\n if (!user) return;\n\n process.env.PI_GOV_USER = `${parsed.channel}:${parsed.peerId}`;\n process.env.PI_GOV_ROLE = user.role;\n if (user.org_unit) {\n process.env.PI_GOV_ORG_UNIT = user.org_unit;\n }\n}\n\nexport const plugin = {\n id: 'grwnd-openclaw-governance',\n\n configSchema: {\n type: 'object' as const,\n properties: {\n users_file: { type: 'string' as const, default: './openclaw-users.yaml' },\n },\n },\n\n register(api: PluginApi, config: PluginConfig = {}) {\n const usersFile = resolve(config.users_file ?? './openclaw-users.yaml');\n const usersConfig = loadUsers(usersFile);\n\n api.on('session_start', (ctx) => {\n applyIdentity(usersConfig, ctx.sessionKey);\n });\n\n api.on('message_received', (ctx) => {\n applyIdentity(usersConfig, ctx.sessionKey);\n });\n },\n};\n\n// Top-level named export expected by OpenClaw plugin loader\nexport const register = plugin.register.bind(plugin);\n","export interface ParsedSessionKey {\n agentId: string;\n channel: string;\n chatType: 'dm' | 'group';\n peerId: string;\n groupId?: string;\n}\n\n/**\n * Parse an OpenClaw session key into its components.\n *\n * Supported formats:\n * agent:<agentId>:<channel>:dm:<peerId>\n * agent:<agentId>:<channel>:group:<groupId>:<peerId>\n *\n * Returns null for unrecognised formats (e.g. \"agent:<id>:main\").\n */\nexport function parseSessionKey(key: string): ParsedSessionKey | null {\n const parts = key.split(':');\n\n if (parts[0] !== 'agent' || parts.length < 5) return null;\n\n const agentId = parts[1]!;\n const channel = parts[2]!;\n const chatType = parts[3];\n\n if (chatType === 'dm' && parts.length === 5) {\n return { agentId, channel, chatType: 'dm', peerId: parts[4]! };\n }\n\n if (chatType === 'group' && parts.length === 6) {\n return { agentId, channel, chatType: 'group', groupId: parts[4]!, peerId: parts[5]! };\n }\n\n return null;\n}\n","import { readFileSync } from 'node:fs';\nimport { parse as parseYaml } from 'yaml';\n\nexport interface UserEntry {\n role: string;\n org_unit?: string;\n}\n\nexport interface UsersConfig {\n users: Record<string, UserEntry>;\n default?: UserEntry;\n}\n\n/** Load and parse an openclaw-users.yaml file. */\nexport function loadUsers(filePath: string): UsersConfig {\n const raw = readFileSync(filePath, 'utf-8');\n const parsed = parseYaml(raw) as UsersConfig;\n return {\n users: parsed.users ?? {},\n default: parsed.default,\n };\n}\n\n/** Look up a channel user, falling back to `default`. Returns null if neither matches. */\nexport function lookupUser(config: UsersConfig, channel: string, peerId: string): UserEntry | null {\n const key = `${channel}:${peerId}`;\n return config.users[key] ?? config.default ?? null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAwB;;;ACiBjB,SAAS,gBAAgB,KAAsC;AACpE,QAAM,QAAQ,IAAI,MAAM,GAAG;AAE3B,MAAI,MAAM,CAAC,MAAM,WAAW,MAAM,SAAS,EAAG,QAAO;AAErD,QAAM,UAAU,MAAM,CAAC;AACvB,QAAM,UAAU,MAAM,CAAC;AACvB,QAAM,WAAW,MAAM,CAAC;AAExB,MAAI,aAAa,QAAQ,MAAM,WAAW,GAAG;AAC3C,WAAO,EAAE,SAAS,SAAS,UAAU,MAAM,QAAQ,MAAM,CAAC,EAAG;AAAA,EAC/D;AAEA,MAAI,aAAa,WAAW,MAAM,WAAW,GAAG;AAC9C,WAAO,EAAE,SAAS,SAAS,UAAU,SAAS,SAAS,MAAM,CAAC,GAAI,QAAQ,MAAM,CAAC,EAAG;AAAA,EACtF;AAEA,SAAO;AACT;;;ACnCA,qBAA6B;AAC7B,kBAAmC;AAa5B,SAAS,UAAU,UAA+B;AACvD,QAAM,UAAM,6BAAa,UAAU,OAAO;AAC1C,QAAM,aAAS,YAAAA,OAAU,GAAG;AAC5B,SAAO;AAAA,IACL,OAAO,OAAO,SAAS,CAAC;AAAA,IACxB,SAAS,OAAO;AAAA,EAClB;AACF;AAGO,SAAS,WAAW,QAAqB,SAAiB,QAAkC;AACjG,QAAM,MAAM,GAAG,OAAO,IAAI,MAAM;AAChC,SAAO,OAAO,MAAM,GAAG,KAAK,OAAO,WAAW;AAChD;;;AFZA,SAAS,cAAc,aAA0B,YAAsC;AACrF,MAAI,CAAC,WAAY;AAEjB,QAAM,SAAS,gBAAgB,UAAU;AACzC,MAAI,CAAC,OAAQ;AAEb,QAAM,OAAO,YAAY,MAAM,GAAG,OAAO,OAAO,IAAI,OAAO,MAAM,EAAE,KAAK,YAAY;AACpF,MAAI,CAAC,KAAM;AAEX,UAAQ,IAAI,cAAc,GAAG,OAAO,OAAO,IAAI,OAAO,MAAM;AAC5D,UAAQ,IAAI,cAAc,KAAK;AAC/B,MAAI,KAAK,UAAU;AACjB,YAAQ,IAAI,kBAAkB,KAAK;AAAA,EACrC;AACF;AAEO,IAAM,SAAS;AAAA,EACpB,IAAI;AAAA,EAEJ,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,YAAY;AAAA,MACV,YAAY,EAAE,MAAM,UAAmB,SAAS,wBAAwB;AAAA,IAC1E;AAAA,EACF;AAAA,EAEA,SAAS,KAAgB,SAAuB,CAAC,GAAG;AAClD,UAAM,gBAAY,0BAAQ,OAAO,cAAc,uBAAuB;AACtE,UAAM,cAAc,UAAU,SAAS;AAEvC,QAAI,GAAG,iBAAiB,CAAC,QAAQ;AAC/B,oBAAc,aAAa,IAAI,UAAU;AAAA,IAC3C,CAAC;AAED,QAAI,GAAG,oBAAoB,CAAC,QAAQ;AAClC,oBAAc,aAAa,IAAI,UAAU;AAAA,IAC3C,CAAC;AAAA,EACH;AACF;AAGO,IAAM,WAAW,OAAO,SAAS,KAAK,MAAM;","names":["parseYaml"]}
|
package/dist/index.js
CHANGED
|
@@ -40,10 +40,10 @@ function applyIdentity(usersConfig, sessionKey) {
|
|
|
40
40
|
if (!parsed) return;
|
|
41
41
|
const user = usersConfig.users[`${parsed.channel}:${parsed.peerId}`] ?? usersConfig.default;
|
|
42
42
|
if (!user) return;
|
|
43
|
-
process.env.
|
|
44
|
-
process.env.
|
|
43
|
+
process.env.PI_GOV_USER = `${parsed.channel}:${parsed.peerId}`;
|
|
44
|
+
process.env.PI_GOV_ROLE = user.role;
|
|
45
45
|
if (user.org_unit) {
|
|
46
|
-
process.env.
|
|
46
|
+
process.env.PI_GOV_ORG_UNIT = user.org_unit;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
var plugin = {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/parse-session-key.ts","../src/load-users.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { parseSessionKey } from './parse-session-key.js';\nimport { loadUsers, type UsersConfig } from './load-users.js';\n\nexport { parseSessionKey, type ParsedSessionKey } from './parse-session-key.js';\nexport { loadUsers, lookupUser, type UserEntry, type UsersConfig } from './load-users.js';\n\ninterface PluginConfig {\n users_file?: string;\n}\n\ninterface PluginApi {\n on(event: string, handler: (ctx: { sessionKey?: string }) => void): void;\n}\n\nfunction applyIdentity(usersConfig: UsersConfig, sessionKey: string | undefined): void {\n if (!sessionKey) return;\n\n const parsed = parseSessionKey(sessionKey);\n if (!parsed) return;\n\n const user = usersConfig.users[`${parsed.channel}:${parsed.peerId}`] ?? usersConfig.default;\n if (!user) return;\n\n process.env.
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/parse-session-key.ts","../src/load-users.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { parseSessionKey } from './parse-session-key.js';\nimport { loadUsers, type UsersConfig } from './load-users.js';\n\nexport { parseSessionKey, type ParsedSessionKey } from './parse-session-key.js';\nexport { loadUsers, lookupUser, type UserEntry, type UsersConfig } from './load-users.js';\n\ninterface PluginConfig {\n users_file?: string;\n}\n\ninterface PluginApi {\n on(event: string, handler: (ctx: { sessionKey?: string }) => void): void;\n}\n\nfunction applyIdentity(usersConfig: UsersConfig, sessionKey: string | undefined): void {\n if (!sessionKey) return;\n\n const parsed = parseSessionKey(sessionKey);\n if (!parsed) return;\n\n const user = usersConfig.users[`${parsed.channel}:${parsed.peerId}`] ?? usersConfig.default;\n if (!user) return;\n\n process.env.PI_GOV_USER = `${parsed.channel}:${parsed.peerId}`;\n process.env.PI_GOV_ROLE = user.role;\n if (user.org_unit) {\n process.env.PI_GOV_ORG_UNIT = user.org_unit;\n }\n}\n\nexport const plugin = {\n id: 'grwnd-openclaw-governance',\n\n configSchema: {\n type: 'object' as const,\n properties: {\n users_file: { type: 'string' as const, default: './openclaw-users.yaml' },\n },\n },\n\n register(api: PluginApi, config: PluginConfig = {}) {\n const usersFile = resolve(config.users_file ?? './openclaw-users.yaml');\n const usersConfig = loadUsers(usersFile);\n\n api.on('session_start', (ctx) => {\n applyIdentity(usersConfig, ctx.sessionKey);\n });\n\n api.on('message_received', (ctx) => {\n applyIdentity(usersConfig, ctx.sessionKey);\n });\n },\n};\n\n// Top-level named export expected by OpenClaw plugin loader\nexport const register = plugin.register.bind(plugin);\n","export interface ParsedSessionKey {\n agentId: string;\n channel: string;\n chatType: 'dm' | 'group';\n peerId: string;\n groupId?: string;\n}\n\n/**\n * Parse an OpenClaw session key into its components.\n *\n * Supported formats:\n * agent:<agentId>:<channel>:dm:<peerId>\n * agent:<agentId>:<channel>:group:<groupId>:<peerId>\n *\n * Returns null for unrecognised formats (e.g. \"agent:<id>:main\").\n */\nexport function parseSessionKey(key: string): ParsedSessionKey | null {\n const parts = key.split(':');\n\n if (parts[0] !== 'agent' || parts.length < 5) return null;\n\n const agentId = parts[1]!;\n const channel = parts[2]!;\n const chatType = parts[3];\n\n if (chatType === 'dm' && parts.length === 5) {\n return { agentId, channel, chatType: 'dm', peerId: parts[4]! };\n }\n\n if (chatType === 'group' && parts.length === 6) {\n return { agentId, channel, chatType: 'group', groupId: parts[4]!, peerId: parts[5]! };\n }\n\n return null;\n}\n","import { readFileSync } from 'node:fs';\nimport { parse as parseYaml } from 'yaml';\n\nexport interface UserEntry {\n role: string;\n org_unit?: string;\n}\n\nexport interface UsersConfig {\n users: Record<string, UserEntry>;\n default?: UserEntry;\n}\n\n/** Load and parse an openclaw-users.yaml file. */\nexport function loadUsers(filePath: string): UsersConfig {\n const raw = readFileSync(filePath, 'utf-8');\n const parsed = parseYaml(raw) as UsersConfig;\n return {\n users: parsed.users ?? {},\n default: parsed.default,\n };\n}\n\n/** Look up a channel user, falling back to `default`. Returns null if neither matches. */\nexport function lookupUser(config: UsersConfig, channel: string, peerId: string): UserEntry | null {\n const key = `${channel}:${peerId}`;\n return config.users[key] ?? config.default ?? null;\n}\n"],"mappings":";AAAA,SAAS,eAAe;;;ACiBjB,SAAS,gBAAgB,KAAsC;AACpE,QAAM,QAAQ,IAAI,MAAM,GAAG;AAE3B,MAAI,MAAM,CAAC,MAAM,WAAW,MAAM,SAAS,EAAG,QAAO;AAErD,QAAM,UAAU,MAAM,CAAC;AACvB,QAAM,UAAU,MAAM,CAAC;AACvB,QAAM,WAAW,MAAM,CAAC;AAExB,MAAI,aAAa,QAAQ,MAAM,WAAW,GAAG;AAC3C,WAAO,EAAE,SAAS,SAAS,UAAU,MAAM,QAAQ,MAAM,CAAC,EAAG;AAAA,EAC/D;AAEA,MAAI,aAAa,WAAW,MAAM,WAAW,GAAG;AAC9C,WAAO,EAAE,SAAS,SAAS,UAAU,SAAS,SAAS,MAAM,CAAC,GAAI,QAAQ,MAAM,CAAC,EAAG;AAAA,EACtF;AAEA,SAAO;AACT;;;ACnCA,SAAS,oBAAoB;AAC7B,SAAS,SAAS,iBAAiB;AAa5B,SAAS,UAAU,UAA+B;AACvD,QAAM,MAAM,aAAa,UAAU,OAAO;AAC1C,QAAM,SAAS,UAAU,GAAG;AAC5B,SAAO;AAAA,IACL,OAAO,OAAO,SAAS,CAAC;AAAA,IACxB,SAAS,OAAO;AAAA,EAClB;AACF;AAGO,SAAS,WAAW,QAAqB,SAAiB,QAAkC;AACjG,QAAM,MAAM,GAAG,OAAO,IAAI,MAAM;AAChC,SAAO,OAAO,MAAM,GAAG,KAAK,OAAO,WAAW;AAChD;;;AFZA,SAAS,cAAc,aAA0B,YAAsC;AACrF,MAAI,CAAC,WAAY;AAEjB,QAAM,SAAS,gBAAgB,UAAU;AACzC,MAAI,CAAC,OAAQ;AAEb,QAAM,OAAO,YAAY,MAAM,GAAG,OAAO,OAAO,IAAI,OAAO,MAAM,EAAE,KAAK,YAAY;AACpF,MAAI,CAAC,KAAM;AAEX,UAAQ,IAAI,cAAc,GAAG,OAAO,OAAO,IAAI,OAAO,MAAM;AAC5D,UAAQ,IAAI,cAAc,KAAK;AAC/B,MAAI,KAAK,UAAU;AACjB,YAAQ,IAAI,kBAAkB,KAAK;AAAA,EACrC;AACF;AAEO,IAAM,SAAS;AAAA,EACpB,IAAI;AAAA,EAEJ,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,YAAY;AAAA,MACV,YAAY,EAAE,MAAM,UAAmB,SAAS,wBAAwB;AAAA,IAC1E;AAAA,EACF;AAAA,EAEA,SAAS,KAAgB,SAAuB,CAAC,GAAG;AAClD,UAAM,YAAY,QAAQ,OAAO,cAAc,uBAAuB;AACtE,UAAM,cAAc,UAAU,SAAS;AAEvC,QAAI,GAAG,iBAAiB,CAAC,QAAQ;AAC/B,oBAAc,aAAa,IAAI,UAAU;AAAA,IAC3C,CAAC;AAED,QAAI,GAAG,oBAAoB,CAAC,QAAQ;AAClC,oBAAc,aAAa,IAAI,UAAU;AAAA,IAC3C,CAAC;AAAA,EACH;AACF;AAGO,IAAM,WAAW,OAAO,SAAS,KAAK,MAAM;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grwnd/openclaw-governance",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "OpenClaw identity bridge plugin for @grwnd/pi-governance",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
|
-
"author": "
|
|
6
|
+
"author": "dtmirizzi",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/
|
|
9
|
+
"url": "https://github.com/dtmirizzi/pi-governance.git",
|
|
10
10
|
"directory": "packages/openclaw-plugin"
|
|
11
11
|
},
|
|
12
12
|
"type": "module",
|