@grwnd/openclaw-governance 2.0.0 → 3.0.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/README.md CHANGED
@@ -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.PI_RBAC_USER, PI_RBAC_ROLE, PI_RBAC_ORG_UNIT
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
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.PI_RBAC_USER = `${parsed.channel}:${parsed.peerId}`;
72
- process.env.PI_RBAC_ROLE = user.role;
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.PI_RBAC_ORG_UNIT = user.org_unit;
74
+ process.env.PI_GOV_ORG_UNIT = user.org_unit;
75
75
  }
76
76
  }
77
77
  var plugin = {
@@ -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.PI_RBAC_USER = `${parsed.channel}:${parsed.peerId}`;\n process.env.PI_RBAC_ROLE = user.role;\n if (user.org_unit) {\n process.env.PI_RBAC_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,eAAe,GAAG,OAAO,OAAO,IAAI,OAAO,MAAM;AAC7D,UAAQ,IAAI,eAAe,KAAK;AAChC,MAAI,KAAK,UAAU;AACjB,YAAQ,IAAI,mBAAmB,KAAK;AAAA,EACtC;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"]}
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.PI_RBAC_USER = `${parsed.channel}:${parsed.peerId}`;
44
- process.env.PI_RBAC_ROLE = user.role;
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.PI_RBAC_ORG_UNIT = user.org_unit;
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.PI_RBAC_USER = `${parsed.channel}:${parsed.peerId}`;\n process.env.PI_RBAC_ROLE = user.role;\n if (user.org_unit) {\n process.env.PI_RBAC_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,eAAe,GAAG,OAAO,OAAO,IAAI,OAAO,MAAM;AAC7D,UAAQ,IAAI,eAAe,KAAK;AAChC,MAAI,KAAK,UAAU;AACjB,YAAQ,IAAI,mBAAmB,KAAK;AAAA,EACtC;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":[]}
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@grwnd/openclaw-governance",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "OpenClaw identity bridge plugin for @grwnd/pi-governance",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Grwnd AI",