botmux 2.44.0 → 2.46.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 (192) hide show
  1. package/README.en.md +1 -2
  2. package/README.md +1 -2
  3. package/dist/cli/bots-list-output.d.ts +8 -0
  4. package/dist/cli/bots-list-output.d.ts.map +1 -1
  5. package/dist/cli/bots-list-output.js +9 -0
  6. package/dist/cli/bots-list-output.js.map +1 -1
  7. package/dist/cli.d.ts.map +1 -1
  8. package/dist/cli.js +6 -0
  9. package/dist/cli.js.map +1 -1
  10. package/dist/core/command-handler.d.ts.map +1 -1
  11. package/dist/core/command-handler.js +601 -50
  12. package/dist/core/command-handler.js.map +1 -1
  13. package/dist/core/dashboard-ipc-server.d.ts +2 -0
  14. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  15. package/dist/core/dashboard-ipc-server.js +170 -2
  16. package/dist/core/dashboard-ipc-server.js.map +1 -1
  17. package/dist/core/role-resolver.d.ts +17 -1
  18. package/dist/core/role-resolver.d.ts.map +1 -1
  19. package/dist/core/role-resolver.js +64 -10
  20. package/dist/core/role-resolver.js.map +1 -1
  21. package/dist/core/session-manager.d.ts +1 -1
  22. package/dist/core/session-manager.d.ts.map +1 -1
  23. package/dist/core/session-manager.js +24 -13
  24. package/dist/core/session-manager.js.map +1 -1
  25. package/dist/core/trigger-session.d.ts +9 -0
  26. package/dist/core/trigger-session.d.ts.map +1 -0
  27. package/dist/core/trigger-session.js +158 -0
  28. package/dist/core/trigger-session.js.map +1 -0
  29. package/dist/core/worker-pool.d.ts +105 -0
  30. package/dist/core/worker-pool.d.ts.map +1 -1
  31. package/dist/core/worker-pool.js +284 -4
  32. package/dist/core/worker-pool.js.map +1 -1
  33. package/dist/daemon.d.ts.map +1 -1
  34. package/dist/daemon.js +80 -45
  35. package/dist/daemon.js.map +1 -1
  36. package/dist/dashboard/connector-api.d.ts +3 -0
  37. package/dist/dashboard/connector-api.d.ts.map +1 -0
  38. package/dist/dashboard/connector-api.js +351 -0
  39. package/dist/dashboard/connector-api.js.map +1 -0
  40. package/dist/dashboard/federated-group-core.d.ts +54 -0
  41. package/dist/dashboard/federated-group-core.d.ts.map +1 -0
  42. package/dist/dashboard/federated-group-core.js +165 -0
  43. package/dist/dashboard/federated-group-core.js.map +1 -0
  44. package/dist/dashboard/federation-api.d.ts +42 -0
  45. package/dist/dashboard/federation-api.d.ts.map +1 -0
  46. package/dist/dashboard/federation-api.js +408 -0
  47. package/dist/dashboard/federation-api.js.map +1 -0
  48. package/dist/dashboard/federation-spoke-api.d.ts +76 -0
  49. package/dist/dashboard/federation-spoke-api.d.ts.map +1 -0
  50. package/dist/dashboard/federation-spoke-api.js +618 -0
  51. package/dist/dashboard/federation-spoke-api.js.map +1 -0
  52. package/dist/dashboard/team-group.d.ts +18 -0
  53. package/dist/dashboard/team-group.d.ts.map +1 -0
  54. package/dist/dashboard/team-group.js +7 -0
  55. package/dist/dashboard/team-group.js.map +1 -0
  56. package/dist/dashboard/trigger-api.d.ts +13 -0
  57. package/dist/dashboard/trigger-api.d.ts.map +1 -0
  58. package/dist/dashboard/trigger-api.js +77 -0
  59. package/dist/dashboard/trigger-api.js.map +1 -0
  60. package/dist/dashboard/web/app.js +8 -0
  61. package/dist/dashboard/web/app.js.map +1 -1
  62. package/dist/dashboard/web/connectors.d.ts +2 -0
  63. package/dist/dashboard/web/connectors.d.ts.map +1 -0
  64. package/dist/dashboard/web/connectors.js +187 -0
  65. package/dist/dashboard/web/connectors.js.map +1 -0
  66. package/dist/dashboard/web/team-federation.d.ts +3 -0
  67. package/dist/dashboard/web/team-federation.d.ts.map +1 -0
  68. package/dist/dashboard/web/team-federation.js +487 -0
  69. package/dist/dashboard/web/team-federation.js.map +1 -0
  70. package/dist/dashboard/webhook-routes.d.ts +19 -0
  71. package/dist/dashboard/webhook-routes.d.ts.map +1 -0
  72. package/dist/dashboard/webhook-routes.js +321 -0
  73. package/dist/dashboard/webhook-routes.js.map +1 -0
  74. package/dist/dashboard-web/app.js +509 -386
  75. package/dist/dashboard-web/index.html +2 -0
  76. package/dist/dashboard.js +152 -1
  77. package/dist/dashboard.js.map +1 -1
  78. package/dist/i18n/en.d.ts.map +1 -1
  79. package/dist/i18n/en.js +85 -8
  80. package/dist/i18n/en.js.map +1 -1
  81. package/dist/i18n/zh.d.ts.map +1 -1
  82. package/dist/i18n/zh.js +85 -8
  83. package/dist/i18n/zh.js.map +1 -1
  84. package/dist/im/lark/card-builder.d.ts +82 -0
  85. package/dist/im/lark/card-builder.d.ts.map +1 -1
  86. package/dist/im/lark/card-builder.js +315 -0
  87. package/dist/im/lark/card-builder.js.map +1 -1
  88. package/dist/im/lark/card-handler.d.ts.map +1 -1
  89. package/dist/im/lark/card-handler.js +195 -1
  90. package/dist/im/lark/card-handler.js.map +1 -1
  91. package/dist/im/lark/client.d.ts +45 -0
  92. package/dist/im/lark/client.d.ts.map +1 -1
  93. package/dist/im/lark/client.js +169 -18
  94. package/dist/im/lark/client.js.map +1 -1
  95. package/dist/im/lark/message-parser.d.ts.map +1 -1
  96. package/dist/im/lark/message-parser.js +1 -0
  97. package/dist/im/lark/message-parser.js.map +1 -1
  98. package/dist/services/bot-owner-store.d.ts +28 -0
  99. package/dist/services/bot-owner-store.d.ts.map +1 -0
  100. package/dist/services/bot-owner-store.js +82 -0
  101. package/dist/services/bot-owner-store.js.map +1 -0
  102. package/dist/services/bot-profile-store.d.ts +16 -0
  103. package/dist/services/bot-profile-store.d.ts.map +1 -0
  104. package/dist/services/bot-profile-store.js +98 -0
  105. package/dist/services/bot-profile-store.js.map +1 -0
  106. package/dist/services/connector-store.d.ts +58 -0
  107. package/dist/services/connector-store.d.ts.map +1 -0
  108. package/dist/services/connector-store.js +79 -0
  109. package/dist/services/connector-store.js.map +1 -0
  110. package/dist/services/deployment-identity.d.ts +22 -0
  111. package/dist/services/deployment-identity.d.ts.map +1 -0
  112. package/dist/services/deployment-identity.js +67 -0
  113. package/dist/services/deployment-identity.js.map +1 -0
  114. package/dist/services/federation-membership-store.d.ts +23 -0
  115. package/dist/services/federation-membership-store.d.ts.map +1 -0
  116. package/dist/services/federation-membership-store.js +66 -0
  117. package/dist/services/federation-membership-store.js.map +1 -0
  118. package/dist/services/federation-roster.d.ts +54 -0
  119. package/dist/services/federation-roster.d.ts.map +1 -0
  120. package/dist/services/federation-roster.js +51 -0
  121. package/dist/services/federation-roster.js.map +1 -0
  122. package/dist/services/federation-store.d.ts +76 -0
  123. package/dist/services/federation-store.d.ts.map +1 -0
  124. package/dist/services/federation-store.js +133 -0
  125. package/dist/services/federation-store.js.map +1 -0
  126. package/dist/services/group-creator.d.ts +6 -0
  127. package/dist/services/group-creator.d.ts.map +1 -1
  128. package/dist/services/group-creator.js +10 -1
  129. package/dist/services/group-creator.js.map +1 -1
  130. package/dist/services/groups-store.d.ts +11 -0
  131. package/dist/services/groups-store.d.ts.map +1 -1
  132. package/dist/services/groups-store.js +42 -0
  133. package/dist/services/groups-store.js.map +1 -1
  134. package/dist/services/invite-store.d.ts +28 -0
  135. package/dist/services/invite-store.d.ts.map +1 -0
  136. package/dist/services/invite-store.js +85 -0
  137. package/dist/services/invite-store.js.map +1 -0
  138. package/dist/services/pairing-store.d.ts +47 -0
  139. package/dist/services/pairing-store.d.ts.map +1 -0
  140. package/dist/services/pairing-store.js +132 -0
  141. package/dist/services/pairing-store.js.map +1 -0
  142. package/dist/services/relay-picker.d.ts +22 -0
  143. package/dist/services/relay-picker.d.ts.map +1 -0
  144. package/dist/services/relay-picker.js +62 -0
  145. package/dist/services/relay-picker.js.map +1 -0
  146. package/dist/services/team-roster.d.ts +38 -0
  147. package/dist/services/team-roster.d.ts.map +1 -0
  148. package/dist/services/team-roster.js +82 -0
  149. package/dist/services/team-roster.js.map +1 -0
  150. package/dist/services/team-store.d.ts +54 -0
  151. package/dist/services/team-store.d.ts.map +1 -0
  152. package/dist/services/team-store.js +156 -0
  153. package/dist/services/team-store.js.map +1 -0
  154. package/dist/services/trigger-log-store.d.ts +46 -0
  155. package/dist/services/trigger-log-store.d.ts.map +1 -0
  156. package/dist/services/trigger-log-store.js +132 -0
  157. package/dist/services/trigger-log-store.js.map +1 -0
  158. package/dist/services/trigger-types.d.ts +57 -0
  159. package/dist/services/trigger-types.d.ts.map +1 -0
  160. package/dist/services/trigger-types.js +28 -0
  161. package/dist/services/trigger-types.js.map +1 -0
  162. package/dist/services/webhook-key.d.ts +16 -0
  163. package/dist/services/webhook-key.d.ts.map +1 -0
  164. package/dist/services/webhook-key.js +123 -0
  165. package/dist/services/webhook-key.js.map +1 -0
  166. package/dist/services/webhook-lifecycle-extractors.d.ts +15 -0
  167. package/dist/services/webhook-lifecycle-extractors.d.ts.map +1 -0
  168. package/dist/services/webhook-lifecycle-extractors.js +59 -0
  169. package/dist/services/webhook-lifecycle-extractors.js.map +1 -0
  170. package/dist/services/webhook-lifecycle-store.d.ts +45 -0
  171. package/dist/services/webhook-lifecycle-store.d.ts.map +1 -0
  172. package/dist/services/webhook-lifecycle-store.js +159 -0
  173. package/dist/services/webhook-lifecycle-store.js.map +1 -0
  174. package/dist/setup/verify-permissions.d.ts.map +1 -1
  175. package/dist/setup/verify-permissions.js +4 -0
  176. package/dist/setup/verify-permissions.js.map +1 -1
  177. package/dist/skills/definitions.d.ts.map +1 -1
  178. package/dist/skills/definitions.js +64 -10
  179. package/dist/skills/definitions.js.map +1 -1
  180. package/dist/types.d.ts +14 -0
  181. package/dist/types.d.ts.map +1 -1
  182. package/dist/utils/daemon-discovery.d.ts +11 -0
  183. package/dist/utils/daemon-discovery.d.ts.map +1 -0
  184. package/dist/utils/daemon-discovery.js +59 -0
  185. package/dist/utils/daemon-discovery.js.map +1 -0
  186. package/dist/workflows/events/payloads.d.ts +2 -2
  187. package/dist/workflows/events/schema.d.ts +8 -8
  188. package/dist/workflows/trigger-from-envelope.d.ts +13 -0
  189. package/dist/workflows/trigger-from-envelope.d.ts.map +1 -0
  190. package/dist/workflows/trigger-from-envelope.js +67 -0
  191. package/dist/workflows/trigger-from-envelope.js.map +1 -0
  192. package/package.json +1 -1
@@ -0,0 +1,28 @@
1
+ export interface BotOwner {
2
+ unionId?: string;
3
+ openId?: string;
4
+ name?: string;
5
+ assignedAt: number;
6
+ assignedBy: 'auto' | 'manual';
7
+ }
8
+ type FileShape = Record<string, BotOwner>;
9
+ /** Current owner of a bot, or null. */
10
+ export declare function getBotOwner(dataDir: string, larkAppId: string): BotOwner | null;
11
+ /**
12
+ * Assign a bot's owner. By default does NOT overwrite an existing owner (auto
13
+ * assignment on pairing). Pass `override: true` for an explicit claim. Returns
14
+ * true if it wrote.
15
+ */
16
+ export declare function setBotOwner(dataDir: string, larkAppId: string, owner: {
17
+ unionId?: string;
18
+ openId?: string;
19
+ name?: string;
20
+ }, opts?: {
21
+ override?: boolean;
22
+ }, now?: number): boolean;
23
+ /** Remove a bot's owner. Returns true if one existed. */
24
+ export declare function clearBotOwner(dataDir: string, larkAppId: string): boolean;
25
+ /** All owners, keyed by larkAppId. */
26
+ export declare function listBotOwners(dataDir: string): FileShape;
27
+ export {};
28
+ //# sourceMappingURL=bot-owner-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bot-owner-store.d.ts","sourceRoot":"","sources":["../../src/services/bot-owner-store.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,QAAQ;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC/B;AAED,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AA0B1C,uCAAuC;AACvC,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAG/E;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3D,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,EACjC,GAAG,GAAE,MAAmB,GACvB,OAAO,CAaT;AAED,yDAAyD;AACzD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAMzE;AAED,sCAAsC;AACtC,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAExD"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Bot ownership store: which team member "owns" each bot, used to group the
3
+ * team roster by person (so multi-person teams stay organized).
4
+ *
5
+ * Ownership is mostly automatic: when a member logs in via pairing through a
6
+ * given bot, that bot is assigned to them IF it has no owner yet (we never
7
+ * silently steal an existing owner — see setBotOwner override). A member can
8
+ * also explicitly claim bots ("归到我名下", override=true).
9
+ *
10
+ * Identity: union_id is the canonical owner key (stable across apps); openId
11
+ * and name are kept for display/fallback only.
12
+ *
13
+ * Storage: `{dataDir}/bot-owners.json`, atomic writes.
14
+ */
15
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync } from 'node:fs';
16
+ import { join } from 'node:path';
17
+ import { randomUUID } from 'node:crypto';
18
+ function filePath(dataDir) { return join(dataDir, 'bot-owners.json'); }
19
+ function readFile(dataDir) {
20
+ const fp = filePath(dataDir);
21
+ if (!existsSync(fp))
22
+ return {};
23
+ try {
24
+ const parsed = JSON.parse(readFileSync(fp, 'utf-8'));
25
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
26
+ return parsed;
27
+ }
28
+ catch { /* corrupt */ }
29
+ return {};
30
+ }
31
+ function writeFileAtomic(dataDir, data) {
32
+ if (!existsSync(dataDir))
33
+ mkdirSync(dataDir, { recursive: true });
34
+ const fp = filePath(dataDir);
35
+ const tmp = `${fp}.${process.pid}.${randomUUID()}.tmp`;
36
+ writeFileSync(tmp, JSON.stringify(data, null, 2) + '\n', 'utf-8');
37
+ renameSync(tmp, fp);
38
+ }
39
+ function hasIdentity(o) {
40
+ return !!(o.unionId || o.openId);
41
+ }
42
+ /** Current owner of a bot, or null. */
43
+ export function getBotOwner(dataDir, larkAppId) {
44
+ if (!larkAppId)
45
+ return null;
46
+ return readFile(dataDir)[larkAppId] ?? null;
47
+ }
48
+ /**
49
+ * Assign a bot's owner. By default does NOT overwrite an existing owner (auto
50
+ * assignment on pairing). Pass `override: true` for an explicit claim. Returns
51
+ * true if it wrote.
52
+ */
53
+ export function setBotOwner(dataDir, larkAppId, owner, opts = {}, now = Date.now()) {
54
+ if (!larkAppId || !hasIdentity(owner))
55
+ return false;
56
+ const data = readFile(dataDir);
57
+ if (data[larkAppId] && !opts.override)
58
+ return false; // don't steal an existing owner
59
+ data[larkAppId] = {
60
+ ...(owner.unionId ? { unionId: owner.unionId } : {}),
61
+ ...(owner.openId ? { openId: owner.openId } : {}),
62
+ ...(owner.name ? { name: owner.name } : {}),
63
+ assignedAt: now,
64
+ assignedBy: opts.override ? 'manual' : 'auto',
65
+ };
66
+ writeFileAtomic(dataDir, data);
67
+ return true;
68
+ }
69
+ /** Remove a bot's owner. Returns true if one existed. */
70
+ export function clearBotOwner(dataDir, larkAppId) {
71
+ const data = readFile(dataDir);
72
+ if (!data[larkAppId])
73
+ return false;
74
+ delete data[larkAppId];
75
+ writeFileAtomic(dataDir, data);
76
+ return true;
77
+ }
78
+ /** All owners, keyed by larkAppId. */
79
+ export function listBotOwners(dataDir) {
80
+ return readFile(dataDir);
81
+ }
82
+ //# sourceMappingURL=bot-owner-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bot-owner-store.js","sourceRoot":"","sources":["../../src/services/bot-owner-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAYzC,SAAS,QAAQ,CAAC,OAAe,IAAY,OAAO,IAAI,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC;AAEvF,SAAS,QAAQ,CAAC,OAAe;IAC/B,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC7B,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;QACrD,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,MAAmB,CAAC;IACjG,CAAC;IAAC,MAAM,CAAC,CAAC,aAAa,CAAC,CAAC;IACzB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,eAAe,CAAC,OAAe,EAAE,IAAe;IACvD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClE,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC7B,MAAM,GAAG,GAAG,GAAG,EAAE,IAAI,OAAO,CAAC,GAAG,IAAI,UAAU,EAAE,MAAM,CAAC;IACvD,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IAClE,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,WAAW,CAAC,CAAwC;IAC3D,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,SAAiB;IAC5D,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC;AAC9C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,OAAe,EACf,SAAiB,EACjB,KAA2D,EAC3D,OAA+B,EAAE,EACjC,MAAc,IAAI,CAAC,GAAG,EAAE;IAExB,IAAI,CAAC,SAAS,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACpD,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC,CAAC,gCAAgC;IACrF,IAAI,CAAC,SAAS,CAAC,GAAG;QAChB,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,UAAU,EAAE,GAAG;QACf,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM;KAC9C,CAAC;IACF,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC/B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,SAAiB;IAC9D,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IACnC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC;IACvB,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC/B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,16 @@
1
+ export interface BotProfile {
2
+ capability?: string;
3
+ updatedAt: number;
4
+ updatedBy?: string;
5
+ }
6
+ /** Full profile for a bot, or null if none recorded. */
7
+ export declare function getBotProfile(dataDir: string, larkAppId: string): BotProfile | null;
8
+ /** Just the capability label for a bot, or null. */
9
+ export declare function getBotCapability(dataDir: string, larkAppId: string): string | null;
10
+ /** Set (or overwrite) a bot's capability label. Trimmed and length-capped. */
11
+ export declare function setBotCapability(dataDir: string, larkAppId: string, capability: string, updatedBy?: string, now?: number): void;
12
+ /** Remove a bot's capability label (deletes its profile file). Returns true if one existed. */
13
+ export declare function clearBotCapability(dataDir: string, larkAppId: string): boolean;
14
+ /** All recorded profiles, keyed by larkAppId. */
15
+ export declare function listBotProfiles(dataDir: string): Record<string, BotProfile>;
16
+ //# sourceMappingURL=bot-profile-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bot-profile-store.d.ts","sourceRoot":"","sources":["../../src/services/bot-profile-store.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA6BD,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAGnF;AAED,oDAAoD;AACpD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAElF;AAED,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,IAAI,CAI3I;AAED,+FAA+F;AAC/F,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAK9E;AAED,iDAAiD;AACjD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAY3E"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Team-level bot profile store: a short, human-facing **capability label** per
3
+ * bot (keyed by larkAppId), separate from the full team role markdown.
4
+ *
5
+ * Why separate from the team role (see role-resolver.ts):
6
+ * - The capability label is a one-liner used in the collaboration roster
7
+ * (`botmux bots list`) for discovery/selection — "后端 bot,擅长服务端排查".
8
+ * - The full team role is the persona injected into the CLI `<role>` block.
9
+ * Keeping them apart lets the roster stay scannable while the role stays rich.
10
+ *
11
+ * Storage: **one file per bot** at `{dataDir}/bot-profiles/{larkAppId}.json`.
12
+ * Per-bot files (not one shared map) matter because production is one daemon
13
+ * per bot: a shared read-modify-write map would lose updates when two daemons
14
+ * write different bots' capabilities concurrently. Each daemon owns its bot's
15
+ * file, so there is no cross-bot lost-update window. Same rationale as the
16
+ * per-bot team-role files in role-resolver.ts.
17
+ */
18
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync, unlinkSync, readdirSync } from 'node:fs';
19
+ import { join } from 'node:path';
20
+ import { randomUUID } from 'node:crypto';
21
+ /** A capability label longer than this is almost certainly a full role, not a tag. */
22
+ const MAX_CAPABILITY_CHARS = 120;
23
+ function profilesDir(dataDir) {
24
+ return join(dataDir, 'bot-profiles');
25
+ }
26
+ function profilePath(dataDir, larkAppId) {
27
+ return join(profilesDir(dataDir), `${larkAppId}.json`);
28
+ }
29
+ function readProfile(dataDir, larkAppId) {
30
+ const fp = profilePath(dataDir, larkAppId);
31
+ if (!existsSync(fp))
32
+ return null;
33
+ try {
34
+ const parsed = JSON.parse(readFileSync(fp, 'utf-8'));
35
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
36
+ return parsed;
37
+ }
38
+ catch { /* corrupt — treat as absent */ }
39
+ return null;
40
+ }
41
+ function writeProfileAtomic(dataDir, larkAppId, profile) {
42
+ const dir = profilesDir(dataDir);
43
+ if (!existsSync(dir))
44
+ mkdirSync(dir, { recursive: true });
45
+ const fp = profilePath(dataDir, larkAppId);
46
+ const tmp = `${fp}.${process.pid}.${randomUUID()}.tmp`;
47
+ writeFileSync(tmp, JSON.stringify(profile, null, 2) + '\n', 'utf-8');
48
+ renameSync(tmp, fp);
49
+ }
50
+ /** Full profile for a bot, or null if none recorded. */
51
+ export function getBotProfile(dataDir, larkAppId) {
52
+ if (!larkAppId)
53
+ return null;
54
+ return readProfile(dataDir, larkAppId);
55
+ }
56
+ /** Just the capability label for a bot, or null. */
57
+ export function getBotCapability(dataDir, larkAppId) {
58
+ return getBotProfile(dataDir, larkAppId)?.capability ?? null;
59
+ }
60
+ /** Set (or overwrite) a bot's capability label. Trimmed and length-capped. */
61
+ export function setBotCapability(dataDir, larkAppId, capability, updatedBy, now = Date.now()) {
62
+ if (!larkAppId)
63
+ return;
64
+ const label = capability.trim().slice(0, MAX_CAPABILITY_CHARS);
65
+ writeProfileAtomic(dataDir, larkAppId, { capability: label, updatedAt: now, ...(updatedBy ? { updatedBy } : {}) });
66
+ }
67
+ /** Remove a bot's capability label (deletes its profile file). Returns true if one existed. */
68
+ export function clearBotCapability(dataDir, larkAppId) {
69
+ const fp = profilePath(dataDir, larkAppId);
70
+ const had = readProfile(dataDir, larkAppId)?.capability !== undefined;
71
+ try {
72
+ unlinkSync(fp);
73
+ }
74
+ catch { /* already gone */ }
75
+ return had;
76
+ }
77
+ /** All recorded profiles, keyed by larkAppId. */
78
+ export function listBotProfiles(dataDir) {
79
+ const dir = profilesDir(dataDir);
80
+ const out = {};
81
+ let files;
82
+ try {
83
+ files = readdirSync(dir);
84
+ }
85
+ catch {
86
+ return out;
87
+ }
88
+ for (const f of files) {
89
+ if (!f.endsWith('.json'))
90
+ continue;
91
+ const larkAppId = f.slice(0, -'.json'.length);
92
+ const p = readProfile(dataDir, larkAppId);
93
+ if (p)
94
+ out[larkAppId] = p;
95
+ }
96
+ return out;
97
+ }
98
+ //# sourceMappingURL=bot-profile-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bot-profile-store.js","sourceRoot":"","sources":["../../src/services/bot-profile-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ export type ConnectorVerifyType = 'hmac-sha256';
2
+ export type ConnectorTargetMode = 'dynamic' | 'fixed' | 'new-group';
3
+ export type ConnectorTargetKind = 'turn' | 'workflow';
4
+ export interface ConnectorDefinition {
5
+ id: string;
6
+ name: string;
7
+ enabled: boolean;
8
+ verify: {
9
+ type: ConnectorVerifyType;
10
+ secretRef: string;
11
+ signatureHeader: string;
12
+ timestampHeader: string;
13
+ nonceHeader: string;
14
+ toleranceSeconds: number;
15
+ };
16
+ target: {
17
+ mode: ConnectorTargetMode;
18
+ kind: ConnectorTargetKind;
19
+ botId: string;
20
+ botIds?: string[];
21
+ chatId?: string;
22
+ allowChats?: string[];
23
+ workflowId?: string;
24
+ };
25
+ promptEnvelope: {
26
+ sourceName: string;
27
+ headerAllowlist: string[];
28
+ includeRawText: boolean;
29
+ maxBodyBytes: number;
30
+ };
31
+ loggingPolicy: {
32
+ storePayload: boolean;
33
+ storeHeaders: boolean;
34
+ retentionDays: number;
35
+ };
36
+ lifecycleExtractors: null | {
37
+ dedupKey: string;
38
+ status: string;
39
+ statusMap?: Record<string, string>;
40
+ };
41
+ rateLimit?: {
42
+ windowSeconds: number;
43
+ maxRequests: number;
44
+ };
45
+ createdAt: string;
46
+ updatedAt: string;
47
+ }
48
+ export interface ConnectorStoreFile {
49
+ version: 1;
50
+ connectors: ConnectorDefinition[];
51
+ }
52
+ export declare function readConnectorStore(dataDir?: string): ConnectorStoreFile;
53
+ export declare function listConnectors(dataDir?: string): ConnectorDefinition[];
54
+ export declare function getConnector(id: string, dataDir?: string): ConnectorDefinition | null;
55
+ export declare function upsertConnector(connector: ConnectorDefinition, dataDir?: string): ConnectorDefinition;
56
+ export declare function deleteConnector(id: string, dataDir?: string): boolean;
57
+ export declare function newConnectorId(): string;
58
+ //# sourceMappingURL=connector-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector-store.d.ts","sourceRoot":"","sources":["../../src/services/connector-store.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,mBAAmB,GAAG,aAAa,CAAC;AAChD,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,OAAO,GAAG,WAAW,CAAC;AACpE,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,UAAU,CAAC;AAEtD,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IAGjB,MAAM,EAAE;QACN,IAAI,EAAE,mBAAmB,CAAC;QAC1B,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,MAAM,EAAE;QACN,IAAI,EAAE,mBAAmB,CAAC;QAC1B,IAAI,EAAE,mBAAmB,CAAC;QAC1B,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,cAAc,EAAE;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,cAAc,EAAE,OAAO,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,aAAa,EAAE;QACb,YAAY,EAAE,OAAO,CAAC;QACtB,YAAY,EAAE,OAAO,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,mBAAmB,EAAE,IAAI,GAAG;QAC1B,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACpC,CAAC;IACF,SAAS,CAAC,EAAE;QACV,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,EAAE,mBAAmB,EAAE,CAAC;CACnC;AAqBD,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,MAA+B,GAAG,kBAAkB,CAQ/F;AAUD,wBAAgB,cAAc,CAAC,OAAO,GAAE,MAA+B,GAAG,mBAAmB,EAAE,CAE9F;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,MAA+B,GAAG,mBAAmB,GAAG,IAAI,CAG7G;AAED,wBAAgB,eAAe,CAC7B,SAAS,EAAE,mBAAmB,EAC9B,OAAO,GAAE,MAA+B,GACvC,mBAAmB,CAerB;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,MAA+B,GAAG,OAAO,CAO7F;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC"}
@@ -0,0 +1,79 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { config } from '../config.js';
5
+ function storePath(dataDir = config.session.dataDir) {
6
+ return join(dataDir, 'connectors.json');
7
+ }
8
+ function emptyStore() {
9
+ return { version: 1, connectors: [] };
10
+ }
11
+ function normalizeStore(raw) {
12
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
13
+ return emptyStore();
14
+ const r = raw;
15
+ return {
16
+ version: 1,
17
+ connectors: Array.isArray(r.connectors)
18
+ ? r.connectors.filter((c) => !!c && typeof c === 'object' && typeof c.id === 'string')
19
+ : [],
20
+ };
21
+ }
22
+ export function readConnectorStore(dataDir = config.session.dataDir) {
23
+ const fp = storePath(dataDir);
24
+ if (!existsSync(fp))
25
+ return emptyStore();
26
+ try {
27
+ return normalizeStore(JSON.parse(readFileSync(fp, 'utf-8')));
28
+ }
29
+ catch {
30
+ return emptyStore();
31
+ }
32
+ }
33
+ function writeConnectorStore(dataDir, store) {
34
+ const fp = storePath(dataDir);
35
+ mkdirSync(dirname(fp), { recursive: true });
36
+ const tmp = `${fp}.${process.pid}.${randomUUID()}.tmp`;
37
+ writeFileSync(tmp, JSON.stringify(normalizeStore(store), null, 2) + '\n', { encoding: 'utf-8', mode: 0o600 });
38
+ renameSync(tmp, fp);
39
+ }
40
+ export function listConnectors(dataDir = config.session.dataDir) {
41
+ return readConnectorStore(dataDir).connectors;
42
+ }
43
+ export function getConnector(id, dataDir = config.session.dataDir) {
44
+ if (!id)
45
+ return null;
46
+ return listConnectors(dataDir).find(c => c.id === id) ?? null;
47
+ }
48
+ export function upsertConnector(connector, dataDir = config.session.dataDir) {
49
+ if (!connector.id)
50
+ throw new Error('connector id is required');
51
+ const now = new Date().toISOString();
52
+ const store = readConnectorStore(dataDir);
53
+ const idx = store.connectors.findIndex(c => c.id === connector.id);
54
+ const prior = idx >= 0 ? store.connectors[idx] : undefined;
55
+ const next = {
56
+ ...connector,
57
+ createdAt: connector.createdAt || prior?.createdAt || now,
58
+ updatedAt: now,
59
+ };
60
+ if (idx >= 0)
61
+ store.connectors[idx] = next;
62
+ else
63
+ store.connectors.push(next);
64
+ writeConnectorStore(dataDir, store);
65
+ return next;
66
+ }
67
+ export function deleteConnector(id, dataDir = config.session.dataDir) {
68
+ const store = readConnectorStore(dataDir);
69
+ const before = store.connectors.length;
70
+ store.connectors = store.connectors.filter(c => c.id !== id);
71
+ if (store.connectors.length === before)
72
+ return false;
73
+ writeConnectorStore(dataDir, store);
74
+ return true;
75
+ }
76
+ export function newConnectorId() {
77
+ return `conn_${randomUUID()}`;
78
+ }
79
+ //# sourceMappingURL=connector-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector-store.js","sourceRoot":"","sources":["../../src/services/connector-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AA0DtC,SAAS,SAAS,CAAC,UAAkB,MAAM,CAAC,OAAO,CAAC,OAAO;IACzD,OAAO,IAAI,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;AACxC,CAAC;AAED,SAAS,cAAc,CAAC,GAAY;IAClC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,UAAU,EAAE,CAAC;IAC/E,MAAM,CAAC,GAAG,GAAkC,CAAC;IAC7C,OAAO;QACL,OAAO,EAAE,CAAC;QACV,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;YACrC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAA4B,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAQ,CAAS,CAAC,EAAE,KAAK,QAAQ,CAAC;YACzH,CAAC,CAAC,EAAE;KACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,UAAkB,MAAM,CAAC,OAAO,CAAC,OAAO;IACzE,MAAM,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;IAC9B,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAAE,OAAO,UAAU,EAAE,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,UAAU,EAAE,CAAC;IACtB,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,OAAe,EAAE,KAAyB;IACrE,MAAM,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;IAC9B,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,GAAG,EAAE,IAAI,OAAO,CAAC,GAAG,IAAI,UAAU,EAAE,MAAM,CAAC;IACvD,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9G,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,UAAkB,MAAM,CAAC,OAAO,CAAC,OAAO;IACrE,OAAO,kBAAkB,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAU,EAAE,UAAkB,MAAM,CAAC,OAAO,CAAC,OAAO;IAC/E,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IACrB,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,IAAI,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,SAA8B,EAC9B,UAAkB,MAAM,CAAC,OAAO,CAAC,OAAO;IAExC,IAAI,CAAC,SAAS,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC/D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,MAAM,IAAI,GAAwB;QAChC,GAAG,SAAS;QACZ,SAAS,EAAE,SAAS,CAAC,SAAS,IAAI,KAAK,EAAE,SAAS,IAAI,GAAG;QACzD,SAAS,EAAE,GAAG;KACf,CAAC;IACF,IAAI,GAAG,IAAI,CAAC;QAAE,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;;QACtC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACpC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,EAAU,EAAE,UAAkB,MAAM,CAAC,OAAO,CAAC,OAAO;IAClF,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC;IACvC,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IACrD,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACpC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,QAAQ,UAAU,EAAE,EAAE,CAAC;AAChC,CAAC"}
@@ -0,0 +1,22 @@
1
+ export interface DeploymentIdentity {
2
+ deploymentId: string;
3
+ name: string;
4
+ /** The owner's tenant-stable Feishu identity, bound once via /pair. Used as
5
+ * the "operator" when this deployment initiates 拉群 (so the operator is
6
+ * pulled into the group). Absent until bound. */
7
+ ownerUnionId?: string;
8
+ ownerName?: string;
9
+ }
10
+ /** Load (creating + persisting on first call) this deployment's identity. */
11
+ export declare function getDeploymentIdentity(dataDir: string): DeploymentIdentity;
12
+ /** Rename this deployment (owner-facing label). Returns the updated identity. */
13
+ export declare function setDeploymentName(dataDir: string, name: string): DeploymentIdentity;
14
+ /** Bind the owner's Feishu identity (via /pair or auto-bind). Also adopts the
15
+ * owner's Feishu name as the DEPLOYMENT name — the deployment is shown to the
16
+ * team by its owner's real name (no custom labels), so the roster reads
17
+ * naturally ("申晗 的部署" instead of a hostname). Returns the updated identity. */
18
+ export declare function setDeploymentOwner(dataDir: string, owner: {
19
+ unionId?: string;
20
+ name?: string;
21
+ }): DeploymentIdentity;
22
+ //# sourceMappingURL=deployment-identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment-identity.d.ts","sourceRoot":"","sources":["../../src/services/deployment-identity.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb;;sDAEkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAcD,6EAA6E;AAC7E,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAkBzE;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,kBAAkB,CAKnF;AAED;;;gFAGgF;AAChF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,kBAAkB,CAUlH"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Deployment identity: a stable id + human-readable name for THIS botmux
3
+ * deployment (one install = one owner). Used by federation so a hub can tell
4
+ * member deployments apart and group their bots in the shared roster.
5
+ *
6
+ * Generated once and persisted to `{dataDir}/deployment-identity.json`; the name
7
+ * defaults to the machine hostname and can be renamed by the owner.
8
+ */
9
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+ import { randomUUID } from 'node:crypto';
12
+ import { hostname } from 'node:os';
13
+ function filePath(dataDir) {
14
+ return join(dataDir, 'deployment-identity.json');
15
+ }
16
+ function write(dataDir, id) {
17
+ if (!existsSync(dataDir))
18
+ mkdirSync(dataDir, { recursive: true });
19
+ const fp = filePath(dataDir);
20
+ const tmp = `${fp}.${process.pid}.${randomUUID()}.tmp`;
21
+ writeFileSync(tmp, JSON.stringify(id, null, 2) + '\n', 'utf-8');
22
+ renameSync(tmp, fp);
23
+ }
24
+ /** Load (creating + persisting on first call) this deployment's identity. */
25
+ export function getDeploymentIdentity(dataDir) {
26
+ const fp = filePath(dataDir);
27
+ if (existsSync(fp)) {
28
+ try {
29
+ const p = JSON.parse(readFileSync(fp, 'utf-8'));
30
+ if (p && typeof p.deploymentId === 'string' && p.deploymentId) {
31
+ return {
32
+ deploymentId: p.deploymentId,
33
+ name: typeof p.name === 'string' && p.name ? p.name : 'botmux',
34
+ ownerUnionId: typeof p.ownerUnionId === 'string' ? p.ownerUnionId : undefined,
35
+ ownerName: typeof p.ownerName === 'string' ? p.ownerName : undefined,
36
+ };
37
+ }
38
+ }
39
+ catch { /* corrupt — regenerate */ }
40
+ }
41
+ const id = { deploymentId: `dep_${randomUUID().slice(0, 12)}`, name: hostname() || 'botmux' };
42
+ write(dataDir, id);
43
+ return id;
44
+ }
45
+ /** Rename this deployment (owner-facing label). Returns the updated identity. */
46
+ export function setDeploymentName(dataDir, name) {
47
+ const cur = getDeploymentIdentity(dataDir);
48
+ const next = { ...cur, name: name.trim() || cur.name };
49
+ write(dataDir, next);
50
+ return next;
51
+ }
52
+ /** Bind the owner's Feishu identity (via /pair or auto-bind). Also adopts the
53
+ * owner's Feishu name as the DEPLOYMENT name — the deployment is shown to the
54
+ * team by its owner's real name (no custom labels), so the roster reads
55
+ * naturally ("申晗 的部署" instead of a hostname). Returns the updated identity. */
56
+ export function setDeploymentOwner(dataDir, owner) {
57
+ const cur = getDeploymentIdentity(dataDir);
58
+ const next = {
59
+ ...cur,
60
+ ownerUnionId: owner.unionId || cur.ownerUnionId,
61
+ ownerName: owner.name || cur.ownerName,
62
+ name: owner.name || cur.name, // default the deployment label to the owner's Feishu name
63
+ };
64
+ write(dataDir, next);
65
+ return next;
66
+ }
67
+ //# sourceMappingURL=deployment-identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment-identity.js","sourceRoot":"","sources":["../../src/services/deployment-identity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ export interface RemoteMembership {
2
+ hubUrl: string;
3
+ teamId: string;
4
+ teamName: string;
5
+ syncToken: string;
6
+ deploymentId: string;
7
+ joinedAt: number;
8
+ /** Token THIS spoke issued to the hub at join; the hub presents it on
9
+ * delegate-group calls so we can verify the request came from a hub we joined. */
10
+ delegationToken?: string;
11
+ }
12
+ /** Record (or replace) a remote-team membership. */
13
+ export declare function addMembership(dataDir: string, m: Omit<RemoteMembership, 'joinedAt'> & {
14
+ joinedAt?: number;
15
+ }, now?: number): RemoteMembership;
16
+ /** All remote teams this deployment has joined. */
17
+ export declare function listMemberships(dataDir: string): RemoteMembership[];
18
+ /** Find the membership whose delegationToken matches (verifies an incoming
19
+ * hub→spoke delegate call came from a hub this deployment actually joined). */
20
+ export declare function findMembershipByDelegationToken(dataDir: string, token: string): RemoteMembership | null;
21
+ /** Remove one membership. Returns true if removed. */
22
+ export declare function removeMembership(dataDir: string, hubUrl: string, teamId: string): boolean;
23
+ //# sourceMappingURL=federation-membership-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"federation-membership-store.d.ts","sourceRoot":"","sources":["../../src/services/federation-membership-store.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB;uFACmF;IACnF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AA8BD,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,GAAE,MAAmB,GAAG,gBAAgB,CAMxJ;AAED,mDAAmD;AACnD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAEnE;AAED;gFACgF;AAChF,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAGvG;AAED,sDAAsD;AACtD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAOzF"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Federation membership store (SPOKE side): which remote teams THIS deployment
3
+ * has joined, and the syncToken/hub needed to push bots + pull the shared roster.
4
+ *
5
+ * Storage: `{dataDir}/federation-memberships.json`, atomic writes. Keyed by
6
+ * `${hubUrl}::${teamId}` so a deployment can join multiple teams / hubs.
7
+ */
8
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ import { randomUUID } from 'node:crypto';
11
+ function filePath(dataDir) {
12
+ return join(dataDir, 'federation-memberships.json');
13
+ }
14
+ function keyOf(hubUrl, teamId) {
15
+ return `${hubUrl}::${teamId}`;
16
+ }
17
+ function readFile(dataDir) {
18
+ const fp = filePath(dataDir);
19
+ if (!existsSync(fp))
20
+ return {};
21
+ try {
22
+ const parsed = JSON.parse(readFileSync(fp, 'utf-8'));
23
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
24
+ return parsed;
25
+ }
26
+ catch { /* corrupt */ }
27
+ return {};
28
+ }
29
+ function writeFileAtomic(dataDir, data) {
30
+ if (!existsSync(dataDir))
31
+ mkdirSync(dataDir, { recursive: true });
32
+ const fp = filePath(dataDir);
33
+ const tmp = `${fp}.${process.pid}.${randomUUID()}.tmp`;
34
+ writeFileSync(tmp, JSON.stringify(data, null, 2) + '\n', 'utf-8');
35
+ renameSync(tmp, fp);
36
+ }
37
+ /** Record (or replace) a remote-team membership. */
38
+ export function addMembership(dataDir, m, now = Date.now()) {
39
+ const data = readFile(dataDir);
40
+ const full = { ...m, joinedAt: m.joinedAt ?? now };
41
+ data[keyOf(m.hubUrl, m.teamId)] = full;
42
+ writeFileAtomic(dataDir, data);
43
+ return full;
44
+ }
45
+ /** All remote teams this deployment has joined. */
46
+ export function listMemberships(dataDir) {
47
+ return Object.values(readFile(dataDir));
48
+ }
49
+ /** Find the membership whose delegationToken matches (verifies an incoming
50
+ * hub→spoke delegate call came from a hub this deployment actually joined). */
51
+ export function findMembershipByDelegationToken(dataDir, token) {
52
+ if (!token)
53
+ return null;
54
+ return Object.values(readFile(dataDir)).find(m => m.delegationToken === token) ?? null;
55
+ }
56
+ /** Remove one membership. Returns true if removed. */
57
+ export function removeMembership(dataDir, hubUrl, teamId) {
58
+ const data = readFile(dataDir);
59
+ const key = keyOf(hubUrl, teamId);
60
+ if (!data[key])
61
+ return false;
62
+ delete data[key];
63
+ writeFileAtomic(dataDir, data);
64
+ return true;
65
+ }
66
+ //# sourceMappingURL=federation-membership-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"federation-membership-store.js","sourceRoot":"","sources":["../../src/services/federation-membership-store.ts"],"names":[],"mappings":"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"}