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.
- package/README.en.md +1 -2
- package/README.md +1 -2
- package/dist/cli/bots-list-output.d.ts +8 -0
- package/dist/cli/bots-list-output.d.ts.map +1 -1
- package/dist/cli/bots-list-output.js +9 -0
- package/dist/cli/bots-list-output.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +601 -50
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts +2 -0
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +170 -2
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/role-resolver.d.ts +17 -1
- package/dist/core/role-resolver.d.ts.map +1 -1
- package/dist/core/role-resolver.js +64 -10
- package/dist/core/role-resolver.js.map +1 -1
- package/dist/core/session-manager.d.ts +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +24 -13
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/trigger-session.d.ts +9 -0
- package/dist/core/trigger-session.d.ts.map +1 -0
- package/dist/core/trigger-session.js +158 -0
- package/dist/core/trigger-session.js.map +1 -0
- package/dist/core/worker-pool.d.ts +105 -0
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +284 -4
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +80 -45
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/connector-api.d.ts +3 -0
- package/dist/dashboard/connector-api.d.ts.map +1 -0
- package/dist/dashboard/connector-api.js +351 -0
- package/dist/dashboard/connector-api.js.map +1 -0
- package/dist/dashboard/federated-group-core.d.ts +54 -0
- package/dist/dashboard/federated-group-core.d.ts.map +1 -0
- package/dist/dashboard/federated-group-core.js +165 -0
- package/dist/dashboard/federated-group-core.js.map +1 -0
- package/dist/dashboard/federation-api.d.ts +42 -0
- package/dist/dashboard/federation-api.d.ts.map +1 -0
- package/dist/dashboard/federation-api.js +408 -0
- package/dist/dashboard/federation-api.js.map +1 -0
- package/dist/dashboard/federation-spoke-api.d.ts +76 -0
- package/dist/dashboard/federation-spoke-api.d.ts.map +1 -0
- package/dist/dashboard/federation-spoke-api.js +618 -0
- package/dist/dashboard/federation-spoke-api.js.map +1 -0
- package/dist/dashboard/team-group.d.ts +18 -0
- package/dist/dashboard/team-group.d.ts.map +1 -0
- package/dist/dashboard/team-group.js +7 -0
- package/dist/dashboard/team-group.js.map +1 -0
- package/dist/dashboard/trigger-api.d.ts +13 -0
- package/dist/dashboard/trigger-api.d.ts.map +1 -0
- package/dist/dashboard/trigger-api.js +77 -0
- package/dist/dashboard/trigger-api.js.map +1 -0
- package/dist/dashboard/web/app.js +8 -0
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/connectors.d.ts +2 -0
- package/dist/dashboard/web/connectors.d.ts.map +1 -0
- package/dist/dashboard/web/connectors.js +187 -0
- package/dist/dashboard/web/connectors.js.map +1 -0
- package/dist/dashboard/web/team-federation.d.ts +3 -0
- package/dist/dashboard/web/team-federation.d.ts.map +1 -0
- package/dist/dashboard/web/team-federation.js +487 -0
- package/dist/dashboard/web/team-federation.js.map +1 -0
- package/dist/dashboard/webhook-routes.d.ts +19 -0
- package/dist/dashboard/webhook-routes.d.ts.map +1 -0
- package/dist/dashboard/webhook-routes.js +321 -0
- package/dist/dashboard/webhook-routes.js.map +1 -0
- package/dist/dashboard-web/app.js +509 -386
- package/dist/dashboard-web/index.html +2 -0
- package/dist/dashboard.js +152 -1
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +85 -8
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +85 -8
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts +82 -0
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +315 -0
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +195 -1
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +45 -0
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +169 -18
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +1 -0
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/services/bot-owner-store.d.ts +28 -0
- package/dist/services/bot-owner-store.d.ts.map +1 -0
- package/dist/services/bot-owner-store.js +82 -0
- package/dist/services/bot-owner-store.js.map +1 -0
- package/dist/services/bot-profile-store.d.ts +16 -0
- package/dist/services/bot-profile-store.d.ts.map +1 -0
- package/dist/services/bot-profile-store.js +98 -0
- package/dist/services/bot-profile-store.js.map +1 -0
- package/dist/services/connector-store.d.ts +58 -0
- package/dist/services/connector-store.d.ts.map +1 -0
- package/dist/services/connector-store.js +79 -0
- package/dist/services/connector-store.js.map +1 -0
- package/dist/services/deployment-identity.d.ts +22 -0
- package/dist/services/deployment-identity.d.ts.map +1 -0
- package/dist/services/deployment-identity.js +67 -0
- package/dist/services/deployment-identity.js.map +1 -0
- package/dist/services/federation-membership-store.d.ts +23 -0
- package/dist/services/federation-membership-store.d.ts.map +1 -0
- package/dist/services/federation-membership-store.js +66 -0
- package/dist/services/federation-membership-store.js.map +1 -0
- package/dist/services/federation-roster.d.ts +54 -0
- package/dist/services/federation-roster.d.ts.map +1 -0
- package/dist/services/federation-roster.js +51 -0
- package/dist/services/federation-roster.js.map +1 -0
- package/dist/services/federation-store.d.ts +76 -0
- package/dist/services/federation-store.d.ts.map +1 -0
- package/dist/services/federation-store.js +133 -0
- package/dist/services/federation-store.js.map +1 -0
- package/dist/services/group-creator.d.ts +6 -0
- package/dist/services/group-creator.d.ts.map +1 -1
- package/dist/services/group-creator.js +10 -1
- package/dist/services/group-creator.js.map +1 -1
- package/dist/services/groups-store.d.ts +11 -0
- package/dist/services/groups-store.d.ts.map +1 -1
- package/dist/services/groups-store.js +42 -0
- package/dist/services/groups-store.js.map +1 -1
- package/dist/services/invite-store.d.ts +28 -0
- package/dist/services/invite-store.d.ts.map +1 -0
- package/dist/services/invite-store.js +85 -0
- package/dist/services/invite-store.js.map +1 -0
- package/dist/services/pairing-store.d.ts +47 -0
- package/dist/services/pairing-store.d.ts.map +1 -0
- package/dist/services/pairing-store.js +132 -0
- package/dist/services/pairing-store.js.map +1 -0
- package/dist/services/relay-picker.d.ts +22 -0
- package/dist/services/relay-picker.d.ts.map +1 -0
- package/dist/services/relay-picker.js +62 -0
- package/dist/services/relay-picker.js.map +1 -0
- package/dist/services/team-roster.d.ts +38 -0
- package/dist/services/team-roster.d.ts.map +1 -0
- package/dist/services/team-roster.js +82 -0
- package/dist/services/team-roster.js.map +1 -0
- package/dist/services/team-store.d.ts +54 -0
- package/dist/services/team-store.d.ts.map +1 -0
- package/dist/services/team-store.js +156 -0
- package/dist/services/team-store.js.map +1 -0
- package/dist/services/trigger-log-store.d.ts +46 -0
- package/dist/services/trigger-log-store.d.ts.map +1 -0
- package/dist/services/trigger-log-store.js +132 -0
- package/dist/services/trigger-log-store.js.map +1 -0
- package/dist/services/trigger-types.d.ts +57 -0
- package/dist/services/trigger-types.d.ts.map +1 -0
- package/dist/services/trigger-types.js +28 -0
- package/dist/services/trigger-types.js.map +1 -0
- package/dist/services/webhook-key.d.ts +16 -0
- package/dist/services/webhook-key.d.ts.map +1 -0
- package/dist/services/webhook-key.js +123 -0
- package/dist/services/webhook-key.js.map +1 -0
- package/dist/services/webhook-lifecycle-extractors.d.ts +15 -0
- package/dist/services/webhook-lifecycle-extractors.d.ts.map +1 -0
- package/dist/services/webhook-lifecycle-extractors.js +59 -0
- package/dist/services/webhook-lifecycle-extractors.js.map +1 -0
- package/dist/services/webhook-lifecycle-store.d.ts +45 -0
- package/dist/services/webhook-lifecycle-store.d.ts.map +1 -0
- package/dist/services/webhook-lifecycle-store.js +159 -0
- package/dist/services/webhook-lifecycle-store.js.map +1 -0
- package/dist/setup/verify-permissions.d.ts.map +1 -1
- package/dist/setup/verify-permissions.js +4 -0
- package/dist/setup/verify-permissions.js.map +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +64 -10
- package/dist/skills/definitions.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/daemon-discovery.d.ts +11 -0
- package/dist/utils/daemon-discovery.d.ts.map +1 -0
- package/dist/utils/daemon-discovery.js +59 -0
- package/dist/utils/daemon-discovery.js.map +1 -0
- package/dist/workflows/events/payloads.d.ts +2 -2
- package/dist/workflows/events/schema.d.ts +8 -8
- package/dist/workflows/trigger-from-envelope.d.ts +13 -0
- package/dist/workflows/trigger-from-envelope.d.ts.map +1 -0
- package/dist/workflows/trigger-from-envelope.js +67 -0
- package/dist/workflows/trigger-from-envelope.js.map +1 -0
- 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":"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"}
|
|
@@ -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":"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"}
|
|
@@ -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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"}
|