@cydm/happy-elves 0.1.0-beta.31 → 0.1.0-beta.310
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/apps/cli/dist/commands/app.js +10 -0
- package/apps/cli/dist/commands/daemon.js +92 -4
- package/apps/cli/dist/commands/gateway.d.ts +2 -0
- package/apps/cli/dist/commands/gateway.js +429 -0
- package/apps/cli/dist/commands/lib/args.js +49 -0
- package/apps/cli/dist/commands/lib/bootstrap-daemon.d.ts +2 -2
- package/apps/cli/dist/commands/lib/bootstrap-daemon.js +2 -2
- package/apps/cli/dist/commands/lib/doctor.d.ts +11 -0
- package/apps/cli/dist/commands/lib/doctor.js +276 -11
- package/apps/cli/dist/commands/lib/gateway-store.d.ts +38 -0
- package/apps/cli/dist/commands/lib/gateway-store.js +33 -0
- package/apps/cli/dist/commands/lib/index.d.ts +4 -2
- package/apps/cli/dist/commands/lib/index.js +2 -0
- package/apps/cli/dist/commands/lib/json.d.ts +2 -2
- package/apps/cli/dist/commands/lib/json.js +5 -2
- package/apps/cli/dist/commands/lib/local-daemon.d.ts +1 -0
- package/apps/cli/dist/commands/lib/local-daemon.js +14 -0
- package/apps/cli/dist/commands/lib/paths.d.ts +2 -0
- package/apps/cli/dist/commands/lib/paths.js +2 -0
- package/apps/cli/dist/commands/lib/relay-db-audit.d.ts +37 -0
- package/apps/cli/dist/commands/lib/relay-db-audit.js +300 -0
- package/apps/cli/dist/commands/lib/relay-http.d.ts +1 -1
- package/apps/cli/dist/commands/lib/scope.d.ts +1 -1
- package/apps/cli/dist/commands/lib/session-output.d.ts +15 -1
- package/apps/cli/dist/commands/lib/session-output.js +214 -6
- package/apps/cli/dist/commands/lib/session-view.d.ts +2 -2
- package/apps/cli/dist/commands/lib/status.js +5 -1
- package/apps/cli/dist/commands/lib/types.d.ts +4 -2
- package/apps/cli/dist/commands/lib/usage.js +123 -10
- package/apps/cli/dist/commands/lib/workspace.d.ts +2 -2
- package/apps/cli/dist/commands/lib/workspace.js +63 -10
- package/apps/cli/dist/commands/machine.js +50 -9
- package/apps/cli/dist/commands/memory.d.ts +2 -0
- package/apps/cli/dist/commands/memory.js +179 -0
- package/apps/cli/dist/commands/relay.js +29 -2
- package/apps/cli/dist/commands/release.d.ts +2 -0
- package/apps/cli/dist/commands/release.js +197 -0
- package/apps/cli/dist/commands/remote.js +10 -2
- package/apps/cli/dist/commands/schedule.d.ts +2 -0
- package/apps/cli/dist/commands/schedule.js +273 -0
- package/apps/cli/dist/commands/session.js +517 -5
- package/apps/cli/dist/commands/turn.js +225 -9
- package/apps/cli/dist/commands/workspace.d.ts +2 -0
- package/apps/cli/dist/commands/workspace.js +68 -0
- package/apps/cli/package.json +6 -0
- package/apps/daemon/dist/gateway/adapters.d.ts +38 -0
- package/apps/daemon/dist/gateway/adapters.js +2 -0
- package/apps/daemon/dist/gateway/feishu.d.ts +65 -0
- package/apps/daemon/dist/gateway/feishu.js +493 -0
- package/apps/daemon/dist/gateway/format.d.ts +2 -0
- package/apps/daemon/dist/gateway/format.js +19 -0
- package/apps/daemon/dist/gateway/handler.d.ts +7 -0
- package/apps/daemon/dist/gateway/handler.js +151 -0
- package/apps/daemon/dist/gateway/runtime.d.ts +33 -0
- package/apps/daemon/dist/gateway/runtime.js +850 -0
- package/apps/daemon/dist/gateway/store.d.ts +92 -0
- package/apps/daemon/dist/gateway/store.js +268 -0
- package/apps/daemon/dist/memory/handler.d.ts +7 -0
- package/apps/daemon/dist/memory/handler.js +105 -0
- package/apps/daemon/dist/memory/manager.d.ts +44 -0
- package/apps/daemon/dist/memory/manager.js +359 -0
- package/apps/daemon/dist/paths.d.ts +3 -0
- package/apps/daemon/dist/paths.js +3 -0
- package/apps/daemon/dist/relay/admission.d.ts +30 -0
- package/apps/daemon/dist/relay/admission.js +31 -0
- package/apps/daemon/dist/relay/connection.js +37 -1
- package/apps/daemon/dist/relay/devtools.d.ts +1 -1
- package/apps/daemon/dist/relay/register.js +2 -0
- package/apps/daemon/dist/relay/send.d.ts +2 -2
- package/apps/daemon/dist/relay/send.js +2 -0
- package/apps/daemon/dist/relay-http.d.ts +1 -1
- package/apps/daemon/dist/runtime/external-provider.d.ts +1 -1
- package/apps/daemon/dist/runtime/external-provider.js +187 -17
- package/apps/daemon/dist/runtime.d.ts +1 -2
- package/apps/daemon/dist/runtime.js +0 -3
- package/apps/daemon/dist/schedule/handler.d.ts +9 -0
- package/apps/daemon/dist/schedule/handler.js +148 -0
- package/apps/daemon/dist/schedule/runner.d.ts +21 -0
- package/apps/daemon/dist/schedule/runner.js +547 -0
- package/apps/daemon/dist/schedule/store.d.ts +9 -0
- package/apps/daemon/dist/schedule/store.js +143 -0
- package/apps/daemon/dist/session/create-session.d.ts +1 -1
- package/apps/daemon/dist/session/directory.d.ts +1 -1
- package/apps/daemon/dist/session/events.d.ts +15 -2
- package/apps/daemon/dist/session/events.js +76 -24
- package/apps/daemon/dist/session/file-preview.d.ts +1 -1
- package/apps/daemon/dist/session/lifecycle.d.ts +26 -3
- package/apps/daemon/dist/session/lifecycle.js +161 -22
- package/apps/daemon/dist/session/metadata.d.ts +2 -2
- package/apps/daemon/dist/session/metadata.js +42 -1
- package/apps/daemon/dist/session/primitives.d.ts +1 -1
- package/apps/daemon/dist/session/primitives.js +37 -6
- package/apps/daemon/dist/session/prompt.d.ts +28 -1
- package/apps/daemon/dist/session/prompt.js +340 -5
- package/apps/daemon/dist/session/reconcile.d.ts +3 -0
- package/apps/daemon/dist/session/reconcile.js +235 -0
- package/apps/daemon/dist/session/runtime-handle.d.ts +1 -1
- package/apps/daemon/dist/start.js +5 -1
- package/apps/daemon/dist/state.d.ts +2 -2
- package/apps/daemon/dist/turn-coordinator.d.ts +1 -0
- package/apps/daemon/dist/turn-coordinator.js +4 -0
- package/apps/daemon/dist/types.d.ts +16 -1
- package/apps/daemon/package.json +3 -25
- package/apps/relay/dist/connections.d.ts +2 -0
- package/apps/relay/dist/controller-handlers.d.ts +42 -2
- package/apps/relay/dist/controller-handlers.js +349 -37
- package/apps/relay/dist/db.js +296 -2
- package/apps/relay/dist/errors.d.ts +2 -1
- package/apps/relay/dist/errors.js +3 -1
- package/apps/relay/dist/http-routes.js +659 -33
- package/apps/relay/dist/http-schemas.d.ts +156 -0
- package/apps/relay/dist/http-schemas.js +85 -0
- package/apps/relay/dist/index.js +31 -1
- package/apps/relay/dist/machine-handlers.d.ts +15 -7
- package/apps/relay/dist/machine-handlers.js +567 -27
- package/apps/relay/dist/projections.d.ts +1 -1
- package/apps/relay/dist/projections.js +13 -0
- package/apps/relay/dist/relay-context.d.ts +41 -3
- package/apps/relay/dist/relay-context.js +244 -19
- package/apps/relay/dist/repositories.d.ts +2 -2
- package/apps/relay/dist/repositories.js +28 -1
- package/apps/relay/dist/runtime-bindings.d.ts +25 -0
- package/apps/relay/dist/runtime-bindings.js +49 -0
- package/apps/relay/dist/scope.d.ts +1 -1
- package/apps/relay/dist/types.d.ts +71 -1
- package/apps/relay/dist/websocket.js +2 -0
- package/package.json +5 -11
- package/packages/agent-sdk/dist/index.d.ts +80 -0
- package/packages/agent-sdk/dist/index.js +16 -0
- package/packages/client/dist/account.d.ts +35 -2
- package/packages/client/dist/account.js +98 -1
- package/packages/client/dist/client.d.ts +59 -6
- package/packages/client/dist/client.js +312 -58
- package/packages/client/dist/index.d.ts +1 -1
- package/packages/client/dist/parsers.d.ts +28 -2
- package/packages/client/dist/parsers.js +167 -0
- package/packages/client/dist/transport.d.ts +21 -1
- package/packages/client/dist/transport.js +69 -6
- package/packages/client/dist/types.d.ts +177 -1
- package/packages/client/dist/validation.d.ts +1 -1
- package/packages/runtime/dist/index.d.ts +66 -0
- package/packages/runtime-acpx/dist/index.d.ts +1 -1
- package/packages/runtime-acpx/dist/index.js +8 -0
- package/packages/runtime-cli/dist/claude-history.d.ts +1 -2
- package/packages/runtime-cli/dist/claude-history.js +0 -3
- package/packages/runtime-cli/dist/claude-session-store.d.ts +0 -1
- package/packages/runtime-cli/dist/claude-session-store.js +1 -1
- package/packages/runtime-cli/dist/claude-turn.d.ts +2 -1
- package/packages/runtime-cli/dist/claude-turn.js +29 -9
- package/packages/runtime-cli/dist/codex-app-server.d.ts +8 -3
- package/packages/runtime-cli/dist/codex-app-server.js +122 -25
- package/packages/runtime-cli/dist/codex-history.d.ts +1 -2
- package/packages/runtime-cli/dist/codex-history.js +11 -5
- package/packages/runtime-cli/dist/codex-protocol.d.ts +2 -2
- package/packages/runtime-cli/dist/codex-protocol.js +4 -4
- package/packages/runtime-cli/dist/history-merge.d.ts +1 -1
- package/packages/runtime-cli/dist/index.d.ts +1 -1
- package/packages/runtime-cli/dist/index.js +108 -2
- package/packages/runtime-cli/dist/permissions.d.ts +1 -1
- package/packages/runtime-cli/dist/session-store.d.ts +2 -1
- package/packages/runtime-cli/dist/session-store.js +9 -8
- package/packages/shared/dist/index.d.ts +2 -0
- package/packages/shared/dist/index.js +2 -0
- package/packages/shared/dist/protocol-schemas.d.ts +1077 -0
- package/packages/shared/dist/protocol-schemas.js +273 -0
- package/packages/shared/dist/protocol-types.d.ts +371 -3
- package/packages/shared/dist/protocol.d.ts +181 -2
- package/packages/shared/dist/protocol.js +197 -1
- package/packages/shared/dist/session-tags.d.ts +12 -0
- package/packages/shared/dist/session-tags.js +50 -0
- package/apps/cli/dist/commands/account.js.map +0 -1
- package/apps/cli/dist/commands/app.js.map +0 -1
- package/apps/cli/dist/commands/collect.js.map +0 -1
- package/apps/cli/dist/commands/command.js.map +0 -1
- package/apps/cli/dist/commands/config.js.map +0 -1
- package/apps/cli/dist/commands/daemon.js.map +0 -1
- package/apps/cli/dist/commands/lib/args.js.map +0 -1
- package/apps/cli/dist/commands/lib/audit.js.map +0 -1
- package/apps/cli/dist/commands/lib/bootstrap-config.js.map +0 -1
- package/apps/cli/dist/commands/lib/bootstrap-daemon.js.map +0 -1
- package/apps/cli/dist/commands/lib/bootstrap-output.js.map +0 -1
- package/apps/cli/dist/commands/lib/bootstrap.js.map +0 -1
- package/apps/cli/dist/commands/lib/config.js.map +0 -1
- package/apps/cli/dist/commands/lib/doctor.js.map +0 -1
- package/apps/cli/dist/commands/lib/exit.js.map +0 -1
- package/apps/cli/dist/commands/lib/index.js.map +0 -1
- package/apps/cli/dist/commands/lib/json.js.map +0 -1
- package/apps/cli/dist/commands/lib/local-daemon.js.map +0 -1
- package/apps/cli/dist/commands/lib/loop-store.js.map +0 -1
- package/apps/cli/dist/commands/lib/paths.js.map +0 -1
- package/apps/cli/dist/commands/lib/relay-http.js.map +0 -1
- package/apps/cli/dist/commands/lib/scope.js.map +0 -1
- package/apps/cli/dist/commands/lib/session-output.js.map +0 -1
- package/apps/cli/dist/commands/lib/session-view.js.map +0 -1
- package/apps/cli/dist/commands/lib/status.js.map +0 -1
- package/apps/cli/dist/commands/lib/types.js.map +0 -1
- package/apps/cli/dist/commands/lib/usage.js.map +0 -1
- package/apps/cli/dist/commands/lib/workspace.js.map +0 -1
- package/apps/cli/dist/commands/loop.js.map +0 -1
- package/apps/cli/dist/commands/machine.js.map +0 -1
- package/apps/cli/dist/commands/relay.js.map +0 -1
- package/apps/cli/dist/commands/remote.js.map +0 -1
- package/apps/cli/dist/commands/session.js.map +0 -1
- package/apps/cli/dist/commands/token.js.map +0 -1
- package/apps/cli/dist/commands/turn.js.map +0 -1
- package/apps/cli/dist/errors.js.map +0 -1
- package/apps/cli/dist/index.js.map +0 -1
- package/apps/daemon/dist/app.js.map +0 -1
- package/apps/daemon/dist/args.js.map +0 -1
- package/apps/daemon/dist/audit.js.map +0 -1
- package/apps/daemon/dist/cli.js.map +0 -1
- package/apps/daemon/dist/config.js.map +0 -1
- package/apps/daemon/dist/errors.js.map +0 -1
- package/apps/daemon/dist/loop/runner.js.map +0 -1
- package/apps/daemon/dist/loop/store.js.map +0 -1
- package/apps/daemon/dist/pair.js.map +0 -1
- package/apps/daemon/dist/paths.js.map +0 -1
- package/apps/daemon/dist/process.js.map +0 -1
- package/apps/daemon/dist/relay/connection.js.map +0 -1
- package/apps/daemon/dist/relay/devtools.js.map +0 -1
- package/apps/daemon/dist/relay/register.js.map +0 -1
- package/apps/daemon/dist/relay/send.js.map +0 -1
- package/apps/daemon/dist/relay-http.js.map +0 -1
- package/apps/daemon/dist/runtime/external-provider.js.map +0 -1
- package/apps/daemon/dist/runtime.js.map +0 -1
- package/apps/daemon/dist/session/create-session.js.map +0 -1
- package/apps/daemon/dist/session/directory.js.map +0 -1
- package/apps/daemon/dist/session/events.js.map +0 -1
- package/apps/daemon/dist/session/file-preview.js.map +0 -1
- package/apps/daemon/dist/session/lifecycle.js.map +0 -1
- package/apps/daemon/dist/session/metadata.js.map +0 -1
- package/apps/daemon/dist/session/primitives.js.map +0 -1
- package/apps/daemon/dist/session/prompt.js.map +0 -1
- package/apps/daemon/dist/session/runtime-handle.js.map +0 -1
- package/apps/daemon/dist/start.js.map +0 -1
- package/apps/daemon/dist/state.js.map +0 -1
- package/apps/daemon/dist/turn-coordinator.js.map +0 -1
- package/apps/daemon/dist/types.js.map +0 -1
- package/apps/daemon/dist/utils.js.map +0 -1
- package/apps/relay/dist/auth.js.map +0 -1
- package/apps/relay/dist/connections.js.map +0 -1
- package/apps/relay/dist/controller-handlers.js.map +0 -1
- package/apps/relay/dist/db.js.map +0 -1
- package/apps/relay/dist/errors.js.map +0 -1
- package/apps/relay/dist/http-routes.js.map +0 -1
- package/apps/relay/dist/http-schemas.js.map +0 -1
- package/apps/relay/dist/index.js.map +0 -1
- package/apps/relay/dist/machine-handlers.js.map +0 -1
- package/apps/relay/dist/projections.js.map +0 -1
- package/apps/relay/dist/relay-context.js.map +0 -1
- package/apps/relay/dist/repositories.js.map +0 -1
- package/apps/relay/dist/retention.js.map +0 -1
- package/apps/relay/dist/scope.js.map +0 -1
- package/apps/relay/dist/security.js.map +0 -1
- package/apps/relay/dist/types.js.map +0 -1
- package/apps/relay/dist/websocket.js.map +0 -1
- package/apps/relay/package.json +0 -29
- package/packages/agent-sdk/dist/index.d.ts.map +0 -1
- package/packages/agent-sdk/dist/index.js.map +0 -1
- package/packages/agent-sdk/package.json +0 -20
- package/packages/client/dist/account.d.ts.map +0 -1
- package/packages/client/dist/account.js.map +0 -1
- package/packages/client/dist/client.d.ts.map +0 -1
- package/packages/client/dist/client.js.map +0 -1
- package/packages/client/dist/errors.d.ts.map +0 -1
- package/packages/client/dist/errors.js.map +0 -1
- package/packages/client/dist/http.d.ts.map +0 -1
- package/packages/client/dist/http.js.map +0 -1
- package/packages/client/dist/index.d.ts.map +0 -1
- package/packages/client/dist/index.js.map +0 -1
- package/packages/client/dist/parsers.d.ts.map +0 -1
- package/packages/client/dist/parsers.js.map +0 -1
- package/packages/client/dist/transport.d.ts.map +0 -1
- package/packages/client/dist/transport.js.map +0 -1
- package/packages/client/dist/types.d.ts.map +0 -1
- package/packages/client/dist/types.js.map +0 -1
- package/packages/client/dist/validation.d.ts.map +0 -1
- package/packages/client/dist/validation.js.map +0 -1
- package/packages/client/package.json +0 -22
- package/packages/pie-provider/dist/index.d.ts.map +0 -1
- package/packages/pie-provider/dist/index.js.map +0 -1
- package/packages/pie-provider/package.json +0 -26
- package/packages/runtime/dist/index.d.ts.map +0 -1
- package/packages/runtime/dist/index.js.map +0 -1
- package/packages/runtime/package.json +0 -19
- package/packages/runtime-acpx/dist/index.d.ts.map +0 -1
- package/packages/runtime-acpx/dist/index.js.map +0 -1
- package/packages/runtime-acpx/package.json +0 -23
- package/packages/runtime-cli/dist/async-queue.d.ts.map +0 -1
- package/packages/runtime-cli/dist/async-queue.js.map +0 -1
- package/packages/runtime-cli/dist/claude-history.d.ts.map +0 -1
- package/packages/runtime-cli/dist/claude-history.js.map +0 -1
- package/packages/runtime-cli/dist/claude-json.d.ts.map +0 -1
- package/packages/runtime-cli/dist/claude-json.js.map +0 -1
- package/packages/runtime-cli/dist/claude-rename.d.ts.map +0 -1
- package/packages/runtime-cli/dist/claude-rename.js.map +0 -1
- package/packages/runtime-cli/dist/claude-session-store.d.ts.map +0 -1
- package/packages/runtime-cli/dist/claude-session-store.js.map +0 -1
- package/packages/runtime-cli/dist/claude-turn.d.ts.map +0 -1
- package/packages/runtime-cli/dist/claude-turn.js.map +0 -1
- package/packages/runtime-cli/dist/codex-app-server.d.ts.map +0 -1
- package/packages/runtime-cli/dist/codex-app-server.js.map +0 -1
- package/packages/runtime-cli/dist/codex-approvals.d.ts.map +0 -1
- package/packages/runtime-cli/dist/codex-approvals.js.map +0 -1
- package/packages/runtime-cli/dist/codex-history.d.ts.map +0 -1
- package/packages/runtime-cli/dist/codex-history.js.map +0 -1
- package/packages/runtime-cli/dist/codex-json-rpc.d.ts.map +0 -1
- package/packages/runtime-cli/dist/codex-json-rpc.js.map +0 -1
- package/packages/runtime-cli/dist/codex-lifecycle.d.ts.map +0 -1
- package/packages/runtime-cli/dist/codex-lifecycle.js.map +0 -1
- package/packages/runtime-cli/dist/codex-protocol.d.ts.map +0 -1
- package/packages/runtime-cli/dist/codex-protocol.js.map +0 -1
- package/packages/runtime-cli/dist/command-resolver.d.ts.map +0 -1
- package/packages/runtime-cli/dist/command-resolver.js.map +0 -1
- package/packages/runtime-cli/dist/history-merge.d.ts.map +0 -1
- package/packages/runtime-cli/dist/history-merge.js.map +0 -1
- package/packages/runtime-cli/dist/index.d.ts.map +0 -1
- package/packages/runtime-cli/dist/index.js.map +0 -1
- package/packages/runtime-cli/dist/permissions.d.ts.map +0 -1
- package/packages/runtime-cli/dist/permissions.js.map +0 -1
- package/packages/runtime-cli/dist/session-store.d.ts.map +0 -1
- package/packages/runtime-cli/dist/session-store.js.map +0 -1
- package/packages/runtime-cli/package.json +0 -22
- package/packages/shared/dist/args.d.ts.map +0 -1
- package/packages/shared/dist/args.js.map +0 -1
- package/packages/shared/dist/crypto.d.ts.map +0 -1
- package/packages/shared/dist/crypto.js.map +0 -1
- package/packages/shared/dist/ids.d.ts.map +0 -1
- package/packages/shared/dist/ids.js.map +0 -1
- package/packages/shared/dist/index.d.ts.map +0 -1
- package/packages/shared/dist/index.js.map +0 -1
- package/packages/shared/dist/protocol-schemas.d.ts.map +0 -1
- package/packages/shared/dist/protocol-schemas.js.map +0 -1
- package/packages/shared/dist/protocol-types.d.ts.map +0 -1
- package/packages/shared/dist/protocol-types.js.map +0 -1
- package/packages/shared/dist/protocol.d.ts.map +0 -1
- package/packages/shared/dist/protocol.js.map +0 -1
- package/packages/shared/dist/session-name.d.ts.map +0 -1
- package/packages/shared/dist/session-name.js.map +0 -1
- package/packages/shared/dist/session-state.d.ts.map +0 -1
- package/packages/shared/dist/session-state.js.map +0 -1
- package/packages/shared/package.json +0 -24
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import { formatGatewayPayload } from "./format.js";
|
|
2
|
+
const feishuProcessingReactionEmoji = "Typing";
|
|
3
|
+
const maxTrackedProcessingReactions = 200;
|
|
4
|
+
export async function createFeishuGatewayAdapter(context) {
|
|
5
|
+
if (!context.channel.appId || !context.channel.appSecret) {
|
|
6
|
+
throw new Error(`Gateway ${context.channel.id} is missing Feishu appId/appSecret`);
|
|
7
|
+
}
|
|
8
|
+
const lark = await import("@larksuiteoapi/node-sdk");
|
|
9
|
+
const baseConfig = {
|
|
10
|
+
appId: context.channel.appId,
|
|
11
|
+
appSecret: context.channel.appSecret,
|
|
12
|
+
};
|
|
13
|
+
const domain = context.channel.domain === "lark" ? lark.Domain?.Lark : lark.Domain?.Feishu;
|
|
14
|
+
if (domain)
|
|
15
|
+
baseConfig.domain = domain;
|
|
16
|
+
const client = new lark.Client(baseConfig);
|
|
17
|
+
const processingReactions = createFeishuProcessingReactionRegistry(client);
|
|
18
|
+
const botOpenIdResolver = createFeishuBotOpenIdResolver(client);
|
|
19
|
+
const wsClient = new lark.WSClient({
|
|
20
|
+
...baseConfig,
|
|
21
|
+
...(lark.LoggerLevel?.warn ? { loggerLevel: lark.LoggerLevel.warn } : {}),
|
|
22
|
+
});
|
|
23
|
+
const dispatcher = new lark.EventDispatcher({}).register({
|
|
24
|
+
"im.message.receive_v1": async (data) => {
|
|
25
|
+
const inbound = normalizeFeishuMessage(data);
|
|
26
|
+
if (!inbound)
|
|
27
|
+
return;
|
|
28
|
+
const channel = await context.readChannel();
|
|
29
|
+
if (!channel?.enabled)
|
|
30
|
+
return;
|
|
31
|
+
const botIdentity = channel.inboundMode === "mention"
|
|
32
|
+
? await botOpenIdResolver.get() ?? { appId: channel.appId }
|
|
33
|
+
: undefined;
|
|
34
|
+
if (!shouldHandleFeishuInbound(channel, inbound, botIdentity))
|
|
35
|
+
return;
|
|
36
|
+
const startedReaction = await processingReactions.start(inbound.platformMessageId, channel.processingReaction);
|
|
37
|
+
try {
|
|
38
|
+
const result = await context.sendInbound({
|
|
39
|
+
gatewayId: channel.id,
|
|
40
|
+
platform: "feishu",
|
|
41
|
+
platformEventId: inbound.platformEventId,
|
|
42
|
+
platformMessageId: inbound.platformMessageId,
|
|
43
|
+
senderPlatformUserId: inbound.senderPlatformUserId,
|
|
44
|
+
...(inbound.senderDisplayName ? { senderDisplayName: inbound.senderDisplayName } : {}),
|
|
45
|
+
destination: inbound.destination,
|
|
46
|
+
text: stripFeishuBotMention(inbound.text, inbound.mentions, botIdentity),
|
|
47
|
+
});
|
|
48
|
+
if (shouldCompleteProcessingReaction(result, { startedReaction }))
|
|
49
|
+
await processingReactions.complete(inbound.platformMessageId);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
await processingReactions.complete(inbound.platformMessageId);
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
async start() {
|
|
59
|
+
await wsClient.start({ eventDispatcher: dispatcher });
|
|
60
|
+
},
|
|
61
|
+
async stop() {
|
|
62
|
+
if (wsClient.stop)
|
|
63
|
+
await wsClient.stop();
|
|
64
|
+
else if (wsClient.close)
|
|
65
|
+
await wsClient.close();
|
|
66
|
+
await processingReactions.clear();
|
|
67
|
+
},
|
|
68
|
+
async send(input) {
|
|
69
|
+
const text = formatGatewayPayload(input.payload);
|
|
70
|
+
try {
|
|
71
|
+
const response = await sendFeishuText(client, input.destination, text, input.deliveryId);
|
|
72
|
+
return { platformMessageId: platformMessageId(response) };
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
if (input.destination.replyToMessageId) {
|
|
76
|
+
await processingReactions.complete(input.destination.replyToMessageId);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function shouldCompleteProcessingReaction(result, input = {}) {
|
|
83
|
+
return !result.accepted && (!result.duplicate || input.startedReaction === true);
|
|
84
|
+
}
|
|
85
|
+
export function createFeishuProcessingReactionTracker(client, input = {}) {
|
|
86
|
+
const emojiType = input.emojiType ?? feishuProcessingReactionEmoji;
|
|
87
|
+
const maxTracked = Math.max(1, input.maxTracked ?? maxTrackedProcessingReactions);
|
|
88
|
+
const now = input.now ?? Date.now;
|
|
89
|
+
const pending = new Map();
|
|
90
|
+
async function start(messageId) {
|
|
91
|
+
if (!messageId || pending.has(messageId))
|
|
92
|
+
return false;
|
|
93
|
+
await trimPending();
|
|
94
|
+
const api = reactionApi(client);
|
|
95
|
+
if (!api?.create)
|
|
96
|
+
return false;
|
|
97
|
+
try {
|
|
98
|
+
const response = await api.create({
|
|
99
|
+
path: { message_id: messageId },
|
|
100
|
+
data: { reaction_type: { emoji_type: emojiType } },
|
|
101
|
+
});
|
|
102
|
+
const reactionId = platformReactionId(response);
|
|
103
|
+
if (reactionId) {
|
|
104
|
+
pending.set(messageId, { reactionId, createdAt: now() });
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Reaction status is best-effort UI feedback; never block gateway processing.
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
async function complete(messageId) {
|
|
114
|
+
const tracked = pending.get(messageId);
|
|
115
|
+
if (!tracked)
|
|
116
|
+
return;
|
|
117
|
+
const api = reactionApi(client);
|
|
118
|
+
if (!api?.delete) {
|
|
119
|
+
pending.delete(messageId);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
await api.delete({ path: { message_id: messageId, reaction_id: tracked.reactionId } });
|
|
124
|
+
pending.delete(messageId);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Keep the handle so a later clear/complete can retry the deletion.
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async function clear() {
|
|
131
|
+
for (const messageId of Array.from(pending.keys())) {
|
|
132
|
+
await complete(messageId);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function trimPending() {
|
|
136
|
+
while (pending.size >= maxTracked) {
|
|
137
|
+
const oldest = oldestPendingMessageId(pending);
|
|
138
|
+
if (!oldest)
|
|
139
|
+
return;
|
|
140
|
+
await complete(oldest);
|
|
141
|
+
if (pending.has(oldest))
|
|
142
|
+
pending.delete(oldest);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return { start, complete, clear, pendingCount: () => pending.size };
|
|
146
|
+
}
|
|
147
|
+
function createFeishuBotOpenIdResolver(client) {
|
|
148
|
+
let cached;
|
|
149
|
+
let inflight;
|
|
150
|
+
async function get() {
|
|
151
|
+
if (cached)
|
|
152
|
+
return cached;
|
|
153
|
+
if (inflight)
|
|
154
|
+
return await inflight;
|
|
155
|
+
inflight = resolveFeishuBotIdentity(client)
|
|
156
|
+
.then((identity) => {
|
|
157
|
+
if (identity && Object.keys(identity).length > 0)
|
|
158
|
+
cached = identity;
|
|
159
|
+
return identity;
|
|
160
|
+
})
|
|
161
|
+
.catch(() => undefined)
|
|
162
|
+
.finally(() => {
|
|
163
|
+
inflight = undefined;
|
|
164
|
+
});
|
|
165
|
+
return await inflight;
|
|
166
|
+
}
|
|
167
|
+
return { get, cached: () => cached };
|
|
168
|
+
}
|
|
169
|
+
function createFeishuProcessingReactionRegistry(client) {
|
|
170
|
+
const trackers = new Map();
|
|
171
|
+
function trackerFor(emojiType) {
|
|
172
|
+
const key = emojiType || feishuProcessingReactionEmoji;
|
|
173
|
+
const existing = trackers.get(key);
|
|
174
|
+
if (existing)
|
|
175
|
+
return existing;
|
|
176
|
+
const tracker = createFeishuProcessingReactionTracker(client, { emojiType: key });
|
|
177
|
+
trackers.set(key, tracker);
|
|
178
|
+
return tracker;
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
async start(messageId, config) {
|
|
182
|
+
if (!config.enabled)
|
|
183
|
+
return false;
|
|
184
|
+
return await trackerFor(config.emojiType).start(messageId);
|
|
185
|
+
},
|
|
186
|
+
async complete(messageId) {
|
|
187
|
+
for (const tracker of trackers.values())
|
|
188
|
+
await tracker.complete(messageId);
|
|
189
|
+
},
|
|
190
|
+
async clear() {
|
|
191
|
+
for (const tracker of trackers.values())
|
|
192
|
+
await tracker.clear();
|
|
193
|
+
trackers.clear();
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
export function normalizeFeishuMessage(data) {
|
|
198
|
+
const record = asRecord(data);
|
|
199
|
+
if (!record)
|
|
200
|
+
return undefined;
|
|
201
|
+
const event = asRecord(record.event) ?? record;
|
|
202
|
+
const message = asRecord(event.message);
|
|
203
|
+
if (!message)
|
|
204
|
+
return undefined;
|
|
205
|
+
const chatId = stringField(message, "chat_id");
|
|
206
|
+
const messageId = stringField(message, "message_id");
|
|
207
|
+
if (!chatId || !messageId)
|
|
208
|
+
return undefined;
|
|
209
|
+
const sender = asRecord(event.sender);
|
|
210
|
+
const senderId = asRecord(sender?.sender_id);
|
|
211
|
+
const senderPlatformUserId = stringField(senderId, "open_id") ??
|
|
212
|
+
stringField(senderId, "union_id") ??
|
|
213
|
+
stringField(senderId, "user_id") ??
|
|
214
|
+
stringField(sender, "sender_id") ??
|
|
215
|
+
"unknown";
|
|
216
|
+
const threadId = stringField(message, "thread_id");
|
|
217
|
+
const chatType = stringField(message, "chat_type");
|
|
218
|
+
const text = feishuContentText(message.content);
|
|
219
|
+
if (!text.trim())
|
|
220
|
+
return undefined;
|
|
221
|
+
const eventId = stringField(asRecord(record.header), "event_id") ?? stringField(record, "event_id") ?? messageId;
|
|
222
|
+
const destination = threadId
|
|
223
|
+
? {
|
|
224
|
+
platformChatId: chatId,
|
|
225
|
+
contextKind: "thread",
|
|
226
|
+
contextKey: threadId,
|
|
227
|
+
replyToMessageId: messageId,
|
|
228
|
+
}
|
|
229
|
+
: {
|
|
230
|
+
platformChatId: chatId,
|
|
231
|
+
contextKind: "main",
|
|
232
|
+
replyToMessageId: messageId,
|
|
233
|
+
};
|
|
234
|
+
return {
|
|
235
|
+
platformEventId: eventId,
|
|
236
|
+
platformMessageId: messageId,
|
|
237
|
+
senderPlatformUserId,
|
|
238
|
+
senderDisplayName: stringField(sender, "sender_type"),
|
|
239
|
+
destination,
|
|
240
|
+
text,
|
|
241
|
+
...(chatType ? { chatType } : {}),
|
|
242
|
+
mentions: feishuMentions(message),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
export function shouldHandleFeishuInbound(profile, inbound, botIdentity) {
|
|
246
|
+
if (profile.inboundMode === "all")
|
|
247
|
+
return true;
|
|
248
|
+
if (isFeishuDirectChat(inbound.chatType))
|
|
249
|
+
return true;
|
|
250
|
+
const identity = normalizeBotIdentity(botIdentity);
|
|
251
|
+
if (!identity)
|
|
252
|
+
return false;
|
|
253
|
+
return inbound.mentions.some((mention) => (identity.openId && mention.openId === identity.openId)
|
|
254
|
+
|| (identity.unionId && mention.unionId === identity.unionId)
|
|
255
|
+
|| (identity.userId && mention.userId === identity.userId)
|
|
256
|
+
|| (identity.appId && mention.appId === identity.appId));
|
|
257
|
+
}
|
|
258
|
+
export async function sendFeishuText(client, destination, text, deliveryId) {
|
|
259
|
+
const content = JSON.stringify({ text });
|
|
260
|
+
const messageApi = client.im?.v1?.message ?? client.im?.message;
|
|
261
|
+
if (!messageApi?.create)
|
|
262
|
+
throw new Error("Feishu SDK message.create is unavailable");
|
|
263
|
+
const errors = [];
|
|
264
|
+
if (destination.replyToMessageId && messageApi.reply) {
|
|
265
|
+
try {
|
|
266
|
+
return await messageApi.reply({
|
|
267
|
+
path: { message_id: destination.replyToMessageId },
|
|
268
|
+
data: {
|
|
269
|
+
msg_type: "text",
|
|
270
|
+
content,
|
|
271
|
+
...(deliveryId ? { uuid: deliveryId } : {}),
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
errors.push(errorMessage(error));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (destination.contextKind === "thread") {
|
|
280
|
+
if (destination.contextKey) {
|
|
281
|
+
try {
|
|
282
|
+
return await messageApi.create({
|
|
283
|
+
params: { receive_id_type: "thread_id" },
|
|
284
|
+
data: {
|
|
285
|
+
receive_id: destination.contextKey,
|
|
286
|
+
msg_type: "text",
|
|
287
|
+
content,
|
|
288
|
+
...(deliveryId ? { uuid: deliveryId } : {}),
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
errors.push(errorMessage(error));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
throw new Error(`Feishu thread delivery failed${errors.length > 0 ? `: ${errors.join("; ")}` : ""}`);
|
|
297
|
+
}
|
|
298
|
+
try {
|
|
299
|
+
return await messageApi.create({
|
|
300
|
+
params: { receive_id_type: "chat_id" },
|
|
301
|
+
data: {
|
|
302
|
+
receive_id: destination.platformChatId,
|
|
303
|
+
msg_type: "text",
|
|
304
|
+
content,
|
|
305
|
+
...(deliveryId ? { uuid: deliveryId } : {}),
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
errors.push(errorMessage(error));
|
|
311
|
+
throw new Error(`Feishu chat delivery failed: ${errors.join("; ")}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
export function stripFeishuBotMention(text, mentions, botIdentity) {
|
|
315
|
+
const identity = normalizeBotIdentity(botIdentity);
|
|
316
|
+
if (!identity)
|
|
317
|
+
return text;
|
|
318
|
+
const botKeys = mentions
|
|
319
|
+
.filter((mention) => (identity.openId && mention.openId === identity.openId)
|
|
320
|
+
|| (identity.unionId && mention.unionId === identity.unionId)
|
|
321
|
+
|| (identity.userId && mention.userId === identity.userId)
|
|
322
|
+
|| (identity.appId && mention.appId === identity.appId))
|
|
323
|
+
.map((mention) => mention.key)
|
|
324
|
+
.filter((key) => Boolean(key));
|
|
325
|
+
let stripped = text;
|
|
326
|
+
for (const key of botKeys) {
|
|
327
|
+
stripped = stripped.replaceAll(key, "");
|
|
328
|
+
}
|
|
329
|
+
return stripped.trim() || text;
|
|
330
|
+
}
|
|
331
|
+
function errorMessage(error) {
|
|
332
|
+
return error instanceof Error ? error.message : String(error);
|
|
333
|
+
}
|
|
334
|
+
function feishuContentText(value) {
|
|
335
|
+
if (typeof value !== "string")
|
|
336
|
+
return "";
|
|
337
|
+
try {
|
|
338
|
+
const parsed = JSON.parse(value);
|
|
339
|
+
const record = asRecord(parsed);
|
|
340
|
+
if (typeof record?.text === "string")
|
|
341
|
+
return record.text;
|
|
342
|
+
if (typeof record?.content === "string")
|
|
343
|
+
return record.content;
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
return value;
|
|
347
|
+
}
|
|
348
|
+
return "";
|
|
349
|
+
}
|
|
350
|
+
function feishuMentions(message) {
|
|
351
|
+
const mentions = [];
|
|
352
|
+
const rawMentions = message.mentions;
|
|
353
|
+
if (Array.isArray(rawMentions)) {
|
|
354
|
+
for (const item of rawMentions) {
|
|
355
|
+
const mention = feishuMentionFromRecord(asRecord(item));
|
|
356
|
+
if (mention)
|
|
357
|
+
mentions.push(mention);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const contentMentions = feishuMentionsFromContent(message.content);
|
|
361
|
+
for (const mention of contentMentions) {
|
|
362
|
+
if (!mentions.some((item) => mentionKey(item) === mentionKey(mention)))
|
|
363
|
+
mentions.push(mention);
|
|
364
|
+
}
|
|
365
|
+
return mentions;
|
|
366
|
+
}
|
|
367
|
+
function feishuMentionFromRecord(record) {
|
|
368
|
+
if (!record)
|
|
369
|
+
return undefined;
|
|
370
|
+
const id = asRecord(record.id) ?? asRecord(record.user_id) ?? asRecord(record.userId);
|
|
371
|
+
const mention = {
|
|
372
|
+
...(stringField(record, "key") ? { key: stringField(record, "key") } : {}),
|
|
373
|
+
...(stringField(id, "open_id") ?? stringField(record, "open_id") ? { openId: stringField(id, "open_id") ?? stringField(record, "open_id") } : {}),
|
|
374
|
+
...(stringField(id, "user_id") ?? stringField(record, "user_id") ? { userId: stringField(id, "user_id") ?? stringField(record, "user_id") } : {}),
|
|
375
|
+
...(stringField(id, "union_id") ?? stringField(record, "union_id") ? { unionId: stringField(id, "union_id") ?? stringField(record, "union_id") } : {}),
|
|
376
|
+
...(stringField(id, "app_id") ?? stringField(record, "app_id") ? { appId: stringField(id, "app_id") ?? stringField(record, "app_id") } : {}),
|
|
377
|
+
...(stringField(record, "name") ? { name: stringField(record, "name") } : {}),
|
|
378
|
+
...(record.is_bot === true || record.isBot === true ? { isBot: true } : {}),
|
|
379
|
+
};
|
|
380
|
+
return mention.openId || mention.userId || mention.unionId || mention.appId || mention.key || mention.name ? mention : undefined;
|
|
381
|
+
}
|
|
382
|
+
function feishuMentionsFromContent(value) {
|
|
383
|
+
if (typeof value !== "string")
|
|
384
|
+
return [];
|
|
385
|
+
const mentions = [];
|
|
386
|
+
try {
|
|
387
|
+
collectMentionsFromJson(JSON.parse(value), mentions);
|
|
388
|
+
}
|
|
389
|
+
catch {
|
|
390
|
+
collectMentionIdsFromText(value, mentions);
|
|
391
|
+
}
|
|
392
|
+
collectMentionIdsFromText(value, mentions);
|
|
393
|
+
return mentions;
|
|
394
|
+
}
|
|
395
|
+
function collectMentionsFromJson(value, output) {
|
|
396
|
+
if (Array.isArray(value)) {
|
|
397
|
+
for (const item of value)
|
|
398
|
+
collectMentionsFromJson(item, output);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
const record = asRecord(value);
|
|
402
|
+
if (!record)
|
|
403
|
+
return;
|
|
404
|
+
const mention = feishuMentionFromRecord(record);
|
|
405
|
+
if (mention && !output.some((item) => mentionKey(item) === mentionKey(mention)))
|
|
406
|
+
output.push(mention);
|
|
407
|
+
for (const item of Object.values(record))
|
|
408
|
+
collectMentionsFromJson(item, output);
|
|
409
|
+
}
|
|
410
|
+
function collectMentionIdsFromText(value, output) {
|
|
411
|
+
for (const match of value.matchAll(/open_id=["']([^"']+)["']/giu)) {
|
|
412
|
+
const mention = { openId: match[1] };
|
|
413
|
+
if (!output.some((item) => mentionKey(item) === mentionKey(mention)))
|
|
414
|
+
output.push(mention);
|
|
415
|
+
}
|
|
416
|
+
for (const match of value.matchAll(/user_id=["']([^"']+)["']/giu)) {
|
|
417
|
+
const mention = { userId: match[1] };
|
|
418
|
+
if (!output.some((item) => mentionKey(item) === mentionKey(mention)))
|
|
419
|
+
output.push(mention);
|
|
420
|
+
}
|
|
421
|
+
for (const match of value.matchAll(/app_id=["']([^"']+)["']/giu)) {
|
|
422
|
+
const mention = { appId: match[1] };
|
|
423
|
+
if (!output.some((item) => mentionKey(item) === mentionKey(mention)))
|
|
424
|
+
output.push(mention);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
function mentionKey(mention) {
|
|
428
|
+
return mention.openId ?? mention.userId ?? mention.unionId ?? mention.appId ?? mention.key ?? mention.name ?? "";
|
|
429
|
+
}
|
|
430
|
+
function isFeishuDirectChat(chatType) {
|
|
431
|
+
return chatType === "p2p" || chatType === "dm" || chatType === "direct" || chatType === "private";
|
|
432
|
+
}
|
|
433
|
+
function normalizeBotIdentity(value) {
|
|
434
|
+
if (typeof value === "string")
|
|
435
|
+
return value.trim() ? { openId: value.trim() } : undefined;
|
|
436
|
+
if (!value)
|
|
437
|
+
return undefined;
|
|
438
|
+
const identity = {
|
|
439
|
+
...(value.openId ? { openId: value.openId } : {}),
|
|
440
|
+
...(value.unionId ? { unionId: value.unionId } : {}),
|
|
441
|
+
...(value.userId ? { userId: value.userId } : {}),
|
|
442
|
+
...(value.appId ? { appId: value.appId } : {}),
|
|
443
|
+
};
|
|
444
|
+
return Object.keys(identity).length > 0 ? identity : undefined;
|
|
445
|
+
}
|
|
446
|
+
async function resolveFeishuBotIdentity(client) {
|
|
447
|
+
if (!client.request)
|
|
448
|
+
return undefined;
|
|
449
|
+
const response = await client.request({
|
|
450
|
+
url: "/open-apis/bot/v3/info",
|
|
451
|
+
method: "GET",
|
|
452
|
+
});
|
|
453
|
+
const record = asRecord(response);
|
|
454
|
+
const bot = asRecord(record?.bot) ?? asRecord(asRecord(record?.data)?.bot);
|
|
455
|
+
return normalizeBotIdentity({
|
|
456
|
+
...(stringField(bot, "open_id") ?? stringField(record, "open_id") ? { openId: stringField(bot, "open_id") ?? stringField(record, "open_id") } : {}),
|
|
457
|
+
...(stringField(bot, "union_id") ?? stringField(record, "union_id") ? { unionId: stringField(bot, "union_id") ?? stringField(record, "union_id") } : {}),
|
|
458
|
+
...(stringField(bot, "user_id") ?? stringField(record, "user_id") ? { userId: stringField(bot, "user_id") ?? stringField(record, "user_id") } : {}),
|
|
459
|
+
...(stringField(bot, "app_id") ?? stringField(record, "app_id") ? { appId: stringField(bot, "app_id") ?? stringField(record, "app_id") } : {}),
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
function platformMessageId(value) {
|
|
463
|
+
const record = asRecord(value);
|
|
464
|
+
const data = asRecord(record?.data);
|
|
465
|
+
return stringField(data, "message_id") ?? stringField(record, "message_id");
|
|
466
|
+
}
|
|
467
|
+
function platformReactionId(value) {
|
|
468
|
+
const record = asRecord(value);
|
|
469
|
+
const data = asRecord(record?.data);
|
|
470
|
+
return stringField(data, "reaction_id") ?? stringField(record, "reaction_id");
|
|
471
|
+
}
|
|
472
|
+
function reactionApi(client) {
|
|
473
|
+
return client.im?.v1?.messageReaction ?? client.im?.v1?.message_reaction;
|
|
474
|
+
}
|
|
475
|
+
function oldestPendingMessageId(pending) {
|
|
476
|
+
let oldestId;
|
|
477
|
+
let oldestTime = Number.POSITIVE_INFINITY;
|
|
478
|
+
for (const [messageId, value] of pending) {
|
|
479
|
+
if (value.createdAt < oldestTime) {
|
|
480
|
+
oldestId = messageId;
|
|
481
|
+
oldestTime = value.createdAt;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return oldestId;
|
|
485
|
+
}
|
|
486
|
+
function asRecord(value) {
|
|
487
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
488
|
+
}
|
|
489
|
+
function stringField(record, key) {
|
|
490
|
+
const value = record?.[key];
|
|
491
|
+
return typeof value === "string" && value.trim() ? value : undefined;
|
|
492
|
+
}
|
|
493
|
+
//# sourceMappingURL=feishu.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function formatGatewayPayload(payload) {
|
|
2
|
+
if (payload.type === "notice") {
|
|
3
|
+
return [`[${payload.severity ?? "info"}] ${payload.title}`, payload.text].filter(Boolean).join("\n\n");
|
|
4
|
+
}
|
|
5
|
+
const lines = [
|
|
6
|
+
`Happy Elves turn ${payload.status}`,
|
|
7
|
+
payload.sessionName ? `Session: ${payload.sessionName}` : `Session: ${payload.sessionId}`,
|
|
8
|
+
`Turn: ${payload.turnId}`,
|
|
9
|
+
];
|
|
10
|
+
const reason = payload.error ?? payload.stopReason;
|
|
11
|
+
if (reason)
|
|
12
|
+
lines.push(`Reason: ${reason}`);
|
|
13
|
+
if (payload.outputPreview?.trim()) {
|
|
14
|
+
lines.push("", "Output:", payload.outputPreview.trim());
|
|
15
|
+
}
|
|
16
|
+
lines.push("", "Reply in this chat/thread to continue the same Happy Elves session.");
|
|
17
|
+
return lines.join("\n");
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type MachineCommand } from "../../../../packages/shared/dist/index.js";
|
|
2
|
+
import type { DaemonConfig } from "../types.js";
|
|
3
|
+
type GatewayCommand = Extract<MachineCommand, {
|
|
4
|
+
type: "machine:gateway";
|
|
5
|
+
}>;
|
|
6
|
+
export declare function handleGatewayCommand(ws: WebSocket, config: DaemonConfig, command: GatewayCommand): Promise<void>;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { decryptJson } from "../../../../packages/shared/dist/index.js";
|
|
2
|
+
import { claimCommandRequest, sendCommandResponse } from "../relay/send.js";
|
|
3
|
+
import { deliverGatewayMessage, notifyGatewayConversation, publicChannelsForStore, setGatewayLastError, syncGatewayRuntime } from "./runtime.js";
|
|
4
|
+
import { readGatewayStore, updateGatewayStore, upsertGatewayChannel } from "./store.js";
|
|
5
|
+
export async function handleGatewayCommand(ws, config, command) {
|
|
6
|
+
if (!claimCommandRequest(ws, command.requestId))
|
|
7
|
+
return;
|
|
8
|
+
const result = await executeGatewayCommand(config, command);
|
|
9
|
+
await sendCommandResponse(ws, {
|
|
10
|
+
type: "machine:gatewayResult",
|
|
11
|
+
requestId: command.requestId,
|
|
12
|
+
machineId: config.machineId,
|
|
13
|
+
result,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
async function executeGatewayCommand(config, command) {
|
|
17
|
+
if (command.action === "list") {
|
|
18
|
+
const store = await readGatewayStore();
|
|
19
|
+
return { action: "list", channels: publicChannelsForStore(store) };
|
|
20
|
+
}
|
|
21
|
+
if (command.action === "get") {
|
|
22
|
+
const store = await readGatewayStore();
|
|
23
|
+
const channel = requireChannel(store, command.channelId);
|
|
24
|
+
return { action: "get", channel: publicChannelsForStore(store).find((item) => item.id === channel.id) };
|
|
25
|
+
}
|
|
26
|
+
if (command.action === "upsert") {
|
|
27
|
+
if (!command.channel)
|
|
28
|
+
throw new Error("gateway upsert requires channel");
|
|
29
|
+
assertLocalTarget(config.machineId, command.channel.targetMachineId);
|
|
30
|
+
const input = await decryptGatewayChannelInput(config, command.channel);
|
|
31
|
+
const store = await updateGatewayStore((current) => upsertGatewayChannel(current, input, config.machineId));
|
|
32
|
+
const channel = requireChannel(store, command.channel.id ?? command.channel.type);
|
|
33
|
+
assertUsableChannel(channel);
|
|
34
|
+
await syncGatewayRuntime(config);
|
|
35
|
+
const nextStore = await readGatewayStore();
|
|
36
|
+
return { action: "upsert", channel: publicChannelsForStore(nextStore).find((item) => item.id === channel.id) };
|
|
37
|
+
}
|
|
38
|
+
if (command.action === "enable") {
|
|
39
|
+
const store = await updateGatewayStore((current) => ({
|
|
40
|
+
...current,
|
|
41
|
+
channels: current.channels.map((channel) => channel.id === command.channelId
|
|
42
|
+
? { ...channel, enabled: true, updatedAt: new Date().toISOString() }
|
|
43
|
+
: channel),
|
|
44
|
+
}));
|
|
45
|
+
const channel = requireChannel(store, command.channelId);
|
|
46
|
+
assertUsableChannel(channel);
|
|
47
|
+
await syncGatewayRuntime(config);
|
|
48
|
+
const nextStore = await readGatewayStore();
|
|
49
|
+
return { action: "enable", channel: publicChannelsForStore(nextStore).find((item) => item.id === channel.id) };
|
|
50
|
+
}
|
|
51
|
+
if (command.action === "disable") {
|
|
52
|
+
const store = await updateGatewayStore((current) => ({
|
|
53
|
+
...current,
|
|
54
|
+
channels: current.channels.map((channel) => channel.id === command.channelId
|
|
55
|
+
? { ...channel, enabled: false, updatedAt: new Date().toISOString() }
|
|
56
|
+
: channel),
|
|
57
|
+
}));
|
|
58
|
+
const channel = requireChannel(store, command.channelId);
|
|
59
|
+
await syncGatewayRuntime(config);
|
|
60
|
+
const nextStore = await readGatewayStore();
|
|
61
|
+
return { action: "disable", channel: publicChannelsForStore(nextStore).find((item) => item.id === channel.id) };
|
|
62
|
+
}
|
|
63
|
+
if (command.action === "test") {
|
|
64
|
+
const store = await readGatewayStore();
|
|
65
|
+
const channel = requireChannel(store, command.channelId);
|
|
66
|
+
if (!channel.enabled)
|
|
67
|
+
throw new Error("Gateway channel is disabled");
|
|
68
|
+
if (!command.externalConversationId || !command.externalMessageId || !command.text) {
|
|
69
|
+
throw new Error("gateway test requires externalConversationId, externalMessageId, and text");
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const delivery = await deliverGatewayMessage(config, channel, {
|
|
73
|
+
externalConversationId: command.externalConversationId,
|
|
74
|
+
externalMessageId: command.externalMessageId,
|
|
75
|
+
text: command.text,
|
|
76
|
+
});
|
|
77
|
+
const nextStore = await readGatewayStore();
|
|
78
|
+
return {
|
|
79
|
+
action: "test",
|
|
80
|
+
channel: publicChannelsForStore(nextStore).find((item) => item.id === channel.id),
|
|
81
|
+
delivery,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
await setGatewayLastError(channel.id, error);
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (command.action === "notify") {
|
|
90
|
+
const store = await readGatewayStore();
|
|
91
|
+
const channel = requireChannel(store, command.channelId);
|
|
92
|
+
if (!channel.enabled)
|
|
93
|
+
throw new Error("Gateway channel is disabled");
|
|
94
|
+
if (!command.externalConversationId || !command.externalMessageId || !command.text) {
|
|
95
|
+
throw new Error("gateway notify requires externalConversationId, externalMessageId, and text");
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const delivery = await notifyGatewayConversation(channel, {
|
|
99
|
+
externalConversationId: command.externalConversationId,
|
|
100
|
+
externalMessageId: command.externalMessageId,
|
|
101
|
+
text: command.text,
|
|
102
|
+
});
|
|
103
|
+
const nextStore = await readGatewayStore();
|
|
104
|
+
return {
|
|
105
|
+
action: "notify",
|
|
106
|
+
channel: publicChannelsForStore(nextStore).find((item) => item.id === channel.id),
|
|
107
|
+
delivery,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
await setGatewayLastError(channel.id, error);
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
throw new Error(`Unsupported gateway action: ${command.action}`);
|
|
116
|
+
}
|
|
117
|
+
async function decryptGatewayChannelInput(config, input) {
|
|
118
|
+
if (!input.encryptedAppSecret)
|
|
119
|
+
return input;
|
|
120
|
+
const secret = await decryptJson(config.accountSecret, input.encryptedAppSecret);
|
|
121
|
+
if (typeof secret.appSecret !== "string" || secret.appSecret.length === 0) {
|
|
122
|
+
throw new Error("Lark gateway encrypted appSecret is invalid");
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
...input,
|
|
126
|
+
appSecret: secret.appSecret,
|
|
127
|
+
encryptedAppSecret: undefined,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function requireChannel(store, channelId) {
|
|
131
|
+
if (!channelId)
|
|
132
|
+
throw new Error("gateway channelId is required");
|
|
133
|
+
const channel = store.channels.find((item) => item.id === channelId);
|
|
134
|
+
if (!channel)
|
|
135
|
+
throw new Error("Gateway channel not found");
|
|
136
|
+
return channel;
|
|
137
|
+
}
|
|
138
|
+
function assertLocalTarget(machineId, targetMachineId) {
|
|
139
|
+
if (targetMachineId && targetMachineId !== machineId) {
|
|
140
|
+
throw new Error("Gateway targetMachineId must match the daemon handling the command");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function assertUsableChannel(channel) {
|
|
144
|
+
if (channel.type === "lark" && (!channel.appId || !channel.appSecret)) {
|
|
145
|
+
throw new Error("Lark gateway requires appId and appSecret");
|
|
146
|
+
}
|
|
147
|
+
if (!channel.targetSessionId && !channel.targetCwd) {
|
|
148
|
+
throw new Error("Gateway needs target cwd or target session");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=handler.js.map
|