@fased/fased 0.1.46 → 0.1.48
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/dist/acp/control-plane/spawn.js +1 -1
- package/dist/agents/acp-spawn-parent-stream.js +2 -2
- package/dist/agents/acp-spawn.js +3 -3
- package/dist/agents/bash-tools.exec-approval-request.js +1 -1
- package/dist/agents/bash-tools.exec-host-node.js +1 -1
- package/dist/agents/bash-tools.exec-runtime.js +1 -1
- package/dist/agents/bash-tools.exec.js +1 -1
- package/dist/agents/current-model-catalog.js +21 -3
- package/dist/agents/defaults.js +1 -1
- package/dist/agents/fased-tools.js +4 -4
- package/dist/agents/identity-avatar.js +1 -1
- package/dist/agents/pi-embedded-runner/compact.js +4 -4
- package/dist/agents/pi-embedded-runner/run/attempt.js +5 -5
- package/dist/agents/pi-embedded-runner/run/images.js +1 -1
- package/dist/agents/pi-embedded-runner/run.js +2 -2
- package/dist/agents/pi-embedded-runner/system-prompt.js +1 -1
- package/dist/agents/pi-embedded-subscribe.handlers.compaction.js +1 -1
- package/dist/agents/pi-embedded-subscribe.handlers.messages.js +1 -1
- package/dist/agents/pi-embedded-subscribe.js +2 -2
- package/dist/agents/pi-embedded-subscribe.tools.js +1 -1
- package/dist/agents/pi-tools.js +2 -2
- package/dist/agents/pi-tools.read.js +1 -1
- package/dist/agents/sandbox/browser.js +2 -2
- package/dist/agents/sandbox/context.js +2 -2
- package/dist/agents/sandbox/docker.js +1 -1
- package/dist/agents/sandbox/manage.js +2 -2
- package/dist/agents/sandbox/prune.js +2 -2
- package/dist/agents/subagent-announce-output.js +3 -3
- package/dist/agents/subagent-announce.js +6 -6
- package/dist/agents/subagent-announce.runtime.js +1 -1
- package/dist/agents/subagent-registry.js +3 -3
- package/dist/agents/subagent-spawn.js +2 -2
- package/dist/agents/system-prompt-params.js +1 -1
- package/dist/agents/tool-images.js +1 -1
- package/dist/agents/tools/canvas-tool.js +2 -2
- package/dist/agents/tools/cron-tool.js +1 -1
- package/dist/agents/tools/diff-view-tool.js +1 -1
- package/dist/agents/tools/discord-actions-messaging.js +1 -1
- package/dist/agents/tools/gateway-tool.js +1 -1
- package/dist/agents/tools/gateway.js +1 -1
- package/dist/agents/tools/marketplace-offer-draft-tool.js +3 -3
- package/dist/agents/tools/message-tool.js +5 -5
- package/dist/agents/tools/mining-tool.js +1 -1
- package/dist/agents/tools/nodes-tool.js +1 -1
- package/dist/agents/tools/session-status-tool.js +3 -3
- package/dist/agents/tools/sessions-history-tool.js +1 -1
- package/dist/agents/tools/sessions-list-tool.js +1 -1
- package/dist/agents/tools/sessions-send-tool.a2a.js +1 -1
- package/dist/agents/tools/sessions-send-tool.js +1 -1
- package/dist/agents/tools/slack-actions.js +1 -1
- package/dist/agents/tools/subagents-tool.js +3 -3
- package/dist/agents/tools/telegram-actions.js +1 -1
- package/dist/agents/tools/tts-tool.js +1 -1
- package/dist/agents/tools/wallet-action-tool.js +4 -4
- package/dist/agents/tools/wallet-tool.js +7 -7
- package/dist/agents/tools/web-fetch.js +1 -1
- package/dist/auto-reply/chunk.js +1 -1
- package/dist/auto-reply/envelope.js +1 -1
- package/dist/auto-reply/reply/abort.js +2 -2
- package/dist/auto-reply/reply/agent-runner-execution.js +2 -2
- package/dist/auto-reply/reply/agent-runner-memory.js +2 -2
- package/dist/auto-reply/reply/agent-runner-payloads.js +1 -1
- package/dist/auto-reply/reply/agent-runner.js +6 -6
- package/dist/auto-reply/reply/bash-command.js +1 -1
- package/dist/auto-reply/reply/commands-acp/diagnostics.js +1 -1
- package/dist/auto-reply/reply/commands-acp/lifecycle.js +2 -2
- package/dist/auto-reply/reply/commands-acp/shared.js +1 -1
- package/dist/auto-reply/reply/commands-compact.js +2 -2
- package/dist/auto-reply/reply/commands-control.js +1 -1
- package/dist/auto-reply/reply/commands-models.js +1 -1
- package/dist/auto-reply/reply/commands-session-abort.js +2 -2
- package/dist/auto-reply/reply/commands-status.js +2 -2
- package/dist/auto-reply/reply/commands-subagents/action-focus.js +1 -1
- package/dist/auto-reply/reply/commands-subagents/action-kill.js +2 -2
- package/dist/auto-reply/reply/commands-subagents/action-send.js +1 -1
- package/dist/auto-reply/reply/commands-subagents/shared.js +1 -1
- package/dist/auto-reply/reply/dispatch-acp.js +5 -5
- package/dist/auto-reply/reply/dispatch-from-config.js +2 -2
- package/dist/auto-reply/reply/followup-runner.js +4 -4
- package/dist/auto-reply/reply/get-reply-directives.js +2 -2
- package/dist/auto-reply/reply/get-reply-inline-actions.js +2 -2
- package/dist/auto-reply/reply/get-reply-run.js +4 -4
- package/dist/auto-reply/reply/model-selection.js +2 -2
- package/dist/auto-reply/reply/queue/cleanup.js +1 -1
- package/dist/auto-reply/reply/reply-delivery.js +1 -1
- package/dist/auto-reply/reply/reply-directives.js +1 -1
- package/dist/auto-reply/reply/route-reply.js +1 -1
- package/dist/auto-reply/reply/session-reset-model.js +1 -1
- package/dist/auto-reply/reply/session-updates.js +1 -1
- package/dist/auto-reply/reply/streaming-directives.js +1 -1
- package/dist/auto-reply/reply.js +1 -1
- package/dist/auto-reply/status.js +3 -3
- package/dist/brand.js +1 -1
- package/dist/build-info.json +3 -3
- package/dist/bundled/session-memory/handler.js +1 -1
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/canvas-host/a2ui.js +1 -1
- package/dist/canvas-host/server.js +1 -1
- package/dist/channel-web.js +1 -1
- package/dist/channels/plugins/actions/signal.js +1 -1
- package/dist/channels/plugins/actions/telegram.js +2 -2
- package/dist/channels/plugins/onboarding/discord.js +1 -1
- package/dist/channels/plugins/onboarding/imessage.js +1 -1
- package/dist/cli/browser-cli-extension.js +1 -1
- package/dist/cli/components-cli.js +1 -1
- package/dist/cli/cron-cli/shared.js +1 -1
- package/dist/cli/daemon-cli/install.js +2 -2
- package/dist/cli/daemon-cli/status.gather.js +1 -1
- package/dist/cli/daemon-cli/status.print.js +1 -1
- package/dist/cli/directory-cli.js +1 -1
- package/dist/cli/exec-approvals-cli.js +2 -2
- package/dist/cli/federation-cli.js +3 -3
- package/dist/cli/gateway-cli/register.js +3 -3
- package/dist/cli/gateway-cli/run.js +1 -1
- package/dist/cli/hooks-cli.js +1 -1
- package/dist/cli/node-cli/daemon.js +1 -1
- package/dist/cli/nodes-cli/register.status.js +1 -1
- package/dist/cli/nodes-cli/rpc.js +1 -1
- package/dist/cli/plugins-cli.js +1 -1
- package/dist/cli/program/message/helpers.js +1 -1
- package/dist/cli/program/register.agent.js +1 -1
- package/dist/cli/program/register.status-health-sessions.js +1 -1
- package/dist/cli/program/register.wallet.js +1 -1
- package/dist/cli/run-main.js +1 -1
- package/dist/cli/tui-cli.js +1 -1
- package/dist/cli/update-cli/status.js +1 -1
- package/dist/cli/update-cli/update-command.js +3 -3
- package/dist/commands/agent/attempt.js +1 -1
- package/dist/commands/agent/delivery.js +2 -2
- package/dist/commands/agent.js +9 -9
- package/dist/commands/agents.commands.add.js +1 -1
- package/dist/commands/agents.commands.delete.js +1 -1
- package/dist/commands/channels/add.js +2 -2
- package/dist/commands/channels/list.js +2 -2
- package/dist/commands/channels/status.js +1 -1
- package/dist/commands/configure.daemon.js +1 -1
- package/dist/commands/configure.wizard.js +1 -1
- package/dist/commands/dashboard.js +2 -2
- package/dist/commands/docs.js +1 -1
- package/dist/commands/doctor-config-flow.js +2 -2
- package/dist/commands/doctor-format.js +1 -1
- package/dist/commands/doctor-gateway-daemon-flow.js +5 -5
- package/dist/commands/doctor-gateway-services.js +2 -2
- package/dist/commands/doctor-security.js +1 -1
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/gateway-status.js +2 -2
- package/dist/commands/hosted-dashboard-probe.js +2 -2
- package/dist/commands/message.js +1 -1
- package/dist/commands/models/list.probe.js +3 -3
- package/dist/commands/models/list.status-command.js +3 -3
- package/dist/commands/onboard-channels.js +1 -1
- package/dist/commands/onboard-helpers.js +1 -1
- package/dist/commands/onboard-non-interactive/local/daemon-install.js +2 -2
- package/dist/commands/onboard-non-interactive/local.js +1 -1
- package/dist/commands/onboard-skills.js +1 -1
- package/dist/commands/onboarding/plugin-install.js +1 -1
- package/dist/commands/openai-model-default.js +1 -1
- package/dist/commands/status-all/channels.js +1 -1
- package/dist/commands/status-all/diagnosis.js +1 -1
- package/dist/commands/status-all/format.js +1 -1
- package/dist/commands/status-all.js +8 -8
- package/dist/commands/status.command.js +4 -4
- package/dist/commands/status.gateway-probe.js +1 -1
- package/dist/commands/status.scan.js +3 -3
- package/dist/commands/status.summary.js +1 -1
- package/dist/commands/wallet.js +4 -3
- package/dist/control-ui/assets/{app-JBrTSsNu.js → app-CHqTx3P_.js} +4 -4
- package/dist/control-ui/assets/chat-model-ref-BbrdMKjQ.js +2 -0
- package/dist/control-ui/assets/{index-H-2DWAnH.js → index-CFHfNlLv.js} +3 -3
- package/dist/control-ui/assets/{providers-C9hLw4o6.js → providers-DqFL6WrJ.js} +2 -2
- package/dist/control-ui/index.html +1 -1
- package/dist/cron/isolated-agent/delivery-dispatch.js +2 -2
- package/dist/cron/isolated-agent/delivery-target.js +1 -1
- package/dist/cron/isolated-agent/run.js +5 -5
- package/dist/daemon/service-audit.js +2 -2
- package/dist/discord/monitor/agent-components.js +9 -9
- package/dist/discord/monitor/gateway-plugin.js +1 -1
- package/dist/discord/monitor/listeners.js +2 -2
- package/dist/discord/monitor/message-handler.js +1 -1
- package/dist/discord/monitor/message-handler.preflight.js +8 -8
- package/dist/discord/monitor/message-handler.process.js +5 -5
- package/dist/discord/monitor/native-command.js +9 -9
- package/dist/discord/monitor/provider.js +2 -2
- package/dist/discord/monitor/reply-delivery.js +1 -1
- package/dist/discord/send.outbound.js +1 -1
- package/dist/discord/voice/command.js +1 -1
- package/dist/extensionAPI.js +1 -1
- package/dist/extensions/sat-mining/src/protocol-contract.js +63 -0
- package/dist/extensions/sat-mining/src/rpc-read.js +7 -3
- package/dist/extensions/sat-mining/src/solana-submit.js +5 -54
- package/dist/federation/auto-connect.js +1 -1
- package/dist/federation/marketplace-capability-adapter.js +1 -1
- package/dist/federation/marketplace-delivery.js +1 -1
- package/dist/federation/marketplace-direct-payment.js +1 -1
- package/dist/federation/marketplace-escrow.js +2 -2
- package/dist/federation/settlement-evidence.js +1 -1
- package/dist/gateway/a2a-http.js +1 -1
- package/dist/gateway/call.js +2 -2
- package/dist/gateway/client.js +3 -3
- package/dist/gateway/control-ui-boot-check.js +1 -1
- package/dist/gateway/control-ui.js +1 -1
- package/dist/gateway/federation-http.js +3 -3
- package/dist/gateway/federation-marketplace.js +1 -1
- package/dist/gateway/hooks.js +1 -1
- package/dist/gateway/openai-http.js +1 -1
- package/dist/gateway/openresponses-http.js +1 -1
- package/dist/gateway/server/health-state.js +1 -1
- package/dist/gateway/server/hooks.js +2 -2
- package/dist/gateway/server/ws-connection/message-handler.js +6 -6
- package/dist/gateway/server-channels.js +1 -1
- package/dist/gateway/server-cron.js +3 -3
- package/dist/gateway/server-http.js +13 -13
- package/dist/gateway/server-methods/agent.js +4 -4
- package/dist/gateway/server-methods/agents.js +1 -1
- package/dist/gateway/server-methods/channels.js +1 -1
- package/dist/gateway/server-methods/commands.js +1 -1
- package/dist/gateway/server-methods/devices.js +1 -1
- package/dist/gateway/server-methods/doctor.js +2 -2
- package/dist/gateway/server-methods/hooks.js +1 -1
- package/dist/gateway/server-methods/models.js +1 -1
- package/dist/gateway/server-methods/nodes.js +2 -2
- package/dist/gateway/server-methods/send.js +4 -4
- package/dist/gateway/server-methods/services.js +1 -1
- package/dist/gateway/server-methods/sessions.js +6 -6
- package/dist/gateway/server-methods/skills.js +2 -2
- package/dist/gateway/server-methods/system.js +1 -1
- package/dist/gateway/server-methods/tts.js +2 -2
- package/dist/gateway/server-node-events.js +5 -5
- package/dist/gateway/server-reload-handlers.js +3 -3
- package/dist/gateway/server-restart-sentinel.js +3 -3
- package/dist/gateway/server.impl.js +16 -16
- package/dist/gateway/sessions-patch.js +2 -2
- package/dist/gateway/tools-invoke-http.js +2 -2
- package/dist/hooks/loader.js +1 -1
- package/dist/imessage/monitor/deliver.js +1 -1
- package/dist/imessage/monitor/inbound-processing.js +4 -4
- package/dist/imessage/monitor/monitor-provider.js +2 -2
- package/dist/imessage/send.js +2 -2
- package/dist/index.js +1 -1
- package/dist/infra/exec-approvals.js +1 -1
- package/dist/infra/heartbeat-runner.js +8 -8
- package/dist/infra/outbound/channel-resolution.js +1 -1
- package/dist/infra/outbound/deliver.js +3 -3
- package/dist/infra/outbound/message-action-params.js +2 -2
- package/dist/infra/outbound/message-action-runner.js +4 -4
- package/dist/infra/outbound/message.js +3 -3
- package/dist/infra/outbound/outbound-session.js +1 -1
- package/dist/infra/outbound/payloads.js +1 -1
- package/dist/infra/session-maintenance-warning.js +1 -1
- package/dist/infra/skills-remote.js +1 -1
- package/dist/infra/update-runner.js +1 -1
- package/dist/light-update-status.js +11 -11
- package/dist/line/bot-handlers.js +2 -2
- package/dist/line/bot-message-context.js +2 -2
- package/dist/line/download.js +1 -1
- package/dist/line/monitor.js +3 -3
- package/dist/line/webhook-node.js +1 -1
- package/dist/llm-slug-generator.js +1 -1
- package/dist/media/fetch.js +1 -1
- package/dist/media/input-files.js +2 -2
- package/dist/media/store.js +1 -1
- package/dist/media-understanding/attachments.js +4 -4
- package/dist/media-understanding/providers/google/inline-data.js +1 -1
- package/dist/media-understanding/providers/shared.js +1 -1
- package/dist/memory/inventory.js +25 -4
- package/dist/mining/mainnet-sync.js +70 -26
- package/dist/node-host/runner.js +2 -2
- package/dist/plugin-sdk/{accounts-DoZdNyoY.js → accounts-DI5226U8.js} +1 -1
- package/dist/plugin-sdk/{actions-Cuc5luN8.js → actions-CEj4gOU2.js} +4 -4
- package/dist/plugin-sdk/{active-listener-Bj8f1upQ.js → active-listener-Ss4UKXZb.js} +1 -1
- package/dist/plugin-sdk/{allow-list-yqasgPpz.js → allow-list-BYeKk0Sy.js} +2 -2
- package/dist/plugin-sdk/{audio-preflight-02OYHVMR.js → audio-preflight-Cu4alGjb.js} +51 -49
- package/dist/plugin-sdk/{audit-D_Kb-kIQ.js → audit-BTskqjJ_.js} +1 -1
- package/dist/plugin-sdk/{channel-access-I3pe5AGo.js → channel-access-B1KciKmw.js} +1 -1
- package/dist/plugin-sdk/{channel-access-configure-CgPf_K08.js → channel-access-configure-Y91FLOJc.js} +1 -1
- package/dist/plugin-sdk/{channel-adapters.discord-g8Do3f5N.js → channel-adapters.discord-IOZP8Cv5.js} +1 -1
- package/dist/plugin-sdk/channel-plugin-common.js +8 -8
- package/dist/plugin-sdk/{chunk-BezNQ2ms.js → chunk-CSUjIQSx.js} +1 -1
- package/dist/plugin-sdk/{chunk-items-CfOSVg-A.js → chunk-items-Q8HhMuSe.js} +1 -1
- package/dist/plugin-sdk/command-status.js +51 -49
- package/dist/plugin-sdk/{commands-registry-CFhJ8pUg.js → commands-registry-DJ0pfCcY.js} +2 -2
- package/dist/plugin-sdk/{common-DBwy4zpd.js → common-Cjm8psuW.js} +1 -1
- package/dist/plugin-sdk/{config-DHDPT24D.js → config-DutIeFge.js} +81 -9
- package/dist/plugin-sdk/{pi-embedded-runner-r_J_Xhv9.js → config-schema-Cg6PYSHR.js} +4843 -449
- package/dist/plugin-sdk/device-pair.js +3 -3
- package/dist/plugin-sdk/{device-pairing-J1IH1jFl.js → device-pairing-D2Yq-_U6.js} +1 -1
- package/dist/plugin-sdk/{diagnostic-C16gioN6.js → diagnostic-Dfn1iERm.js} +4 -4
- package/dist/plugin-sdk/{directory-live-DK_83362.js → directory-live-ClwN5KKq.js} +1 -1
- package/dist/plugin-sdk/{directory-live-G7AN8y8N.js → directory-live-DatqU-eS.js} +1 -1
- package/dist/plugin-sdk/discord.d.ts +1 -1
- package/dist/plugin-sdk/discord.js +36 -37
- package/dist/plugin-sdk/{draft-stream-controls-DGYBMJYc.js → draft-stream-controls-D4Tb_iS_.js} +1 -1
- package/dist/plugin-sdk/{fetch-RV7QDF43.js → fetch-Cu-ahkie.js} +2 -2
- package/dist/plugin-sdk/{fetch-guard-CxXvFYq5.js → fetch-guard-BuYrl6q2.js} +3 -3
- package/dist/plugin-sdk/{filesystem-manager-CeSmCA_A.js → filesystem-manager-B9DDPgfU.js} +2 -2
- package/dist/plugin-sdk/{fs-safe-DztQeyQe.js → fs-safe-BG-VzVkL.js} +1 -1
- package/dist/plugin-sdk/{guilds-n7MMnX1c.js → guilds-BeT3iHHJ.js} +1 -1
- package/dist/plugin-sdk/{handle-action-De9_BMM9.js → handle-action-Dp2741iu.js} +29 -29
- package/dist/plugin-sdk/{helpers-BEIzSOp1.js → helpers-BCZ9d3fl.js} +74 -2
- package/dist/plugin-sdk/{image-ops-Dj5U2UMr.js → image-ops-B-QR_M9Z.js} +1 -1
- package/dist/plugin-sdk/index.d.ts +6 -2
- package/dist/plugin-sdk/index.js +63 -4294
- package/dist/plugin-sdk/{internal-CC6TMTtc.js → internal-qo16nNU1.js} +1 -1
- package/dist/plugin-sdk/{login-DXqhYXdm.js → login-BFvQxf9o.js} +3 -3
- package/dist/plugin-sdk/{login-qr-3320RF3u.js → login-qr-CERTe_nD.js} +6 -6
- package/dist/plugin-sdk/{manager-gssr8OXa.js → manager-Dhq3SXpO.js} +11 -11
- package/dist/plugin-sdk/{markdown-tables-BWPlKBlJ.js → markdown-tables-B1Tx7c77.js} +1 -1
- package/dist/plugin-sdk/{media-DkIzq3KV.js → media-BwsqooWC.js} +4 -4
- package/dist/plugin-sdk/{monitor-CUy-o-xZ.js → monitor-BYzN5Qr4.js} +67 -67
- package/dist/plugin-sdk/{monitor-DEWXcOl0.js → monitor-CJiacqtR.js} +59 -57
- package/dist/plugin-sdk/{openresponses-http-Cd3mZSdn.js → openresponses-http-VLSSqCZZ.js} +51 -49
- package/dist/plugin-sdk/{optional-runtime-dependency-DRIuSMGn.js → optional-runtime-dependency-BEBP4VFJ.js} +1 -1
- package/dist/plugin-sdk/{outbound-Dzyqev2x.js → outbound-BqWgaC5M.js} +9 -9
- package/dist/plugin-sdk/{outbound-attachment-DaAFYQmc.js → outbound-attachment-L1q40kiu.js} +2 -2
- package/dist/plugin-sdk/{pairing-message-Cma7gg4r.js → pairing-message-CZBahREg.js} +1 -1
- package/dist/plugin-sdk/{pairing-store-Xfn7m8FO.js → pairing-store-D7I1Qmiw.js} +2 -2
- package/dist/plugin-sdk/{pi-model-discovery-CyBJpoHh.js → pi-model-discovery-C5klknQZ.js} +1 -1
- package/dist/plugin-sdk/{pi-model-discovery-runtime-BI9BIUxX.js → pi-model-discovery-runtime-DIEMhCZ5.js} +5 -5
- package/dist/plugin-sdk/{probe-CJ6lN-jD.js → probe-Cyb-DGA3.js} +3 -3
- package/dist/plugin-sdk/{probe-CvXWxzag.js → probe-D8JAILir.js} +2 -2
- package/dist/plugin-sdk/{probe-DO4M1Qd1.js → probe-Dlk0Jxpw.js} +2 -2
- package/dist/plugin-sdk/{pw-ai-BN0M5PNa.js → pw-ai-Bv-q25gY.js} +11 -11
- package/dist/plugin-sdk/{qmd-manager-D2LMB27z.js → qmd-manager-DCUE21Kj.js} +5 -5
- package/dist/plugin-sdk/{query-expansion-BM7lmqaP.js → query-expansion-Zr9qQixm.js} +3 -3
- package/dist/plugin-sdk/{redact-sensitive-url-B0RJQqO7.js → redact-sensitive-url-dPvnFFsK.js} +1 -0
- package/dist/plugin-sdk/{replies-CWZ8Wh3v.js → replies-Bd9ktEkq.js} +4 -4
- package/dist/plugin-sdk/{resolve-channels-sscr14hU.js → resolve-channels-ChWKWtA9.js} +2 -2
- package/dist/plugin-sdk/{probe-DbhD54Aw.js → resolve-channels-zEnvugag.js} +4 -3
- package/dist/plugin-sdk/{resolve-outbound-target-XUMvbg29.js → resolve-outbound-target-CCIyBb5H.js} +2 -2
- package/dist/plugin-sdk/{resolve-route-DaIOKVVx.js → resolve-route-B7vZ3nKp.js} +1 -1
- package/dist/plugin-sdk/{resolve-users-C1sjzEsV.js → resolve-users-BTvjQuui.js} +1 -1
- package/dist/plugin-sdk/{resolve-users-B63RqPIO.js → resolve-users-CTzoruIZ.js} +2 -2
- package/dist/plugin-sdk/{retry-policy-CHtx8LYv.js → retry-policy-ChC3G4lv.js} +2 -2
- package/dist/plugin-sdk/{runtime-ptM6yJKi.js → runtime-BLknHFGz.js} +5 -5
- package/dist/plugin-sdk/{runtime-dependency-BoJ6eFyY.js → runtime-dependency-C5rJfC0u.js} +5 -5
- package/dist/plugin-sdk/{sandbox-paths-fc-HFGJr.js → sandbox-paths-lj7_EVuA.js} +1 -1
- package/dist/plugin-sdk/sat-runtime.d.ts +1 -0
- package/dist/plugin-sdk/sat-runtime.js +14 -14
- package/dist/plugin-sdk/{send-BMVtOmUX.js → send-Bd3Z-jAd.js} +9 -9
- package/dist/plugin-sdk/{send-bQfJLpNY.js → send-Bmtz9ICm.js} +10 -10
- package/dist/plugin-sdk/{send-Bbmuq5OU.js → send-CMoWt-EX.js} +14 -14
- package/dist/plugin-sdk/{send-C_vcGOCA.js → send-CUGEhiUa.js} +6 -6
- package/dist/plugin-sdk/{send-CjwgOMFS.js → send-mkyZzhCO.js} +9 -9
- package/dist/plugin-sdk/{session-D-JrYrAm.js → session-D2zmelOL.js} +4 -4
- package/dist/plugin-sdk/{skill-commands-Dqr7X9q3.js → skill-commands-BybEjJ1_.js} +4 -4
- package/dist/plugin-sdk/{slack-BK4sFHMP.js → slack-DogXvGG3.js} +72 -71
- package/dist/plugin-sdk/{slack-actions-C97ocE17.js → slack-actions-uXb55QWW.js} +25 -25
- package/dist/plugin-sdk/slack.d.ts +1 -1
- package/dist/plugin-sdk/slack.js +17 -18
- package/dist/plugin-sdk/{ssrf-D_4q0mKh.js → ssrf-BZG8Ztod.js} +1 -1
- package/dist/plugin-sdk/{sticker-cache-oLmDP9zb.js → sticker-cache-BuPw7fTf.js} +4 -4
- package/dist/plugin-sdk/{tables-CeCs0Fx6.js → tables-CVDOT5dX.js} +2 -2
- package/dist/plugin-sdk/{targets-CI7i2tSK.js → targets-DskkhJAp.js} +2 -2
- package/dist/plugin-sdk/{telegram-actions-Dc3_LrTb.js → telegram-actions-_KQPbjpI.js} +56 -54
- package/dist/plugin-sdk/telegram.d.ts +1 -1
- package/dist/plugin-sdk/telegram.js +15 -16
- package/dist/plugin-sdk/{thread-bindings.lifecycle-BEiYGYlC.js → thread-bindings.lifecycle-CrhQXYEU.js} +3 -3
- package/dist/plugin-sdk/{thread-bindings.state-DFCnsoeW.js → thread-bindings.state-ssxdG7xV.js} +4 -4
- package/dist/plugin-sdk/{wallet-status-B9rwi_D9.js → wallet-status-CQI89c_6.js} +74 -32
- package/dist/plugin-sdk/{web-C9k3omL7.js → web-C8DwA0eO.js} +60 -58
- package/dist/plugin-sdk/{whatsapp-actions-DJgg1fZn.js → whatsapp-actions-DfvM-mlZ.js} +23 -23
- package/dist/plugin-sdk/{whatsapp-heartbeat-DU_VCZWM.js → whatsapp-heartbeat-DyJasLDB.js} +3 -3
- package/dist/plugin-sdk/whatsapp.js +18 -19
- package/dist/plugin-sdk/{ws-CxmRDbD9.js → ws-CocpWGhl.js} +1 -1
- package/dist/plugin-sdk/{wsl-DzFPHgJ6.js → wsl-JwmI6Zu8.js} +3 -3
- package/dist/plugin-sdk/{zod-schema.providers-core-BEbNizPe.d.ts → zod-schema.providers-core-BE_5DNhj.d.ts} +10 -10
- package/dist/plugins/providers.js +1 -1
- package/dist/plugins/runtime/index.js +12 -12
- package/dist/plugins/status.js +1 -1
- package/dist/plugins/tools.js +1 -1
- package/dist/plugins/web-search-providers.runtime.js +1 -1
- package/dist/process/command-queue.js +1 -1
- package/dist/providers/refresh.js +2 -2
- package/dist/providers/registry.js +53 -2
- package/dist/security/audit-channel.js +1 -1
- package/dist/security/audit-extra.async.js +3 -3
- package/dist/security/audit-extra.sync.js +2 -2
- package/dist/security/audit.js +4 -4
- package/dist/security/dm-policy-shared.js +1 -1
- package/dist/shared/model-thinking.js +4 -1
- package/dist/shared/net/redact-sensitive-url.js +1 -0
- package/dist/signal/client.js +1 -1
- package/dist/signal/index.js +1 -1
- package/dist/signal/monitor/access-policy.js +1 -1
- package/dist/signal/monitor/event-handler.js +6 -6
- package/dist/signal/monitor.js +4 -4
- package/dist/signal/send.js +3 -3
- package/dist/slack/monitor/auth.js +1 -1
- package/dist/slack/monitor/context.js +1 -1
- package/dist/slack/monitor/dm-auth.js +1 -1
- package/dist/slack/monitor/message-handler/prepare.js +4 -4
- package/dist/slack/monitor/provider.js +3 -3
- package/dist/slack/monitor/replies.js +1 -1
- package/dist/slack/monitor/slash.js +1 -1
- package/dist/slack/send.js +1 -1
- package/dist/telegram/bot/helpers.js +2 -2
- package/dist/telegram/bot-handlers.js +6 -6
- package/dist/telegram/bot-message-context.js +6 -6
- package/dist/telegram/bot-message-dispatch.js +2 -2
- package/dist/telegram/bot-native-commands.js +4 -4
- package/dist/telegram/bot.js +1 -1
- package/dist/telegram/dm-access.js +1 -1
- package/dist/telegram/group-access.js +1 -1
- package/dist/telegram/monitor.js +2 -2
- package/dist/telegram/send.js +2 -2
- package/dist/telegram/webhook.js +1 -1
- package/dist/tts/tts-core.js +1 -1
- package/dist/video-generation/dashscope-compatible.js +1 -1
- package/dist/wallet/local-socket-signer-broker.js +2 -2
- package/dist/wallet/providers/embedded-keystore-adapter.js +1 -1
- package/dist/wallet/providers/local-socket-signer-adapter.js +3 -3
- package/dist/wallet/wallet-canary.js +1 -1
- package/dist/wallet/wallet-custody.js +4 -4
- package/dist/wallet/wallet-inbound-events.js +2 -2
- package/dist/wallet/wallet-policy.js +2 -2
- package/dist/wallet/wallet-provider-resolver.js +1 -1
- package/dist/wallet/wallet-send-approvals.js +5 -5
- package/dist/wallet/wallet-status.js +2 -2
- package/dist/web/auto-reply/deliver-reply.js +2 -2
- package/dist/web/auto-reply/heartbeat-runner.js +3 -3
- package/dist/web/auto-reply/monitor/group-gating.js +1 -1
- package/dist/web/auto-reply/monitor/process-message.js +4 -4
- package/dist/web/auto-reply/monitor.js +4 -4
- package/dist/web/inbound/access-control.js +3 -3
- package/dist/web/inbound/monitor.js +2 -2
- package/dist/web/media.js +2 -2
- package/dist/web/outbound.js +3 -3
- package/dist/wizard/onboarding.federation.js +1 -1
- package/dist/wizard/onboarding.finalize.js +7 -7
- package/dist/wizard/onboarding.js +1 -1
- package/dist/wizard/onboarding.wallet.js +1 -1
- package/extensions/acpx/package.json +1 -1
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/fased-federation/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +12 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +2 -2
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +12 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +12 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/sat-mining/index.ts +14 -2
- package/extensions/sat-mining/package.json +1 -1
- package/extensions/sat-mining/src/epoch-service.ts +16 -0
- package/extensions/sat-mining/src/protocol-contract.ts +64 -0
- package/extensions/sat-mining/src/round-watcher.ts +27 -4
- package/extensions/sat-mining/src/rpc-read.ts +9 -2
- package/extensions/sat-mining/src/runtime.ts +5 -1
- package/extensions/sat-mining/src/solana-submit.ts +7 -53
- package/extensions/signal/package.json +1 -1
- package/extensions/synology-chat/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +12 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +12 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +12 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +12 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
- package/dist/control-ui/assets/chat-model-ref-DV2P__mw.js +0 -2
- package/dist/plugin-sdk/links-D5ecKoG9.js +0 -74
- package/dist/plugin-sdk/media-payload-DpYEcV-X.js +0 -18
- package/dist/plugin-sdk/resolve-channels-B-dUtZYa.js +0 -4
- package/dist/plugin-sdk/resolve-utils-DrvLA82C.js +0 -136
- /package/dist/plugin-sdk/{blocks-input-DdQwSVtP.js → blocks-input-Dcitfh7h.js} +0 -0
- /package/dist/plugin-sdk/{channel-activity-B7MIaDei.js → channel-activity-Cg1xRoc2.js} +0 -0
- /package/dist/plugin-sdk/{channel-core-CMPoGCik.js → channel-core-BOkyXXLa.js} +0 -0
- /package/dist/plugin-sdk/{client-BTUzO_8g.js → client-D2N7hqVu.js} +0 -0
- /package/dist/plugin-sdk/{command-poll-backoff-CBq0jVRw.js → command-poll-backoff-NAePk3eL.js} +0 -0
- /package/dist/plugin-sdk/{date-time-Bbp5JvNw.js → date-time-CE3Uer3C.js} +0 -0
- /package/dist/plugin-sdk/{diagnostic-session-state-B-9PL5r7.js → diagnostic-session-state-HO94DMou.js} +0 -0
- /package/dist/plugin-sdk/{draft-stream-loop-DZ1OAyye.js → draft-stream-loop-CKyHn8IX.js} +0 -0
- /package/dist/plugin-sdk/{fetch-headers-DvXyXtoI.js → fetch-headers-my7hgV2G.js} +0 -0
- /package/dist/plugin-sdk/{fetch-timeout-jOIu9Xud.js → fetch-timeout-l_6RdoMw.js} +0 -0
- /package/dist/plugin-sdk/{gateway-registry-C-MTEdvG.js → gateway-registry-DFsoY6C0.js} +0 -0
- /package/dist/plugin-sdk/{github-copilot-token-Dgug856m.js → github-copilot-token-CUK__1s1.js} +0 -0
- /package/dist/plugin-sdk/{inbound-context-BNCTzgjG.js → inbound-context-BFnIdcqI.js} +0 -0
- /package/dist/plugin-sdk/{ip-BkeyFRJr.js → ip-C70a-YlD.js} +0 -0
- /package/dist/plugin-sdk/{memory-search-DHSeU4pJ.js → memory-search-C3C0bD2H.js} +0 -0
- /package/dist/plugin-sdk/{pairing-token-HQpaqod5.js → pairing-token-N_6Jbt1L.js} +0 -0
- /package/dist/plugin-sdk/{path-alias-guards-BlIJ0AmG.js → path-alias-guards-Dg3gU-MA.js} +0 -0
- /package/dist/plugin-sdk/{paths-B88DQEK8.js → paths-CTZ2RS_E.js} +0 -0
- /package/dist/plugin-sdk/{polls-Dy1NsYKT.js → polls-DQrR-qT3.js} +0 -0
- /package/dist/plugin-sdk/{proxy-R5TjOIFS.js → proxy-7xs_rsnV.js} +0 -0
- /package/dist/plugin-sdk/{qmd-scope-C9tRnohc.js → qmd-scope-D1sSG_Rx.js} +0 -0
- /package/dist/plugin-sdk/{redact-CwCz68Mi.js → redact-Bm8vu_ju.js} +0 -0
- /package/dist/plugin-sdk/{redact-identifier-CDvAeiFm.js → redact-identifier-Q3tkvxhm.js} +0 -0
- /package/dist/plugin-sdk/{render-K7NwXvVu.js → render-Bwu_WxwA.js} +0 -0
- /package/dist/plugin-sdk/{reply-prefix-CbPHNXlm.js → reply-prefix-Bqzm6DC1.js} +0 -0
- /package/dist/plugin-sdk/{reply-reference-CWEIswqw.js → reply-reference-BkbK_DWT.js} +0 -0
- /package/dist/plugin-sdk/{retry-DycQ4ymF.js → retry-DstNBhDW.js} +0 -0
- /package/dist/plugin-sdk/{run-with-concurrency-CLARJMM7.js → run-with-concurrency-BsDJWzgM.js} +0 -0
- /package/dist/plugin-sdk/{secure-random-C2EhANj2.js → secure-random-Bl-AVLVb.js} +0 -0
- /package/dist/plugin-sdk/{shared-V35XNAbz.js → shared-clhhbWN3.js} +0 -0
- /package/dist/plugin-sdk/{tailscale-status-BGBRraZW.js → tailscale-status-ClsquRGs.js} +0 -0
- /package/dist/plugin-sdk/{target-errors-C0yNDbGS.js → target-errors-C_SVKoEv.js} +0 -0
- /package/dist/plugin-sdk/{targets-CuTZl1mO.js → targets-BDvvnlph.js} +0 -0
- /package/dist/plugin-sdk/{tokens-Do_JU4Rh.js → tokens-BvtlOzVP.js} +0 -0
- /package/dist/plugin-sdk/{tool-loop-detection-P5u2k0_f.js → tool-loop-detection-CWphm1k_.js} +0 -0
- /package/dist/plugin-sdk/{transcript-events-NZiMKWBK.js → transcript-events-BJQWUdmU.js} +0 -0
- /package/dist/plugin-sdk/{ui-KGr1WRXC.js → ui-e020ouhJ.js} +0 -0
- /package/dist/plugin-sdk/{with-timeout-Dl513q34.js → with-timeout-BYrRAYFV.js} +0 -0
package/dist/plugin-sdk/index.js
CHANGED
|
@@ -1,4298 +1,67 @@
|
|
|
1
|
-
import { $n as
|
|
2
|
-
import { Bt as resolveBlueBubblesGroupRequireMention,
|
|
3
|
-
import { n as stripAnsi
|
|
1
|
+
import { $ as clearHistoryEntriesIfEnabled, $n as createInfoCard, $r as extractDashscopeVideoUrls, Ar as resolveSenderCommandAuthorization, B as normalizeAccountId$1, Bn as resolveChannelMediaMaxBytes, Br as getRealtimeVoiceProvider, C as resolveEffectiveToolInventory, Cn as unregisterAcpRuntimeBackend, Cr as sendMediaWithLeadingCaption, D as runEmbeddedPiAgent, Dr as buildAgentMediaPayload, E as ACPX_PUSH_TEST_WRAPPER_ID, Er as fetchWithBearerAuthScopeFallback, F as issuePairingChallenge, Fn as isRequestBodyLimitError, Fr as resolveEffectiveAllowFromLists, G as logTypingFailure, Gr as listRealtimeTranscriptionProviders, H as resolveLineAccount, Hn as startFederationAutoConnect, Hr as normalizeRealtimeVoiceProviderId, I as createTypingCallbacks, In as readJsonBodyWithLimit, Jn as processLineMessage, Jr as DEFAULT_VIDEO_GENERATION_TIMEOUT_MS, Kr as normalizeRealtimeTranscriptionProviderId, L as resolveMentionGating, Ln as readRequestBodyWithLimit, Lr as resolveControlCommandGate, Mn as DEFAULT_WEBHOOK_MAX_BODY_BYTES, Mr as readStoreAllowFromForDmPolicy, Nn as RequestBodyLimitError, Nr as resolveDmGroupAccessWithCommandGate, Or as chunkTextForOutbound, Pn as installRequestBodyLimitGuard, Pr as resolveDmGroupAccessWithLists, Q as clearHistoryEntries, Qn as createImageCard, Qr as downloadDashscopeGeneratedVideos, R as resolveMentionGatingWithBypass, Rn as requestBodyErrorToText, Rr as evaluateSenderGroupAccess, S as getPublicGatewayIdentity, Sn as registerAcpRuntimeBackend, Sr as resolveOutboundMediaUrls, T as ACPX_PUSH_TEST_METHOD, Tr as createScopedPairingAccess, U as logAckFailure, Ur as canonicalizeRealtimeTranscriptionProviderId, V as resolveDefaultLineAccountId, Vn as runFederationAutoConnectOnce, Vr as listRealtimeVoiceProviders, W as logInboundDrop, Wr as getRealtimeTranscriptionProvider, Xn as createReceiptCard, Xr as buildDashscopeVideoGenerationInput, Y as DEFAULT_GROUP_HISTORY_LIMIT, Yn as stripMarkdown, Yr as DEFAULT_VIDEO_RESOLUTION_TO_SIZE, Z as buildPendingHistoryContextFromMap, Zn as createActionCard, Zr as buildDashscopeVideoGenerationParameters, _ as getGatewayUpdateStatus, _r as buildHostnameAllowlistPolicyFromSuffixAllowlist, a as normalizeIMessageMessagingTarget, ai as BLUEBUBBLES_ACTIONS, ar as createPersistentDedupe, at as shouldAckReaction, b as getStatusSummary, br as createNormalizedOutboundDeliverer, ci as CHANNEL_MESSAGE_ACTION_NAMES, cn as stringEnum, cr as registerWebhookTarget, d as mergeAllowlist, dr as resolveSingleWebhookTargetAsync, ei as pollDashscopeVideoTaskUntilComplete, er as createListCard, et as evictOldHistoryKeys, fr as resolveWebhookTargets, g as buildCommandsListResult, gr as withTempDownloadPath, h as getAcpStatusSnapshot, hr as extractToolSend, i as looksLikeIMessageTargetId, ii as resolveAllowlistMatchSimple, ir as createLoggerBackedRuntime, it as removeAckReactionAfterReply, jn as DEFAULT_WEBHOOK_BODY_TIMEOUT_MS, jr as DM_GROUP_ACCESS_REASON, kr as resolveChannelAccountConfigBasePath, lr as rejectNonPostWebhookRequest, m as pushHandlers, mr as resolveWebhookPath, n as collectBlueBubblesStatusIssues, ni as registerPluginHttpRoute, nt as recordPendingHistoryEntryIfEnabled, o as imessageOnboardingAdapter, oi as BLUEBUBBLES_ACTION_NAMES, or as createDedupeCache, ot as shouldAckReactionForWhatsApp, p as summarizeMapping, pr as normalizeWebhookPath, pt as formatInboundFromLabel, qn as hasMarkdownToConvert, qr as DEFAULT_VIDEO_GENERATION_DURATION_SECONDS, r as signalOnboardingAdapter, ri as formatAllowlistMatchMeta, rr as waitForAbortSignal, rt as recordInboundSession, s as buildMediaPayload, si as BLUEBUBBLES_GROUP_ACTIONS, sn as optionalStringEnum, t as LineConfigSchema, ti as resolveVideoGenerationReferenceUrls, tt as recordPendingHistoryEntry, ur as resolveSingleWebhookTarget, v as buildModelCatalogStatus, vr as isHttpsUrlAllowedByHostnameSuffixAllowlist, w as executeAcpxPushTestRequest, wn as AcpRuntimeError, wr as buildOauthProviderAuthResult, x as isDangerousNameMatchingEnabled, xr as formatTextWithAttachmentLinks, y as loadGatewayModelCatalog, yr as normalizeHostnameSuffixAllowlist, z as listLineAccountIds, zr as canonicalizeRealtimeVoiceProviderId } from "./config-schema-Cg6PYSHR.js";
|
|
2
|
+
import { Bt as resolveBlueBubblesGroupRequireMention, Gt as resolveGoogleChatGroupToolPolicy, Kt as resolveIMessageGroupRequireMention, Nn as getChatChannelMeta, Rt as looksLikeSignalTargetId, Vt as resolveBlueBubblesGroupToolPolicy, Wt as resolveGoogleChatGroupRequireMention, Xn as normalizePluginHttpPath, dn as resolveDefaultSignalAccountId, fn as resolveSignalAccount, hn as resolveIMessageAccount, mn as resolveDefaultIMessageAccountId, pn as listIMessageAccountIds, qt as resolveIMessageGroupToolPolicy, un as listSignalAccountIds, yn as resolveToolsBySender, zt as normalizeSignalMessagingTarget } from "./runtime-BLknHFGz.js";
|
|
3
|
+
import { n as stripAnsi } from "./subsystem-DBPNbQDQ.js";
|
|
4
4
|
import { g as registerLogTransport, w as resolvePreferredFasedAgentTmpDir } from "./globals-B_jjtAje.js";
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import { A as GroupPolicySchema,
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
5
|
+
import "./paths-BEmvSyOV.js";
|
|
6
|
+
import { d as normalizeE164, g as safeParseJson, n as clamp, o as escapeRegExp, y as sleep } from "./utils-CYk9EKoq.js";
|
|
7
|
+
import { A as GroupPolicySchema, D as BlockStreamingCoalesceSchema, E as ToolPolicySchema, F as TtsConfigSchema, Fn as withFileLock, I as TtsModeSchema, L as TtsProviderSchema, M as MarkdownTableModeSchema, N as ReplyRuntimeConfigSchemaShape, O as DmConfigSchema, P as TtsAutoSchema, Pn as acquireFileLock, R as normalizeAllowFrom, Tr as isTruthyEnvValue, d as GoogleChatConfigSchema, f as IMessageConfigSchema, fr as formatErrorMessage, j as MarkdownConfigSchema, k as DmPolicySchema, m as SignalConfigSchema, p as MSTeamsConfigSchema, z as requireOpenAllowFrom } from "./config-DutIeFge.js";
|
|
8
|
+
import { r as listAgentIds, u as resolveDefaultAgentId } from "./agent-scope-BbgPlhtw.js";
|
|
9
|
+
import { f as DEFAULT_ACCOUNT_ID, p as normalizeAccountId, u as resolveThreadSessionKeys } from "./session-key-HSJ2KQTc.js";
|
|
10
10
|
import "./accounts-RHZyaB73.js";
|
|
11
11
|
import "./accounts-CuqIKU8X.js";
|
|
12
12
|
import "./logger-BbFvCoKk.js";
|
|
13
|
-
import
|
|
14
|
-
import "./accounts-
|
|
15
|
-
import { C as resolveNestedAllowlistDecision, S as resolveChannelEntryMatchWithFallback, b as normalizeChannelSlug, x as resolveChannelEntryMatch, y as buildChannelKeyCandidates } from "./allow-list-
|
|
16
|
-
import {
|
|
17
|
-
import
|
|
18
|
-
import "./
|
|
19
|
-
import "./
|
|
20
|
-
import {
|
|
21
|
-
import
|
|
22
|
-
import {
|
|
23
|
-
import { t as
|
|
24
|
-
import "./
|
|
25
|
-
import {
|
|
26
|
-
import { t as
|
|
27
|
-
import { t as
|
|
28
|
-
import {
|
|
29
|
-
import
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import
|
|
35
|
-
import {
|
|
36
|
-
import
|
|
37
|
-
import
|
|
38
|
-
import {
|
|
39
|
-
import "./
|
|
40
|
-
import "./
|
|
41
|
-
import
|
|
42
|
-
import {
|
|
43
|
-
import "./
|
|
44
|
-
import "./
|
|
45
|
-
import
|
|
46
|
-
import "./
|
|
47
|
-
import "./
|
|
48
|
-
import "./
|
|
49
|
-
import
|
|
50
|
-
import "./
|
|
51
|
-
import
|
|
52
|
-
import "./
|
|
53
|
-
import
|
|
54
|
-
import "./
|
|
55
|
-
import "./
|
|
56
|
-
import "./
|
|
57
|
-
import
|
|
58
|
-
import
|
|
59
|
-
import
|
|
60
|
-
import "./
|
|
61
|
-
import "./
|
|
62
|
-
import "./
|
|
63
|
-
import
|
|
64
|
-
import "./
|
|
65
|
-
import "./
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
import { t as buildMediaPayload } from "./media-payload-DpYEcV-X.js";
|
|
69
|
-
import { t as promptChannelAccessConfig } from "./channel-access-I3pe5AGo.js";
|
|
70
|
-
import { n as asString, r as collectIssuesForEnabledAccounts } from "./shared-V35XNAbz.js";
|
|
71
|
-
import path from "node:path";
|
|
72
|
-
import chalk from "chalk";
|
|
73
|
-
import fs, { createWriteStream, existsSync } from "node:fs";
|
|
74
|
-
import os from "node:os";
|
|
75
|
-
import JSON5 from "json5";
|
|
76
|
-
import { format } from "node:util";
|
|
77
|
-
import fs$1 from "node:fs/promises";
|
|
78
|
-
import { spawn } from "node:child_process";
|
|
79
|
-
import { createHash, createPrivateKey, randomUUID, sign } from "node:crypto";
|
|
80
|
-
import { pipeline } from "node:stream/promises";
|
|
81
|
-
import { z } from "zod";
|
|
82
|
-
import { request } from "node:https";
|
|
83
|
-
import { WebSocket as WebSocket$1 } from "ws";
|
|
84
|
-
import http2 from "node:http2";
|
|
85
|
-
|
|
86
|
-
//#region src/video-generation/dashscope-compatible.ts
|
|
87
|
-
const DEFAULT_VIDEO_GENERATION_DURATION_SECONDS = 5;
|
|
88
|
-
const DEFAULT_VIDEO_GENERATION_TIMEOUT_MS = 12e4;
|
|
89
|
-
const DEFAULT_VIDEO_RESOLUTION_TO_SIZE = {
|
|
90
|
-
"480P": "832*480",
|
|
91
|
-
"720P": "1280*720",
|
|
92
|
-
"1080P": "1920*1080"
|
|
93
|
-
};
|
|
94
|
-
const DEFAULT_VIDEO_GENERATION_POLL_INTERVAL_MS = 2500;
|
|
95
|
-
const DEFAULT_VIDEO_GENERATION_MAX_POLL_ATTEMPTS = 120;
|
|
96
|
-
function buildDashscopeVideoGenerationInput(params) {
|
|
97
|
-
if ([...params.req.inputImages ?? [], ...params.req.inputVideos ?? []].some((asset) => !asset.url?.trim() && asset.buffer)) throw new Error(`${params.providerLabel} video generation currently requires remote http(s) URLs for reference images/videos.`);
|
|
98
|
-
const input = { prompt: params.req.prompt };
|
|
99
|
-
const referenceUrls = resolveVideoGenerationReferenceUrls(params.req.inputImages, params.req.inputVideos);
|
|
100
|
-
if (referenceUrls.length === 1 && (params.req.inputImages?.length ?? 0) === 1 && !params.req.inputVideos?.length) input.img_url = referenceUrls[0];
|
|
101
|
-
else if (referenceUrls.length > 0) input.reference_urls = referenceUrls;
|
|
102
|
-
return input;
|
|
103
|
-
}
|
|
104
|
-
function resolveVideoGenerationReferenceUrls(inputImages, inputVideos) {
|
|
105
|
-
return [...inputImages ?? [], ...inputVideos ?? []].map((asset) => asset.url?.trim()).filter((value) => Boolean(value));
|
|
106
|
-
}
|
|
107
|
-
function buildDashscopeVideoGenerationParameters(req, resolutionToSize = DEFAULT_VIDEO_RESOLUTION_TO_SIZE) {
|
|
108
|
-
const parameters = {};
|
|
109
|
-
const size = req.size?.trim() || (req.resolution ? resolutionToSize[req.resolution] : void 0);
|
|
110
|
-
if (size) parameters.size = size;
|
|
111
|
-
if (req.aspectRatio?.trim()) parameters.aspect_ratio = req.aspectRatio.trim();
|
|
112
|
-
if (typeof req.durationSeconds === "number" && Number.isFinite(req.durationSeconds)) parameters.duration = Math.max(1, Math.round(req.durationSeconds));
|
|
113
|
-
if (typeof req.audio === "boolean") parameters.enable_audio = req.audio;
|
|
114
|
-
if (typeof req.watermark === "boolean") parameters.watermark = req.watermark;
|
|
115
|
-
return Object.keys(parameters).length > 0 ? parameters : void 0;
|
|
116
|
-
}
|
|
117
|
-
function extractDashscopeVideoUrls(payload) {
|
|
118
|
-
const urls = [...payload.output?.results?.map((entry) => entry.video_url).filter(Boolean) ?? [], payload.output?.video_url].filter((value) => typeof value === "string" && value.trim().length > 0);
|
|
119
|
-
return [...new Set(urls)];
|
|
120
|
-
}
|
|
121
|
-
async function pollDashscopeVideoTaskUntilComplete(params) {
|
|
122
|
-
for (let attempt = 0; attempt < DEFAULT_VIDEO_GENERATION_MAX_POLL_ATTEMPTS; attempt += 1) {
|
|
123
|
-
const response = await fetchWithTimeout(`${params.baseUrl}/api/v1/tasks/${params.taskId}`, {
|
|
124
|
-
method: "GET",
|
|
125
|
-
headers: params.headers
|
|
126
|
-
}, params.timeoutMs ?? params.defaultTimeoutMs ?? DEFAULT_VIDEO_GENERATION_TIMEOUT_MS, params.fetchFn);
|
|
127
|
-
await assertOkOrThrowHttpError(response, `${params.providerLabel} video-generation task poll failed`);
|
|
128
|
-
const payload = await response.json();
|
|
129
|
-
const status = payload.output?.task_status?.trim().toUpperCase();
|
|
130
|
-
if (status === "SUCCEEDED") return payload;
|
|
131
|
-
if (status === "FAILED" || status === "CANCELED") throw new Error(payload.output?.message?.trim() || payload.message?.trim() || `${params.providerLabel} video generation task ${params.taskId} ${status.toLowerCase()}`);
|
|
132
|
-
await new Promise((resolve) => setTimeout(resolve, DEFAULT_VIDEO_GENERATION_POLL_INTERVAL_MS));
|
|
133
|
-
}
|
|
134
|
-
throw new Error(`${params.providerLabel} video generation task ${params.taskId} did not finish in time`);
|
|
135
|
-
}
|
|
136
|
-
async function downloadDashscopeGeneratedVideos(params) {
|
|
137
|
-
const videos = [];
|
|
138
|
-
for (const [index, url] of params.urls.entries()) {
|
|
139
|
-
const response = await fetchWithTimeout(url, { method: "GET" }, params.timeoutMs ?? params.defaultTimeoutMs ?? DEFAULT_VIDEO_GENERATION_TIMEOUT_MS, params.fetchFn);
|
|
140
|
-
await assertOkOrThrowHttpError(response, `${params.providerLabel} generated video download failed`);
|
|
141
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
142
|
-
videos.push({
|
|
143
|
-
buffer: Buffer.from(arrayBuffer),
|
|
144
|
-
mimeType: response.headers.get("content-type")?.trim() || "video/mp4",
|
|
145
|
-
fileName: `video-${index + 1}.mp4`,
|
|
146
|
-
metadata: { sourceUrl: redactSensitiveUrl(url) }
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
return videos;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
//#endregion
|
|
153
|
-
//#region src/plugins/capability-provider-runtime.ts
|
|
154
|
-
function resolvePluginCapabilityProviders(params) {
|
|
155
|
-
return (getActivePluginRegistry()?.[params.key] ?? []).map((entry) => entry.provider);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
//#endregion
|
|
159
|
-
//#region src/realtime-transcription/provider-registry.ts
|
|
160
|
-
function trimToUndefined$1(value) {
|
|
161
|
-
const trimmed = value?.trim().toLowerCase();
|
|
162
|
-
return trimmed ? trimmed : void 0;
|
|
163
|
-
}
|
|
164
|
-
function normalizeRealtimeTranscriptionProviderId(providerId) {
|
|
165
|
-
return trimToUndefined$1(providerId);
|
|
166
|
-
}
|
|
167
|
-
function resolveRealtimeTranscriptionProviderEntries(_cfg) {
|
|
168
|
-
return resolvePluginCapabilityProviders({ key: "realtimeTranscriptionProviders" });
|
|
169
|
-
}
|
|
170
|
-
function buildProviderMaps$1(_cfg) {
|
|
171
|
-
const canonical = /* @__PURE__ */ new Map();
|
|
172
|
-
const aliases = /* @__PURE__ */ new Map();
|
|
173
|
-
const register = (provider) => {
|
|
174
|
-
const id = normalizeRealtimeTranscriptionProviderId(provider.id);
|
|
175
|
-
if (!id) return;
|
|
176
|
-
canonical.set(id, provider);
|
|
177
|
-
aliases.set(id, provider);
|
|
178
|
-
for (const alias of provider.aliases ?? []) {
|
|
179
|
-
const normalizedAlias = normalizeRealtimeTranscriptionProviderId(alias);
|
|
180
|
-
if (normalizedAlias) aliases.set(normalizedAlias, provider);
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
for (const provider of resolveRealtimeTranscriptionProviderEntries(_cfg)) register(provider);
|
|
184
|
-
return {
|
|
185
|
-
canonical,
|
|
186
|
-
aliases
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
function listRealtimeTranscriptionProviders(cfg) {
|
|
190
|
-
return [...buildProviderMaps$1(cfg).canonical.values()];
|
|
191
|
-
}
|
|
192
|
-
function getRealtimeTranscriptionProvider(providerId, cfg) {
|
|
193
|
-
const normalized = normalizeRealtimeTranscriptionProviderId(providerId);
|
|
194
|
-
if (!normalized) return;
|
|
195
|
-
return buildProviderMaps$1(cfg).aliases.get(normalized);
|
|
196
|
-
}
|
|
197
|
-
function canonicalizeRealtimeTranscriptionProviderId(providerId, cfg) {
|
|
198
|
-
const normalized = normalizeRealtimeTranscriptionProviderId(providerId);
|
|
199
|
-
if (!normalized) return;
|
|
200
|
-
return getRealtimeTranscriptionProvider(normalized, cfg)?.id ?? normalized;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
//#endregion
|
|
204
|
-
//#region src/realtime-voice/provider-registry.ts
|
|
205
|
-
function trimToUndefined(value) {
|
|
206
|
-
const trimmed = value?.trim().toLowerCase();
|
|
207
|
-
return trimmed ? trimmed : void 0;
|
|
208
|
-
}
|
|
209
|
-
function normalizeRealtimeVoiceProviderId(providerId) {
|
|
210
|
-
return trimToUndefined(providerId);
|
|
211
|
-
}
|
|
212
|
-
function resolveRealtimeVoiceProviderEntries(_cfg) {
|
|
213
|
-
return resolvePluginCapabilityProviders({ key: "realtimeVoiceProviders" });
|
|
214
|
-
}
|
|
215
|
-
function buildProviderMaps(_cfg) {
|
|
216
|
-
const canonical = /* @__PURE__ */ new Map();
|
|
217
|
-
const aliases = /* @__PURE__ */ new Map();
|
|
218
|
-
const register = (provider) => {
|
|
219
|
-
const id = normalizeRealtimeVoiceProviderId(provider.id);
|
|
220
|
-
if (!id) return;
|
|
221
|
-
canonical.set(id, provider);
|
|
222
|
-
aliases.set(id, provider);
|
|
223
|
-
for (const alias of provider.aliases ?? []) {
|
|
224
|
-
const normalizedAlias = normalizeRealtimeVoiceProviderId(alias);
|
|
225
|
-
if (normalizedAlias) aliases.set(normalizedAlias, provider);
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
for (const provider of resolveRealtimeVoiceProviderEntries(_cfg)) register(provider);
|
|
229
|
-
return {
|
|
230
|
-
canonical,
|
|
231
|
-
aliases
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
function listRealtimeVoiceProviders(cfg) {
|
|
235
|
-
return [...buildProviderMaps(cfg).canonical.values()];
|
|
236
|
-
}
|
|
237
|
-
function getRealtimeVoiceProvider(providerId, cfg) {
|
|
238
|
-
const normalized = normalizeRealtimeVoiceProviderId(providerId);
|
|
239
|
-
if (!normalized) return;
|
|
240
|
-
return buildProviderMaps(cfg).aliases.get(normalized);
|
|
241
|
-
}
|
|
242
|
-
function canonicalizeRealtimeVoiceProviderId(providerId, cfg) {
|
|
243
|
-
const normalized = normalizeRealtimeVoiceProviderId(providerId);
|
|
244
|
-
if (!normalized) return;
|
|
245
|
-
return getRealtimeVoiceProvider(normalized, cfg)?.id ?? normalized;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
//#endregion
|
|
249
|
-
//#region src/plugin-sdk/group-access.ts
|
|
250
|
-
function evaluateSenderGroupAccess(params) {
|
|
251
|
-
const { groupPolicy, providerMissingFallbackApplied } = resolveOpenProviderRuntimeGroupPolicy({
|
|
252
|
-
providerConfigPresent: params.providerConfigPresent,
|
|
253
|
-
groupPolicy: params.configuredGroupPolicy,
|
|
254
|
-
defaultGroupPolicy: params.defaultGroupPolicy
|
|
255
|
-
});
|
|
256
|
-
if (groupPolicy === "disabled") return {
|
|
257
|
-
allowed: false,
|
|
258
|
-
groupPolicy,
|
|
259
|
-
providerMissingFallbackApplied,
|
|
260
|
-
reason: "disabled"
|
|
261
|
-
};
|
|
262
|
-
if (groupPolicy === "allowlist") {
|
|
263
|
-
if (params.groupAllowFrom.length === 0) return {
|
|
264
|
-
allowed: false,
|
|
265
|
-
groupPolicy,
|
|
266
|
-
providerMissingFallbackApplied,
|
|
267
|
-
reason: "empty_allowlist"
|
|
268
|
-
};
|
|
269
|
-
if (!params.isSenderAllowed(params.senderId, params.groupAllowFrom)) return {
|
|
270
|
-
allowed: false,
|
|
271
|
-
groupPolicy,
|
|
272
|
-
providerMissingFallbackApplied,
|
|
273
|
-
reason: "sender_not_allowlisted"
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
return {
|
|
277
|
-
allowed: true,
|
|
278
|
-
groupPolicy,
|
|
279
|
-
providerMissingFallbackApplied,
|
|
280
|
-
reason: "allowed"
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
//#endregion
|
|
285
|
-
//#region src/plugin-sdk/command-auth.ts
|
|
286
|
-
async function resolveSenderCommandAuthorization(params) {
|
|
287
|
-
const shouldComputeAuth = params.shouldComputeCommandAuthorized(params.rawBody, params.cfg);
|
|
288
|
-
const storeAllowFrom = !params.isGroup && params.dmPolicy !== "allowlist" && (params.dmPolicy !== "open" || shouldComputeAuth) ? await params.readAllowFromStore().catch(() => []) : [];
|
|
289
|
-
const access = resolveDmGroupAccessWithLists({
|
|
290
|
-
isGroup: params.isGroup,
|
|
291
|
-
dmPolicy: params.dmPolicy,
|
|
292
|
-
groupPolicy: "allowlist",
|
|
293
|
-
allowFrom: params.configuredAllowFrom,
|
|
294
|
-
groupAllowFrom: params.configuredGroupAllowFrom ?? [],
|
|
295
|
-
storeAllowFrom,
|
|
296
|
-
isSenderAllowed: (allowFrom) => params.isSenderAllowed(params.senderId, allowFrom)
|
|
297
|
-
});
|
|
298
|
-
const effectiveAllowFrom = access.effectiveAllowFrom;
|
|
299
|
-
const effectiveGroupAllowFrom = access.effectiveGroupAllowFrom;
|
|
300
|
-
const useAccessGroups = params.cfg.commands?.useAccessGroups !== false;
|
|
301
|
-
const senderAllowedForCommands = params.isSenderAllowed(params.senderId, params.isGroup ? effectiveGroupAllowFrom : effectiveAllowFrom);
|
|
302
|
-
const ownerAllowedForCommands = params.isSenderAllowed(params.senderId, effectiveAllowFrom);
|
|
303
|
-
const groupAllowedForCommands = params.isSenderAllowed(params.senderId, effectiveGroupAllowFrom);
|
|
304
|
-
return {
|
|
305
|
-
shouldComputeAuth,
|
|
306
|
-
effectiveAllowFrom,
|
|
307
|
-
effectiveGroupAllowFrom,
|
|
308
|
-
senderAllowedForCommands,
|
|
309
|
-
commandAuthorized: shouldComputeAuth ? params.resolveCommandAuthorizedFromAuthorizers({
|
|
310
|
-
useAccessGroups,
|
|
311
|
-
authorizers: [{
|
|
312
|
-
configured: effectiveAllowFrom.length > 0,
|
|
313
|
-
allowed: ownerAllowedForCommands
|
|
314
|
-
}, {
|
|
315
|
-
configured: effectiveGroupAllowFrom.length > 0,
|
|
316
|
-
allowed: groupAllowedForCommands
|
|
317
|
-
}]
|
|
318
|
-
}) : void 0
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
//#endregion
|
|
323
|
-
//#region src/plugin-sdk/config-paths.ts
|
|
324
|
-
function resolveChannelAccountConfigBasePath(params) {
|
|
325
|
-
const accounts = (params.cfg.channels?.[params.channelKey])?.accounts;
|
|
326
|
-
return Boolean(accounts?.[params.accountId]) ? `channels.${params.channelKey}.accounts.${params.accountId}.` : `channels.${params.channelKey}.`;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
//#endregion
|
|
330
|
-
//#region src/plugin-sdk/text-chunking.ts
|
|
331
|
-
function chunkTextForOutbound(text, limit) {
|
|
332
|
-
return chunkTextByBreakResolver(text, limit, (window) => {
|
|
333
|
-
const lastNewline = window.lastIndexOf("\n");
|
|
334
|
-
const lastSpace = window.lastIndexOf(" ");
|
|
335
|
-
return lastNewline > 0 ? lastNewline : lastSpace;
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
//#endregion
|
|
340
|
-
//#region src/plugin-sdk/agent-media-payload.ts
|
|
341
|
-
function buildAgentMediaPayload(mediaList) {
|
|
342
|
-
const first = mediaList[0];
|
|
343
|
-
const mediaPaths = mediaList.map((media) => media.path);
|
|
344
|
-
const mediaTypes = mediaList.map((media) => media.contentType).filter(Boolean);
|
|
345
|
-
return {
|
|
346
|
-
MediaPath: first?.path,
|
|
347
|
-
MediaType: first?.contentType ?? void 0,
|
|
348
|
-
MediaUrl: first?.path,
|
|
349
|
-
MediaPaths: mediaPaths.length > 0 ? mediaPaths : void 0,
|
|
350
|
-
MediaUrls: mediaPaths.length > 0 ? mediaPaths : void 0,
|
|
351
|
-
MediaTypes: mediaTypes.length > 0 ? mediaTypes : void 0
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
//#endregion
|
|
356
|
-
//#region src/plugin-sdk/fetch-auth.ts
|
|
357
|
-
function isAuthFailureStatus(status) {
|
|
358
|
-
return status === 401 || status === 403;
|
|
359
|
-
}
|
|
360
|
-
async function fetchWithBearerAuthScopeFallback(params) {
|
|
361
|
-
const fetchFn = params.fetchFn ?? fetch;
|
|
362
|
-
let parsedUrl;
|
|
363
|
-
try {
|
|
364
|
-
parsedUrl = new URL(params.url);
|
|
365
|
-
} catch {
|
|
366
|
-
throw new Error(`Invalid URL: ${params.url}`);
|
|
367
|
-
}
|
|
368
|
-
if (params.requireHttps === true && parsedUrl.protocol !== "https:") throw new Error(`URL must use HTTPS: ${params.url}`);
|
|
369
|
-
const requestInit = normalizeRequestInitHeadersForFetch(params.requestInit);
|
|
370
|
-
const fetchOnce = (headers) => fetchFn(params.url, {
|
|
371
|
-
...requestInit,
|
|
372
|
-
...headers ? { headers } : {}
|
|
373
|
-
});
|
|
374
|
-
const firstAttempt = await fetchOnce();
|
|
375
|
-
if (firstAttempt.ok) return firstAttempt;
|
|
376
|
-
if (!params.tokenProvider) return firstAttempt;
|
|
377
|
-
const shouldRetry = params.shouldRetry ?? ((response) => isAuthFailureStatus(response.status));
|
|
378
|
-
if (!shouldRetry(firstAttempt)) return firstAttempt;
|
|
379
|
-
if (params.shouldAttachAuth && !params.shouldAttachAuth(params.url)) return firstAttempt;
|
|
380
|
-
for (const scope of params.scopes) try {
|
|
381
|
-
const token = await params.tokenProvider.getAccessToken(scope);
|
|
382
|
-
const authHeaders = new Headers(requestInit?.headers);
|
|
383
|
-
authHeaders.set("Authorization", `Bearer ${token}`);
|
|
384
|
-
const authAttempt = await fetchOnce(authHeaders);
|
|
385
|
-
if (authAttempt.ok) return authAttempt;
|
|
386
|
-
if (!shouldRetry(authAttempt)) continue;
|
|
387
|
-
} catch {}
|
|
388
|
-
return firstAttempt;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
//#endregion
|
|
392
|
-
//#region src/plugin-sdk/pairing-access.ts
|
|
393
|
-
function createScopedPairingAccess(params) {
|
|
394
|
-
const resolvedAccountId = normalizeAccountId(params.accountId);
|
|
395
|
-
return {
|
|
396
|
-
accountId: resolvedAccountId,
|
|
397
|
-
readAllowFromStore: () => params.core.channel.pairing.readAllowFromStore({
|
|
398
|
-
channel: params.channel,
|
|
399
|
-
accountId: resolvedAccountId
|
|
400
|
-
}),
|
|
401
|
-
readStoreForDmPolicy: (provider, accountId) => params.core.channel.pairing.readAllowFromStore({
|
|
402
|
-
channel: provider,
|
|
403
|
-
accountId: normalizeAccountId(accountId)
|
|
404
|
-
}),
|
|
405
|
-
upsertPairingRequest: (input) => params.core.channel.pairing.upsertPairingRequest({
|
|
406
|
-
channel: params.channel,
|
|
407
|
-
accountId: resolvedAccountId,
|
|
408
|
-
...input
|
|
409
|
-
})
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
//#endregion
|
|
414
|
-
//#region src/plugin-sdk/provider-auth-result.ts
|
|
415
|
-
function buildOauthProviderAuthResult(params) {
|
|
416
|
-
const email = params.email ?? void 0;
|
|
417
|
-
return {
|
|
418
|
-
profiles: [{
|
|
419
|
-
profileId: `${params.profilePrefix ?? params.providerId}:${email ?? "default"}`,
|
|
420
|
-
credential: {
|
|
421
|
-
type: "oauth",
|
|
422
|
-
provider: params.providerId,
|
|
423
|
-
access: params.access,
|
|
424
|
-
...params.refresh ? { refresh: params.refresh } : {},
|
|
425
|
-
...Number.isFinite(params.expires) ? { expires: params.expires } : {},
|
|
426
|
-
...email ? { email } : {},
|
|
427
|
-
...params.credentialExtra
|
|
428
|
-
}
|
|
429
|
-
}],
|
|
430
|
-
configPatch: params.configPatch ?? { agents: { defaults: { models: { [params.defaultModel]: {} } } } },
|
|
431
|
-
defaultModel: params.defaultModel,
|
|
432
|
-
notes: params.notes
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
//#endregion
|
|
437
|
-
//#region src/plugin-sdk/reply-payload.ts
|
|
438
|
-
function normalizeOutboundReplyPayload(payload) {
|
|
439
|
-
return {
|
|
440
|
-
text: typeof payload.text === "string" ? payload.text : void 0,
|
|
441
|
-
mediaUrls: Array.isArray(payload.mediaUrls) ? payload.mediaUrls.filter((entry) => typeof entry === "string" && entry.length > 0) : void 0,
|
|
442
|
-
mediaUrl: typeof payload.mediaUrl === "string" ? payload.mediaUrl : void 0,
|
|
443
|
-
replyToId: typeof payload.replyToId === "string" ? payload.replyToId : void 0
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
function createNormalizedOutboundDeliverer(handler) {
|
|
447
|
-
return async (payload) => {
|
|
448
|
-
await handler(payload && typeof payload === "object" ? normalizeOutboundReplyPayload(payload) : {});
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
function resolveOutboundMediaUrls(payload) {
|
|
452
|
-
if (payload.mediaUrls?.length) return payload.mediaUrls;
|
|
453
|
-
if (payload.mediaUrl) return [payload.mediaUrl];
|
|
454
|
-
return [];
|
|
455
|
-
}
|
|
456
|
-
function formatTextWithAttachmentLinks(text, mediaUrls) {
|
|
457
|
-
const trimmedText = text?.trim() ?? "";
|
|
458
|
-
if (!trimmedText && mediaUrls.length === 0) return "";
|
|
459
|
-
const mediaBlock = mediaUrls.length ? mediaUrls.map((url) => `Attachment: ${url}`).join("\n") : "";
|
|
460
|
-
if (!trimmedText) return mediaBlock;
|
|
461
|
-
if (!mediaBlock) return trimmedText;
|
|
462
|
-
return `${trimmedText}\n\n${mediaBlock}`;
|
|
463
|
-
}
|
|
464
|
-
async function sendMediaWithLeadingCaption(params) {
|
|
465
|
-
if (params.mediaUrls.length === 0) return false;
|
|
466
|
-
let first = true;
|
|
467
|
-
for (const mediaUrl of params.mediaUrls) {
|
|
468
|
-
const caption = first ? params.caption : void 0;
|
|
469
|
-
first = false;
|
|
470
|
-
try {
|
|
471
|
-
await params.send({
|
|
472
|
-
mediaUrl,
|
|
473
|
-
caption
|
|
474
|
-
});
|
|
475
|
-
} catch (error) {
|
|
476
|
-
if (params.onError) {
|
|
477
|
-
params.onError(error, mediaUrl);
|
|
478
|
-
continue;
|
|
479
|
-
}
|
|
480
|
-
throw error;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
return true;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
//#endregion
|
|
487
|
-
//#region src/plugin-sdk/ssrf-policy.ts
|
|
488
|
-
function normalizeHostnameSuffix(value) {
|
|
489
|
-
const trimmed = value.trim().toLowerCase();
|
|
490
|
-
if (!trimmed) return "";
|
|
491
|
-
if (trimmed === "*" || trimmed === "*.") return "*";
|
|
492
|
-
return trimmed.replace(/^\*\.?/, "").replace(/^\.+/, "").replace(/\.+$/, "");
|
|
493
|
-
}
|
|
494
|
-
function isHostnameAllowedBySuffixAllowlist(hostname, allowlist) {
|
|
495
|
-
if (allowlist.includes("*")) return true;
|
|
496
|
-
const normalized = hostname.toLowerCase();
|
|
497
|
-
return allowlist.some((entry) => normalized === entry || normalized.endsWith(`.${entry}`));
|
|
498
|
-
}
|
|
499
|
-
function normalizeHostnameSuffixAllowlist(input, defaults) {
|
|
500
|
-
const source = input && input.length > 0 ? input : defaults;
|
|
501
|
-
if (!source || source.length === 0) return [];
|
|
502
|
-
const normalized = source.map(normalizeHostnameSuffix).filter(Boolean);
|
|
503
|
-
if (normalized.includes("*")) return ["*"];
|
|
504
|
-
return Array.from(new Set(normalized));
|
|
505
|
-
}
|
|
506
|
-
function isHttpsUrlAllowedByHostnameSuffixAllowlist(url, allowlist) {
|
|
507
|
-
try {
|
|
508
|
-
const parsed = new URL(url);
|
|
509
|
-
if (parsed.protocol !== "https:") return false;
|
|
510
|
-
return isHostnameAllowedBySuffixAllowlist(parsed.hostname, allowlist);
|
|
511
|
-
} catch {
|
|
512
|
-
return false;
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
/**
|
|
516
|
-
* Converts suffix-style host allowlists (for example "example.com") into SSRF
|
|
517
|
-
* hostname allowlist patterns used by the shared fetch guard.
|
|
518
|
-
*
|
|
519
|
-
* Suffix semantics:
|
|
520
|
-
* - "example.com" allows "example.com" and "*.example.com"
|
|
521
|
-
* - "*" disables hostname allowlist restrictions
|
|
522
|
-
*/
|
|
523
|
-
function buildHostnameAllowlistPolicyFromSuffixAllowlist(allowHosts) {
|
|
524
|
-
const normalizedAllowHosts = normalizeHostnameSuffixAllowlist(allowHosts);
|
|
525
|
-
if (normalizedAllowHosts.length === 0) return;
|
|
526
|
-
const patterns = /* @__PURE__ */ new Set();
|
|
527
|
-
for (const normalized of normalizedAllowHosts) {
|
|
528
|
-
if (normalized === "*") return;
|
|
529
|
-
patterns.add(normalized);
|
|
530
|
-
patterns.add(`*.${normalized}`);
|
|
531
|
-
}
|
|
532
|
-
if (patterns.size === 0) return;
|
|
533
|
-
return { hostnameAllowlist: Array.from(patterns) };
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
//#endregion
|
|
537
|
-
//#region src/plugin-sdk/webhook-path.ts
|
|
538
|
-
function normalizeWebhookPath(raw) {
|
|
539
|
-
const trimmed = raw.trim();
|
|
540
|
-
if (!trimmed) return "/";
|
|
541
|
-
const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
542
|
-
if (withSlash.length > 1 && withSlash.endsWith("/")) return withSlash.slice(0, -1);
|
|
543
|
-
return withSlash;
|
|
544
|
-
}
|
|
545
|
-
function resolveWebhookPath(params) {
|
|
546
|
-
const trimmedPath = params.webhookPath?.trim();
|
|
547
|
-
if (trimmedPath) return normalizeWebhookPath(trimmedPath);
|
|
548
|
-
if (params.webhookUrl?.trim()) try {
|
|
549
|
-
return normalizeWebhookPath(new URL(params.webhookUrl).pathname || "/");
|
|
550
|
-
} catch {
|
|
551
|
-
return null;
|
|
552
|
-
}
|
|
553
|
-
return params.defaultPath ?? null;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
//#endregion
|
|
557
|
-
//#region src/plugin-sdk/webhook-targets.ts
|
|
558
|
-
function registerWebhookTarget(targetsByPath, target) {
|
|
559
|
-
const key = normalizeWebhookPath(target.path);
|
|
560
|
-
const normalizedTarget = {
|
|
561
|
-
...target,
|
|
562
|
-
path: key
|
|
563
|
-
};
|
|
564
|
-
const existing = targetsByPath.get(key) ?? [];
|
|
565
|
-
targetsByPath.set(key, [...existing, normalizedTarget]);
|
|
566
|
-
const unregister = () => {
|
|
567
|
-
const updated = (targetsByPath.get(key) ?? []).filter((entry) => entry !== normalizedTarget);
|
|
568
|
-
if (updated.length > 0) {
|
|
569
|
-
targetsByPath.set(key, updated);
|
|
570
|
-
return;
|
|
571
|
-
}
|
|
572
|
-
targetsByPath.delete(key);
|
|
573
|
-
};
|
|
574
|
-
return {
|
|
575
|
-
target: normalizedTarget,
|
|
576
|
-
unregister
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
function resolveWebhookTargets(req, targetsByPath) {
|
|
580
|
-
const path = normalizeWebhookPath(new URL(req.url ?? "/", "http://localhost").pathname);
|
|
581
|
-
const targets = targetsByPath.get(path);
|
|
582
|
-
if (!targets || targets.length === 0) return null;
|
|
583
|
-
return {
|
|
584
|
-
path,
|
|
585
|
-
targets
|
|
586
|
-
};
|
|
587
|
-
}
|
|
588
|
-
function resolveSingleWebhookTarget(targets, isMatch) {
|
|
589
|
-
let matched;
|
|
590
|
-
for (const target of targets) {
|
|
591
|
-
if (!isMatch(target)) continue;
|
|
592
|
-
if (matched) return { kind: "ambiguous" };
|
|
593
|
-
matched = target;
|
|
594
|
-
}
|
|
595
|
-
if (!matched) return { kind: "none" };
|
|
596
|
-
return {
|
|
597
|
-
kind: "single",
|
|
598
|
-
target: matched
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
async function resolveSingleWebhookTargetAsync(targets, isMatch) {
|
|
602
|
-
let matched;
|
|
603
|
-
for (const target of targets) {
|
|
604
|
-
if (!await isMatch(target)) continue;
|
|
605
|
-
if (matched) return { kind: "ambiguous" };
|
|
606
|
-
matched = target;
|
|
607
|
-
}
|
|
608
|
-
if (!matched) return { kind: "none" };
|
|
609
|
-
return {
|
|
610
|
-
kind: "single",
|
|
611
|
-
target: matched
|
|
612
|
-
};
|
|
613
|
-
}
|
|
614
|
-
function rejectNonPostWebhookRequest(req, res) {
|
|
615
|
-
if (req.method === "POST") return false;
|
|
616
|
-
res.statusCode = 405;
|
|
617
|
-
res.setHeader("Allow", "POST");
|
|
618
|
-
res.end("Method Not Allowed");
|
|
619
|
-
return true;
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
//#endregion
|
|
623
|
-
//#region src/plugin-sdk/persistent-dedupe.ts
|
|
624
|
-
const DEFAULT_LOCK_OPTIONS = {
|
|
625
|
-
retries: {
|
|
626
|
-
retries: 6,
|
|
627
|
-
factor: 1.35,
|
|
628
|
-
minTimeout: 8,
|
|
629
|
-
maxTimeout: 180,
|
|
630
|
-
randomize: true
|
|
631
|
-
},
|
|
632
|
-
stale: 6e4
|
|
633
|
-
};
|
|
634
|
-
function mergeLockOptions(overrides) {
|
|
635
|
-
return {
|
|
636
|
-
stale: overrides?.stale ?? DEFAULT_LOCK_OPTIONS.stale,
|
|
637
|
-
retries: {
|
|
638
|
-
retries: overrides?.retries?.retries ?? DEFAULT_LOCK_OPTIONS.retries.retries,
|
|
639
|
-
factor: overrides?.retries?.factor ?? DEFAULT_LOCK_OPTIONS.retries.factor,
|
|
640
|
-
minTimeout: overrides?.retries?.minTimeout ?? DEFAULT_LOCK_OPTIONS.retries.minTimeout,
|
|
641
|
-
maxTimeout: overrides?.retries?.maxTimeout ?? DEFAULT_LOCK_OPTIONS.retries.maxTimeout,
|
|
642
|
-
randomize: overrides?.retries?.randomize ?? DEFAULT_LOCK_OPTIONS.retries.randomize
|
|
643
|
-
}
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
function sanitizeData(value) {
|
|
647
|
-
if (!value || typeof value !== "object") return {};
|
|
648
|
-
const out = {};
|
|
649
|
-
for (const [key, ts] of Object.entries(value)) if (typeof ts === "number" && Number.isFinite(ts) && ts > 0) out[key] = ts;
|
|
650
|
-
return out;
|
|
651
|
-
}
|
|
652
|
-
function pruneData(data, now, ttlMs, maxEntries) {
|
|
653
|
-
if (ttlMs > 0) {
|
|
654
|
-
for (const [key, ts] of Object.entries(data)) if (now - ts >= ttlMs) delete data[key];
|
|
655
|
-
}
|
|
656
|
-
const keys = Object.keys(data);
|
|
657
|
-
if (keys.length <= maxEntries) return;
|
|
658
|
-
keys.toSorted((a, b) => data[a] - data[b]).slice(0, keys.length - maxEntries).forEach((key) => {
|
|
659
|
-
delete data[key];
|
|
660
|
-
});
|
|
661
|
-
}
|
|
662
|
-
function createPersistentDedupe(options) {
|
|
663
|
-
const ttlMs = Math.max(0, Math.floor(options.ttlMs));
|
|
664
|
-
const memoryMaxSize = Math.max(0, Math.floor(options.memoryMaxSize));
|
|
665
|
-
const fileMaxEntries = Math.max(1, Math.floor(options.fileMaxEntries));
|
|
666
|
-
const lockOptions = mergeLockOptions(options.lockOptions);
|
|
667
|
-
const memory = createDedupeCache({
|
|
668
|
-
ttlMs,
|
|
669
|
-
maxSize: memoryMaxSize
|
|
670
|
-
});
|
|
671
|
-
const inflight = /* @__PURE__ */ new Map();
|
|
672
|
-
async function checkAndRecordInner(key, namespace, scopedKey, now, onDiskError) {
|
|
673
|
-
if (memory.check(scopedKey, now)) return false;
|
|
674
|
-
const path = options.resolveFilePath(namespace);
|
|
675
|
-
try {
|
|
676
|
-
return !await withFileLock(path, lockOptions, async () => {
|
|
677
|
-
const { value } = await readJsonFileWithFallback(path, {});
|
|
678
|
-
const data = sanitizeData(value);
|
|
679
|
-
const seenAt = data[key];
|
|
680
|
-
if (seenAt != null && (ttlMs <= 0 || now - seenAt < ttlMs)) return true;
|
|
681
|
-
data[key] = now;
|
|
682
|
-
pruneData(data, now, ttlMs, fileMaxEntries);
|
|
683
|
-
await writeJsonFileAtomically(path, data);
|
|
684
|
-
return false;
|
|
685
|
-
});
|
|
686
|
-
} catch (error) {
|
|
687
|
-
onDiskError?.(error);
|
|
688
|
-
return true;
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
async function checkAndRecord(key, dedupeOptions) {
|
|
692
|
-
const trimmed = key.trim();
|
|
693
|
-
if (!trimmed) return true;
|
|
694
|
-
const namespace = dedupeOptions?.namespace?.trim() || "global";
|
|
695
|
-
const scopedKey = `${namespace}:${trimmed}`;
|
|
696
|
-
if (inflight.has(scopedKey)) return false;
|
|
697
|
-
const onDiskError = dedupeOptions?.onDiskError ?? options.onDiskError;
|
|
698
|
-
const work = checkAndRecordInner(trimmed, namespace, scopedKey, dedupeOptions?.now ?? Date.now(), onDiskError);
|
|
699
|
-
inflight.set(scopedKey, work);
|
|
700
|
-
try {
|
|
701
|
-
return await work;
|
|
702
|
-
} finally {
|
|
703
|
-
inflight.delete(scopedKey);
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
return {
|
|
707
|
-
checkAndRecord,
|
|
708
|
-
clearMemory: () => memory.clear(),
|
|
709
|
-
memorySize: () => memory.size()
|
|
710
|
-
};
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
//#endregion
|
|
714
|
-
//#region src/plugin-sdk/runtime.ts
|
|
715
|
-
function createLoggerBackedRuntime(params) {
|
|
716
|
-
return {
|
|
717
|
-
log: (...args) => {
|
|
718
|
-
params.logger.info(format(...args));
|
|
719
|
-
},
|
|
720
|
-
error: (...args) => {
|
|
721
|
-
params.logger.error(format(...args));
|
|
722
|
-
},
|
|
723
|
-
exit: (code) => {
|
|
724
|
-
throw params.exitError?.(code) ?? /* @__PURE__ */ new Error(`exit ${code}`);
|
|
725
|
-
}
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
//#endregion
|
|
730
|
-
//#region src/line/flex-templates/basic-cards.ts
|
|
731
|
-
/**
|
|
732
|
-
* Create an info card with title, body, and optional footer
|
|
733
|
-
*
|
|
734
|
-
* Editorial design: Clean hierarchy with accent bar, generous spacing,
|
|
735
|
-
* and subtle background zones for visual separation.
|
|
736
|
-
*/
|
|
737
|
-
function createInfoCard(title, body, footer) {
|
|
738
|
-
const bubble = {
|
|
739
|
-
type: "bubble",
|
|
740
|
-
size: "mega",
|
|
741
|
-
body: {
|
|
742
|
-
type: "box",
|
|
743
|
-
layout: "vertical",
|
|
744
|
-
contents: [{
|
|
745
|
-
type: "box",
|
|
746
|
-
layout: "horizontal",
|
|
747
|
-
contents: [{
|
|
748
|
-
type: "box",
|
|
749
|
-
layout: "vertical",
|
|
750
|
-
contents: [],
|
|
751
|
-
width: "4px",
|
|
752
|
-
backgroundColor: "#06C755",
|
|
753
|
-
cornerRadius: "2px"
|
|
754
|
-
}, {
|
|
755
|
-
type: "text",
|
|
756
|
-
text: title,
|
|
757
|
-
weight: "bold",
|
|
758
|
-
size: "xl",
|
|
759
|
-
color: "#111111",
|
|
760
|
-
wrap: true,
|
|
761
|
-
flex: 1,
|
|
762
|
-
margin: "lg"
|
|
763
|
-
}]
|
|
764
|
-
}, {
|
|
765
|
-
type: "box",
|
|
766
|
-
layout: "vertical",
|
|
767
|
-
contents: [{
|
|
768
|
-
type: "text",
|
|
769
|
-
text: body,
|
|
770
|
-
size: "md",
|
|
771
|
-
color: "#444444",
|
|
772
|
-
wrap: true,
|
|
773
|
-
lineSpacing: "6px"
|
|
774
|
-
}],
|
|
775
|
-
margin: "xl",
|
|
776
|
-
paddingAll: "lg",
|
|
777
|
-
backgroundColor: "#F8F9FA",
|
|
778
|
-
cornerRadius: "lg"
|
|
779
|
-
}],
|
|
780
|
-
paddingAll: "xl",
|
|
781
|
-
backgroundColor: "#FFFFFF"
|
|
782
|
-
}
|
|
783
|
-
};
|
|
784
|
-
if (footer) attachFooterText(bubble, footer);
|
|
785
|
-
return bubble;
|
|
786
|
-
}
|
|
787
|
-
/**
|
|
788
|
-
* Create a list card with title and multiple items
|
|
789
|
-
*
|
|
790
|
-
* Editorial design: Numbered/bulleted list with clear visual hierarchy,
|
|
791
|
-
* accent dots for each item, and generous spacing.
|
|
792
|
-
*/
|
|
793
|
-
function createListCard(title, items) {
|
|
794
|
-
const itemContents = items.slice(0, 8).map((item, index) => {
|
|
795
|
-
const itemContents = [{
|
|
796
|
-
type: "text",
|
|
797
|
-
text: item.title,
|
|
798
|
-
size: "md",
|
|
799
|
-
weight: "bold",
|
|
800
|
-
color: "#1a1a1a",
|
|
801
|
-
wrap: true
|
|
802
|
-
}];
|
|
803
|
-
if (item.subtitle) itemContents.push({
|
|
804
|
-
type: "text",
|
|
805
|
-
text: item.subtitle,
|
|
806
|
-
size: "sm",
|
|
807
|
-
color: "#888888",
|
|
808
|
-
wrap: true,
|
|
809
|
-
margin: "xs"
|
|
810
|
-
});
|
|
811
|
-
const itemBox = {
|
|
812
|
-
type: "box",
|
|
813
|
-
layout: "horizontal",
|
|
814
|
-
contents: [{
|
|
815
|
-
type: "box",
|
|
816
|
-
layout: "vertical",
|
|
817
|
-
contents: [{
|
|
818
|
-
type: "box",
|
|
819
|
-
layout: "vertical",
|
|
820
|
-
contents: [],
|
|
821
|
-
width: "8px",
|
|
822
|
-
height: "8px",
|
|
823
|
-
backgroundColor: index === 0 ? "#06C755" : "#DDDDDD",
|
|
824
|
-
cornerRadius: "4px"
|
|
825
|
-
}],
|
|
826
|
-
width: "20px",
|
|
827
|
-
alignItems: "center",
|
|
828
|
-
paddingTop: "sm"
|
|
829
|
-
}, {
|
|
830
|
-
type: "box",
|
|
831
|
-
layout: "vertical",
|
|
832
|
-
contents: itemContents,
|
|
833
|
-
flex: 1
|
|
834
|
-
}],
|
|
835
|
-
margin: index > 0 ? "lg" : void 0
|
|
836
|
-
};
|
|
837
|
-
if (item.action) itemBox.action = item.action;
|
|
838
|
-
return itemBox;
|
|
839
|
-
});
|
|
840
|
-
return {
|
|
841
|
-
type: "bubble",
|
|
842
|
-
size: "mega",
|
|
843
|
-
body: {
|
|
844
|
-
type: "box",
|
|
845
|
-
layout: "vertical",
|
|
846
|
-
contents: [
|
|
847
|
-
{
|
|
848
|
-
type: "text",
|
|
849
|
-
text: title,
|
|
850
|
-
weight: "bold",
|
|
851
|
-
size: "xl",
|
|
852
|
-
color: "#111111",
|
|
853
|
-
wrap: true
|
|
854
|
-
},
|
|
855
|
-
{
|
|
856
|
-
type: "separator",
|
|
857
|
-
margin: "lg",
|
|
858
|
-
color: "#EEEEEE"
|
|
859
|
-
},
|
|
860
|
-
{
|
|
861
|
-
type: "box",
|
|
862
|
-
layout: "vertical",
|
|
863
|
-
contents: itemContents,
|
|
864
|
-
margin: "lg"
|
|
865
|
-
}
|
|
866
|
-
],
|
|
867
|
-
paddingAll: "xl",
|
|
868
|
-
backgroundColor: "#FFFFFF"
|
|
869
|
-
}
|
|
870
|
-
};
|
|
871
|
-
}
|
|
872
|
-
/**
|
|
873
|
-
* Create an image card with image, title, and optional body text
|
|
874
|
-
*/
|
|
875
|
-
function createImageCard(imageUrl, title, body, options) {
|
|
876
|
-
const bubble = {
|
|
877
|
-
type: "bubble",
|
|
878
|
-
hero: {
|
|
879
|
-
type: "image",
|
|
880
|
-
url: imageUrl,
|
|
881
|
-
size: "full",
|
|
882
|
-
aspectRatio: options?.aspectRatio ?? "20:13",
|
|
883
|
-
aspectMode: options?.aspectMode ?? "cover",
|
|
884
|
-
action: options?.action
|
|
885
|
-
},
|
|
886
|
-
body: {
|
|
887
|
-
type: "box",
|
|
888
|
-
layout: "vertical",
|
|
889
|
-
contents: [{
|
|
890
|
-
type: "text",
|
|
891
|
-
text: title,
|
|
892
|
-
weight: "bold",
|
|
893
|
-
size: "xl",
|
|
894
|
-
wrap: true
|
|
895
|
-
}],
|
|
896
|
-
paddingAll: "lg"
|
|
897
|
-
}
|
|
898
|
-
};
|
|
899
|
-
if (body && bubble.body) bubble.body.contents.push({
|
|
900
|
-
type: "text",
|
|
901
|
-
text: body,
|
|
902
|
-
size: "md",
|
|
903
|
-
wrap: true,
|
|
904
|
-
margin: "md",
|
|
905
|
-
color: "#666666"
|
|
906
|
-
});
|
|
907
|
-
return bubble;
|
|
908
|
-
}
|
|
909
|
-
/**
|
|
910
|
-
* Create an action card with title, body, and action buttons
|
|
911
|
-
*/
|
|
912
|
-
function createActionCard(title, body, actions, options) {
|
|
913
|
-
const bubble = {
|
|
914
|
-
type: "bubble",
|
|
915
|
-
body: {
|
|
916
|
-
type: "box",
|
|
917
|
-
layout: "vertical",
|
|
918
|
-
contents: [{
|
|
919
|
-
type: "text",
|
|
920
|
-
text: title,
|
|
921
|
-
weight: "bold",
|
|
922
|
-
size: "xl",
|
|
923
|
-
wrap: true
|
|
924
|
-
}, {
|
|
925
|
-
type: "text",
|
|
926
|
-
text: body,
|
|
927
|
-
size: "md",
|
|
928
|
-
wrap: true,
|
|
929
|
-
margin: "md",
|
|
930
|
-
color: "#666666"
|
|
931
|
-
}],
|
|
932
|
-
paddingAll: "lg"
|
|
933
|
-
},
|
|
934
|
-
footer: {
|
|
935
|
-
type: "box",
|
|
936
|
-
layout: "vertical",
|
|
937
|
-
contents: actions.slice(0, 4).map((action, index) => ({
|
|
938
|
-
type: "button",
|
|
939
|
-
action: action.action,
|
|
940
|
-
style: index === 0 ? "primary" : "secondary",
|
|
941
|
-
margin: index > 0 ? "sm" : void 0
|
|
942
|
-
})),
|
|
943
|
-
paddingAll: "md"
|
|
944
|
-
}
|
|
945
|
-
};
|
|
946
|
-
if (options?.imageUrl) bubble.hero = {
|
|
947
|
-
type: "image",
|
|
948
|
-
url: options.imageUrl,
|
|
949
|
-
size: "full",
|
|
950
|
-
aspectRatio: options.aspectRatio ?? "20:13",
|
|
951
|
-
aspectMode: "cover"
|
|
952
|
-
};
|
|
953
|
-
return bubble;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
//#endregion
|
|
957
|
-
//#region src/acp/acpx-mutating-wrapper-policy.ts
|
|
958
|
-
const REQUIRED_MUTATING_WRAPPER_GATES = [
|
|
959
|
-
"operator-scope",
|
|
960
|
-
"operator-confirmation",
|
|
961
|
-
"plugin-admin-rpc-grant",
|
|
962
|
-
"plugin-source-allowlist",
|
|
963
|
-
"audit",
|
|
964
|
-
"rate-limit",
|
|
965
|
-
"gateway-token",
|
|
966
|
-
"explicit-wrapper-enable"
|
|
967
|
-
];
|
|
968
|
-
const FORBIDDEN_MUTATING_WRAPPER_SURFACES = [
|
|
969
|
-
"generic-gateway-dispatcher",
|
|
970
|
-
"client-supplied-mcpServers",
|
|
971
|
-
"cli-mcp",
|
|
972
|
-
"untrusted-plugin-source",
|
|
973
|
-
"bulk-enable-all-admin-rpcs"
|
|
974
|
-
];
|
|
975
|
-
const ACPX_MUTATING_WRAPPER_DESIGNS = [
|
|
976
|
-
{
|
|
977
|
-
id: "fased_chat_inject_request",
|
|
978
|
-
method: "chat.inject",
|
|
979
|
-
status: "design-only",
|
|
980
|
-
executionEnabled: false,
|
|
981
|
-
description: "Future fixed wrapper for requesting an operator-approved chat injection into a scoped session.",
|
|
982
|
-
requiredGates: REQUIRED_MUTATING_WRAPPER_GATES,
|
|
983
|
-
forbiddenSurfaces: FORBIDDEN_MUTATING_WRAPPER_SURFACES
|
|
984
|
-
},
|
|
985
|
-
{
|
|
986
|
-
id: "fased_push_test_request",
|
|
987
|
-
method: "push.test",
|
|
988
|
-
status: "design-only",
|
|
989
|
-
executionEnabled: false,
|
|
990
|
-
description: "Future fixed wrapper for requesting an operator-approved test push to a scoped node.",
|
|
991
|
-
requiredGates: REQUIRED_MUTATING_WRAPPER_GATES,
|
|
992
|
-
forbiddenSurfaces: FORBIDDEN_MUTATING_WRAPPER_SURFACES
|
|
993
|
-
},
|
|
994
|
-
{
|
|
995
|
-
id: "fased_web_login_start_request",
|
|
996
|
-
method: "web.login.start",
|
|
997
|
-
status: "design-only",
|
|
998
|
-
executionEnabled: false,
|
|
999
|
-
description: "Future fixed wrapper for requesting an operator-approved web login start for a scoped account.",
|
|
1000
|
-
requiredGates: REQUIRED_MUTATING_WRAPPER_GATES,
|
|
1001
|
-
forbiddenSurfaces: FORBIDDEN_MUTATING_WRAPPER_SURFACES
|
|
1002
|
-
},
|
|
1003
|
-
{
|
|
1004
|
-
id: "fased_web_login_wait_request",
|
|
1005
|
-
method: "web.login.wait",
|
|
1006
|
-
status: "design-only",
|
|
1007
|
-
executionEnabled: false,
|
|
1008
|
-
description: "Future fixed wrapper for requesting an operator-approved web login wait for a scoped account.",
|
|
1009
|
-
requiredGates: REQUIRED_MUTATING_WRAPPER_GATES,
|
|
1010
|
-
forbiddenSurfaces: FORBIDDEN_MUTATING_WRAPPER_SURFACES
|
|
1011
|
-
}
|
|
1012
|
-
];
|
|
1013
|
-
function findAcpxMutatingWrapperDesign(id) {
|
|
1014
|
-
return ACPX_MUTATING_WRAPPER_DESIGNS.find((design) => design.id === id);
|
|
1015
|
-
}
|
|
1016
|
-
function uniqueStrings(values) {
|
|
1017
|
-
return new Set((values ?? []).map((value) => value.trim()).filter(Boolean));
|
|
1018
|
-
}
|
|
1019
|
-
function evaluateAcpxMutatingWrapperRuntimeGate(input) {
|
|
1020
|
-
const wrapperId = input.wrapperId.trim();
|
|
1021
|
-
const design = findAcpxMutatingWrapperDesign(wrapperId);
|
|
1022
|
-
if (!design) return {
|
|
1023
|
-
wrapperId,
|
|
1024
|
-
allowed: false,
|
|
1025
|
-
reason: "unknown-wrapper",
|
|
1026
|
-
missingGates: [],
|
|
1027
|
-
activeForbiddenSurfaces: []
|
|
1028
|
-
};
|
|
1029
|
-
if (uniqueStrings(input.denyWrappers).has(design.id)) return {
|
|
1030
|
-
wrapperId: design.id,
|
|
1031
|
-
method: design.method,
|
|
1032
|
-
allowed: false,
|
|
1033
|
-
reason: "denied-wrapper",
|
|
1034
|
-
missingGates: [],
|
|
1035
|
-
activeForbiddenSurfaces: []
|
|
1036
|
-
};
|
|
1037
|
-
const allowWrappers = uniqueStrings(input.allowWrappers);
|
|
1038
|
-
if (allowWrappers.size > 0 && !allowWrappers.has(design.id)) return {
|
|
1039
|
-
wrapperId: design.id,
|
|
1040
|
-
method: design.method,
|
|
1041
|
-
allowed: false,
|
|
1042
|
-
reason: "not-allowlisted",
|
|
1043
|
-
missingGates: [],
|
|
1044
|
-
activeForbiddenSurfaces: []
|
|
1045
|
-
};
|
|
1046
|
-
const activeForbiddenSurfaces = design.forbiddenSurfaces.filter((surface) => input.forbiddenSurfaces?.[surface] === true);
|
|
1047
|
-
if (activeForbiddenSurfaces.length > 0) return {
|
|
1048
|
-
wrapperId: design.id,
|
|
1049
|
-
method: design.method,
|
|
1050
|
-
allowed: false,
|
|
1051
|
-
reason: "forbidden-surface",
|
|
1052
|
-
missingGates: [],
|
|
1053
|
-
activeForbiddenSurfaces
|
|
1054
|
-
};
|
|
1055
|
-
const missingGates = design.requiredGates.filter((gate) => input.gates?.[gate] !== true);
|
|
1056
|
-
if (missingGates.length > 0) return {
|
|
1057
|
-
wrapperId: design.id,
|
|
1058
|
-
method: design.method,
|
|
1059
|
-
allowed: false,
|
|
1060
|
-
reason: "missing-gates",
|
|
1061
|
-
missingGates,
|
|
1062
|
-
activeForbiddenSurfaces: []
|
|
1063
|
-
};
|
|
1064
|
-
return {
|
|
1065
|
-
wrapperId: design.id,
|
|
1066
|
-
method: design.method,
|
|
1067
|
-
allowed: true,
|
|
1068
|
-
reason: "allowed",
|
|
1069
|
-
missingGates: [],
|
|
1070
|
-
activeForbiddenSurfaces: []
|
|
1071
|
-
};
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
//#endregion
|
|
1075
|
-
//#region src/acp/acpx-push-test-approval-contract.ts
|
|
1076
|
-
const ACPX_PUSH_TEST_WRAPPER_ID = "fased_push_test_request";
|
|
1077
|
-
const ACPX_PUSH_TEST_METHOD = "push.test";
|
|
1078
|
-
function normalizeForStableJson(value) {
|
|
1079
|
-
if (value === null || typeof value === "string" || typeof value === "number") return value;
|
|
1080
|
-
if (typeof value === "boolean") return value;
|
|
1081
|
-
if (Array.isArray(value)) return value.map((entry) => normalizeForStableJson(entry));
|
|
1082
|
-
if (typeof value === "object" && value) {
|
|
1083
|
-
const normalized = {};
|
|
1084
|
-
for (const key of Object.keys(value).toSorted()) {
|
|
1085
|
-
const entry = value[key];
|
|
1086
|
-
if (entry !== void 0) normalized[key] = normalizeForStableJson(entry);
|
|
1087
|
-
}
|
|
1088
|
-
return normalized;
|
|
1089
|
-
}
|
|
1090
|
-
return null;
|
|
1091
|
-
}
|
|
1092
|
-
function stableJson(value) {
|
|
1093
|
-
return JSON.stringify(normalizeForStableJson(value));
|
|
1094
|
-
}
|
|
1095
|
-
function createAcpxPushTestRequestFingerprint(input) {
|
|
1096
|
-
return createHash("sha256").update(stableJson({
|
|
1097
|
-
method: input.method,
|
|
1098
|
-
params: input.params,
|
|
1099
|
-
wrapperId: input.wrapperId
|
|
1100
|
-
})).digest("hex");
|
|
1101
|
-
}
|
|
1102
|
-
function createAcpxPushTestSafeSummary(params) {
|
|
1103
|
-
const record = typeof params === "object" && params !== null && !Array.isArray(params) ? params : {};
|
|
1104
|
-
const environment = record.environment === "sandbox" || record.environment === "production" ? record.environment : null;
|
|
1105
|
-
return {
|
|
1106
|
-
nodeId: typeof record.nodeId === "string" && record.nodeId.trim() ? record.nodeId.trim() : null,
|
|
1107
|
-
environment,
|
|
1108
|
-
titleProvided: typeof record.title === "string" && record.title.length > 0,
|
|
1109
|
-
bodyProvided: typeof record.body === "string" && record.body.length > 0
|
|
1110
|
-
};
|
|
1111
|
-
}
|
|
1112
|
-
function evaluateAcpxPushTestApprovalContract(request) {
|
|
1113
|
-
const requestFingerprint = createAcpxPushTestRequestFingerprint({
|
|
1114
|
-
wrapperId: request.wrapperId,
|
|
1115
|
-
method: request.method,
|
|
1116
|
-
params: request.params
|
|
1117
|
-
});
|
|
1118
|
-
const requestReasons = collectRequestDenyReasons(request);
|
|
1119
|
-
if (requestReasons.length > 0) return deniedResponse({
|
|
1120
|
-
request,
|
|
1121
|
-
requestFingerprint,
|
|
1122
|
-
stage: "request",
|
|
1123
|
-
reasons: requestReasons
|
|
1124
|
-
});
|
|
1125
|
-
const approvalReasons = collectApprovalDenyReasons({
|
|
1126
|
-
approval: request.approval,
|
|
1127
|
-
requestFingerprint
|
|
1128
|
-
});
|
|
1129
|
-
if (approvalReasons.length > 0) return deniedResponse({
|
|
1130
|
-
request,
|
|
1131
|
-
requestFingerprint,
|
|
1132
|
-
stage: "operator-approval",
|
|
1133
|
-
reasons: approvalReasons
|
|
1134
|
-
});
|
|
1135
|
-
const gate = evaluateAcpxMutatingWrapperRuntimeGate({
|
|
1136
|
-
wrapperId: request.wrapperId,
|
|
1137
|
-
gates: request.gate.gates,
|
|
1138
|
-
forbiddenSurfaces: request.gate.forbiddenSurfaces,
|
|
1139
|
-
allowWrappers: request.gate.allowWrappers,
|
|
1140
|
-
denyWrappers: request.gate.denyWrappers
|
|
1141
|
-
});
|
|
1142
|
-
if (!gate.allowed) return deniedResponse({
|
|
1143
|
-
request,
|
|
1144
|
-
requestFingerprint,
|
|
1145
|
-
stage: "runtime-gate",
|
|
1146
|
-
reasons: [`mutating wrapper runtime gate denied: ${gate.reason}`],
|
|
1147
|
-
gate
|
|
1148
|
-
});
|
|
1149
|
-
return {
|
|
1150
|
-
schemaVersion: 1,
|
|
1151
|
-
kind: "acpx.mutating-wrapper.push-test.execution.response",
|
|
1152
|
-
wrapperId: ACPX_PUSH_TEST_WRAPPER_ID,
|
|
1153
|
-
method: ACPX_PUSH_TEST_METHOD,
|
|
1154
|
-
dryRun: true,
|
|
1155
|
-
noExecutionPerformed: true,
|
|
1156
|
-
requestId: request.requestId,
|
|
1157
|
-
createdAt: request.createdAt,
|
|
1158
|
-
status: "admitted",
|
|
1159
|
-
stage: "admitted",
|
|
1160
|
-
requestFingerprint,
|
|
1161
|
-
reasons: [],
|
|
1162
|
-
safeSummary: createAcpxPushTestSafeSummary(request.params),
|
|
1163
|
-
gate
|
|
1164
|
-
};
|
|
1165
|
-
}
|
|
1166
|
-
function collectRequestDenyReasons(request) {
|
|
1167
|
-
const reasons = [];
|
|
1168
|
-
if (request.schemaVersion !== 1) reasons.push("ACPX push-test execution request schema version is unsupported");
|
|
1169
|
-
if (request.kind !== "acpx.mutating-wrapper.push-test.execution.request") reasons.push("ACPX push-test execution request kind is unsupported");
|
|
1170
|
-
if (request.wrapperId !== ACPX_PUSH_TEST_WRAPPER_ID) reasons.push("ACPX push-test execution request wrapper is unsupported");
|
|
1171
|
-
if (request.method !== ACPX_PUSH_TEST_METHOD) reasons.push("ACPX push-test execution request method is unsupported");
|
|
1172
|
-
if (!request.dryRun) reasons.push("ACPX push-test execution contract must be evaluated as a dry run");
|
|
1173
|
-
if (typeof request.requestId !== "string" || request.requestId.trim() === "") reasons.push("ACPX push-test execution request requires requestId");
|
|
1174
|
-
if (typeof request.createdAt !== "string" || request.createdAt.trim() === "") reasons.push("ACPX push-test execution request requires createdAt");
|
|
1175
|
-
if (!validatePushTestParams(request.params)) reasons.push("ACPX push-test execution request params are invalid");
|
|
1176
|
-
return reasons;
|
|
1177
|
-
}
|
|
1178
|
-
function collectApprovalDenyReasons(params) {
|
|
1179
|
-
const reasons = [];
|
|
1180
|
-
if (params.approval.confirmation !== "operator-confirmed") reasons.push("ACPX push-test execution requires explicit operator confirmation");
|
|
1181
|
-
if (params.approval.acceptedRequestFingerprint !== params.requestFingerprint) reasons.push("ACPX push-test approval fingerprint does not match request");
|
|
1182
|
-
if (params.approval.confirmation === "operator-confirmed" && (typeof params.approval.approvedAt !== "string" || params.approval.approvedAt.trim() === "")) reasons.push("ACPX push-test approval requires approvedAt");
|
|
1183
|
-
return reasons;
|
|
1184
|
-
}
|
|
1185
|
-
function deniedResponse(params) {
|
|
1186
|
-
return {
|
|
1187
|
-
schemaVersion: 1,
|
|
1188
|
-
kind: "acpx.mutating-wrapper.push-test.execution.response",
|
|
1189
|
-
wrapperId: ACPX_PUSH_TEST_WRAPPER_ID,
|
|
1190
|
-
method: ACPX_PUSH_TEST_METHOD,
|
|
1191
|
-
dryRun: true,
|
|
1192
|
-
noExecutionPerformed: true,
|
|
1193
|
-
requestId: typeof params.request.requestId === "string" ? params.request.requestId : "",
|
|
1194
|
-
createdAt: typeof params.request.createdAt === "string" ? params.request.createdAt : "",
|
|
1195
|
-
status: "denied",
|
|
1196
|
-
stage: params.stage,
|
|
1197
|
-
requestFingerprint: params.requestFingerprint,
|
|
1198
|
-
reasons: params.reasons,
|
|
1199
|
-
safeSummary: createAcpxPushTestSafeSummary(params.request.params),
|
|
1200
|
-
...params.gate ? { gate: params.gate } : {}
|
|
1201
|
-
};
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
//#endregion
|
|
1205
|
-
//#region src/acp/acpx-push-test-execution-adapter.ts
|
|
1206
|
-
function sanitizePushResult(payload) {
|
|
1207
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return {};
|
|
1208
|
-
const record = payload;
|
|
1209
|
-
return {
|
|
1210
|
-
ok: typeof record.ok === "boolean" ? record.ok : void 0,
|
|
1211
|
-
status: typeof record.status === "number" ? record.status : void 0,
|
|
1212
|
-
reason: typeof record.reason === "string" ? record.reason : void 0,
|
|
1213
|
-
environment: typeof record.environment === "string" ? record.environment : void 0,
|
|
1214
|
-
apnsId: typeof record.apnsId === "string" ? record.apnsId : void 0,
|
|
1215
|
-
tokenSuffix: typeof record.tokenSuffix === "string" ? record.tokenSuffix : void 0,
|
|
1216
|
-
topic: typeof record.topic === "string" ? record.topic : void 0
|
|
1217
|
-
};
|
|
1218
|
-
}
|
|
1219
|
-
function collectExecutionGateReasons(input) {
|
|
1220
|
-
const reasons = [];
|
|
1221
|
-
if (!input.executionGate.enabled) reasons.push("ACPX push-test execution adapter is disabled");
|
|
1222
|
-
if (!input.executionGate.allowExecution) reasons.push("ACPX push-test execution is not explicitly enabled");
|
|
1223
|
-
if (!input.executionGate.gatewayHandlerRegistered || !input.handler) reasons.push("ACPX push-test gateway handler is not registered");
|
|
1224
|
-
const role = input.client?.connect?.role ?? "operator";
|
|
1225
|
-
if (!input.client?.connect || role !== "operator") reasons.push("ACPX push-test execution requires an operator client");
|
|
1226
|
-
else {
|
|
1227
|
-
const auth = authorizeOperatorScopesForMethod(ACPX_PUSH_TEST_METHOD, input.client.connect.scopes ?? []);
|
|
1228
|
-
if (!auth.allowed) reasons.push(`ACPX push-test execution requires ${auth.missingScope}`);
|
|
1229
|
-
}
|
|
1230
|
-
return reasons;
|
|
1231
|
-
}
|
|
1232
|
-
function deniedResult(params) {
|
|
1233
|
-
logMutatingAdminRpcAudit({
|
|
1234
|
-
context: params.input.context,
|
|
1235
|
-
client: params.input.client,
|
|
1236
|
-
method: ACPX_PUSH_TEST_METHOD,
|
|
1237
|
-
outcome: "denied",
|
|
1238
|
-
details: {
|
|
1239
|
-
wrapperId: ACPX_PUSH_TEST_WRAPPER_ID,
|
|
1240
|
-
requestId: params.input.request.requestId,
|
|
1241
|
-
reason: params.reasons.join("|")
|
|
1242
|
-
}
|
|
1243
|
-
});
|
|
1244
|
-
return {
|
|
1245
|
-
schemaVersion: 1,
|
|
1246
|
-
kind: "acpx.mutating-wrapper.push-test.execution.result",
|
|
1247
|
-
wrapperId: ACPX_PUSH_TEST_WRAPPER_ID,
|
|
1248
|
-
method: ACPX_PUSH_TEST_METHOD,
|
|
1249
|
-
requestId: params.input.request.requestId,
|
|
1250
|
-
status: "denied",
|
|
1251
|
-
executionPerformed: false,
|
|
1252
|
-
noGenericDispatcher: true,
|
|
1253
|
-
contract: params.contract,
|
|
1254
|
-
reasons: params.reasons,
|
|
1255
|
-
safeSummary: createAcpxPushTestSafeSummary(params.input.request.params)
|
|
1256
|
-
};
|
|
1257
|
-
}
|
|
1258
|
-
async function invokePushTestHandler(input) {
|
|
1259
|
-
return await new Promise((resolve, reject) => {
|
|
1260
|
-
let responded = false;
|
|
1261
|
-
const respond = (ok, payload, error) => {
|
|
1262
|
-
responded = true;
|
|
1263
|
-
resolve({
|
|
1264
|
-
ok,
|
|
1265
|
-
payload,
|
|
1266
|
-
error
|
|
1267
|
-
});
|
|
1268
|
-
};
|
|
1269
|
-
Promise.resolve(input.handler({
|
|
1270
|
-
req: {
|
|
1271
|
-
type: "req",
|
|
1272
|
-
id: `acpx-push-test:${input.request.requestId}`,
|
|
1273
|
-
method: ACPX_PUSH_TEST_METHOD,
|
|
1274
|
-
params: input.request.params
|
|
1275
|
-
},
|
|
1276
|
-
params: input.request.params,
|
|
1277
|
-
client: input.client,
|
|
1278
|
-
context: input.context,
|
|
1279
|
-
isWebchatConnect: input.isWebchatConnect ?? (() => false),
|
|
1280
|
-
respond
|
|
1281
|
-
})).then(() => {
|
|
1282
|
-
if (!responded) resolve({
|
|
1283
|
-
ok: true,
|
|
1284
|
-
payload: void 0
|
|
1285
|
-
});
|
|
1286
|
-
}, (err) => reject(err));
|
|
1287
|
-
});
|
|
1288
|
-
}
|
|
1289
|
-
async function executeAcpxPushTestRequest(input) {
|
|
1290
|
-
const contract = evaluateAcpxPushTestApprovalContract(input.request);
|
|
1291
|
-
if (contract.status !== "admitted") return deniedResult({
|
|
1292
|
-
input,
|
|
1293
|
-
contract,
|
|
1294
|
-
reasons: contract.reasons
|
|
1295
|
-
});
|
|
1296
|
-
const executionGateReasons = collectExecutionGateReasons(input);
|
|
1297
|
-
if (executionGateReasons.length > 0) return deniedResult({
|
|
1298
|
-
input,
|
|
1299
|
-
contract,
|
|
1300
|
-
reasons: executionGateReasons
|
|
1301
|
-
});
|
|
1302
|
-
const budget = consumeMutatingAdminRpcBudget({
|
|
1303
|
-
method: ACPX_PUSH_TEST_METHOD,
|
|
1304
|
-
client: input.client
|
|
1305
|
-
});
|
|
1306
|
-
if (budget.applies && !budget.allowed) return deniedResult({
|
|
1307
|
-
input,
|
|
1308
|
-
contract,
|
|
1309
|
-
reasons: [`ACPX push-test execution rate-limited:${budget.policy.label}`]
|
|
1310
|
-
});
|
|
1311
|
-
try {
|
|
1312
|
-
const response = await invokePushTestHandler({
|
|
1313
|
-
...input,
|
|
1314
|
-
handler: input.handler
|
|
1315
|
-
});
|
|
1316
|
-
if (!response.ok) return {
|
|
1317
|
-
schemaVersion: 1,
|
|
1318
|
-
kind: "acpx.mutating-wrapper.push-test.execution.result",
|
|
1319
|
-
wrapperId: ACPX_PUSH_TEST_WRAPPER_ID,
|
|
1320
|
-
method: ACPX_PUSH_TEST_METHOD,
|
|
1321
|
-
requestId: input.request.requestId,
|
|
1322
|
-
status: "failed",
|
|
1323
|
-
executionPerformed: true,
|
|
1324
|
-
noGenericDispatcher: true,
|
|
1325
|
-
contract,
|
|
1326
|
-
reasons: [response.error?.message ?? "push.test handler failed"],
|
|
1327
|
-
safeSummary: createAcpxPushTestSafeSummary(input.request.params),
|
|
1328
|
-
error: {
|
|
1329
|
-
code: response.error?.code,
|
|
1330
|
-
message: response.error?.message ?? "push.test handler failed"
|
|
1331
|
-
}
|
|
1332
|
-
};
|
|
1333
|
-
return {
|
|
1334
|
-
schemaVersion: 1,
|
|
1335
|
-
kind: "acpx.mutating-wrapper.push-test.execution.result",
|
|
1336
|
-
wrapperId: ACPX_PUSH_TEST_WRAPPER_ID,
|
|
1337
|
-
method: ACPX_PUSH_TEST_METHOD,
|
|
1338
|
-
requestId: input.request.requestId,
|
|
1339
|
-
status: "executed",
|
|
1340
|
-
executionPerformed: true,
|
|
1341
|
-
noGenericDispatcher: true,
|
|
1342
|
-
contract,
|
|
1343
|
-
reasons: [],
|
|
1344
|
-
safeSummary: createAcpxPushTestSafeSummary(input.request.params),
|
|
1345
|
-
result: sanitizePushResult(response.payload)
|
|
1346
|
-
};
|
|
1347
|
-
} catch (err) {
|
|
1348
|
-
logMutatingAdminRpcAudit({
|
|
1349
|
-
context: input.context,
|
|
1350
|
-
client: input.client,
|
|
1351
|
-
method: ACPX_PUSH_TEST_METHOD,
|
|
1352
|
-
outcome: "failed",
|
|
1353
|
-
details: {
|
|
1354
|
-
wrapperId: ACPX_PUSH_TEST_WRAPPER_ID,
|
|
1355
|
-
requestId: input.request.requestId,
|
|
1356
|
-
reason: "handler_threw"
|
|
1357
|
-
}
|
|
1358
|
-
});
|
|
1359
|
-
return {
|
|
1360
|
-
schemaVersion: 1,
|
|
1361
|
-
kind: "acpx.mutating-wrapper.push-test.execution.result",
|
|
1362
|
-
wrapperId: ACPX_PUSH_TEST_WRAPPER_ID,
|
|
1363
|
-
method: ACPX_PUSH_TEST_METHOD,
|
|
1364
|
-
requestId: input.request.requestId,
|
|
1365
|
-
status: "failed",
|
|
1366
|
-
executionPerformed: true,
|
|
1367
|
-
noGenericDispatcher: true,
|
|
1368
|
-
contract,
|
|
1369
|
-
reasons: [err instanceof Error ? err.message : "push.test handler failed"],
|
|
1370
|
-
safeSummary: createAcpxPushTestSafeSummary(input.request.params),
|
|
1371
|
-
error: { message: err instanceof Error ? err.message : "push.test handler failed" }
|
|
1372
|
-
};
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
//#endregion
|
|
1377
|
-
//#region src/agents/tool-description-summary.ts
|
|
1378
|
-
function normalizeSummaryWhitespace(value) {
|
|
1379
|
-
return value.replace(/\s+/g, " ").trim();
|
|
1380
|
-
}
|
|
1381
|
-
function truncateSummary(value, maxLen = 120) {
|
|
1382
|
-
if (value.length <= maxLen) return value;
|
|
1383
|
-
const sliced = value.slice(0, maxLen - 3);
|
|
1384
|
-
const boundary = sliced.lastIndexOf(" ");
|
|
1385
|
-
return `${(boundary >= 48 ? sliced.slice(0, boundary) : sliced).trimEnd()}...`;
|
|
1386
|
-
}
|
|
1387
|
-
function isToolDocBlockStart(line) {
|
|
1388
|
-
const normalized = line.trim().toUpperCase();
|
|
1389
|
-
if (!normalized) return false;
|
|
1390
|
-
if (normalized === "ACTIONS:" || normalized === "JOB SCHEMA (FOR ADD ACTION):" || normalized === "JOB SCHEMA:" || normalized === "SESSION TARGET OPTIONS:" || normalized === "DEFAULT BEHAVIOR (UNCHANGED FOR BACKWARD COMPATIBILITY):" || normalized === "SCHEDULE TYPES (SCHEDULE.KIND):" || normalized === "PAYLOAD TYPES (PAYLOAD.KIND):" || normalized === "DELIVERY (TOP-LEVEL):" || normalized === "CRITICAL CONSTRAINTS:" || normalized === "WAKE MODES (FOR WAKE ACTION):") return true;
|
|
1391
|
-
return normalized.endsWith(":") && normalized === normalized.toUpperCase() && normalized.length > 12;
|
|
1392
|
-
}
|
|
1393
|
-
function summarizeToolDescriptionText(params) {
|
|
1394
|
-
const explicit = typeof params.displaySummary === "string" ? params.displaySummary.trim() : "";
|
|
1395
|
-
if (explicit) return truncateSummary(normalizeSummaryWhitespace(explicit), params.maxLen);
|
|
1396
|
-
const raw = typeof params.rawDescription === "string" ? params.rawDescription.trim() : "";
|
|
1397
|
-
if (!raw) return "Tool";
|
|
1398
|
-
const paragraphs = raw.split(/\n\s*\n/g).map((part) => part.trim()).filter(Boolean);
|
|
1399
|
-
for (const paragraph of paragraphs) {
|
|
1400
|
-
const lines = paragraph.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
1401
|
-
if (lines.length === 0) continue;
|
|
1402
|
-
const first = lines[0] ?? "";
|
|
1403
|
-
if (!first || isToolDocBlockStart(first)) continue;
|
|
1404
|
-
if (first.startsWith("{") || first.startsWith("[") || first.startsWith("- ")) continue;
|
|
1405
|
-
return truncateSummary(normalizeSummaryWhitespace(first), params.maxLen);
|
|
1406
|
-
}
|
|
1407
|
-
const firstLine = raw.split("\n").map((line) => line.trim()).find((line) => line.length > 0 && !isToolDocBlockStart(line) && !line.startsWith("{") && !line.startsWith("[") && !line.startsWith("- "));
|
|
1408
|
-
return firstLine ? truncateSummary(normalizeSummaryWhitespace(firstLine), params.maxLen) : "Tool";
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
|
-
//#endregion
|
|
1412
|
-
//#region src/agents/tools-effective-inventory.ts
|
|
1413
|
-
function resolveEffectiveToolLabel(tool) {
|
|
1414
|
-
const rawLabel = typeof tool.label === "string" ? tool.label.trim() : "";
|
|
1415
|
-
if (rawLabel && rawLabel.toLowerCase() !== tool.name.toLowerCase()) return rawLabel;
|
|
1416
|
-
return resolveToolDisplay({ name: tool.name }).title;
|
|
1417
|
-
}
|
|
1418
|
-
function resolveRawToolDescription(tool) {
|
|
1419
|
-
return typeof tool.description === "string" ? tool.description.trim() : "";
|
|
1420
|
-
}
|
|
1421
|
-
function summarizeToolDescription(tool) {
|
|
1422
|
-
return summarizeToolDescriptionText({
|
|
1423
|
-
rawDescription: resolveRawToolDescription(tool),
|
|
1424
|
-
displaySummary: tool.displaySummary
|
|
1425
|
-
});
|
|
1426
|
-
}
|
|
1427
|
-
function resolveEffectiveToolSource(tool) {
|
|
1428
|
-
const pluginMeta = getPluginToolMeta(tool);
|
|
1429
|
-
if (pluginMeta) return {
|
|
1430
|
-
source: "plugin",
|
|
1431
|
-
pluginId: pluginMeta.pluginId
|
|
1432
|
-
};
|
|
1433
|
-
const channelMeta = getChannelAgentToolMeta(tool);
|
|
1434
|
-
if (channelMeta) return {
|
|
1435
|
-
source: "channel",
|
|
1436
|
-
channelId: channelMeta.channelId
|
|
1437
|
-
};
|
|
1438
|
-
return { source: "core" };
|
|
1439
|
-
}
|
|
1440
|
-
function groupLabel(source) {
|
|
1441
|
-
switch (source) {
|
|
1442
|
-
case "plugin": return "Connected tools";
|
|
1443
|
-
case "channel": return "Channel tools";
|
|
1444
|
-
default: return "Built-in tools";
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
function disambiguateLabels(entries) {
|
|
1448
|
-
const counts = /* @__PURE__ */ new Map();
|
|
1449
|
-
for (const entry of entries) counts.set(entry.label, (counts.get(entry.label) ?? 0) + 1);
|
|
1450
|
-
return entries.map((entry) => {
|
|
1451
|
-
if ((counts.get(entry.label) ?? 0) < 2) return entry;
|
|
1452
|
-
const suffix = entry.pluginId ?? entry.channelId ?? entry.id;
|
|
1453
|
-
return {
|
|
1454
|
-
...entry,
|
|
1455
|
-
label: `${entry.label} (${suffix})`
|
|
1456
|
-
};
|
|
1457
|
-
});
|
|
1458
|
-
}
|
|
1459
|
-
function resolveEffectiveModelCompat(params) {
|
|
1460
|
-
const provider = params.modelProvider?.trim();
|
|
1461
|
-
const modelId = params.modelId?.trim();
|
|
1462
|
-
if (!provider || !modelId) return;
|
|
1463
|
-
try {
|
|
1464
|
-
return resolveModel(provider, modelId, params.agentDir, params.cfg).model?.compat;
|
|
1465
|
-
} catch {
|
|
1466
|
-
return;
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
function resolveEffectiveToolInventory(params) {
|
|
1470
|
-
const agentId = params.agentId?.trim() || resolveSessionAgentId({
|
|
1471
|
-
sessionKey: params.sessionKey,
|
|
1472
|
-
config: params.cfg
|
|
1473
|
-
});
|
|
1474
|
-
const workspaceDir = params.workspaceDir ?? resolveAgentWorkspaceDir(params.cfg, agentId);
|
|
1475
|
-
const agentDir = params.agentDir ?? resolveAgentDir(params.cfg, agentId);
|
|
1476
|
-
const modelCompat = resolveEffectiveModelCompat({
|
|
1477
|
-
cfg: params.cfg,
|
|
1478
|
-
agentDir,
|
|
1479
|
-
modelProvider: params.modelProvider,
|
|
1480
|
-
modelId: params.modelId
|
|
1481
|
-
});
|
|
1482
|
-
const effectiveTools = createFasedAgentCodingTools({
|
|
1483
|
-
agentId,
|
|
1484
|
-
sessionKey: params.sessionKey,
|
|
1485
|
-
workspaceDir,
|
|
1486
|
-
agentDir,
|
|
1487
|
-
config: params.cfg,
|
|
1488
|
-
modelProvider: params.modelProvider,
|
|
1489
|
-
modelId: params.modelId,
|
|
1490
|
-
modelCompat,
|
|
1491
|
-
messageProvider: params.messageProvider,
|
|
1492
|
-
senderIsOwner: params.senderIsOwner,
|
|
1493
|
-
senderId: params.senderId,
|
|
1494
|
-
senderName: params.senderName ?? void 0,
|
|
1495
|
-
senderUsername: params.senderUsername ?? void 0,
|
|
1496
|
-
senderE164: params.senderE164 ?? void 0,
|
|
1497
|
-
agentAccountId: params.accountId ?? void 0,
|
|
1498
|
-
currentChannelId: params.currentChannelId,
|
|
1499
|
-
currentThreadTs: params.currentThreadTs,
|
|
1500
|
-
currentMessageId: params.currentMessageId,
|
|
1501
|
-
groupId: params.groupId ?? void 0,
|
|
1502
|
-
groupChannel: params.groupChannel ?? void 0,
|
|
1503
|
-
groupSpace: params.groupSpace ?? void 0,
|
|
1504
|
-
replyToMode: params.replyToMode === "batched" ? "all" : params.replyToMode,
|
|
1505
|
-
modelHasVision: params.modelHasVision,
|
|
1506
|
-
requireExplicitMessageTarget: params.requireExplicitMessageTarget,
|
|
1507
|
-
disableMessageTool: params.disableMessageTool
|
|
1508
|
-
});
|
|
1509
|
-
const effectivePolicy = resolveEffectiveToolPolicy({
|
|
1510
|
-
config: params.cfg,
|
|
1511
|
-
agentId,
|
|
1512
|
-
sessionKey: params.sessionKey,
|
|
1513
|
-
modelProvider: params.modelProvider,
|
|
1514
|
-
modelId: params.modelId
|
|
1515
|
-
});
|
|
1516
|
-
const profile = effectivePolicy.providerProfile ?? effectivePolicy.profile ?? "full";
|
|
1517
|
-
const entries = disambiguateLabels(effectiveTools.map((tool) => {
|
|
1518
|
-
const source = resolveEffectiveToolSource(tool);
|
|
1519
|
-
return {
|
|
1520
|
-
id: tool.name,
|
|
1521
|
-
label: resolveEffectiveToolLabel(tool),
|
|
1522
|
-
description: summarizeToolDescription(tool),
|
|
1523
|
-
rawDescription: resolveRawToolDescription(tool) || summarizeToolDescription(tool),
|
|
1524
|
-
...source
|
|
1525
|
-
};
|
|
1526
|
-
}).toSorted((a, b) => a.label.localeCompare(b.label)));
|
|
1527
|
-
const groupsBySource = /* @__PURE__ */ new Map();
|
|
1528
|
-
for (const entry of entries) {
|
|
1529
|
-
const tools = groupsBySource.get(entry.source) ?? [];
|
|
1530
|
-
tools.push(entry);
|
|
1531
|
-
groupsBySource.set(entry.source, tools);
|
|
1532
|
-
}
|
|
1533
|
-
return {
|
|
1534
|
-
agentId,
|
|
1535
|
-
profile,
|
|
1536
|
-
groups: [
|
|
1537
|
-
"core",
|
|
1538
|
-
"plugin",
|
|
1539
|
-
"channel"
|
|
1540
|
-
].map((source) => {
|
|
1541
|
-
const tools = groupsBySource.get(source);
|
|
1542
|
-
if (!tools || tools.length === 0) return null;
|
|
1543
|
-
return {
|
|
1544
|
-
id: source,
|
|
1545
|
-
label: groupLabel(source),
|
|
1546
|
-
source,
|
|
1547
|
-
tools
|
|
1548
|
-
};
|
|
1549
|
-
}).filter((group) => group !== null)
|
|
1550
|
-
};
|
|
1551
|
-
}
|
|
1552
|
-
|
|
1553
|
-
//#endregion
|
|
1554
|
-
//#region src/gateway/gateway-identity.ts
|
|
1555
|
-
function getPublicGatewayIdentity() {
|
|
1556
|
-
const identity = loadOrCreateDeviceIdentity();
|
|
1557
|
-
return {
|
|
1558
|
-
deviceId: identity.deviceId,
|
|
1559
|
-
publicKey: publicKeyRawBase64UrlFromPem(identity.publicKeyPem)
|
|
1560
|
-
};
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
//#endregion
|
|
1564
|
-
//#region src/infra/update-channels.ts
|
|
1565
|
-
const DEFAULT_PACKAGE_CHANNEL = "stable";
|
|
1566
|
-
const DEFAULT_GIT_CHANNEL = "stable";
|
|
1567
|
-
function normalizeUpdateChannel(value) {
|
|
1568
|
-
if (!value) return null;
|
|
1569
|
-
const normalized = value.trim().toLowerCase();
|
|
1570
|
-
if (normalized === "stable" || normalized === "beta" || normalized === "dev") return normalized;
|
|
1571
|
-
return null;
|
|
1572
|
-
}
|
|
1573
|
-
function isBetaTag(tag) {
|
|
1574
|
-
return /(?:^|[.-])beta(?:[.-]|$)/i.test(tag);
|
|
1575
|
-
}
|
|
1576
|
-
function resolveEffectiveUpdateChannel(params) {
|
|
1577
|
-
if (params.configChannel) return {
|
|
1578
|
-
channel: params.configChannel,
|
|
1579
|
-
source: "config"
|
|
1580
|
-
};
|
|
1581
|
-
if (params.installKind === "git") {
|
|
1582
|
-
const tag = params.git?.tag;
|
|
1583
|
-
if (tag) return {
|
|
1584
|
-
channel: isBetaTag(tag) ? "beta" : "stable",
|
|
1585
|
-
source: "git-tag"
|
|
1586
|
-
};
|
|
1587
|
-
return {
|
|
1588
|
-
channel: DEFAULT_GIT_CHANNEL,
|
|
1589
|
-
source: "default"
|
|
1590
|
-
};
|
|
1591
|
-
}
|
|
1592
|
-
if (params.installKind === "package") return {
|
|
1593
|
-
channel: DEFAULT_PACKAGE_CHANNEL,
|
|
1594
|
-
source: "default"
|
|
1595
|
-
};
|
|
1596
|
-
return {
|
|
1597
|
-
channel: DEFAULT_PACKAGE_CHANNEL,
|
|
1598
|
-
source: "default"
|
|
1599
|
-
};
|
|
1600
|
-
}
|
|
1601
|
-
function formatUpdateChannelLabel(params) {
|
|
1602
|
-
if (params.source === "config") return `${params.channel} (config)`;
|
|
1603
|
-
if (params.source === "git-tag") return params.gitTag ? `${params.channel} (${params.gitTag})` : `${params.channel} (tag)`;
|
|
1604
|
-
if (params.source === "git-branch") return params.gitBranch ? `${params.channel} (${params.gitBranch})` : `${params.channel} (branch)`;
|
|
1605
|
-
return `${params.channel} (default)`;
|
|
1606
|
-
}
|
|
1607
|
-
function resolveUpdateChannelDisplay(params) {
|
|
1608
|
-
const channelInfo = resolveEffectiveUpdateChannel({
|
|
1609
|
-
configChannel: params.configChannel,
|
|
1610
|
-
installKind: params.installKind,
|
|
1611
|
-
git: params.gitTag || params.gitBranch ? {
|
|
1612
|
-
tag: params.gitTag ?? null,
|
|
1613
|
-
branch: params.gitBranch ?? null
|
|
1614
|
-
} : void 0
|
|
1615
|
-
});
|
|
1616
|
-
return {
|
|
1617
|
-
channel: channelInfo.channel,
|
|
1618
|
-
source: channelInfo.source,
|
|
1619
|
-
label: formatUpdateChannelLabel({
|
|
1620
|
-
channel: channelInfo.channel,
|
|
1621
|
-
source: channelInfo.source,
|
|
1622
|
-
gitTag: params.gitTag ?? null,
|
|
1623
|
-
gitBranch: params.gitBranch ?? null
|
|
1624
|
-
})
|
|
1625
|
-
};
|
|
1626
|
-
}
|
|
1627
|
-
|
|
1628
|
-
//#endregion
|
|
1629
|
-
//#region src/infra/detect-package-manager.ts
|
|
1630
|
-
async function detectPackageManager$1(root) {
|
|
1631
|
-
try {
|
|
1632
|
-
const raw = await fs$1.readFile(path.join(root, "package.json"), "utf-8");
|
|
1633
|
-
const pm = JSON.parse(raw)?.packageManager?.split("@")[0]?.trim();
|
|
1634
|
-
if (pm === "pnpm" || pm === "bun" || pm === "npm") return pm;
|
|
1635
|
-
} catch {}
|
|
1636
|
-
const files = await fs$1.readdir(root).catch(() => []);
|
|
1637
|
-
if (files.includes("pnpm-lock.yaml")) return "pnpm";
|
|
1638
|
-
if (files.includes("bun.lockb")) return "bun";
|
|
1639
|
-
if (files.includes("package-lock.json")) return "npm";
|
|
1640
|
-
return null;
|
|
1641
|
-
}
|
|
1642
|
-
|
|
1643
|
-
//#endregion
|
|
1644
|
-
//#region src/infra/update-global.ts
|
|
1645
|
-
const NPM_GLOBAL_INSTALL_QUIET_FLAGS = [
|
|
1646
|
-
"--no-fund",
|
|
1647
|
-
"--no-audit",
|
|
1648
|
-
"--loglevel=error",
|
|
1649
|
-
"--prefer-offline",
|
|
1650
|
-
"--no-progress"
|
|
1651
|
-
];
|
|
1652
|
-
const NPM_GLOBAL_INSTALL_OMIT_OPTIONAL_FLAGS = ["--omit=optional", ...NPM_GLOBAL_INSTALL_QUIET_FLAGS];
|
|
1653
|
-
function resolveNodeModulesRootForPackageRoot(pkgRoot) {
|
|
1654
|
-
const parent = path.dirname(pkgRoot);
|
|
1655
|
-
if (path.basename(parent).startsWith("@")) return path.dirname(parent);
|
|
1656
|
-
return parent;
|
|
1657
|
-
}
|
|
1658
|
-
function resolveHostedNpmInstallTarget(pkgRoot) {
|
|
1659
|
-
const resolved = path.resolve(pkgRoot);
|
|
1660
|
-
const prefixMarker = `${path.sep}.fased${path.sep}install-cache${path.sep}npm-global`;
|
|
1661
|
-
const globalRootMarker = `${prefixMarker}${`${path.sep}lib${path.sep}node_modules`}`;
|
|
1662
|
-
const markerIndex = resolved.indexOf(`${globalRootMarker}${path.sep}`);
|
|
1663
|
-
if (markerIndex < 0) return null;
|
|
1664
|
-
const prefix = resolved.slice(0, markerIndex + prefixMarker.length);
|
|
1665
|
-
const globalRoot = path.join(prefix, "lib", "node_modules");
|
|
1666
|
-
const cache = path.join(path.dirname(prefix), "npm-cache");
|
|
1667
|
-
const expectedRoot = resolveNodeModulesRootForPackageRoot(resolved);
|
|
1668
|
-
if (path.resolve(expectedRoot) !== path.resolve(globalRoot)) return null;
|
|
1669
|
-
return {
|
|
1670
|
-
manager: "npm",
|
|
1671
|
-
globalRoot,
|
|
1672
|
-
cacheRoot: path.dirname(prefix),
|
|
1673
|
-
env: {
|
|
1674
|
-
npm_config_prefix: prefix,
|
|
1675
|
-
npm_config_cache: cache
|
|
1676
|
-
}
|
|
1677
|
-
};
|
|
1678
|
-
}
|
|
1679
|
-
|
|
1680
|
-
//#endregion
|
|
1681
|
-
//#region src/infra/update-check.ts
|
|
1682
|
-
const NPM_PACKAGE_NAME = "@fased/fased";
|
|
1683
|
-
async function exists(p) {
|
|
1684
|
-
try {
|
|
1685
|
-
await fs$1.access(p);
|
|
1686
|
-
return true;
|
|
1687
|
-
} catch {
|
|
1688
|
-
return false;
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
async function detectPackageManager(root) {
|
|
1692
|
-
return await detectPackageManager$1(root) ?? "unknown";
|
|
1693
|
-
}
|
|
1694
|
-
async function detectGitRoot(root) {
|
|
1695
|
-
const res = await runCommandWithTimeout([
|
|
1696
|
-
"git",
|
|
1697
|
-
"-C",
|
|
1698
|
-
root,
|
|
1699
|
-
"rev-parse",
|
|
1700
|
-
"--show-toplevel"
|
|
1701
|
-
], { timeoutMs: 4e3 }).catch(() => null);
|
|
1702
|
-
if (!res || res.code !== 0) return null;
|
|
1703
|
-
const top = res.stdout.trim();
|
|
1704
|
-
return top ? path.resolve(top) : null;
|
|
1705
|
-
}
|
|
1706
|
-
async function checkGitUpdateStatus(params) {
|
|
1707
|
-
const timeoutMs = params.timeoutMs ?? 6e3;
|
|
1708
|
-
const root = path.resolve(params.root);
|
|
1709
|
-
const base = {
|
|
1710
|
-
root,
|
|
1711
|
-
sha: null,
|
|
1712
|
-
tag: null,
|
|
1713
|
-
branch: null,
|
|
1714
|
-
upstream: null,
|
|
1715
|
-
dirty: null,
|
|
1716
|
-
ahead: null,
|
|
1717
|
-
behind: null,
|
|
1718
|
-
fetchOk: null
|
|
1719
|
-
};
|
|
1720
|
-
const branchRes = await runCommandWithTimeout([
|
|
1721
|
-
"git",
|
|
1722
|
-
"-C",
|
|
1723
|
-
root,
|
|
1724
|
-
"rev-parse",
|
|
1725
|
-
"--abbrev-ref",
|
|
1726
|
-
"HEAD"
|
|
1727
|
-
], { timeoutMs }).catch(() => null);
|
|
1728
|
-
if (!branchRes || branchRes.code !== 0) return {
|
|
1729
|
-
...base,
|
|
1730
|
-
error: branchRes?.stderr?.trim() || "git unavailable"
|
|
1731
|
-
};
|
|
1732
|
-
const branch = branchRes.stdout.trim() || null;
|
|
1733
|
-
const shaRes = await runCommandWithTimeout([
|
|
1734
|
-
"git",
|
|
1735
|
-
"-C",
|
|
1736
|
-
root,
|
|
1737
|
-
"rev-parse",
|
|
1738
|
-
"HEAD"
|
|
1739
|
-
], { timeoutMs }).catch(() => null);
|
|
1740
|
-
const sha = shaRes && shaRes.code === 0 ? shaRes.stdout.trim() : null;
|
|
1741
|
-
const tagRes = await runCommandWithTimeout([
|
|
1742
|
-
"git",
|
|
1743
|
-
"-C",
|
|
1744
|
-
root,
|
|
1745
|
-
"describe",
|
|
1746
|
-
"--tags",
|
|
1747
|
-
"--exact-match"
|
|
1748
|
-
], { timeoutMs }).catch(() => null);
|
|
1749
|
-
const tag = tagRes && tagRes.code === 0 ? tagRes.stdout.trim() : null;
|
|
1750
|
-
const upstreamRes = await runCommandWithTimeout([
|
|
1751
|
-
"git",
|
|
1752
|
-
"-C",
|
|
1753
|
-
root,
|
|
1754
|
-
"rev-parse",
|
|
1755
|
-
"--abbrev-ref",
|
|
1756
|
-
"@{upstream}"
|
|
1757
|
-
], { timeoutMs }).catch(() => null);
|
|
1758
|
-
const upstream = upstreamRes && upstreamRes.code === 0 ? upstreamRes.stdout.trim() : null;
|
|
1759
|
-
const dirtyRes = await runCommandWithTimeout([
|
|
1760
|
-
"git",
|
|
1761
|
-
"-C",
|
|
1762
|
-
root,
|
|
1763
|
-
"status",
|
|
1764
|
-
"--porcelain",
|
|
1765
|
-
"--",
|
|
1766
|
-
":!dist/control-ui/"
|
|
1767
|
-
], { timeoutMs }).catch(() => null);
|
|
1768
|
-
const dirty = dirtyRes && dirtyRes.code === 0 ? dirtyRes.stdout.trim().length > 0 : null;
|
|
1769
|
-
const fetchOk = params.fetch ? await runCommandWithTimeout([
|
|
1770
|
-
"git",
|
|
1771
|
-
"-C",
|
|
1772
|
-
root,
|
|
1773
|
-
"fetch",
|
|
1774
|
-
"--quiet",
|
|
1775
|
-
"--prune"
|
|
1776
|
-
], { timeoutMs }).then((r) => r.code === 0).catch(() => false) : null;
|
|
1777
|
-
const counts = upstream && upstream.length > 0 ? await runCommandWithTimeout([
|
|
1778
|
-
"git",
|
|
1779
|
-
"-C",
|
|
1780
|
-
root,
|
|
1781
|
-
"rev-list",
|
|
1782
|
-
"--left-right",
|
|
1783
|
-
"--count",
|
|
1784
|
-
`HEAD...${upstream}`
|
|
1785
|
-
], { timeoutMs }).catch(() => null) : null;
|
|
1786
|
-
const parseCounts = (raw) => {
|
|
1787
|
-
const parts = raw.trim().split(/\s+/);
|
|
1788
|
-
if (parts.length < 2) return null;
|
|
1789
|
-
const ahead = Number.parseInt(parts[0] ?? "", 10);
|
|
1790
|
-
const behind = Number.parseInt(parts[1] ?? "", 10);
|
|
1791
|
-
if (!Number.isFinite(ahead) || !Number.isFinite(behind)) return null;
|
|
1792
|
-
return {
|
|
1793
|
-
ahead,
|
|
1794
|
-
behind
|
|
1795
|
-
};
|
|
1796
|
-
};
|
|
1797
|
-
const parsed = counts && counts.code === 0 ? parseCounts(counts.stdout) : null;
|
|
1798
|
-
return {
|
|
1799
|
-
root,
|
|
1800
|
-
sha,
|
|
1801
|
-
tag,
|
|
1802
|
-
branch,
|
|
1803
|
-
upstream,
|
|
1804
|
-
dirty,
|
|
1805
|
-
ahead: parsed?.ahead ?? null,
|
|
1806
|
-
behind: parsed?.behind ?? null,
|
|
1807
|
-
fetchOk
|
|
1808
|
-
};
|
|
1809
|
-
}
|
|
1810
|
-
async function statMtimeMs(p) {
|
|
1811
|
-
try {
|
|
1812
|
-
return (await fs$1.stat(p)).mtimeMs;
|
|
1813
|
-
} catch {
|
|
1814
|
-
return null;
|
|
1815
|
-
}
|
|
1816
|
-
}
|
|
1817
|
-
function resolveDepsMarker(params) {
|
|
1818
|
-
const root = params.root;
|
|
1819
|
-
if (params.manager === "pnpm") return {
|
|
1820
|
-
lockfilePath: path.join(root, "pnpm-lock.yaml"),
|
|
1821
|
-
markerPath: path.join(root, "node_modules", ".modules.yaml")
|
|
1822
|
-
};
|
|
1823
|
-
if (params.manager === "bun") return {
|
|
1824
|
-
lockfilePath: path.join(root, "bun.lockb"),
|
|
1825
|
-
markerPath: path.join(root, "node_modules")
|
|
1826
|
-
};
|
|
1827
|
-
if (params.manager === "npm") return {
|
|
1828
|
-
lockfilePath: path.join(root, "package-lock.json"),
|
|
1829
|
-
markerPath: path.join(root, "node_modules")
|
|
1830
|
-
};
|
|
1831
|
-
return {
|
|
1832
|
-
lockfilePath: null,
|
|
1833
|
-
markerPath: null
|
|
1834
|
-
};
|
|
1835
|
-
}
|
|
1836
|
-
async function checkDepsStatus(params) {
|
|
1837
|
-
const { lockfilePath, markerPath } = resolveDepsMarker({
|
|
1838
|
-
root: path.resolve(params.root),
|
|
1839
|
-
manager: params.manager
|
|
1840
|
-
});
|
|
1841
|
-
if (!lockfilePath || !markerPath) return {
|
|
1842
|
-
manager: params.manager,
|
|
1843
|
-
status: "unknown",
|
|
1844
|
-
lockfilePath,
|
|
1845
|
-
markerPath,
|
|
1846
|
-
reason: "unknown package manager"
|
|
1847
|
-
};
|
|
1848
|
-
const lockExists = await exists(lockfilePath);
|
|
1849
|
-
const markerExists = await exists(markerPath);
|
|
1850
|
-
if (!lockExists) return {
|
|
1851
|
-
manager: params.manager,
|
|
1852
|
-
status: "unknown",
|
|
1853
|
-
lockfilePath,
|
|
1854
|
-
markerPath,
|
|
1855
|
-
reason: "lockfile missing"
|
|
1856
|
-
};
|
|
1857
|
-
if (!markerExists) return {
|
|
1858
|
-
manager: params.manager,
|
|
1859
|
-
status: "missing",
|
|
1860
|
-
lockfilePath,
|
|
1861
|
-
markerPath,
|
|
1862
|
-
reason: "node_modules marker missing"
|
|
1863
|
-
};
|
|
1864
|
-
const lockMtime = await statMtimeMs(lockfilePath);
|
|
1865
|
-
const markerMtime = await statMtimeMs(markerPath);
|
|
1866
|
-
if (!lockMtime || !markerMtime) return {
|
|
1867
|
-
manager: params.manager,
|
|
1868
|
-
status: "unknown",
|
|
1869
|
-
lockfilePath,
|
|
1870
|
-
markerPath
|
|
1871
|
-
};
|
|
1872
|
-
if (lockMtime > markerMtime + 1e3) return {
|
|
1873
|
-
manager: params.manager,
|
|
1874
|
-
status: "stale",
|
|
1875
|
-
lockfilePath,
|
|
1876
|
-
markerPath,
|
|
1877
|
-
reason: "lockfile newer than install marker"
|
|
1878
|
-
};
|
|
1879
|
-
return {
|
|
1880
|
-
manager: params.manager,
|
|
1881
|
-
status: "ok",
|
|
1882
|
-
lockfilePath,
|
|
1883
|
-
markerPath
|
|
1884
|
-
};
|
|
1885
|
-
}
|
|
1886
|
-
async function fetchNpmLatestVersion(params) {
|
|
1887
|
-
const res = await fetchNpmTagVersion({
|
|
1888
|
-
tag: "latest",
|
|
1889
|
-
timeoutMs: params?.timeoutMs
|
|
1890
|
-
});
|
|
1891
|
-
return {
|
|
1892
|
-
latestVersion: res.version,
|
|
1893
|
-
error: res.error
|
|
1894
|
-
};
|
|
1895
|
-
}
|
|
1896
|
-
async function fetchNpmTagVersion(params) {
|
|
1897
|
-
const timeoutMs = params?.timeoutMs ?? 3500;
|
|
1898
|
-
const tag = params.tag;
|
|
1899
|
-
try {
|
|
1900
|
-
const res = await fetchWithTimeout(`https://registry.npmjs.org/${encodeURIComponent(NPM_PACKAGE_NAME)}/${encodeURIComponent(tag)}`, {}, Math.max(250, timeoutMs));
|
|
1901
|
-
if (!res.ok) return {
|
|
1902
|
-
tag,
|
|
1903
|
-
version: null,
|
|
1904
|
-
error: `HTTP ${res.status}`
|
|
1905
|
-
};
|
|
1906
|
-
const json = await res.json();
|
|
1907
|
-
return {
|
|
1908
|
-
tag,
|
|
1909
|
-
version: typeof json?.version === "string" ? json.version : null
|
|
1910
|
-
};
|
|
1911
|
-
} catch (err) {
|
|
1912
|
-
return {
|
|
1913
|
-
tag,
|
|
1914
|
-
version: null,
|
|
1915
|
-
error: String(err)
|
|
1916
|
-
};
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
function compareSemverStrings(a, b) {
|
|
1920
|
-
const pa = parseComparableSemver(a);
|
|
1921
|
-
const pb = parseComparableSemver(b);
|
|
1922
|
-
if (!pa || !pb) return null;
|
|
1923
|
-
if (pa.major !== pb.major) return pa.major < pb.major ? -1 : 1;
|
|
1924
|
-
if (pa.minor !== pb.minor) return pa.minor < pb.minor ? -1 : 1;
|
|
1925
|
-
if (pa.patch !== pb.patch) return pa.patch < pb.patch ? -1 : 1;
|
|
1926
|
-
return comparePrerelease(pa.prerelease, pb.prerelease);
|
|
1927
|
-
}
|
|
1928
|
-
function parseComparableSemver(version) {
|
|
1929
|
-
if (!version) return null;
|
|
1930
|
-
const normalized = normalizeLegacyDotBetaVersion(version.trim());
|
|
1931
|
-
const match = /^v?([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(normalized);
|
|
1932
|
-
if (!match) return null;
|
|
1933
|
-
const [, major, minor, patch, prereleaseRaw] = match;
|
|
1934
|
-
if (!major || !minor || !patch) return null;
|
|
1935
|
-
return {
|
|
1936
|
-
major: Number.parseInt(major, 10),
|
|
1937
|
-
minor: Number.parseInt(minor, 10),
|
|
1938
|
-
patch: Number.parseInt(patch, 10),
|
|
1939
|
-
prerelease: prereleaseRaw ? prereleaseRaw.split(".").filter(Boolean) : null
|
|
1940
|
-
};
|
|
1941
|
-
}
|
|
1942
|
-
function normalizeLegacyDotBetaVersion(version) {
|
|
1943
|
-
const trimmed = version.trim();
|
|
1944
|
-
const dotBetaMatch = /^([vV]?[0-9]+\.[0-9]+\.[0-9]+)\.beta(?:\.([0-9A-Za-z.-]+))?$/.exec(trimmed);
|
|
1945
|
-
if (!dotBetaMatch) return trimmed;
|
|
1946
|
-
const base = dotBetaMatch[1];
|
|
1947
|
-
const suffix = dotBetaMatch[2];
|
|
1948
|
-
return suffix ? `${base}-beta.${suffix}` : `${base}-beta`;
|
|
1949
|
-
}
|
|
1950
|
-
function comparePrerelease(a, b) {
|
|
1951
|
-
if (!a?.length && !b?.length) return 0;
|
|
1952
|
-
if (!a?.length) return 1;
|
|
1953
|
-
if (!b?.length) return -1;
|
|
1954
|
-
const max = Math.max(a.length, b.length);
|
|
1955
|
-
for (let i = 0; i < max; i += 1) {
|
|
1956
|
-
const ai = a[i];
|
|
1957
|
-
const bi = b[i];
|
|
1958
|
-
if (ai == null && bi == null) return 0;
|
|
1959
|
-
if (ai == null) return -1;
|
|
1960
|
-
if (bi == null) return 1;
|
|
1961
|
-
if (ai === bi) continue;
|
|
1962
|
-
const aiNumeric = /^[0-9]+$/.test(ai);
|
|
1963
|
-
const biNumeric = /^[0-9]+$/.test(bi);
|
|
1964
|
-
if (aiNumeric && biNumeric) return Number.parseInt(ai, 10) < Number.parseInt(bi, 10) ? -1 : 1;
|
|
1965
|
-
if (aiNumeric && !biNumeric) return -1;
|
|
1966
|
-
if (!aiNumeric && biNumeric) return 1;
|
|
1967
|
-
return ai < bi ? -1 : 1;
|
|
1968
|
-
}
|
|
1969
|
-
return 0;
|
|
1970
|
-
}
|
|
1971
|
-
async function checkUpdateStatus(params) {
|
|
1972
|
-
const timeoutMs = params.timeoutMs ?? 6e3;
|
|
1973
|
-
const root = params.root ? path.resolve(params.root) : null;
|
|
1974
|
-
if (!root) return {
|
|
1975
|
-
root: null,
|
|
1976
|
-
installKind: "unknown",
|
|
1977
|
-
packageManager: "unknown",
|
|
1978
|
-
registry: params.includeRegistry ? await fetchNpmLatestVersion({ timeoutMs }) : void 0
|
|
1979
|
-
};
|
|
1980
|
-
const gitRoot = await detectGitRoot(root);
|
|
1981
|
-
const isGit = gitRoot && path.resolve(gitRoot) === root;
|
|
1982
|
-
const installKind = isGit ? "git" : "package";
|
|
1983
|
-
const pm = (!isGit ? resolveHostedNpmInstallTarget(root) : null) ? "npm" : await detectPackageManager(root);
|
|
1984
|
-
return {
|
|
1985
|
-
root,
|
|
1986
|
-
installKind,
|
|
1987
|
-
packageManager: pm,
|
|
1988
|
-
git: isGit ? await checkGitUpdateStatus({
|
|
1989
|
-
root,
|
|
1990
|
-
timeoutMs,
|
|
1991
|
-
fetch: Boolean(params.fetchGit)
|
|
1992
|
-
}) : void 0,
|
|
1993
|
-
deps: await checkDepsStatus({
|
|
1994
|
-
root,
|
|
1995
|
-
manager: pm
|
|
1996
|
-
}),
|
|
1997
|
-
registry: params.includeRegistry ? await fetchNpmLatestVersion({ timeoutMs }) : void 0
|
|
1998
|
-
};
|
|
1999
|
-
}
|
|
2000
|
-
|
|
2001
|
-
//#endregion
|
|
2002
|
-
//#region src/infra/node-commands.ts
|
|
2003
|
-
const NODE_SYSTEM_RUN_COMMANDS = [
|
|
2004
|
-
"system.run.prepare",
|
|
2005
|
-
"system.run",
|
|
2006
|
-
"system.which"
|
|
2007
|
-
];
|
|
2008
|
-
const NODE_SYSTEM_NOTIFY_COMMAND = "system.notify";
|
|
2009
|
-
const NODE_BROWSER_PROXY_COMMAND = "browser.proxy";
|
|
2010
|
-
|
|
2011
|
-
//#endregion
|
|
2012
|
-
//#region src/gateway/node-command-policy.ts
|
|
2013
|
-
const CANVAS_COMMANDS = [
|
|
2014
|
-
"canvas.present",
|
|
2015
|
-
"canvas.hide",
|
|
2016
|
-
"canvas.navigate",
|
|
2017
|
-
"canvas.eval",
|
|
2018
|
-
"canvas.snapshot",
|
|
2019
|
-
"canvas.a2ui.push",
|
|
2020
|
-
"canvas.a2ui.pushJSONL",
|
|
2021
|
-
"canvas.a2ui.reset"
|
|
2022
|
-
];
|
|
2023
|
-
const CAMERA_COMMANDS = ["camera.list"];
|
|
2024
|
-
const CAMERA_DANGEROUS_COMMANDS = ["camera.snap", "camera.clip"];
|
|
2025
|
-
const SCREEN_DANGEROUS_COMMANDS = ["screen.record"];
|
|
2026
|
-
const LOCATION_COMMANDS = ["location.get"];
|
|
2027
|
-
const ANDROID_NOTIFICATION_COMMANDS = [...["notifications.list"], "notifications.actions"];
|
|
2028
|
-
const DEVICE_COMMANDS = ["device.info", "device.status"];
|
|
2029
|
-
const ANDROID_DEVICE_COMMANDS = [
|
|
2030
|
-
...DEVICE_COMMANDS,
|
|
2031
|
-
"device.permissions",
|
|
2032
|
-
"device.health"
|
|
2033
|
-
];
|
|
2034
|
-
const CONTACTS_COMMANDS = ["contacts.search"];
|
|
2035
|
-
const CONTACTS_DANGEROUS_COMMANDS = ["contacts.add"];
|
|
2036
|
-
const CALENDAR_COMMANDS = ["calendar.events"];
|
|
2037
|
-
const CALENDAR_DANGEROUS_COMMANDS = ["calendar.add"];
|
|
2038
|
-
const REMINDERS_COMMANDS = ["reminders.list"];
|
|
2039
|
-
const REMINDERS_DANGEROUS_COMMANDS = ["reminders.add"];
|
|
2040
|
-
const PHOTOS_COMMANDS = ["photos.latest"];
|
|
2041
|
-
const MOTION_COMMANDS = ["motion.activity", "motion.pedometer"];
|
|
2042
|
-
const SMS_DANGEROUS_COMMANDS = ["sms.send"];
|
|
2043
|
-
const IOS_SYSTEM_COMMANDS = [NODE_SYSTEM_NOTIFY_COMMAND];
|
|
2044
|
-
const SYSTEM_COMMANDS = [
|
|
2045
|
-
...NODE_SYSTEM_RUN_COMMANDS,
|
|
2046
|
-
NODE_SYSTEM_NOTIFY_COMMAND,
|
|
2047
|
-
NODE_BROWSER_PROXY_COMMAND
|
|
2048
|
-
];
|
|
2049
|
-
const DEFAULT_DANGEROUS_NODE_COMMANDS = [
|
|
2050
|
-
...CAMERA_DANGEROUS_COMMANDS,
|
|
2051
|
-
...SCREEN_DANGEROUS_COMMANDS,
|
|
2052
|
-
...CONTACTS_DANGEROUS_COMMANDS,
|
|
2053
|
-
...CALENDAR_DANGEROUS_COMMANDS,
|
|
2054
|
-
...REMINDERS_DANGEROUS_COMMANDS,
|
|
2055
|
-
...SMS_DANGEROUS_COMMANDS
|
|
2056
|
-
];
|
|
2057
|
-
const PLATFORM_DEFAULTS = {
|
|
2058
|
-
ios: [
|
|
2059
|
-
...CANVAS_COMMANDS,
|
|
2060
|
-
...CAMERA_COMMANDS,
|
|
2061
|
-
...LOCATION_COMMANDS,
|
|
2062
|
-
...DEVICE_COMMANDS,
|
|
2063
|
-
...CONTACTS_COMMANDS,
|
|
2064
|
-
...CALENDAR_COMMANDS,
|
|
2065
|
-
...REMINDERS_COMMANDS,
|
|
2066
|
-
...PHOTOS_COMMANDS,
|
|
2067
|
-
...MOTION_COMMANDS,
|
|
2068
|
-
...IOS_SYSTEM_COMMANDS
|
|
2069
|
-
],
|
|
2070
|
-
android: [
|
|
2071
|
-
...CANVAS_COMMANDS,
|
|
2072
|
-
...CAMERA_COMMANDS,
|
|
2073
|
-
...LOCATION_COMMANDS,
|
|
2074
|
-
...ANDROID_NOTIFICATION_COMMANDS,
|
|
2075
|
-
...ANDROID_DEVICE_COMMANDS,
|
|
2076
|
-
...CONTACTS_COMMANDS,
|
|
2077
|
-
...CALENDAR_COMMANDS,
|
|
2078
|
-
...REMINDERS_COMMANDS,
|
|
2079
|
-
...PHOTOS_COMMANDS,
|
|
2080
|
-
...MOTION_COMMANDS
|
|
2081
|
-
],
|
|
2082
|
-
macos: [
|
|
2083
|
-
...CANVAS_COMMANDS,
|
|
2084
|
-
...CAMERA_COMMANDS,
|
|
2085
|
-
...LOCATION_COMMANDS,
|
|
2086
|
-
...DEVICE_COMMANDS,
|
|
2087
|
-
...CONTACTS_COMMANDS,
|
|
2088
|
-
...CALENDAR_COMMANDS,
|
|
2089
|
-
...REMINDERS_COMMANDS,
|
|
2090
|
-
...PHOTOS_COMMANDS,
|
|
2091
|
-
...MOTION_COMMANDS,
|
|
2092
|
-
...SYSTEM_COMMANDS
|
|
2093
|
-
],
|
|
2094
|
-
linux: [...SYSTEM_COMMANDS],
|
|
2095
|
-
windows: [...SYSTEM_COMMANDS],
|
|
2096
|
-
unknown: [
|
|
2097
|
-
...CANVAS_COMMANDS,
|
|
2098
|
-
...CAMERA_COMMANDS,
|
|
2099
|
-
...LOCATION_COMMANDS,
|
|
2100
|
-
...SYSTEM_COMMANDS
|
|
2101
|
-
]
|
|
2102
|
-
};
|
|
2103
|
-
|
|
2104
|
-
//#endregion
|
|
2105
|
-
//#region src/gateway/server-startup-trace.ts
|
|
2106
|
-
let lastGatewayStartupTraceSnapshot = null;
|
|
2107
|
-
function getLastGatewayStartupTraceSnapshot() {
|
|
2108
|
-
if (!lastGatewayStartupTraceSnapshot) return null;
|
|
2109
|
-
return {
|
|
2110
|
-
...lastGatewayStartupTraceSnapshot,
|
|
2111
|
-
entries: lastGatewayStartupTraceSnapshot.entries.map((entry) => ({ ...entry }))
|
|
2112
|
-
};
|
|
2113
|
-
}
|
|
2114
|
-
|
|
2115
|
-
//#endregion
|
|
2116
|
-
//#region src/infra/heartbeat-events-filter.ts
|
|
2117
|
-
const HEARTBEAT_OK_PREFIX = HEARTBEAT_TOKEN.toLowerCase();
|
|
2118
|
-
|
|
2119
|
-
//#endregion
|
|
2120
|
-
//#region src/infra/heartbeat-runner.ts
|
|
2121
|
-
const log = createSubsystemLogger("gateway/heartbeat");
|
|
2122
|
-
const DEFAULT_HEARTBEAT_TARGET = "none";
|
|
2123
|
-
function hasExplicitHeartbeatAgents(cfg) {
|
|
2124
|
-
return (cfg.agents?.list ?? []).some((entry) => Boolean(entry?.heartbeat));
|
|
2125
|
-
}
|
|
2126
|
-
function isHeartbeatEnabledForAgent(cfg, agentId) {
|
|
2127
|
-
const resolvedAgentId = normalizeAgentId(agentId ?? resolveDefaultAgentId(cfg));
|
|
2128
|
-
const list = cfg.agents?.list ?? [];
|
|
2129
|
-
if (hasExplicitHeartbeatAgents(cfg)) return list.some((entry) => Boolean(entry?.heartbeat) && normalizeAgentId(entry?.id) === resolvedAgentId);
|
|
2130
|
-
return resolvedAgentId === resolveDefaultAgentId(cfg);
|
|
2131
|
-
}
|
|
2132
|
-
function resolveHeartbeatSummaryForAgent(cfg, agentId) {
|
|
2133
|
-
const defaults = cfg.agents?.defaults?.heartbeat;
|
|
2134
|
-
const overrides = agentId ? resolveAgentConfig(cfg, agentId)?.heartbeat : void 0;
|
|
2135
|
-
if (!isHeartbeatEnabledForAgent(cfg, agentId)) return {
|
|
2136
|
-
enabled: false,
|
|
2137
|
-
every: "disabled",
|
|
2138
|
-
everyMs: null,
|
|
2139
|
-
prompt: resolveHeartbeatPrompt(defaults?.prompt),
|
|
2140
|
-
target: defaults?.target ?? DEFAULT_HEARTBEAT_TARGET,
|
|
2141
|
-
model: defaults?.model,
|
|
2142
|
-
ackMaxChars: Math.max(0, defaults?.ackMaxChars ?? DEFAULT_HEARTBEAT_ACK_MAX_CHARS)
|
|
2143
|
-
};
|
|
2144
|
-
const merged = defaults || overrides ? {
|
|
2145
|
-
...defaults,
|
|
2146
|
-
...overrides
|
|
2147
|
-
} : void 0;
|
|
2148
|
-
return {
|
|
2149
|
-
enabled: true,
|
|
2150
|
-
every: merged?.every ?? defaults?.every ?? overrides?.every ?? DEFAULT_HEARTBEAT_EVERY,
|
|
2151
|
-
everyMs: resolveHeartbeatIntervalMs(cfg, void 0, merged),
|
|
2152
|
-
prompt: resolveHeartbeatPrompt(merged?.prompt ?? defaults?.prompt ?? overrides?.prompt),
|
|
2153
|
-
target: merged?.target ?? defaults?.target ?? overrides?.target ?? DEFAULT_HEARTBEAT_TARGET,
|
|
2154
|
-
model: merged?.model ?? defaults?.model ?? overrides?.model,
|
|
2155
|
-
ackMaxChars: Math.max(0, merged?.ackMaxChars ?? defaults?.ackMaxChars ?? overrides?.ackMaxChars ?? DEFAULT_HEARTBEAT_ACK_MAX_CHARS)
|
|
2156
|
-
};
|
|
2157
|
-
}
|
|
2158
|
-
function resolveHeartbeatIntervalMs(cfg, overrideEvery, heartbeat) {
|
|
2159
|
-
const raw = overrideEvery ?? heartbeat?.every ?? cfg.agents?.defaults?.heartbeat?.every ?? DEFAULT_HEARTBEAT_EVERY;
|
|
2160
|
-
if (!raw) return null;
|
|
2161
|
-
const trimmed = String(raw).trim();
|
|
2162
|
-
if (!trimmed) return null;
|
|
2163
|
-
let ms;
|
|
2164
|
-
try {
|
|
2165
|
-
ms = parseDurationMs(trimmed, { defaultUnit: "m" });
|
|
2166
|
-
} catch {
|
|
2167
|
-
return null;
|
|
2168
|
-
}
|
|
2169
|
-
if (ms <= 0) return null;
|
|
2170
|
-
return ms;
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
|
-
//#endregion
|
|
2174
|
-
//#region src/infra/brew.ts
|
|
2175
|
-
function isExecutable(filePath) {
|
|
2176
|
-
try {
|
|
2177
|
-
fs.accessSync(filePath, fs.constants.X_OK);
|
|
2178
|
-
return true;
|
|
2179
|
-
} catch {
|
|
2180
|
-
return false;
|
|
2181
|
-
}
|
|
2182
|
-
}
|
|
2183
|
-
function normalizePathValue(value) {
|
|
2184
|
-
if (typeof value !== "string") return;
|
|
2185
|
-
const trimmed = value.trim();
|
|
2186
|
-
return trimmed ? trimmed : void 0;
|
|
2187
|
-
}
|
|
2188
|
-
function resolveBrewExecutable(opts) {
|
|
2189
|
-
const homeDir = opts?.homeDir ?? os.homedir();
|
|
2190
|
-
const env = opts?.env ?? process.env;
|
|
2191
|
-
const candidates = [];
|
|
2192
|
-
const brewFile = normalizePathValue(env.HOMEBREW_BREW_FILE);
|
|
2193
|
-
if (brewFile) candidates.push(brewFile);
|
|
2194
|
-
const prefix = normalizePathValue(env.HOMEBREW_PREFIX);
|
|
2195
|
-
if (prefix) candidates.push(path.join(prefix, "bin", "brew"));
|
|
2196
|
-
candidates.push(path.join(homeDir, ".linuxbrew", "bin", "brew"));
|
|
2197
|
-
candidates.push("/home/linuxbrew/.linuxbrew/bin/brew");
|
|
2198
|
-
candidates.push("/opt/homebrew/bin/brew", "/usr/local/bin/brew");
|
|
2199
|
-
for (const candidate of candidates) if (isExecutable(candidate)) return candidate;
|
|
2200
|
-
}
|
|
2201
|
-
|
|
2202
|
-
//#endregion
|
|
2203
|
-
//#region src/agents/skills/bundled-context.ts
|
|
2204
|
-
const skillsLogger = createSubsystemLogger("skills");
|
|
2205
|
-
|
|
2206
|
-
//#endregion
|
|
2207
|
-
//#region src/daemon/systemd-system.ts
|
|
2208
|
-
const SERVICE_NAME = "fased-gateway";
|
|
2209
|
-
const SYSTEM_UNIT_CANDIDATES = [
|
|
2210
|
-
`/etc/systemd/system/${SERVICE_NAME}.service`,
|
|
2211
|
-
`/usr/lib/systemd/system/${SERVICE_NAME}.service`,
|
|
2212
|
-
`/lib/systemd/system/${SERVICE_NAME}.service`
|
|
2213
|
-
];
|
|
2214
|
-
|
|
2215
|
-
//#endregion
|
|
2216
|
-
//#region src/commands/status.acpx-bridge.ts
|
|
2217
|
-
const ACPX_PUSH_TEST_TOOL_NAME = "fased_push_test_request";
|
|
2218
|
-
const VALID_MODES = new Set([
|
|
2219
|
-
"status-only",
|
|
2220
|
-
"read-only-tools",
|
|
2221
|
-
"operator-approved-mutating-tools"
|
|
2222
|
-
]);
|
|
2223
|
-
function asRecord(value) {
|
|
2224
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2225
|
-
}
|
|
2226
|
-
function readToolList(value) {
|
|
2227
|
-
if (!Array.isArray(value)) return [];
|
|
2228
|
-
return [...new Set(value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter((entry) => entry.length > 0))].toSorted((left, right) => left.localeCompare(right));
|
|
2229
|
-
}
|
|
2230
|
-
function readConfiguredMode(value) {
|
|
2231
|
-
return typeof value === "string" && VALID_MODES.has(value) ? value : "status-only";
|
|
2232
|
-
}
|
|
2233
|
-
function resolveAcpxMcpBridgeStatus(cfg) {
|
|
2234
|
-
const pluginEntry = cfg.plugins?.entries?.acpx;
|
|
2235
|
-
const pluginEnabled = cfg.plugins?.enabled !== false && pluginEntry?.enabled !== false;
|
|
2236
|
-
const bridgeConfig = asRecord(asRecord(pluginEntry?.config).mcpBridge);
|
|
2237
|
-
const configuredMode = readConfiguredMode(bridgeConfig.mode);
|
|
2238
|
-
const bridgeEnabled = pluginEnabled && bridgeConfig.enabled === true;
|
|
2239
|
-
const allowTools = readToolList(bridgeConfig.allowTools);
|
|
2240
|
-
const denyTools = readToolList(bridgeConfig.denyTools);
|
|
2241
|
-
const denied = denyTools.includes(ACPX_PUSH_TEST_TOOL_NAME);
|
|
2242
|
-
const allowed = allowTools.includes(ACPX_PUSH_TEST_TOOL_NAME);
|
|
2243
|
-
const enabled = bridgeEnabled && configuredMode === "operator-approved-mutating-tools" && allowed && !denied;
|
|
2244
|
-
const reason = (() => {
|
|
2245
|
-
if (!bridgeEnabled) return "mcpBridge.disabled";
|
|
2246
|
-
if (configuredMode !== "operator-approved-mutating-tools") return "mcpBridge.not-mutating-mode";
|
|
2247
|
-
if (denied) return "mcpBridge.denyTools";
|
|
2248
|
-
if (!allowed) return "mcpBridge.allowTools";
|
|
2249
|
-
return "enabled";
|
|
2250
|
-
})();
|
|
2251
|
-
return {
|
|
2252
|
-
pluginId: "acpx",
|
|
2253
|
-
enabled: bridgeEnabled,
|
|
2254
|
-
mode: bridgeEnabled ? configuredMode : "disabled",
|
|
2255
|
-
configuredMode,
|
|
2256
|
-
allowTools,
|
|
2257
|
-
denyTools,
|
|
2258
|
-
fasedPushTestRequest: {
|
|
2259
|
-
toolName: ACPX_PUSH_TEST_TOOL_NAME,
|
|
2260
|
-
enabled,
|
|
2261
|
-
allowed,
|
|
2262
|
-
denied,
|
|
2263
|
-
reason
|
|
2264
|
-
}
|
|
2265
|
-
};
|
|
2266
|
-
}
|
|
2267
|
-
|
|
2268
|
-
//#endregion
|
|
2269
|
-
//#region src/commands/channel-account-context.ts
|
|
2270
|
-
async function resolveDefaultChannelAccountContext(plugin, cfg) {
|
|
2271
|
-
const accountIds = plugin.config.listAccountIds(cfg);
|
|
2272
|
-
const defaultAccountId = resolveChannelDefaultAccountId({
|
|
2273
|
-
plugin,
|
|
2274
|
-
cfg,
|
|
2275
|
-
accountIds
|
|
2276
|
-
});
|
|
2277
|
-
const account = plugin.config.resolveAccount(cfg, defaultAccountId);
|
|
2278
|
-
return {
|
|
2279
|
-
accountIds,
|
|
2280
|
-
defaultAccountId,
|
|
2281
|
-
account,
|
|
2282
|
-
enabled: plugin.config.isEnabled ? plugin.config.isEnabled(account, cfg) : true,
|
|
2283
|
-
configured: plugin.config.isConfigured ? await plugin.config.isConfigured(account, cfg) : true
|
|
2284
|
-
};
|
|
2285
|
-
}
|
|
2286
|
-
|
|
2287
|
-
//#endregion
|
|
2288
|
-
//#region src/commands/status.link-channel.ts
|
|
2289
|
-
async function resolveLinkChannelContext(cfg) {
|
|
2290
|
-
for (const plugin of listChannelPlugins()) {
|
|
2291
|
-
const { defaultAccountId, account, enabled, configured } = await resolveDefaultChannelAccountContext(plugin, cfg);
|
|
2292
|
-
const snapshot = plugin.config.describeAccount ? plugin.config.describeAccount(account, cfg) : {
|
|
2293
|
-
accountId: defaultAccountId,
|
|
2294
|
-
enabled,
|
|
2295
|
-
configured
|
|
2296
|
-
};
|
|
2297
|
-
const summaryRecord = plugin.status?.buildChannelSummary ? await plugin.status.buildChannelSummary({
|
|
2298
|
-
account,
|
|
2299
|
-
cfg,
|
|
2300
|
-
defaultAccountId,
|
|
2301
|
-
snapshot
|
|
2302
|
-
}) : void 0;
|
|
2303
|
-
const linked = summaryRecord && typeof summaryRecord.linked === "boolean" ? summaryRecord.linked : null;
|
|
2304
|
-
if (linked === null) continue;
|
|
2305
|
-
return {
|
|
2306
|
-
linked,
|
|
2307
|
-
authAgeMs: summaryRecord && typeof summaryRecord.authAgeMs === "number" ? summaryRecord.authAgeMs : null,
|
|
2308
|
-
account,
|
|
2309
|
-
accountId: defaultAccountId,
|
|
2310
|
-
plugin
|
|
2311
|
-
};
|
|
2312
|
-
}
|
|
2313
|
-
return null;
|
|
2314
|
-
}
|
|
2315
|
-
|
|
2316
|
-
//#endregion
|
|
2317
|
-
//#region src/commands/status.strict-agentic.ts
|
|
2318
|
-
function hasMode(value) {
|
|
2319
|
-
return value === "off" || value === "warn";
|
|
2320
|
-
}
|
|
2321
|
-
function resolveEnvironmentMode(env) {
|
|
2322
|
-
return resolveStrictAgenticRuntimeMode(env) === "warn" ? "warn" : "off";
|
|
2323
|
-
}
|
|
2324
|
-
function resolveStrictAgenticPolicyStatus(cfg, agentIds, env = process.env) {
|
|
2325
|
-
const defaultConfigMode = cfg.agents?.defaults?.strictAgentic?.mode;
|
|
2326
|
-
const envFlagSet = Boolean(env.FASED_STRICT_AGENTIC_MODE?.trim());
|
|
2327
|
-
const fallbackMode = resolveEnvironmentMode(env);
|
|
2328
|
-
const defaultMode = hasMode(defaultConfigMode) ? defaultConfigMode : fallbackMode;
|
|
2329
|
-
const defaultSource = hasMode(defaultConfigMode) ? "default-config" : envFlagSet ? "environment" : "implicit-default";
|
|
2330
|
-
const configuredAgents = new Map((cfg.agents?.list ?? []).map((agent) => [agent.id.trim(), hasMode(agent.strictAgentic?.mode) ? agent.strictAgentic.mode : void 0]));
|
|
2331
|
-
const agents = [...new Set(agentIds.map((agentId) => agentId.trim()).filter(Boolean))].toSorted((left, right) => left.localeCompare(right)).map((agentId) => {
|
|
2332
|
-
const agentMode = configuredAgents.get(agentId);
|
|
2333
|
-
const override = hasMode(agentMode);
|
|
2334
|
-
return {
|
|
2335
|
-
agentId,
|
|
2336
|
-
mode: override ? agentMode : defaultMode,
|
|
2337
|
-
source: override ? "agent-config" : defaultSource,
|
|
2338
|
-
override
|
|
2339
|
-
};
|
|
2340
|
-
});
|
|
2341
|
-
return {
|
|
2342
|
-
mode: defaultMode,
|
|
2343
|
-
source: defaultSource,
|
|
2344
|
-
envFlagSet,
|
|
2345
|
-
enforcementAvailable: false,
|
|
2346
|
-
warningAgents: agents.filter((agent) => agent.mode === "warn").length,
|
|
2347
|
-
totalAgents: agents.length,
|
|
2348
|
-
agents
|
|
2349
|
-
};
|
|
2350
|
-
}
|
|
2351
|
-
|
|
2352
|
-
//#endregion
|
|
2353
|
-
//#region src/commands/status.summary.ts
|
|
2354
|
-
const buildFlags = (entry) => {
|
|
2355
|
-
if (!entry) return [];
|
|
2356
|
-
const flags = [];
|
|
2357
|
-
const think = entry?.thinkingLevel;
|
|
2358
|
-
if (typeof think === "string" && think.length > 0) flags.push(`think:${think}`);
|
|
2359
|
-
const verbose = entry?.verboseLevel;
|
|
2360
|
-
if (typeof verbose === "string" && verbose.length > 0) flags.push(`verbose:${verbose}`);
|
|
2361
|
-
const reasoning = entry?.reasoningLevel;
|
|
2362
|
-
if (typeof reasoning === "string" && reasoning.length > 0) flags.push(`reasoning:${reasoning}`);
|
|
2363
|
-
const elevated = entry?.elevatedLevel;
|
|
2364
|
-
if (typeof elevated === "string" && elevated.length > 0) flags.push(`elevated:${elevated}`);
|
|
2365
|
-
if (entry?.systemSent) flags.push("system");
|
|
2366
|
-
if (entry?.abortedLastRun) flags.push("aborted");
|
|
2367
|
-
const sessionId = entry?.sessionId;
|
|
2368
|
-
if (typeof sessionId === "string" && sessionId.length > 0) flags.push(`id:${sessionId}`);
|
|
2369
|
-
return flags;
|
|
2370
|
-
};
|
|
2371
|
-
function redactSensitiveStatusSummary(summary) {
|
|
2372
|
-
return {
|
|
2373
|
-
...summary,
|
|
2374
|
-
sessions: {
|
|
2375
|
-
...summary.sessions,
|
|
2376
|
-
paths: [],
|
|
2377
|
-
defaults: {
|
|
2378
|
-
model: null,
|
|
2379
|
-
contextTokens: null
|
|
2380
|
-
},
|
|
2381
|
-
recent: [],
|
|
2382
|
-
byAgent: summary.sessions.byAgent.map((entry) => ({
|
|
2383
|
-
...entry,
|
|
2384
|
-
path: "[redacted]",
|
|
2385
|
-
recent: []
|
|
2386
|
-
}))
|
|
2387
|
-
}
|
|
2388
|
-
};
|
|
2389
|
-
}
|
|
2390
|
-
async function getStatusSummary(options = {}) {
|
|
2391
|
-
const { includeSensitive = true } = options;
|
|
2392
|
-
const cfg = loadConfig();
|
|
2393
|
-
const linkContext = await resolveLinkChannelContext(cfg);
|
|
2394
|
-
const agentList = listAgentsForGateway(cfg);
|
|
2395
|
-
const heartbeatAgents = agentList.agents.map((agent) => {
|
|
2396
|
-
const summary = resolveHeartbeatSummaryForAgent(cfg, agent.id);
|
|
2397
|
-
return {
|
|
2398
|
-
agentId: agent.id,
|
|
2399
|
-
enabled: summary.enabled,
|
|
2400
|
-
every: summary.every,
|
|
2401
|
-
everyMs: summary.everyMs
|
|
2402
|
-
};
|
|
2403
|
-
});
|
|
2404
|
-
const channelSummary = await buildChannelSummary(cfg, {
|
|
2405
|
-
colorize: true,
|
|
2406
|
-
includeAllowFrom: true
|
|
2407
|
-
});
|
|
2408
|
-
const queuedSystemEvents = peekSystemEvents(resolveMainSessionKey(cfg));
|
|
2409
|
-
const resolved = resolveConfiguredModelRef({
|
|
2410
|
-
cfg,
|
|
2411
|
-
defaultProvider: DEFAULT_PROVIDER,
|
|
2412
|
-
defaultModel: DEFAULT_MODEL
|
|
2413
|
-
});
|
|
2414
|
-
const configModel = resolved.model ?? DEFAULT_MODEL;
|
|
2415
|
-
const configContextTokens = resolveContextTokensForModel({
|
|
2416
|
-
cfg,
|
|
2417
|
-
provider: resolved.provider ?? DEFAULT_PROVIDER,
|
|
2418
|
-
model: configModel,
|
|
2419
|
-
contextTokensOverride: cfg.agents?.defaults?.contextTokens,
|
|
2420
|
-
fallbackContextTokens: DEFAULT_CONTEXT_TOKENS
|
|
2421
|
-
}) ?? DEFAULT_CONTEXT_TOKENS;
|
|
2422
|
-
const now = Date.now();
|
|
2423
|
-
const storeCache = /* @__PURE__ */ new Map();
|
|
2424
|
-
const loadStore = (storePath) => {
|
|
2425
|
-
const cached = storeCache.get(storePath);
|
|
2426
|
-
if (cached) return cached;
|
|
2427
|
-
const store = loadSessionStore(storePath);
|
|
2428
|
-
storeCache.set(storePath, store);
|
|
2429
|
-
return store;
|
|
2430
|
-
};
|
|
2431
|
-
const buildSessionRows = (store, opts = {}) => Object.entries(store).filter(([key]) => key !== "global" && key !== "unknown").map(([key, entry]) => {
|
|
2432
|
-
const updatedAt = entry?.updatedAt ?? null;
|
|
2433
|
-
const age = updatedAt ? now - updatedAt : null;
|
|
2434
|
-
const resolvedModel = resolveSessionModelRef(cfg, entry, opts.agentIdOverride);
|
|
2435
|
-
const model = resolvedModel.model ?? configModel ?? null;
|
|
2436
|
-
const contextTokens = resolveContextTokensForModel({
|
|
2437
|
-
cfg,
|
|
2438
|
-
provider: resolvedModel.provider,
|
|
2439
|
-
model,
|
|
2440
|
-
contextTokensOverride: entry?.contextTokens,
|
|
2441
|
-
fallbackContextTokens: configContextTokens ?? void 0
|
|
2442
|
-
}) ?? null;
|
|
2443
|
-
const total = resolveFreshSessionTotalTokens(entry);
|
|
2444
|
-
const totalTokensFresh = typeof entry?.totalTokens === "number" ? entry?.totalTokensFresh !== false : false;
|
|
2445
|
-
const remaining = contextTokens != null && total !== void 0 ? Math.max(0, contextTokens - total) : null;
|
|
2446
|
-
const pct = contextTokens && contextTokens > 0 && total !== void 0 ? Math.min(999, Math.round(total / contextTokens * 100)) : null;
|
|
2447
|
-
const parsedAgentId = parseAgentSessionKey(key)?.agentId;
|
|
2448
|
-
return {
|
|
2449
|
-
agentId: opts.agentIdOverride ?? parsedAgentId,
|
|
2450
|
-
key,
|
|
2451
|
-
kind: classifySessionKey(key, entry),
|
|
2452
|
-
sessionId: entry?.sessionId,
|
|
2453
|
-
updatedAt,
|
|
2454
|
-
age,
|
|
2455
|
-
thinkingLevel: entry?.thinkingLevel,
|
|
2456
|
-
verboseLevel: entry?.verboseLevel,
|
|
2457
|
-
reasoningLevel: entry?.reasoningLevel,
|
|
2458
|
-
elevatedLevel: entry?.elevatedLevel,
|
|
2459
|
-
systemSent: entry?.systemSent,
|
|
2460
|
-
abortedLastRun: entry?.abortedLastRun,
|
|
2461
|
-
inputTokens: entry?.inputTokens,
|
|
2462
|
-
outputTokens: entry?.outputTokens,
|
|
2463
|
-
cacheRead: entry?.cacheRead,
|
|
2464
|
-
cacheWrite: entry?.cacheWrite,
|
|
2465
|
-
totalTokens: total ?? null,
|
|
2466
|
-
totalTokensFresh,
|
|
2467
|
-
remainingTokens: remaining,
|
|
2468
|
-
percentUsed: pct,
|
|
2469
|
-
model,
|
|
2470
|
-
contextTokens,
|
|
2471
|
-
flags: buildFlags(entry)
|
|
2472
|
-
};
|
|
2473
|
-
}).sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
|
|
2474
|
-
const paths = /* @__PURE__ */ new Set();
|
|
2475
|
-
const byAgent = agentList.agents.map((agent) => {
|
|
2476
|
-
const storePath = resolveStorePath(cfg.session?.store, { agentId: agent.id });
|
|
2477
|
-
paths.add(storePath);
|
|
2478
|
-
const sessions = buildSessionRows(loadStore(storePath), { agentIdOverride: agent.id });
|
|
2479
|
-
return {
|
|
2480
|
-
agentId: agent.id,
|
|
2481
|
-
path: storePath,
|
|
2482
|
-
count: sessions.length,
|
|
2483
|
-
recent: sessions.slice(0, 10)
|
|
2484
|
-
};
|
|
2485
|
-
});
|
|
2486
|
-
const allSessions = Array.from(paths).flatMap((storePath) => buildSessionRows(loadStore(storePath))).toSorted((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
|
|
2487
|
-
const recent = allSessions.slice(0, 10);
|
|
2488
|
-
const totalSessions = allSessions.length;
|
|
2489
|
-
const summary = {
|
|
2490
|
-
gatewayStartup: getLastGatewayStartupTraceSnapshot(),
|
|
2491
|
-
acpxMcpBridge: resolveAcpxMcpBridgeStatus(cfg),
|
|
2492
|
-
strictAgentic: resolveStrictAgenticPolicyStatus(cfg, agentList.agents.map((agent) => agent.id)),
|
|
2493
|
-
linkChannel: linkContext ? {
|
|
2494
|
-
id: linkContext.plugin.id,
|
|
2495
|
-
label: linkContext.plugin.meta.label ?? "Channel",
|
|
2496
|
-
linked: linkContext.linked,
|
|
2497
|
-
authAgeMs: linkContext.authAgeMs
|
|
2498
|
-
} : void 0,
|
|
2499
|
-
heartbeat: {
|
|
2500
|
-
defaultAgentId: agentList.defaultId,
|
|
2501
|
-
agents: heartbeatAgents
|
|
2502
|
-
},
|
|
2503
|
-
channelSummary,
|
|
2504
|
-
queuedSystemEvents,
|
|
2505
|
-
sessions: {
|
|
2506
|
-
paths: Array.from(paths),
|
|
2507
|
-
count: totalSessions,
|
|
2508
|
-
defaults: {
|
|
2509
|
-
model: configModel ?? null,
|
|
2510
|
-
contextTokens: configContextTokens ?? null
|
|
2511
|
-
},
|
|
2512
|
-
recent,
|
|
2513
|
-
byAgent
|
|
2514
|
-
}
|
|
2515
|
-
};
|
|
2516
|
-
return includeSensitive ? summary : redactSensitiveStatusSummary(summary);
|
|
2517
|
-
}
|
|
2518
|
-
|
|
2519
|
-
//#endregion
|
|
2520
|
-
//#region src/commands/status.update.ts
|
|
2521
|
-
function resolveUpdateAvailability(update, opts = {}) {
|
|
2522
|
-
const latestVersion = update.registry?.latestVersion ?? null;
|
|
2523
|
-
const registryCmp = latestVersion ? compareSemverStrings(VERSION, latestVersion) : null;
|
|
2524
|
-
const hasRegistryUpdate = registryCmp != null && registryCmp < 0;
|
|
2525
|
-
const gitBehind = (opts.channel == null || opts.channel === "dev") && update.installKind === "git" && typeof update.git?.behind === "number" ? update.git.behind : null;
|
|
2526
|
-
const hasGitUpdate = gitBehind != null && gitBehind > 0;
|
|
2527
|
-
return {
|
|
2528
|
-
available: hasGitUpdate || hasRegistryUpdate,
|
|
2529
|
-
hasGitUpdate,
|
|
2530
|
-
hasRegistryUpdate,
|
|
2531
|
-
latestVersion: hasRegistryUpdate ? latestVersion : null,
|
|
2532
|
-
gitBehind
|
|
2533
|
-
};
|
|
2534
|
-
}
|
|
2535
|
-
function formatUpdateOneLiner(update, opts = {}) {
|
|
2536
|
-
const parts = [];
|
|
2537
|
-
const appendRegistryUpdateSummary = () => {
|
|
2538
|
-
if (update.registry?.latestVersion) {
|
|
2539
|
-
const cmp = compareSemverStrings(VERSION, update.registry.latestVersion);
|
|
2540
|
-
if (cmp === 0) parts.push(`npm latest ${update.registry.latestVersion}`);
|
|
2541
|
-
else if (cmp != null && cmp < 0) parts.push(`npm update ${update.registry.latestVersion}`);
|
|
2542
|
-
else parts.push(`npm latest ${update.registry.latestVersion} (local newer)`);
|
|
2543
|
-
return;
|
|
2544
|
-
}
|
|
2545
|
-
if (update.registry?.error) parts.push("npm latest unknown");
|
|
2546
|
-
};
|
|
2547
|
-
if (update.installKind === "git" && update.git) {
|
|
2548
|
-
if (opts.channel && opts.channel !== "dev") {
|
|
2549
|
-
parts.push(`${opts.channel} channel`);
|
|
2550
|
-
if (update.git.dirty === true) parts.push("dirty");
|
|
2551
|
-
} else {
|
|
2552
|
-
const branch = update.git.branch ? `git ${update.git.branch}` : "git";
|
|
2553
|
-
parts.push(branch);
|
|
2554
|
-
if (update.git.upstream) parts.push(`↔ ${update.git.upstream}`);
|
|
2555
|
-
if (update.git.dirty === true) parts.push("dirty");
|
|
2556
|
-
if (update.git.behind != null && update.git.ahead != null) {
|
|
2557
|
-
if (update.git.behind === 0 && update.git.ahead === 0) parts.push("up to date");
|
|
2558
|
-
else if (update.git.behind > 0 && update.git.ahead === 0) parts.push(`behind ${update.git.behind}`);
|
|
2559
|
-
else if (update.git.behind === 0 && update.git.ahead > 0) parts.push(`ahead ${update.git.ahead}`);
|
|
2560
|
-
else if (update.git.behind > 0 && update.git.ahead > 0) parts.push(`diverged (ahead ${update.git.ahead}, behind ${update.git.behind})`);
|
|
2561
|
-
}
|
|
2562
|
-
if (update.git.fetchOk === false) parts.push("fetch failed");
|
|
2563
|
-
}
|
|
2564
|
-
appendRegistryUpdateSummary();
|
|
2565
|
-
} else {
|
|
2566
|
-
parts.push(update.packageManager !== "unknown" ? update.packageManager : "pkg");
|
|
2567
|
-
appendRegistryUpdateSummary();
|
|
2568
|
-
}
|
|
2569
|
-
if (update.deps) {
|
|
2570
|
-
if (update.deps.status === "ok") parts.push("deps ok");
|
|
2571
|
-
if (update.deps.status === "missing") parts.push("deps missing");
|
|
2572
|
-
if (update.deps.status === "stale") parts.push("deps stale");
|
|
2573
|
-
}
|
|
2574
|
-
return `Update: ${parts.join(" · ")}`;
|
|
2575
|
-
}
|
|
2576
|
-
|
|
2577
|
-
//#endregion
|
|
2578
|
-
//#region src/gateway/server-model-catalog.ts
|
|
2579
|
-
function createGatewayModelCatalogCache() {
|
|
2580
|
-
return {
|
|
2581
|
-
lastSuccessfulCatalog: null,
|
|
2582
|
-
inFlightRefresh: null,
|
|
2583
|
-
staleGeneration: 0,
|
|
2584
|
-
appliedGeneration: 0
|
|
2585
|
-
};
|
|
2586
|
-
}
|
|
2587
|
-
const readOnlyModelCatalogCache = createGatewayModelCatalogCache();
|
|
2588
|
-
const fullModelCatalogCache = createGatewayModelCatalogCache();
|
|
2589
|
-
function isGatewayModelCatalogStale(cache) {
|
|
2590
|
-
return cache.appliedGeneration < cache.staleGeneration;
|
|
2591
|
-
}
|
|
2592
|
-
function resolveGatewayModelCatalogCache(params) {
|
|
2593
|
-
return params?.readOnly === false ? fullModelCatalogCache : readOnlyModelCatalogCache;
|
|
2594
|
-
}
|
|
2595
|
-
function resolveLoadModelCatalog(params) {
|
|
2596
|
-
return params?.loadModelCatalog ?? loadModelCatalog;
|
|
2597
|
-
}
|
|
2598
|
-
function startGatewayModelCatalogRefresh(params) {
|
|
2599
|
-
const cache = resolveGatewayModelCatalogCache(params);
|
|
2600
|
-
const config = (params?.getConfig ?? loadConfig)();
|
|
2601
|
-
const readOnly = params?.readOnly !== false;
|
|
2602
|
-
const refreshGeneration = cache.staleGeneration;
|
|
2603
|
-
const refresh = resolveLoadModelCatalog(params)({
|
|
2604
|
-
config,
|
|
2605
|
-
readOnly,
|
|
2606
|
-
useCache: false
|
|
2607
|
-
}).then((catalog) => {
|
|
2608
|
-
if ((readOnly || catalog.length > 0) && refreshGeneration === cache.staleGeneration) {
|
|
2609
|
-
cache.lastSuccessfulCatalog = catalog;
|
|
2610
|
-
cache.appliedGeneration = cache.staleGeneration;
|
|
2611
|
-
}
|
|
2612
|
-
return catalog;
|
|
2613
|
-
}).finally(() => {
|
|
2614
|
-
if (cache.inFlightRefresh === refresh) cache.inFlightRefresh = null;
|
|
2615
|
-
});
|
|
2616
|
-
cache.inFlightRefresh = refresh;
|
|
2617
|
-
return refresh;
|
|
2618
|
-
}
|
|
2619
|
-
async function loadGatewayModelCatalog(params) {
|
|
2620
|
-
const cache = resolveGatewayModelCatalogCache(params);
|
|
2621
|
-
if (!isGatewayModelCatalogStale(cache) && cache.lastSuccessfulCatalog !== null) return cache.lastSuccessfulCatalog;
|
|
2622
|
-
if (isGatewayModelCatalogStale(cache) && cache.lastSuccessfulCatalog !== null) {
|
|
2623
|
-
if (!cache.inFlightRefresh) startGatewayModelCatalogRefresh(params).catch(() => void 0);
|
|
2624
|
-
return cache.lastSuccessfulCatalog;
|
|
2625
|
-
}
|
|
2626
|
-
if (cache.inFlightRefresh) return await cache.inFlightRefresh;
|
|
2627
|
-
return await startGatewayModelCatalogRefresh(params);
|
|
2628
|
-
}
|
|
2629
|
-
|
|
2630
|
-
//#endregion
|
|
2631
|
-
//#region src/agents/provider-extension-catalog-manifest.ts
|
|
2632
|
-
const FASED_PROVIDER_EXTENSION_CATALOG_MANIFEST = [
|
|
2633
|
-
{
|
|
2634
|
-
upstreamProviderId: "anthropic",
|
|
2635
|
-
upstreamCatalogPaths: ["extensions/anthropic/provider-discovery.ts"],
|
|
2636
|
-
status: "mapped",
|
|
2637
|
-
fasedProviderIds: ["anthropic"],
|
|
2638
|
-
docsPath: "docs/providers/anthropic.md",
|
|
2639
|
-
authSurface: "api-key",
|
|
2640
|
-
authChoiceIds: ["apiKey", "token"]
|
|
2641
|
-
},
|
|
2642
|
-
{
|
|
2643
|
-
upstreamProviderId: "byteplus",
|
|
2644
|
-
upstreamCatalogPaths: ["extensions/byteplus/provider-catalog.ts", "extensions/byteplus/provider-discovery.ts"],
|
|
2645
|
-
status: "mapped",
|
|
2646
|
-
fasedProviderIds: [
|
|
2647
|
-
"byteplus",
|
|
2648
|
-
"byteplus-coding",
|
|
2649
|
-
"byteplus-plan"
|
|
2650
|
-
],
|
|
2651
|
-
docsPath: "docs/providers/volcengine.md",
|
|
2652
|
-
authSurface: "api-key",
|
|
2653
|
-
authChoiceIds: ["byteplus-api-key"]
|
|
2654
|
-
},
|
|
2655
|
-
{
|
|
2656
|
-
upstreamProviderId: "chutes",
|
|
2657
|
-
upstreamCatalogPaths: ["extensions/chutes/provider-catalog.ts"],
|
|
2658
|
-
status: "mapped",
|
|
2659
|
-
fasedProviderIds: ["chutes"],
|
|
2660
|
-
docsPath: "docs/providers/chutes.md",
|
|
2661
|
-
authSurface: "oauth",
|
|
2662
|
-
authChoiceIds: ["chutes"]
|
|
2663
|
-
},
|
|
2664
|
-
{
|
|
2665
|
-
upstreamProviderId: "codex",
|
|
2666
|
-
upstreamCatalogPaths: ["extensions/codex/provider-catalog.ts", "extensions/codex/provider-discovery.ts"],
|
|
2667
|
-
status: "mapped",
|
|
2668
|
-
fasedProviderIds: ["openai-codex"],
|
|
2669
|
-
fasedProviderAliases: ["codex"],
|
|
2670
|
-
docsPath: "docs/providers/openai.md",
|
|
2671
|
-
authSurface: "oauth",
|
|
2672
|
-
authChoiceIds: ["openai-codex"]
|
|
2673
|
-
},
|
|
2674
|
-
{
|
|
2675
|
-
upstreamProviderId: "huggingface",
|
|
2676
|
-
upstreamCatalogPaths: ["extensions/huggingface/provider-catalog.ts"],
|
|
2677
|
-
status: "mapped",
|
|
2678
|
-
fasedProviderIds: ["huggingface"],
|
|
2679
|
-
docsPath: "docs/providers/huggingface.md",
|
|
2680
|
-
authSurface: "api-key",
|
|
2681
|
-
authChoiceIds: ["huggingface-api-key"]
|
|
2682
|
-
},
|
|
2683
|
-
{
|
|
2684
|
-
upstreamProviderId: "kimi-coding",
|
|
2685
|
-
upstreamCatalogPaths: ["extensions/kimi-coding/provider-catalog.ts"],
|
|
2686
|
-
status: "mapped",
|
|
2687
|
-
fasedProviderIds: ["kimi"],
|
|
2688
|
-
fasedProviderAliases: ["kimi-coding", "kimi-code"],
|
|
2689
|
-
docsPath: "docs/providers/moonshot.md",
|
|
2690
|
-
authSurface: "api-key",
|
|
2691
|
-
authChoiceIds: ["kimi-code-api-key"]
|
|
2692
|
-
},
|
|
2693
|
-
{
|
|
2694
|
-
upstreamProviderId: "litellm",
|
|
2695
|
-
upstreamCatalogPaths: ["extensions/litellm/provider-catalog.ts"],
|
|
2696
|
-
status: "mapped",
|
|
2697
|
-
fasedProviderIds: ["litellm"],
|
|
2698
|
-
docsPath: "docs/providers/litellm.md",
|
|
2699
|
-
authSurface: "api-key",
|
|
2700
|
-
authChoiceIds: ["litellm-api-key"]
|
|
2701
|
-
},
|
|
2702
|
-
{
|
|
2703
|
-
upstreamProviderId: "minimax",
|
|
2704
|
-
upstreamCatalogPaths: ["extensions/minimax/provider-catalog.ts"],
|
|
2705
|
-
status: "mapped",
|
|
2706
|
-
fasedProviderIds: [
|
|
2707
|
-
"minimax",
|
|
2708
|
-
"minimax-cn",
|
|
2709
|
-
"minimax-portal"
|
|
2710
|
-
],
|
|
2711
|
-
docsPath: "docs/providers/minimax.md",
|
|
2712
|
-
authSurface: "api-key",
|
|
2713
|
-
authChoiceIds: [
|
|
2714
|
-
"minimax-portal",
|
|
2715
|
-
"minimax-api",
|
|
2716
|
-
"minimax-api-key-cn",
|
|
2717
|
-
"minimax-api-lightning"
|
|
2718
|
-
]
|
|
2719
|
-
},
|
|
2720
|
-
{
|
|
2721
|
-
upstreamProviderId: "mistral",
|
|
2722
|
-
upstreamCatalogPaths: ["extensions/mistral/provider-catalog.ts"],
|
|
2723
|
-
status: "mapped",
|
|
2724
|
-
fasedProviderIds: ["mistral"],
|
|
2725
|
-
docsPath: "docs/providers/mistral.md",
|
|
2726
|
-
authSurface: "api-key",
|
|
2727
|
-
authChoiceIds: ["mistral-api-key"]
|
|
2728
|
-
},
|
|
2729
|
-
{
|
|
2730
|
-
upstreamProviderId: "moonshot",
|
|
2731
|
-
upstreamCatalogPaths: ["extensions/moonshot/provider-catalog.ts", "extensions/moonshot/provider-discovery.ts"],
|
|
2732
|
-
status: "mapped",
|
|
2733
|
-
fasedProviderIds: ["moonshot"],
|
|
2734
|
-
docsPath: "docs/providers/moonshot.md",
|
|
2735
|
-
authSurface: "api-key",
|
|
2736
|
-
authChoiceIds: ["moonshot-api-key", "moonshot-api-key-cn"]
|
|
2737
|
-
},
|
|
2738
|
-
{
|
|
2739
|
-
upstreamProviderId: "openrouter",
|
|
2740
|
-
upstreamCatalogPaths: ["extensions/openrouter/provider-catalog.ts"],
|
|
2741
|
-
status: "mapped",
|
|
2742
|
-
fasedProviderIds: ["openrouter"],
|
|
2743
|
-
docsPath: "docs/providers/openrouter.md",
|
|
2744
|
-
authSurface: "api-key",
|
|
2745
|
-
authChoiceIds: ["openrouter-api-key"]
|
|
2746
|
-
},
|
|
2747
|
-
{
|
|
2748
|
-
upstreamProviderId: "qianfan",
|
|
2749
|
-
upstreamCatalogPaths: ["extensions/qianfan/provider-catalog.ts"],
|
|
2750
|
-
status: "mapped",
|
|
2751
|
-
fasedProviderIds: ["qianfan"],
|
|
2752
|
-
docsPath: "docs/providers/qianfan.md",
|
|
2753
|
-
authSurface: "api-key",
|
|
2754
|
-
authChoiceIds: ["qianfan-api-key"]
|
|
2755
|
-
},
|
|
2756
|
-
{
|
|
2757
|
-
upstreamProviderId: "qwen",
|
|
2758
|
-
upstreamCatalogPaths: ["extensions/qwen/provider-catalog.ts"],
|
|
2759
|
-
status: "mapped",
|
|
2760
|
-
fasedProviderIds: ["qwen"],
|
|
2761
|
-
docsPath: "docs/providers/qwen.md",
|
|
2762
|
-
authSurface: "api-key",
|
|
2763
|
-
authChoiceIds: ["qwen-coding-plan-api-key", "qwen-api-key"]
|
|
2764
|
-
},
|
|
2765
|
-
{
|
|
2766
|
-
upstreamProviderId: "synthetic",
|
|
2767
|
-
upstreamCatalogPaths: ["extensions/synthetic/provider-catalog.ts"],
|
|
2768
|
-
status: "mapped",
|
|
2769
|
-
fasedProviderIds: ["synthetic"],
|
|
2770
|
-
docsPath: "docs/providers/synthetic.md",
|
|
2771
|
-
authSurface: "api-key",
|
|
2772
|
-
authChoiceIds: ["synthetic-api-key"]
|
|
2773
|
-
},
|
|
2774
|
-
{
|
|
2775
|
-
upstreamProviderId: "together",
|
|
2776
|
-
upstreamCatalogPaths: ["extensions/together/provider-catalog.ts"],
|
|
2777
|
-
status: "mapped",
|
|
2778
|
-
fasedProviderIds: ["together"],
|
|
2779
|
-
docsPath: "docs/providers/together.md",
|
|
2780
|
-
authSurface: "api-key",
|
|
2781
|
-
authChoiceIds: ["together-api-key"]
|
|
2782
|
-
},
|
|
2783
|
-
{
|
|
2784
|
-
upstreamProviderId: "venice",
|
|
2785
|
-
upstreamCatalogPaths: ["extensions/venice/provider-catalog.ts"],
|
|
2786
|
-
status: "mapped",
|
|
2787
|
-
fasedProviderIds: ["venice"],
|
|
2788
|
-
docsPath: "docs/providers/venice.md",
|
|
2789
|
-
authSurface: "api-key",
|
|
2790
|
-
authChoiceIds: ["venice-api-key"]
|
|
2791
|
-
},
|
|
2792
|
-
{
|
|
2793
|
-
upstreamProviderId: "vercel-ai-gateway",
|
|
2794
|
-
upstreamCatalogPaths: ["extensions/vercel-ai-gateway/provider-catalog.ts"],
|
|
2795
|
-
status: "mapped",
|
|
2796
|
-
fasedProviderIds: ["vercel-ai-gateway"],
|
|
2797
|
-
docsPath: "docs/providers/vercel-ai-gateway.md",
|
|
2798
|
-
authSurface: "api-key",
|
|
2799
|
-
authChoiceIds: ["ai-gateway-api-key"]
|
|
2800
|
-
},
|
|
2801
|
-
{
|
|
2802
|
-
upstreamProviderId: "volcengine",
|
|
2803
|
-
upstreamCatalogPaths: ["extensions/volcengine/provider-catalog.ts", "extensions/volcengine/provider-discovery.ts"],
|
|
2804
|
-
status: "mapped",
|
|
2805
|
-
fasedProviderIds: [
|
|
2806
|
-
"volcengine",
|
|
2807
|
-
"volcengine-coding",
|
|
2808
|
-
"volcengine-plan"
|
|
2809
|
-
],
|
|
2810
|
-
docsPath: "docs/providers/volcengine.md",
|
|
2811
|
-
authSurface: "api-key",
|
|
2812
|
-
authChoiceIds: ["volcengine-api-key"]
|
|
2813
|
-
},
|
|
2814
|
-
{
|
|
2815
|
-
upstreamProviderId: "xai",
|
|
2816
|
-
upstreamCatalogPaths: ["extensions/xai/provider-catalog.ts", "extensions/xai/provider-discovery.ts"],
|
|
2817
|
-
status: "mapped",
|
|
2818
|
-
fasedProviderIds: ["xai"],
|
|
2819
|
-
docsPath: "docs/providers/xai.md",
|
|
2820
|
-
authSurface: "api-key",
|
|
2821
|
-
authChoiceIds: [
|
|
2822
|
-
"xai-oauth",
|
|
2823
|
-
"xai-device-code",
|
|
2824
|
-
"xai-api-key"
|
|
2825
|
-
]
|
|
2826
|
-
},
|
|
2827
|
-
{
|
|
2828
|
-
upstreamProviderId: "xiaomi",
|
|
2829
|
-
upstreamCatalogPaths: ["extensions/xiaomi/provider-catalog.ts"],
|
|
2830
|
-
status: "mapped",
|
|
2831
|
-
fasedProviderIds: ["xiaomi"],
|
|
2832
|
-
docsPath: "docs/providers/xiaomi.md",
|
|
2833
|
-
authSurface: "api-key",
|
|
2834
|
-
authChoiceIds: ["xiaomi-api-key"]
|
|
2835
|
-
}
|
|
2836
|
-
];
|
|
2837
|
-
function listProviderExtensionCatalogManifestEntries() {
|
|
2838
|
-
return FASED_PROVIDER_EXTENSION_CATALOG_MANIFEST;
|
|
2839
|
-
}
|
|
2840
|
-
|
|
2841
|
-
//#endregion
|
|
2842
|
-
//#region src/agents/model-catalog-status.ts
|
|
2843
|
-
const SOURCE_CONFIDENCE_RANK = {
|
|
2844
|
-
configured: 600,
|
|
2845
|
-
runtime: 500,
|
|
2846
|
-
manifest: 400,
|
|
2847
|
-
"provider-index": 300,
|
|
2848
|
-
"current-preview": 200,
|
|
2849
|
-
catalog: 100
|
|
2850
|
-
};
|
|
2851
|
-
function createCapabilityCounts() {
|
|
2852
|
-
return {
|
|
2853
|
-
textModels: 0,
|
|
2854
|
-
visionModels: 0,
|
|
2855
|
-
reasoningModels: 0,
|
|
2856
|
-
toolsModels: 0,
|
|
2857
|
-
jsonModels: 0,
|
|
2858
|
-
audioModels: 0,
|
|
2859
|
-
videoModels: 0,
|
|
2860
|
-
speechModels: 0
|
|
2861
|
-
};
|
|
2862
|
-
}
|
|
2863
|
-
function capabilityKey(feature) {
|
|
2864
|
-
return `${feature}Models`;
|
|
2865
|
-
}
|
|
2866
|
-
function resolveProviderConfig(params) {
|
|
2867
|
-
return params.cfg.models?.providers?.[params.provider];
|
|
2868
|
-
}
|
|
2869
|
-
function deriveStatusMetadata(params) {
|
|
2870
|
-
return params.model.metadata ?? deriveModelMetadata({
|
|
2871
|
-
model: params.model,
|
|
2872
|
-
cfg: params.cfg,
|
|
2873
|
-
providerConfig: resolveProviderConfig({
|
|
2874
|
-
cfg: params.cfg,
|
|
2875
|
-
provider: params.provider
|
|
2876
|
-
})
|
|
2877
|
-
});
|
|
2878
|
-
}
|
|
2879
|
-
function higherConfidence(left, right) {
|
|
2880
|
-
return (SOURCE_CONFIDENCE_RANK[left] ?? 0) >= (SOURCE_CONFIDENCE_RANK[right] ?? 0) ? left : right;
|
|
2881
|
-
}
|
|
2882
|
-
function buildModelCatalogStatus(params) {
|
|
2883
|
-
const configuredProviders = new Set(Object.keys(params.cfg.models?.providers ?? {}).map((provider) => normalizeProviderId(provider)));
|
|
2884
|
-
const sourceCounts = {};
|
|
2885
|
-
const providers = /* @__PURE__ */ new Map();
|
|
2886
|
-
let reasoningModels = 0;
|
|
2887
|
-
let visionModels = 0;
|
|
2888
|
-
const capabilityCounts = createCapabilityCounts();
|
|
2889
|
-
for (const model of params.catalog) {
|
|
2890
|
-
const provider = normalizeProviderId(model.provider);
|
|
2891
|
-
if (!provider) continue;
|
|
2892
|
-
const source = model.catalogSource ?? "catalog";
|
|
2893
|
-
sourceCounts[source] = (sourceCounts[source] ?? 0) + 1;
|
|
2894
|
-
let entry = providers.get(provider);
|
|
2895
|
-
if (!entry) {
|
|
2896
|
-
entry = {
|
|
2897
|
-
provider,
|
|
2898
|
-
totalModels: 0,
|
|
2899
|
-
configured: configuredProviders.has(provider),
|
|
2900
|
-
reasoningModels: 0,
|
|
2901
|
-
visionModels: 0,
|
|
2902
|
-
sources: /* @__PURE__ */ new Set(),
|
|
2903
|
-
sourceConfidence: source,
|
|
2904
|
-
capabilityCounts: createCapabilityCounts(),
|
|
2905
|
-
authModes: /* @__PURE__ */ new Set(),
|
|
2906
|
-
privateNetwork: {
|
|
2907
|
-
models: 0,
|
|
2908
|
-
allowed: 0,
|
|
2909
|
-
blocked: 0
|
|
2910
|
-
}
|
|
2911
|
-
};
|
|
2912
|
-
providers.set(provider, entry);
|
|
2913
|
-
}
|
|
2914
|
-
entry.totalModels += 1;
|
|
2915
|
-
entry.sources.add(source);
|
|
2916
|
-
entry.sourceConfidence = higherConfidence(entry.sourceConfidence, source);
|
|
2917
|
-
const metadata = deriveStatusMetadata({
|
|
2918
|
-
cfg: params.cfg,
|
|
2919
|
-
model,
|
|
2920
|
-
provider
|
|
2921
|
-
});
|
|
2922
|
-
entry.authModes.add(metadata.authMode);
|
|
2923
|
-
for (const feature of metadata.features) {
|
|
2924
|
-
entry.capabilityCounts[capabilityKey(feature)] += 1;
|
|
2925
|
-
capabilityCounts[capabilityKey(feature)] += 1;
|
|
2926
|
-
}
|
|
2927
|
-
if (metadata.privateNetwork) {
|
|
2928
|
-
entry.privateNetwork.models += 1;
|
|
2929
|
-
if (metadata.privateNetworkAllowed) entry.privateNetwork.allowed += 1;
|
|
2930
|
-
else entry.privateNetwork.blocked += 1;
|
|
2931
|
-
}
|
|
2932
|
-
if (typeof metadata.contextWindow === "number") entry.maxContextWindow = Math.max(entry.maxContextWindow ?? 0, metadata.contextWindow);
|
|
2933
|
-
if (typeof metadata.maxTokens === "number") entry.maxOutputTokens = Math.max(entry.maxOutputTokens ?? 0, metadata.maxTokens);
|
|
2934
|
-
if (model.reasoning === true) {
|
|
2935
|
-
reasoningModels += 1;
|
|
2936
|
-
entry.reasoningModels += 1;
|
|
2937
|
-
}
|
|
2938
|
-
if (model.input?.includes("image")) {
|
|
2939
|
-
visionModels += 1;
|
|
2940
|
-
entry.visionModels += 1;
|
|
2941
|
-
}
|
|
2942
|
-
}
|
|
2943
|
-
const providerRows = Array.from(providers.values()).map((provider) => ({
|
|
2944
|
-
provider: provider.provider,
|
|
2945
|
-
totalModels: provider.totalModels,
|
|
2946
|
-
configured: provider.configured,
|
|
2947
|
-
reasoningModels: provider.reasoningModels,
|
|
2948
|
-
visionModels: provider.visionModels,
|
|
2949
|
-
sources: Array.from(provider.sources).toSorted((left, right) => left.localeCompare(right)),
|
|
2950
|
-
sourceConfidence: provider.sourceConfidence,
|
|
2951
|
-
capabilityCounts: provider.capabilityCounts,
|
|
2952
|
-
authModes: Array.from(provider.authModes).toSorted((left, right) => left.localeCompare(right)),
|
|
2953
|
-
privateNetwork: provider.privateNetwork,
|
|
2954
|
-
probeStatus: params.providerHealth?.[provider.provider] ? params.providerHealth[provider.provider].reachable === "ok" && params.providerHealth[provider.provider].auth !== "fail" ? "ok" : params.providerHealth[provider.provider].reachable === "fail" || params.providerHealth[provider.provider].auth === "fail" ? "fail" : "unknown" : "not-run",
|
|
2955
|
-
...params.providerHealth?.[provider.provider] ? { health: {
|
|
2956
|
-
reachable: params.providerHealth[provider.provider].reachable,
|
|
2957
|
-
auth: params.providerHealth[provider.provider].auth,
|
|
2958
|
-
modelsDiscovered: params.providerHealth[provider.provider].modelsDiscovered,
|
|
2959
|
-
privateNetworkApproved: params.providerHealth[provider.provider].privateNetworkApproved,
|
|
2960
|
-
...typeof params.providerHealth[provider.provider].checkedAtMs === "number" ? { checkedAtMs: params.providerHealth[provider.provider].checkedAtMs } : {},
|
|
2961
|
-
...params.providerHealth[provider.provider].detail ? { detail: params.providerHealth[provider.provider].detail } : {}
|
|
2962
|
-
} } : {},
|
|
2963
|
-
...provider.maxContextWindow ? { maxContextWindow: provider.maxContextWindow } : {},
|
|
2964
|
-
...provider.maxOutputTokens ? { maxOutputTokens: provider.maxOutputTokens } : {}
|
|
2965
|
-
})).toSorted((left, right) => left.provider.localeCompare(right.provider));
|
|
2966
|
-
const manifestEntries = listProviderExtensionCatalogManifestEntries();
|
|
2967
|
-
const mappedManifestProviderIds = [...new Set(manifestEntries.flatMap((entry) => entry.status === "mapped" ? entry.fasedProviderIds.map(normalizeProviderId) : []))].filter(Boolean).toSorted((left, right) => left.localeCompare(right));
|
|
2968
|
-
const catalogProviderIds = new Set(providerRows.map((provider) => provider.provider));
|
|
2969
|
-
const deferredProviderIds = manifestEntries.filter((entry) => entry.status === "deferred").map((entry) => normalizeProviderId(entry.upstreamProviderId)).filter(Boolean).toSorted((left, right) => left.localeCompare(right));
|
|
2970
|
-
const providerExtensionEntries = (params.providerExtensionCatalog?.entries ?? []).map((entry) => ({
|
|
2971
|
-
id: entry.id,
|
|
2972
|
-
source: entry.source,
|
|
2973
|
-
trusted: entry.trusted,
|
|
2974
|
-
providerIds: entry.providerIds,
|
|
2975
|
-
loadedProviderIds: entry.loadedProviderIds,
|
|
2976
|
-
modelCount: entry.modelCount,
|
|
2977
|
-
status: entry.status,
|
|
2978
|
-
...entry.error ? { error: entry.error } : {}
|
|
2979
|
-
}));
|
|
2980
|
-
const providerExtensionWarnings = providerExtensionEntries.filter((entry) => entry.status === "error" || entry.status === "skipped-untrusted");
|
|
2981
|
-
return {
|
|
2982
|
-
checkedAtMs: params.checkedAtMs ?? Date.now(),
|
|
2983
|
-
cache: {
|
|
2984
|
-
modelCatalog: "shared-loader",
|
|
2985
|
-
providerExtensionCatalog: params.providerExtensionCatalog ? "fresh-status-load" : "not-loaded"
|
|
2986
|
-
},
|
|
2987
|
-
totalProviders: providerRows.length,
|
|
2988
|
-
totalModels: params.catalog.length,
|
|
2989
|
-
configuredProviders: providerRows.filter((provider) => provider.configured).length,
|
|
2990
|
-
availableProviders: providerRows.filter((provider) => !provider.configured).length,
|
|
2991
|
-
reasoningModels,
|
|
2992
|
-
visionModels,
|
|
2993
|
-
capabilityCounts,
|
|
2994
|
-
sourceCounts,
|
|
2995
|
-
providers: providerRows,
|
|
2996
|
-
providerExtensionCatalog: {
|
|
2997
|
-
totalEntries: providerExtensionEntries.length,
|
|
2998
|
-
loadedEntries: providerExtensionEntries.filter((entry) => entry.status === "loaded").length,
|
|
2999
|
-
skippedUntrustedEntries: providerExtensionEntries.filter((entry) => entry.status === "skipped-untrusted").length,
|
|
3000
|
-
emptyEntries: providerExtensionEntries.filter((entry) => entry.status === "empty").length,
|
|
3001
|
-
errorEntries: providerExtensionEntries.filter((entry) => entry.status === "error").length,
|
|
3002
|
-
modelCount: providerExtensionEntries.reduce((total, entry) => total + entry.modelCount, 0),
|
|
3003
|
-
loadedProviderIds: Object.keys(params.providerExtensionCatalog?.providers ?? {}).toSorted((left, right) => left.localeCompare(right)),
|
|
3004
|
-
warnings: providerExtensionWarnings,
|
|
3005
|
-
entries: providerExtensionEntries
|
|
3006
|
-
},
|
|
3007
|
-
providerExtensionManifest: {
|
|
3008
|
-
upstreamProviderCount: manifestEntries.length,
|
|
3009
|
-
mappedProviderCount: manifestEntries.filter((entry) => entry.status === "mapped").length,
|
|
3010
|
-
deferredProviderCount: manifestEntries.filter((entry) => entry.status === "deferred").length,
|
|
3011
|
-
mappedProviderIds: mappedManifestProviderIds,
|
|
3012
|
-
deferredProviderIds,
|
|
3013
|
-
missingMappedProviderIds: mappedManifestProviderIds.filter((providerId) => !catalogProviderIds.has(providerId))
|
|
3014
|
-
}
|
|
3015
|
-
};
|
|
3016
|
-
}
|
|
3017
|
-
|
|
3018
|
-
//#endregion
|
|
3019
|
-
//#region src/gateway/update-status.ts
|
|
3020
|
-
const DEFAULT_UPDATE_STATUS_TIMEOUT_MS = 3500;
|
|
3021
|
-
function normalizeUpdateStatusTimeout(value) {
|
|
3022
|
-
return typeof value === "number" && Number.isFinite(value) ? Math.max(1e3, Math.floor(value)) : DEFAULT_UPDATE_STATUS_TIMEOUT_MS;
|
|
3023
|
-
}
|
|
3024
|
-
async function getGatewayUpdateStatus(params) {
|
|
3025
|
-
const timeoutMs = normalizeUpdateStatusTimeout(params?.timeoutMs);
|
|
3026
|
-
const fetchGit = params?.fetchGit === true;
|
|
3027
|
-
const includeRegistry = params?.includeRegistry === true;
|
|
3028
|
-
const configSnapshot = await readConfigFileSnapshot();
|
|
3029
|
-
const configChannel = configSnapshot.valid ? normalizeUpdateChannel(configSnapshot.config.update?.channel) : null;
|
|
3030
|
-
const update = await checkUpdateStatus({
|
|
3031
|
-
root: await resolveFasedAgentPackageRoot({
|
|
3032
|
-
moduleUrl: import.meta.url,
|
|
3033
|
-
argv1: process.argv[1],
|
|
3034
|
-
cwd: process.cwd()
|
|
3035
|
-
}) ?? process.cwd(),
|
|
3036
|
-
timeoutMs,
|
|
3037
|
-
fetchGit,
|
|
3038
|
-
includeRegistry
|
|
3039
|
-
});
|
|
3040
|
-
const channel = resolveUpdateChannelDisplay({
|
|
3041
|
-
configChannel,
|
|
3042
|
-
installKind: update.installKind,
|
|
3043
|
-
gitTag: update.git?.tag ?? null,
|
|
3044
|
-
gitBranch: update.git?.branch ?? null
|
|
3045
|
-
});
|
|
3046
|
-
return {
|
|
3047
|
-
ok: true,
|
|
3048
|
-
update,
|
|
3049
|
-
availability: resolveUpdateAvailability(update),
|
|
3050
|
-
channel: {
|
|
3051
|
-
...channel,
|
|
3052
|
-
config: configChannel
|
|
3053
|
-
},
|
|
3054
|
-
probes: {
|
|
3055
|
-
fetchGit,
|
|
3056
|
-
includeRegistry,
|
|
3057
|
-
timeoutMs
|
|
3058
|
-
},
|
|
3059
|
-
summary: formatUpdateOneLiner(update)
|
|
3060
|
-
};
|
|
3061
|
-
}
|
|
3062
|
-
|
|
3063
|
-
//#endregion
|
|
3064
|
-
//#region src/gateway/server-methods/commands.ts
|
|
3065
|
-
function normalizeOptionalLowercaseString(value) {
|
|
3066
|
-
if (typeof value !== "string") return;
|
|
3067
|
-
return value.trim().toLowerCase() || void 0;
|
|
3068
|
-
}
|
|
3069
|
-
function stripLeadingSlash(value) {
|
|
3070
|
-
return value.startsWith("/") ? value.slice(1) : value;
|
|
3071
|
-
}
|
|
3072
|
-
function resolveTextAliases(cmd) {
|
|
3073
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3074
|
-
const aliases = [];
|
|
3075
|
-
for (const alias of cmd.textAliases) {
|
|
3076
|
-
const trimmed = alias.trim();
|
|
3077
|
-
if (!trimmed) continue;
|
|
3078
|
-
const exactAlias = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
3079
|
-
if (seen.has(exactAlias)) continue;
|
|
3080
|
-
seen.add(exactAlias);
|
|
3081
|
-
aliases.push(exactAlias);
|
|
3082
|
-
}
|
|
3083
|
-
if (aliases.length > 0) return aliases;
|
|
3084
|
-
return [`/${cmd.key}`];
|
|
3085
|
-
}
|
|
3086
|
-
function resolvePrimaryTextName(cmd) {
|
|
3087
|
-
return stripLeadingSlash(resolveTextAliases(cmd)[0] ?? `/${cmd.key}`);
|
|
3088
|
-
}
|
|
3089
|
-
function normalizeChoice(choice) {
|
|
3090
|
-
return typeof choice === "string" ? {
|
|
3091
|
-
value: choice,
|
|
3092
|
-
label: choice
|
|
3093
|
-
} : choice;
|
|
3094
|
-
}
|
|
3095
|
-
function serializeArg(arg) {
|
|
3096
|
-
const isDynamic = typeof arg.choices === "function";
|
|
3097
|
-
const staticChoices = Array.isArray(arg.choices) ? arg.choices.map(normalizeChoice) : void 0;
|
|
3098
|
-
return {
|
|
3099
|
-
name: arg.name,
|
|
3100
|
-
description: arg.description,
|
|
3101
|
-
type: arg.type,
|
|
3102
|
-
...arg.required ? { required: true } : {},
|
|
3103
|
-
...staticChoices ? { choices: staticChoices } : {},
|
|
3104
|
-
...isDynamic ? { dynamic: true } : {}
|
|
3105
|
-
};
|
|
3106
|
-
}
|
|
3107
|
-
function mapCommand(params) {
|
|
3108
|
-
const { cmd, includeArgs, nameSurface, provider, source } = params;
|
|
3109
|
-
const shouldIncludeArgs = includeArgs && cmd.acceptsArgs && cmd.args?.length;
|
|
3110
|
-
const nativeName = cmd.scope === "text" ? void 0 : resolveNativeCommandName(cmd, provider) ?? cmd.key;
|
|
3111
|
-
return {
|
|
3112
|
-
name: nameSurface === "text" ? resolvePrimaryTextName(cmd) : nativeName ?? cmd.key,
|
|
3113
|
-
...nativeName ? { nativeName } : {},
|
|
3114
|
-
...cmd.scope !== "native" ? { textAliases: resolveTextAliases(cmd) } : {},
|
|
3115
|
-
description: cmd.description,
|
|
3116
|
-
...cmd.category ? { category: cmd.category } : {},
|
|
3117
|
-
source,
|
|
3118
|
-
scope: cmd.scope,
|
|
3119
|
-
acceptsArgs: Boolean(cmd.acceptsArgs),
|
|
3120
|
-
...shouldIncludeArgs ? { args: cmd.args.map(serializeArg) } : {}
|
|
3121
|
-
};
|
|
3122
|
-
}
|
|
3123
|
-
function buildPluginCommandEntries(params) {
|
|
3124
|
-
const pluginTextSpecs = listPluginCommands();
|
|
3125
|
-
const pluginNativeSpecs = getPluginCommandSpecs();
|
|
3126
|
-
if (params.nameSurface === "text") return pluginTextSpecs.map((spec) => ({
|
|
3127
|
-
name: spec.name,
|
|
3128
|
-
textAliases: [`/${spec.name}`],
|
|
3129
|
-
description: spec.description,
|
|
3130
|
-
source: "plugin",
|
|
3131
|
-
scope: "both",
|
|
3132
|
-
acceptsArgs: spec.acceptsArgs
|
|
3133
|
-
}));
|
|
3134
|
-
return pluginNativeSpecs.map((spec) => ({
|
|
3135
|
-
name: spec.name,
|
|
3136
|
-
nativeName: spec.name,
|
|
3137
|
-
textAliases: [`/${spec.name}`],
|
|
3138
|
-
description: spec.description,
|
|
3139
|
-
source: "plugin",
|
|
3140
|
-
scope: "both",
|
|
3141
|
-
acceptsArgs: spec.acceptsArgs
|
|
3142
|
-
}));
|
|
3143
|
-
}
|
|
3144
|
-
function buildCommandsListResult(params) {
|
|
3145
|
-
const includeArgs = params.includeArgs !== false;
|
|
3146
|
-
const scopeFilter = params.scope ?? "both";
|
|
3147
|
-
const nameSurface = scopeFilter === "text" ? "text" : "native";
|
|
3148
|
-
const provider = normalizeOptionalLowercaseString(params.provider);
|
|
3149
|
-
const skillCommands = listSkillCommandsForAgents({
|
|
3150
|
-
cfg: params.cfg,
|
|
3151
|
-
agentIds: [params.agentId]
|
|
3152
|
-
});
|
|
3153
|
-
const chatCommands = listChatCommandsForConfig(params.cfg, { skillCommands });
|
|
3154
|
-
const skillKeys = new Set(skillCommands.map((command) => `skill:${command.skillName}`));
|
|
3155
|
-
const commands = [];
|
|
3156
|
-
for (const cmd of chatCommands) {
|
|
3157
|
-
if (scopeFilter !== "both" && cmd.scope !== "both" && cmd.scope !== scopeFilter) continue;
|
|
3158
|
-
commands.push(mapCommand({
|
|
3159
|
-
cmd,
|
|
3160
|
-
source: skillKeys.has(cmd.key) ? "skill" : "native",
|
|
3161
|
-
includeArgs,
|
|
3162
|
-
nameSurface,
|
|
3163
|
-
provider
|
|
3164
|
-
}));
|
|
3165
|
-
}
|
|
3166
|
-
commands.push(...buildPluginCommandEntries({ nameSurface }));
|
|
3167
|
-
return { commands };
|
|
3168
|
-
}
|
|
3169
|
-
|
|
3170
|
-
//#endregion
|
|
3171
|
-
//#region src/acp/status-snapshot.ts
|
|
3172
|
-
function normalizeText(value) {
|
|
3173
|
-
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
3174
|
-
}
|
|
3175
|
-
function normalizePositiveInteger(value) {
|
|
3176
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : null;
|
|
3177
|
-
}
|
|
3178
|
-
function resolveAcpBackendHealth(backend) {
|
|
3179
|
-
if (!backend) return null;
|
|
3180
|
-
if (!backend.healthy) return true;
|
|
3181
|
-
try {
|
|
3182
|
-
return backend.healthy();
|
|
3183
|
-
} catch {
|
|
3184
|
-
return false;
|
|
3185
|
-
}
|
|
3186
|
-
}
|
|
3187
|
-
function sanitizeSession(entry) {
|
|
3188
|
-
const acp = entry.acp;
|
|
3189
|
-
if (!acp) return null;
|
|
3190
|
-
const runtimeOptions = resolveRuntimeOptionsFromMeta(acp);
|
|
3191
|
-
const backendExtrasKeys = Object.keys(runtimeOptions.backendExtras ?? {}).toSorted();
|
|
3192
|
-
return {
|
|
3193
|
-
sessionKey: entry.sessionKey,
|
|
3194
|
-
backend: acp.backend,
|
|
3195
|
-
agent: acp.agent,
|
|
3196
|
-
mode: acp.mode,
|
|
3197
|
-
state: acp.state,
|
|
3198
|
-
lastActivityAt: acp.lastActivityAt,
|
|
3199
|
-
...acp.lastError ? { lastError: acp.lastError } : {},
|
|
3200
|
-
...acp.identity ? { identity: {
|
|
3201
|
-
state: acp.identity.state,
|
|
3202
|
-
source: acp.identity.source,
|
|
3203
|
-
...acp.identity.acpxRecordId ? { acpxRecordId: acp.identity.acpxRecordId } : {},
|
|
3204
|
-
...acp.identity.acpxSessionId ? { acpxSessionId: acp.identity.acpxSessionId } : {},
|
|
3205
|
-
...acp.identity.agentSessionId ? { agentSessionId: acp.identity.agentSessionId } : {},
|
|
3206
|
-
lastUpdatedAt: acp.identity.lastUpdatedAt
|
|
3207
|
-
} } : {},
|
|
3208
|
-
runtimeOptions: {
|
|
3209
|
-
...runtimeOptions.runtimeMode ? { runtimeMode: runtimeOptions.runtimeMode } : {},
|
|
3210
|
-
...runtimeOptions.model ? { model: runtimeOptions.model } : {},
|
|
3211
|
-
...runtimeOptions.permissionProfile ? { permissionProfile: runtimeOptions.permissionProfile } : {},
|
|
3212
|
-
...runtimeOptions.timeoutSeconds ? { timeoutSeconds: runtimeOptions.timeoutSeconds } : {},
|
|
3213
|
-
...backendExtrasKeys.length > 0 ? { backendExtrasKeys } : {},
|
|
3214
|
-
cwdConfigured: Boolean(acp.cwd || runtimeOptions.cwd)
|
|
3215
|
-
}
|
|
3216
|
-
};
|
|
3217
|
-
}
|
|
3218
|
-
async function getAcpStatusSnapshot(params) {
|
|
3219
|
-
const cfg = params.cfg;
|
|
3220
|
-
const requestedBackendId = normalizeText(cfg.acp?.backend) ?? "acpx";
|
|
3221
|
-
const backend = getAcpRuntimeBackend(requestedBackendId);
|
|
3222
|
-
const sessions = await listAcpSessionEntries({ cfg });
|
|
3223
|
-
const limit = Math.max(0, Math.min(100, params.limit ?? 20));
|
|
3224
|
-
const items = sessions.map(sanitizeSession).filter((session) => session !== null).toSorted((a, b) => b.lastActivityAt - a.lastActivityAt).slice(0, limit);
|
|
3225
|
-
return {
|
|
3226
|
-
policy: {
|
|
3227
|
-
enabled: cfg.acp?.enabled !== false,
|
|
3228
|
-
dispatchEnabled: cfg.acp?.dispatch?.enabled === true,
|
|
3229
|
-
backend: requestedBackendId,
|
|
3230
|
-
defaultAgent: normalizeText(cfg.acp?.defaultAgent),
|
|
3231
|
-
allowedAgents: (cfg.acp?.allowedAgents ?? []).map((agent) => normalizeText(agent)).filter((agent) => agent !== null).toSorted(),
|
|
3232
|
-
maxConcurrentSessions: normalizePositiveInteger(cfg.acp?.maxConcurrentSessions)
|
|
3233
|
-
},
|
|
3234
|
-
runtimeBackend: {
|
|
3235
|
-
requestedId: requestedBackendId,
|
|
3236
|
-
registered: backend != null,
|
|
3237
|
-
selectedId: backend?.id ?? null,
|
|
3238
|
-
healthy: resolveAcpBackendHealth(backend)
|
|
3239
|
-
},
|
|
3240
|
-
manager: getAcpSessionManager().getObservabilitySnapshot(cfg),
|
|
3241
|
-
sessions: {
|
|
3242
|
-
total: sessions.length,
|
|
3243
|
-
returned: items.length,
|
|
3244
|
-
limit,
|
|
3245
|
-
items
|
|
3246
|
-
}
|
|
3247
|
-
};
|
|
3248
|
-
}
|
|
3249
|
-
|
|
3250
|
-
//#endregion
|
|
3251
|
-
//#region src/infra/push-apns.ts
|
|
3252
|
-
const APNS_STATE_FILENAME = "push/apns-registrations.json";
|
|
3253
|
-
const APNS_JWT_TTL_MS = 3e3 * 1e3;
|
|
3254
|
-
const DEFAULT_APNS_TIMEOUT_MS = 1e4;
|
|
3255
|
-
const withLock = createAsyncLock();
|
|
3256
|
-
let cachedJwt = null;
|
|
3257
|
-
function resolveApnsRegistrationPath(baseDir) {
|
|
3258
|
-
const root = baseDir ?? resolveStateDir();
|
|
3259
|
-
return path.join(root, APNS_STATE_FILENAME);
|
|
3260
|
-
}
|
|
3261
|
-
function normalizeNodeId(value) {
|
|
3262
|
-
return value.trim();
|
|
3263
|
-
}
|
|
3264
|
-
function normalizeApnsToken(value) {
|
|
3265
|
-
return value.trim().replace(/[<>\s]/g, "").toLowerCase();
|
|
3266
|
-
}
|
|
3267
|
-
function normalizeTopic(value) {
|
|
3268
|
-
return value.trim();
|
|
3269
|
-
}
|
|
3270
|
-
function isLikelyApnsToken(value) {
|
|
3271
|
-
return /^[0-9a-f]{32,}$/i.test(value);
|
|
3272
|
-
}
|
|
3273
|
-
function parseReason(body) {
|
|
3274
|
-
const trimmed = body.trim();
|
|
3275
|
-
if (!trimmed) return;
|
|
3276
|
-
try {
|
|
3277
|
-
const parsed = JSON.parse(trimmed);
|
|
3278
|
-
return typeof parsed.reason === "string" && parsed.reason.trim().length > 0 ? parsed.reason.trim() : trimmed.slice(0, 200);
|
|
3279
|
-
} catch {
|
|
3280
|
-
return trimmed.slice(0, 200);
|
|
3281
|
-
}
|
|
3282
|
-
}
|
|
3283
|
-
function toBase64UrlBytes(value) {
|
|
3284
|
-
return Buffer.from(value).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
3285
|
-
}
|
|
3286
|
-
function toBase64UrlJson(value) {
|
|
3287
|
-
return toBase64UrlBytes(Buffer.from(JSON.stringify(value)));
|
|
3288
|
-
}
|
|
3289
|
-
function getJwtCacheKey(auth) {
|
|
3290
|
-
const keyHash = createHash("sha256").update(auth.privateKey).digest("hex");
|
|
3291
|
-
return `${auth.teamId}:${auth.keyId}:${keyHash}`;
|
|
3292
|
-
}
|
|
3293
|
-
function getApnsBearerToken(auth, nowMs = Date.now()) {
|
|
3294
|
-
const cacheKey = getJwtCacheKey(auth);
|
|
3295
|
-
if (cachedJwt && cachedJwt.cacheKey === cacheKey && nowMs < cachedJwt.expiresAtMs) return cachedJwt.token;
|
|
3296
|
-
const iat = Math.floor(nowMs / 1e3);
|
|
3297
|
-
const signingInput = `${toBase64UrlJson({
|
|
3298
|
-
alg: "ES256",
|
|
3299
|
-
kid: auth.keyId,
|
|
3300
|
-
typ: "JWT"
|
|
3301
|
-
})}.${toBase64UrlJson({
|
|
3302
|
-
iss: auth.teamId,
|
|
3303
|
-
iat
|
|
3304
|
-
})}`;
|
|
3305
|
-
const token = `${signingInput}.${toBase64UrlBytes(sign("sha256", Buffer.from(signingInput, "utf8"), {
|
|
3306
|
-
key: createPrivateKey(auth.privateKey),
|
|
3307
|
-
dsaEncoding: "ieee-p1363"
|
|
3308
|
-
}))}`;
|
|
3309
|
-
cachedJwt = {
|
|
3310
|
-
cacheKey,
|
|
3311
|
-
token,
|
|
3312
|
-
expiresAtMs: nowMs + APNS_JWT_TTL_MS
|
|
3313
|
-
};
|
|
3314
|
-
return token;
|
|
3315
|
-
}
|
|
3316
|
-
function normalizePrivateKey(value) {
|
|
3317
|
-
return value.trim().replace(/\\n/g, "\n");
|
|
3318
|
-
}
|
|
3319
|
-
function normalizeNonEmptyString(value) {
|
|
3320
|
-
const trimmed = value?.trim() ?? "";
|
|
3321
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
3322
|
-
}
|
|
3323
|
-
async function loadRegistrationsState(baseDir) {
|
|
3324
|
-
const existing = await readJsonFile(resolveApnsRegistrationPath(baseDir));
|
|
3325
|
-
if (!existing || typeof existing !== "object") return { registrationsByNodeId: {} };
|
|
3326
|
-
return { registrationsByNodeId: existing.registrationsByNodeId && typeof existing.registrationsByNodeId === "object" && !Array.isArray(existing.registrationsByNodeId) ? existing.registrationsByNodeId : {} };
|
|
3327
|
-
}
|
|
3328
|
-
function normalizeApnsEnvironment(value) {
|
|
3329
|
-
if (typeof value !== "string") return null;
|
|
3330
|
-
const normalized = value.trim().toLowerCase();
|
|
3331
|
-
if (normalized === "sandbox" || normalized === "production") return normalized;
|
|
3332
|
-
return null;
|
|
3333
|
-
}
|
|
3334
|
-
async function loadApnsRegistration(nodeId, baseDir) {
|
|
3335
|
-
const normalizedNodeId = normalizeNodeId(nodeId);
|
|
3336
|
-
if (!normalizedNodeId) return null;
|
|
3337
|
-
return (await loadRegistrationsState(baseDir)).registrationsByNodeId[normalizedNodeId] ?? null;
|
|
3338
|
-
}
|
|
3339
|
-
async function resolveApnsAuthConfigFromEnv(env = process.env) {
|
|
3340
|
-
const teamId = normalizeNonEmptyString(env.FASED_APNS_TEAM_ID);
|
|
3341
|
-
const keyId = normalizeNonEmptyString(env.FASED_APNS_KEY_ID);
|
|
3342
|
-
if (!teamId || !keyId) return {
|
|
3343
|
-
ok: false,
|
|
3344
|
-
error: "APNs auth missing: set FASED_APNS_TEAM_ID and FASED_APNS_KEY_ID"
|
|
3345
|
-
};
|
|
3346
|
-
const inlineKeyRaw = normalizeNonEmptyString(env.FASED_APNS_PRIVATE_KEY_P8) ?? normalizeNonEmptyString(env.FASED_APNS_PRIVATE_KEY);
|
|
3347
|
-
if (inlineKeyRaw) return {
|
|
3348
|
-
ok: true,
|
|
3349
|
-
value: {
|
|
3350
|
-
teamId,
|
|
3351
|
-
keyId,
|
|
3352
|
-
privateKey: normalizePrivateKey(inlineKeyRaw)
|
|
3353
|
-
}
|
|
3354
|
-
};
|
|
3355
|
-
const keyPath = normalizeNonEmptyString(env.FASED_APNS_PRIVATE_KEY_PATH);
|
|
3356
|
-
if (!keyPath) return {
|
|
3357
|
-
ok: false,
|
|
3358
|
-
error: "APNs private key missing: set FASED_APNS_PRIVATE_KEY_P8 or FASED_APNS_PRIVATE_KEY_PATH"
|
|
3359
|
-
};
|
|
3360
|
-
try {
|
|
3361
|
-
return {
|
|
3362
|
-
ok: true,
|
|
3363
|
-
value: {
|
|
3364
|
-
teamId,
|
|
3365
|
-
keyId,
|
|
3366
|
-
privateKey: normalizePrivateKey(await fs$1.readFile(keyPath, "utf8"))
|
|
3367
|
-
}
|
|
3368
|
-
};
|
|
3369
|
-
} catch (err) {
|
|
3370
|
-
return {
|
|
3371
|
-
ok: false,
|
|
3372
|
-
error: `failed reading FASED_APNS_PRIVATE_KEY_PATH (${keyPath}): ${err instanceof Error ? err.message : String(err)}`
|
|
3373
|
-
};
|
|
3374
|
-
}
|
|
3375
|
-
}
|
|
3376
|
-
async function sendApnsRequest(params) {
|
|
3377
|
-
const authority = params.environment === "production" ? "https://api.push.apple.com" : "https://api.sandbox.push.apple.com";
|
|
3378
|
-
const body = JSON.stringify(params.payload);
|
|
3379
|
-
const requestPath = `/3/device/${params.token}`;
|
|
3380
|
-
return await new Promise((resolve, reject) => {
|
|
3381
|
-
const client = http2.connect(authority);
|
|
3382
|
-
let settled = false;
|
|
3383
|
-
const fail = (err) => {
|
|
3384
|
-
if (settled) return;
|
|
3385
|
-
settled = true;
|
|
3386
|
-
client.destroy();
|
|
3387
|
-
reject(err);
|
|
3388
|
-
};
|
|
3389
|
-
const finish = (result) => {
|
|
3390
|
-
if (settled) return;
|
|
3391
|
-
settled = true;
|
|
3392
|
-
client.close();
|
|
3393
|
-
resolve(result);
|
|
3394
|
-
};
|
|
3395
|
-
client.once("error", (err) => fail(err));
|
|
3396
|
-
const req = client.request({
|
|
3397
|
-
":method": "POST",
|
|
3398
|
-
":path": requestPath,
|
|
3399
|
-
authorization: `bearer ${params.bearerToken}`,
|
|
3400
|
-
"apns-topic": params.topic,
|
|
3401
|
-
"apns-push-type": params.pushType,
|
|
3402
|
-
"apns-priority": params.priority,
|
|
3403
|
-
"apns-expiration": "0",
|
|
3404
|
-
"content-type": "application/json",
|
|
3405
|
-
"content-length": Buffer.byteLength(body).toString()
|
|
3406
|
-
});
|
|
3407
|
-
let statusCode = 0;
|
|
3408
|
-
let apnsId;
|
|
3409
|
-
let responseBody = "";
|
|
3410
|
-
req.setEncoding("utf8");
|
|
3411
|
-
req.setTimeout(params.timeoutMs, () => {
|
|
3412
|
-
req.close(http2.constants.NGHTTP2_CANCEL);
|
|
3413
|
-
fail(/* @__PURE__ */ new Error(`APNs request timed out after ${params.timeoutMs}ms`));
|
|
3414
|
-
});
|
|
3415
|
-
req.on("response", (headers) => {
|
|
3416
|
-
const statusHeader = headers[":status"];
|
|
3417
|
-
statusCode = typeof statusHeader === "number" ? statusHeader : Number(statusHeader ?? 0);
|
|
3418
|
-
const idHeader = headers["apns-id"];
|
|
3419
|
-
if (typeof idHeader === "string" && idHeader.trim().length > 0) apnsId = idHeader.trim();
|
|
3420
|
-
});
|
|
3421
|
-
req.on("data", (chunk) => {
|
|
3422
|
-
if (typeof chunk === "string") responseBody += chunk;
|
|
3423
|
-
});
|
|
3424
|
-
req.on("end", () => {
|
|
3425
|
-
finish({
|
|
3426
|
-
status: statusCode,
|
|
3427
|
-
apnsId,
|
|
3428
|
-
body: responseBody
|
|
3429
|
-
});
|
|
3430
|
-
});
|
|
3431
|
-
req.on("error", (err) => fail(err));
|
|
3432
|
-
req.end(body);
|
|
3433
|
-
});
|
|
3434
|
-
}
|
|
3435
|
-
function resolveApnsTimeoutMs(timeoutMs) {
|
|
3436
|
-
return typeof timeoutMs === "number" && Number.isFinite(timeoutMs) ? Math.max(1e3, Math.trunc(timeoutMs)) : DEFAULT_APNS_TIMEOUT_MS;
|
|
3437
|
-
}
|
|
3438
|
-
function resolveApnsSendContext(params) {
|
|
3439
|
-
const token = normalizeApnsToken(params.registration.token);
|
|
3440
|
-
if (!isLikelyApnsToken(token)) throw new Error("invalid APNs token");
|
|
3441
|
-
const topic = normalizeTopic(params.registration.topic);
|
|
3442
|
-
if (!topic) throw new Error("topic required");
|
|
3443
|
-
return {
|
|
3444
|
-
token,
|
|
3445
|
-
topic,
|
|
3446
|
-
environment: params.registration.environment,
|
|
3447
|
-
bearerToken: getApnsBearerToken(params.auth)
|
|
3448
|
-
};
|
|
3449
|
-
}
|
|
3450
|
-
function toApnsPushResult(params) {
|
|
3451
|
-
return {
|
|
3452
|
-
ok: params.response.status === 200,
|
|
3453
|
-
status: params.response.status,
|
|
3454
|
-
apnsId: params.response.apnsId,
|
|
3455
|
-
reason: parseReason(params.response.body),
|
|
3456
|
-
tokenSuffix: params.token.slice(-8),
|
|
3457
|
-
topic: params.topic,
|
|
3458
|
-
environment: params.environment
|
|
3459
|
-
};
|
|
3460
|
-
}
|
|
3461
|
-
function createFasedAgentPushMetadata(params) {
|
|
3462
|
-
return {
|
|
3463
|
-
kind: params.kind,
|
|
3464
|
-
nodeId: params.nodeId,
|
|
3465
|
-
ts: Date.now(),
|
|
3466
|
-
...params.reason ? { reason: params.reason } : {}
|
|
3467
|
-
};
|
|
3468
|
-
}
|
|
3469
|
-
async function sendApnsPush(params) {
|
|
3470
|
-
const { token, topic, environment, bearerToken } = resolveApnsSendContext({
|
|
3471
|
-
auth: params.auth,
|
|
3472
|
-
registration: params.registration
|
|
3473
|
-
});
|
|
3474
|
-
return toApnsPushResult({
|
|
3475
|
-
response: await (params.requestSender ?? sendApnsRequest)({
|
|
3476
|
-
token,
|
|
3477
|
-
topic,
|
|
3478
|
-
environment,
|
|
3479
|
-
bearerToken,
|
|
3480
|
-
payload: params.payload,
|
|
3481
|
-
timeoutMs: resolveApnsTimeoutMs(params.timeoutMs),
|
|
3482
|
-
pushType: params.pushType,
|
|
3483
|
-
priority: params.priority
|
|
3484
|
-
}),
|
|
3485
|
-
token,
|
|
3486
|
-
topic,
|
|
3487
|
-
environment
|
|
3488
|
-
});
|
|
3489
|
-
}
|
|
3490
|
-
async function sendApnsAlert(params) {
|
|
3491
|
-
const payload = {
|
|
3492
|
-
aps: {
|
|
3493
|
-
alert: {
|
|
3494
|
-
title: params.title,
|
|
3495
|
-
body: params.body
|
|
3496
|
-
},
|
|
3497
|
-
sound: "default"
|
|
3498
|
-
},
|
|
3499
|
-
fased: createFasedAgentPushMetadata({
|
|
3500
|
-
kind: "push.test",
|
|
3501
|
-
nodeId: params.nodeId
|
|
3502
|
-
})
|
|
3503
|
-
};
|
|
3504
|
-
return await sendApnsPush({
|
|
3505
|
-
auth: params.auth,
|
|
3506
|
-
registration: params.registration,
|
|
3507
|
-
payload,
|
|
3508
|
-
timeoutMs: params.timeoutMs,
|
|
3509
|
-
requestSender: params.requestSender,
|
|
3510
|
-
pushType: "alert",
|
|
3511
|
-
priority: "10"
|
|
3512
|
-
});
|
|
3513
|
-
}
|
|
3514
|
-
|
|
3515
|
-
//#endregion
|
|
3516
|
-
//#region src/gateway/ws-log.ts
|
|
3517
|
-
const LOG_VALUE_LIMIT = 240;
|
|
3518
|
-
const WS_LOG_REDACT_OPTIONS = {
|
|
3519
|
-
mode: "tools",
|
|
3520
|
-
patterns: getDefaultRedactPatterns()
|
|
3521
|
-
};
|
|
3522
|
-
const wsLog = createSubsystemLogger("gateway/ws");
|
|
3523
|
-
function formatForLog(value) {
|
|
3524
|
-
try {
|
|
3525
|
-
if (value instanceof Error) {
|
|
3526
|
-
const parts = [];
|
|
3527
|
-
if (value.name) parts.push(value.name);
|
|
3528
|
-
if (value.message) parts.push(value.message);
|
|
3529
|
-
const code = "code" in value && (typeof value.code === "string" || typeof value.code === "number") ? String(value.code) : "";
|
|
3530
|
-
if (code) parts.push(`code=${code}`);
|
|
3531
|
-
const combined = parts.filter(Boolean).join(": ").trim();
|
|
3532
|
-
if (combined) return combined.length > LOG_VALUE_LIMIT ? `${combined.slice(0, LOG_VALUE_LIMIT)}...` : combined;
|
|
3533
|
-
}
|
|
3534
|
-
if (value && typeof value === "object") {
|
|
3535
|
-
const rec = value;
|
|
3536
|
-
if (typeof rec.message === "string" && rec.message.trim()) {
|
|
3537
|
-
const name = typeof rec.name === "string" ? rec.name.trim() : "";
|
|
3538
|
-
const code = typeof rec.code === "string" || typeof rec.code === "number" ? String(rec.code) : "";
|
|
3539
|
-
const parts = [name, rec.message.trim()].filter(Boolean);
|
|
3540
|
-
if (code) parts.push(`code=${code}`);
|
|
3541
|
-
const combined = parts.join(": ").trim();
|
|
3542
|
-
return combined.length > LOG_VALUE_LIMIT ? `${combined.slice(0, LOG_VALUE_LIMIT)}...` : combined;
|
|
3543
|
-
}
|
|
3544
|
-
}
|
|
3545
|
-
const str = typeof value === "string" || typeof value === "number" ? String(value) : JSON.stringify(value);
|
|
3546
|
-
if (!str) return "";
|
|
3547
|
-
const redacted = redactSensitiveText(str, WS_LOG_REDACT_OPTIONS);
|
|
3548
|
-
return redacted.length > LOG_VALUE_LIMIT ? `${redacted.slice(0, LOG_VALUE_LIMIT)}...` : redacted;
|
|
3549
|
-
} catch {
|
|
3550
|
-
return String(value);
|
|
3551
|
-
}
|
|
3552
|
-
}
|
|
3553
|
-
|
|
3554
|
-
//#endregion
|
|
3555
|
-
//#region src/gateway/server-methods/nodes.helpers.ts
|
|
3556
|
-
function respondInvalidParams(params) {
|
|
3557
|
-
params.respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid ${params.method} params: ${formatValidationErrors(params.validator.errors)}`));
|
|
3558
|
-
}
|
|
3559
|
-
async function respondUnavailableOnThrow(respond, fn) {
|
|
3560
|
-
try {
|
|
3561
|
-
await fn();
|
|
3562
|
-
} catch (err) {
|
|
3563
|
-
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, formatForLog(err)));
|
|
3564
|
-
}
|
|
3565
|
-
}
|
|
3566
|
-
|
|
3567
|
-
//#endregion
|
|
3568
|
-
//#region src/gateway/server-methods/push.ts
|
|
3569
|
-
function normalizeOptionalString(value) {
|
|
3570
|
-
if (typeof value !== "string") return;
|
|
3571
|
-
const trimmed = value.trim();
|
|
3572
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
3573
|
-
}
|
|
3574
|
-
const pushHandlers = { "push.test": async ({ params, respond, client, context }) => {
|
|
3575
|
-
if (!validatePushTestParams(params)) {
|
|
3576
|
-
respondInvalidParams({
|
|
3577
|
-
respond,
|
|
3578
|
-
method: "push.test",
|
|
3579
|
-
validator: validatePushTestParams
|
|
3580
|
-
});
|
|
3581
|
-
return;
|
|
3582
|
-
}
|
|
3583
|
-
const nodeId = String(params.nodeId ?? "").trim();
|
|
3584
|
-
if (!nodeId) {
|
|
3585
|
-
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "nodeId required"));
|
|
3586
|
-
return;
|
|
3587
|
-
}
|
|
3588
|
-
const title = normalizeOptionalString(params.title) ?? "FasedAgent";
|
|
3589
|
-
const body = normalizeOptionalString(params.body) ?? `Push test for node ${nodeId}`;
|
|
3590
|
-
await respondUnavailableOnThrow(respond, async () => {
|
|
3591
|
-
const registration = await loadApnsRegistration(nodeId);
|
|
3592
|
-
if (!registration) {
|
|
3593
|
-
logMutatingAdminRpcAudit({
|
|
3594
|
-
context,
|
|
3595
|
-
client,
|
|
3596
|
-
method: "push.test",
|
|
3597
|
-
outcome: "denied",
|
|
3598
|
-
details: {
|
|
3599
|
-
nodeId,
|
|
3600
|
-
reason: "no_apns_registration"
|
|
3601
|
-
}
|
|
3602
|
-
});
|
|
3603
|
-
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `node ${nodeId} has no APNs registration (connect iOS node first)`));
|
|
3604
|
-
return;
|
|
3605
|
-
}
|
|
3606
|
-
const auth = await resolveApnsAuthConfigFromEnv(process.env);
|
|
3607
|
-
if (!auth.ok) {
|
|
3608
|
-
logMutatingAdminRpcAudit({
|
|
3609
|
-
context,
|
|
3610
|
-
client,
|
|
3611
|
-
method: "push.test",
|
|
3612
|
-
outcome: "failed",
|
|
3613
|
-
details: {
|
|
3614
|
-
nodeId,
|
|
3615
|
-
reason: "apns_auth_unavailable"
|
|
3616
|
-
}
|
|
3617
|
-
});
|
|
3618
|
-
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, auth.error));
|
|
3619
|
-
return;
|
|
3620
|
-
}
|
|
3621
|
-
const overrideEnvironment = normalizeApnsEnvironment(params.environment);
|
|
3622
|
-
const result = await sendApnsAlert({
|
|
3623
|
-
auth: auth.value,
|
|
3624
|
-
registration: {
|
|
3625
|
-
...registration,
|
|
3626
|
-
environment: overrideEnvironment ?? registration.environment
|
|
3627
|
-
},
|
|
3628
|
-
nodeId,
|
|
3629
|
-
title,
|
|
3630
|
-
body
|
|
3631
|
-
});
|
|
3632
|
-
logMutatingAdminRpcAudit({
|
|
3633
|
-
context,
|
|
3634
|
-
client,
|
|
3635
|
-
method: "push.test",
|
|
3636
|
-
outcome: result.ok ? "succeeded" : "failed",
|
|
3637
|
-
details: {
|
|
3638
|
-
nodeId,
|
|
3639
|
-
environment: overrideEnvironment ?? registration.environment,
|
|
3640
|
-
status: result.status
|
|
3641
|
-
}
|
|
3642
|
-
});
|
|
3643
|
-
respond(true, result, void 0);
|
|
3644
|
-
});
|
|
3645
|
-
} };
|
|
3646
|
-
|
|
3647
|
-
//#endregion
|
|
3648
|
-
//#region src/channels/plugins/onboarding/imessage.ts
|
|
3649
|
-
const channel$1 = "imessage";
|
|
3650
|
-
function parseIMessageAllowFromEntries(raw) {
|
|
3651
|
-
return parseOnboardingEntriesAllowingWildcard(raw, (entry) => {
|
|
3652
|
-
const lower = entry.toLowerCase();
|
|
3653
|
-
if (lower.startsWith("chat_id:")) {
|
|
3654
|
-
const id = entry.slice(8).trim();
|
|
3655
|
-
if (!/^\d+$/.test(id)) return { error: `Invalid chat_id: ${entry}` };
|
|
3656
|
-
return { value: entry };
|
|
3657
|
-
}
|
|
3658
|
-
if (lower.startsWith("chat_guid:")) {
|
|
3659
|
-
if (!entry.slice(10).trim()) return { error: "Invalid chat_guid entry" };
|
|
3660
|
-
return { value: entry };
|
|
3661
|
-
}
|
|
3662
|
-
if (lower.startsWith("chat_identifier:")) {
|
|
3663
|
-
if (!entry.slice(16).trim()) return { error: "Invalid chat_identifier entry" };
|
|
3664
|
-
return { value: entry };
|
|
3665
|
-
}
|
|
3666
|
-
if (!normalizeIMessageHandle(entry)) return { error: `Invalid handle: ${entry}` };
|
|
3667
|
-
return { value: entry };
|
|
3668
|
-
});
|
|
3669
|
-
}
|
|
3670
|
-
async function promptIMessageAllowFrom(params) {
|
|
3671
|
-
return promptParsedAllowFromForScopedChannel({
|
|
3672
|
-
cfg: params.cfg,
|
|
3673
|
-
channel: "imessage",
|
|
3674
|
-
accountId: params.accountId,
|
|
3675
|
-
defaultAccountId: resolveDefaultIMessageAccountId(params.cfg),
|
|
3676
|
-
prompter: params.prompter,
|
|
3677
|
-
noteTitle: "iMessage allowlist",
|
|
3678
|
-
noteLines: [
|
|
3679
|
-
"Allowlist iMessage DMs by handle or chat target.",
|
|
3680
|
-
"Examples:",
|
|
3681
|
-
"- +15555550123",
|
|
3682
|
-
"- user@example.com",
|
|
3683
|
-
"- chat_id:123",
|
|
3684
|
-
"- chat_guid:... or chat_identifier:...",
|
|
3685
|
-
"Multiple entries: comma-separated.",
|
|
3686
|
-
`Docs: ${formatDocsLink("/imessage", "imessage")}`
|
|
3687
|
-
],
|
|
3688
|
-
message: "iMessage allowFrom (handle or chat_id)",
|
|
3689
|
-
placeholder: "+15555550123, user@example.com, chat_id:123",
|
|
3690
|
-
parseEntries: parseIMessageAllowFromEntries,
|
|
3691
|
-
getExistingAllowFrom: ({ cfg, accountId }) => {
|
|
3692
|
-
return resolveIMessageAccount({
|
|
3693
|
-
cfg,
|
|
3694
|
-
accountId
|
|
3695
|
-
}).config.allowFrom ?? [];
|
|
3696
|
-
}
|
|
3697
|
-
});
|
|
3698
|
-
}
|
|
3699
|
-
const dmPolicy$1 = {
|
|
3700
|
-
label: "iMessage",
|
|
3701
|
-
channel: channel$1,
|
|
3702
|
-
policyKey: "channels.imessage.dmPolicy",
|
|
3703
|
-
allowFromKey: "channels.imessage.allowFrom",
|
|
3704
|
-
getCurrent: (cfg) => cfg.channels?.imessage?.dmPolicy ?? "pairing",
|
|
3705
|
-
setPolicy: (cfg, policy) => setChannelDmPolicyWithAllowFrom({
|
|
3706
|
-
cfg,
|
|
3707
|
-
channel: "imessage",
|
|
3708
|
-
dmPolicy: policy
|
|
3709
|
-
}),
|
|
3710
|
-
promptAllowFrom: promptIMessageAllowFrom
|
|
3711
|
-
};
|
|
3712
|
-
const imessageOnboardingAdapter = {
|
|
3713
|
-
channel: channel$1,
|
|
3714
|
-
uiSetup: {
|
|
3715
|
-
title: "iMessage",
|
|
3716
|
-
detail: "macOS imsg bridge.",
|
|
3717
|
-
notes: [
|
|
3718
|
-
"This is still a work in progress.",
|
|
3719
|
-
"Ensure FasedAgent has Full Disk Access to the Messages DB.",
|
|
3720
|
-
"Grant Automation permission for Messages when prompted.",
|
|
3721
|
-
"List chats with: imsg chats --limit 20"
|
|
3722
|
-
],
|
|
3723
|
-
fields: [{
|
|
3724
|
-
label: "CLI path",
|
|
3725
|
-
path: [
|
|
3726
|
-
"channels",
|
|
3727
|
-
"imessage",
|
|
3728
|
-
"cliPath"
|
|
3729
|
-
],
|
|
3730
|
-
placeholder: "imsg"
|
|
3731
|
-
}]
|
|
3732
|
-
},
|
|
3733
|
-
getStatus: async ({ cfg }) => {
|
|
3734
|
-
const configured = listIMessageAccountIds(cfg).some((accountId) => resolveIMessageAccount({
|
|
3735
|
-
cfg,
|
|
3736
|
-
accountId
|
|
3737
|
-
}).configured);
|
|
3738
|
-
const imessageCliPath = cfg.channels?.imessage?.cliPath ?? "imsg";
|
|
3739
|
-
const imessageCliDetected = await detectBinary(imessageCliPath);
|
|
3740
|
-
return {
|
|
3741
|
-
channel: channel$1,
|
|
3742
|
-
configured,
|
|
3743
|
-
statusLines: [`iMessage: ${configured ? "configured" : "needs setup"}`, `imsg: ${imessageCliDetected ? "found" : "missing"} (${imessageCliPath})`],
|
|
3744
|
-
selectionHint: imessageCliDetected ? "imsg found" : "imsg missing",
|
|
3745
|
-
quickstartScore: imessageCliDetected ? 1 : 0
|
|
3746
|
-
};
|
|
3747
|
-
},
|
|
3748
|
-
configure: async ({ cfg, prompter, accountOverrides, shouldPromptAccountIds }) => {
|
|
3749
|
-
const defaultIMessageAccountId = resolveDefaultIMessageAccountId(cfg);
|
|
3750
|
-
const imessageAccountId = await resolveAccountIdForConfigure({
|
|
3751
|
-
cfg,
|
|
3752
|
-
prompter,
|
|
3753
|
-
label: "iMessage",
|
|
3754
|
-
accountOverride: accountOverrides.imessage,
|
|
3755
|
-
shouldPromptAccountIds,
|
|
3756
|
-
listAccountIds: listIMessageAccountIds,
|
|
3757
|
-
defaultAccountId: defaultIMessageAccountId
|
|
3758
|
-
});
|
|
3759
|
-
let next = cfg;
|
|
3760
|
-
let resolvedCliPath = resolveIMessageAccount({
|
|
3761
|
-
cfg: next,
|
|
3762
|
-
accountId: imessageAccountId
|
|
3763
|
-
}).config.cliPath ?? "imsg";
|
|
3764
|
-
if (!await detectBinary(resolvedCliPath)) {
|
|
3765
|
-
const entered = await prompter.text({
|
|
3766
|
-
message: "imsg CLI path",
|
|
3767
|
-
initialValue: resolvedCliPath,
|
|
3768
|
-
validate: (value) => value?.trim() ? void 0 : "Required"
|
|
3769
|
-
});
|
|
3770
|
-
resolvedCliPath = String(entered).trim();
|
|
3771
|
-
if (!resolvedCliPath) await prompter.note("imsg CLI path required to enable iMessage.", "iMessage");
|
|
3772
|
-
}
|
|
3773
|
-
if (resolvedCliPath) next = patchChannelConfigForAccount({
|
|
3774
|
-
cfg: next,
|
|
3775
|
-
channel: "imessage",
|
|
3776
|
-
accountId: imessageAccountId,
|
|
3777
|
-
patch: { cliPath: resolvedCliPath }
|
|
3778
|
-
});
|
|
3779
|
-
await prompter.note([
|
|
3780
|
-
"This is still a work in progress.",
|
|
3781
|
-
"Ensure FasedAgent has Full Disk Access to Messages DB.",
|
|
3782
|
-
"Grant Automation permission for Messages when prompted.",
|
|
3783
|
-
"List chats with: imsg chats --limit 20",
|
|
3784
|
-
`Docs: ${formatDocsLink("/imessage", "imessage")}`
|
|
3785
|
-
].join("\n"), "iMessage next steps");
|
|
3786
|
-
return {
|
|
3787
|
-
cfg: next,
|
|
3788
|
-
accountId: imessageAccountId
|
|
3789
|
-
};
|
|
3790
|
-
},
|
|
3791
|
-
dmPolicy: dmPolicy$1,
|
|
3792
|
-
disable: (cfg) => setOnboardingChannelEnabled(cfg, channel$1, false)
|
|
3793
|
-
};
|
|
3794
|
-
|
|
3795
|
-
//#endregion
|
|
3796
|
-
//#region src/channels/plugins/normalize/imessage.ts
|
|
3797
|
-
const SERVICE_PREFIXES = [
|
|
3798
|
-
"imessage:",
|
|
3799
|
-
"sms:",
|
|
3800
|
-
"auto:"
|
|
3801
|
-
];
|
|
3802
|
-
const CHAT_TARGET_PREFIX_RE = /^(chat_id:|chatid:|chat:|chat_guid:|chatguid:|guid:|chat_identifier:|chatidentifier:|chatident:)/i;
|
|
3803
|
-
function normalizeIMessageMessagingTarget(raw) {
|
|
3804
|
-
const trimmed = trimMessagingTarget(raw);
|
|
3805
|
-
if (!trimmed) return;
|
|
3806
|
-
const lower = trimmed.toLowerCase();
|
|
3807
|
-
for (const prefix of SERVICE_PREFIXES) if (lower.startsWith(prefix)) {
|
|
3808
|
-
const normalizedHandle = normalizeIMessageHandle(trimmed.slice(prefix.length).trim());
|
|
3809
|
-
if (!normalizedHandle) return;
|
|
3810
|
-
if (CHAT_TARGET_PREFIX_RE.test(normalizedHandle)) return normalizedHandle;
|
|
3811
|
-
return `${prefix}${normalizedHandle}`;
|
|
3812
|
-
}
|
|
3813
|
-
return normalizeIMessageHandle(trimmed) || void 0;
|
|
3814
|
-
}
|
|
3815
|
-
function looksLikeIMessageTargetId(raw) {
|
|
3816
|
-
const trimmed = trimMessagingTarget(raw);
|
|
3817
|
-
if (!trimmed) return false;
|
|
3818
|
-
if (CHAT_TARGET_PREFIX_RE.test(trimmed)) return true;
|
|
3819
|
-
return looksLikeHandleOrPhoneTarget({
|
|
3820
|
-
raw: trimmed,
|
|
3821
|
-
prefixPattern: /^(imessage:|sms:|auto:)/i
|
|
3822
|
-
});
|
|
3823
|
-
}
|
|
3824
|
-
|
|
3825
|
-
//#endregion
|
|
3826
|
-
//#region src/commands/signal-install.ts
|
|
3827
|
-
/** @internal Exported for testing. */
|
|
3828
|
-
async function extractSignalCliArchive(archivePath, installRoot, timeoutMs) {
|
|
3829
|
-
await extractArchive({
|
|
3830
|
-
archivePath,
|
|
3831
|
-
destDir: installRoot,
|
|
3832
|
-
timeoutMs
|
|
3833
|
-
});
|
|
3834
|
-
}
|
|
3835
|
-
/** @internal Exported for testing. */
|
|
3836
|
-
function looksLikeArchive(name) {
|
|
3837
|
-
return name.endsWith(".tar.gz") || name.endsWith(".tgz") || name.endsWith(".zip");
|
|
3838
|
-
}
|
|
3839
|
-
/**
|
|
3840
|
-
* Pick a native release asset from the official GitHub releases.
|
|
3841
|
-
*
|
|
3842
|
-
* The official signal-cli releases only publish native (GraalVM) binaries for
|
|
3843
|
-
* x86-64 Linux. On architectures where no native asset is available this
|
|
3844
|
-
* returns `undefined` so the caller can fall back to a different install
|
|
3845
|
-
* strategy (e.g. Homebrew).
|
|
3846
|
-
*/
|
|
3847
|
-
/** @internal Exported for testing. */
|
|
3848
|
-
function pickAsset(assets, platform, arch) {
|
|
3849
|
-
const archives = assets.filter((asset) => Boolean(asset.name && asset.browser_download_url)).filter((a) => looksLikeArchive(a.name.toLowerCase()));
|
|
3850
|
-
const byName = (pattern) => archives.find((asset) => pattern.test(asset.name.toLowerCase()));
|
|
3851
|
-
if (platform === "linux") {
|
|
3852
|
-
if (arch === "x64") return byName(/linux-native/) || byName(/linux/) || archives[0];
|
|
3853
|
-
return;
|
|
3854
|
-
}
|
|
3855
|
-
if (platform === "darwin") return byName(/macos|osx|darwin/) || archives[0];
|
|
3856
|
-
if (platform === "win32") return byName(/windows|win/) || archives[0];
|
|
3857
|
-
return archives[0];
|
|
3858
|
-
}
|
|
3859
|
-
async function downloadToFile(url, dest, maxRedirects = 5) {
|
|
3860
|
-
await new Promise((resolve, reject) => {
|
|
3861
|
-
const req = request(url, (res) => {
|
|
3862
|
-
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400) {
|
|
3863
|
-
const location = res.headers.location;
|
|
3864
|
-
if (!location || maxRedirects <= 0) {
|
|
3865
|
-
reject(/* @__PURE__ */ new Error("Redirect loop or missing Location header"));
|
|
3866
|
-
return;
|
|
3867
|
-
}
|
|
3868
|
-
const redirectUrl = new URL(location, url).href;
|
|
3869
|
-
resolve(downloadToFile(redirectUrl, dest, maxRedirects - 1));
|
|
3870
|
-
return;
|
|
3871
|
-
}
|
|
3872
|
-
if (!res.statusCode || res.statusCode >= 400) {
|
|
3873
|
-
reject(/* @__PURE__ */ new Error(`HTTP ${res.statusCode ?? "?"} downloading file`));
|
|
3874
|
-
return;
|
|
3875
|
-
}
|
|
3876
|
-
pipeline(res, createWriteStream(dest)).then(resolve).catch(reject);
|
|
3877
|
-
});
|
|
3878
|
-
req.on("error", reject);
|
|
3879
|
-
req.end();
|
|
3880
|
-
});
|
|
3881
|
-
}
|
|
3882
|
-
async function findSignalCliBinary(root) {
|
|
3883
|
-
const candidates = [];
|
|
3884
|
-
const enqueue = async (dir, depth) => {
|
|
3885
|
-
if (depth > 3) return;
|
|
3886
|
-
const entries = await fs$1.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
3887
|
-
for (const entry of entries) {
|
|
3888
|
-
const full = path.join(dir, entry.name);
|
|
3889
|
-
if (entry.isDirectory()) await enqueue(full, depth + 1);
|
|
3890
|
-
else if (entry.isFile() && entry.name === "signal-cli") candidates.push(full);
|
|
3891
|
-
}
|
|
3892
|
-
};
|
|
3893
|
-
await enqueue(root, 0);
|
|
3894
|
-
return candidates[0] ?? null;
|
|
3895
|
-
}
|
|
3896
|
-
async function resolveBrewSignalCliPath(brewExe) {
|
|
3897
|
-
try {
|
|
3898
|
-
const result = await runCommandWithTimeout([
|
|
3899
|
-
brewExe,
|
|
3900
|
-
"--prefix",
|
|
3901
|
-
"signal-cli"
|
|
3902
|
-
], { timeoutMs: 1e4 });
|
|
3903
|
-
if (result.code === 0 && result.stdout.trim()) {
|
|
3904
|
-
const prefix = result.stdout.trim();
|
|
3905
|
-
const candidate = path.join(prefix, "bin", "signal-cli");
|
|
3906
|
-
try {
|
|
3907
|
-
await fs$1.access(candidate);
|
|
3908
|
-
return candidate;
|
|
3909
|
-
} catch {
|
|
3910
|
-
return findSignalCliBinary(prefix);
|
|
3911
|
-
}
|
|
3912
|
-
}
|
|
3913
|
-
} catch {}
|
|
3914
|
-
return null;
|
|
3915
|
-
}
|
|
3916
|
-
async function installSignalCliViaBrew(runtime) {
|
|
3917
|
-
const brewExe = resolveBrewExecutable();
|
|
3918
|
-
if (!brewExe) return {
|
|
3919
|
-
ok: false,
|
|
3920
|
-
error: `No native signal-cli build is available for ${process.arch}. Install Homebrew (https://brew.sh) and try again, or install signal-cli manually.`
|
|
3921
|
-
};
|
|
3922
|
-
runtime.log(`Installing signal-cli via Homebrew (${brewExe})…`);
|
|
3923
|
-
const result = await runCommandWithTimeout([
|
|
3924
|
-
brewExe,
|
|
3925
|
-
"install",
|
|
3926
|
-
"signal-cli"
|
|
3927
|
-
], { timeoutMs: 15 * 6e4 });
|
|
3928
|
-
if (result.code !== 0) return {
|
|
3929
|
-
ok: false,
|
|
3930
|
-
error: `brew install signal-cli failed (exit ${result.code}): ${result.stderr.trim().slice(0, 200)}`
|
|
3931
|
-
};
|
|
3932
|
-
const cliPath = await resolveBrewSignalCliPath(brewExe);
|
|
3933
|
-
if (!cliPath) return {
|
|
3934
|
-
ok: false,
|
|
3935
|
-
error: "brew install succeeded but signal-cli binary was not found."
|
|
3936
|
-
};
|
|
3937
|
-
let version;
|
|
3938
|
-
try {
|
|
3939
|
-
version = (await runCommandWithTimeout([cliPath, "--version"], { timeoutMs: 1e4 })).stdout.trim().replace(/^signal-cli\s+/, "") || void 0;
|
|
3940
|
-
} catch {}
|
|
3941
|
-
return {
|
|
3942
|
-
ok: true,
|
|
3943
|
-
cliPath,
|
|
3944
|
-
version
|
|
3945
|
-
};
|
|
3946
|
-
}
|
|
3947
|
-
async function installSignalCliFromRelease(runtime) {
|
|
3948
|
-
const response = await fetch("https://api.github.com/repos/AsamK/signal-cli/releases/latest", { headers: {
|
|
3949
|
-
"User-Agent": "fased",
|
|
3950
|
-
Accept: "application/vnd.github+json"
|
|
3951
|
-
} });
|
|
3952
|
-
if (!response.ok) return {
|
|
3953
|
-
ok: false,
|
|
3954
|
-
error: `Failed to fetch release info (${response.status})`
|
|
3955
|
-
};
|
|
3956
|
-
const payload = await response.json();
|
|
3957
|
-
const version = payload.tag_name?.replace(/^v/, "") ?? "unknown";
|
|
3958
|
-
const asset = pickAsset(payload.assets ?? [], process.platform, process.arch);
|
|
3959
|
-
if (!asset) return {
|
|
3960
|
-
ok: false,
|
|
3961
|
-
error: "No compatible release asset found for this platform."
|
|
3962
|
-
};
|
|
3963
|
-
const tmpDir = await fs$1.mkdtemp(path.join(os.tmpdir(), "fased-signal-"));
|
|
3964
|
-
const archivePath = path.join(tmpDir, asset.name);
|
|
3965
|
-
runtime.log(`Downloading signal-cli ${version} (${asset.name})…`);
|
|
3966
|
-
await downloadToFile(asset.browser_download_url, archivePath);
|
|
3967
|
-
const installRoot = path.join(CONFIG_DIR, "tools", "signal-cli", version);
|
|
3968
|
-
await fs$1.mkdir(installRoot, { recursive: true });
|
|
3969
|
-
if (!looksLikeArchive(asset.name.toLowerCase())) return {
|
|
3970
|
-
ok: false,
|
|
3971
|
-
error: `Unsupported archive type: ${asset.name}`
|
|
3972
|
-
};
|
|
3973
|
-
try {
|
|
3974
|
-
await extractSignalCliArchive(archivePath, installRoot, 6e4);
|
|
3975
|
-
} catch (err) {
|
|
3976
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
3977
|
-
return {
|
|
3978
|
-
ok: false,
|
|
3979
|
-
error: `Failed to extract ${asset.name}: ${message}`
|
|
3980
|
-
};
|
|
3981
|
-
}
|
|
3982
|
-
const cliPath = await findSignalCliBinary(installRoot);
|
|
3983
|
-
if (!cliPath) return {
|
|
3984
|
-
ok: false,
|
|
3985
|
-
error: `signal-cli binary not found after extracting ${asset.name}`
|
|
3986
|
-
};
|
|
3987
|
-
await fs$1.chmod(cliPath, 493).catch(() => {});
|
|
3988
|
-
return {
|
|
3989
|
-
ok: true,
|
|
3990
|
-
cliPath,
|
|
3991
|
-
version
|
|
3992
|
-
};
|
|
3993
|
-
}
|
|
3994
|
-
async function installSignalCli(runtime) {
|
|
3995
|
-
if (process.platform === "win32") return {
|
|
3996
|
-
ok: false,
|
|
3997
|
-
error: "Signal CLI auto-install is not supported on Windows yet."
|
|
3998
|
-
};
|
|
3999
|
-
if (process.platform !== "linux" || process.arch === "x64") return installSignalCliFromRelease(runtime);
|
|
4000
|
-
return installSignalCliViaBrew(runtime);
|
|
4001
|
-
}
|
|
4002
|
-
|
|
4003
|
-
//#endregion
|
|
4004
|
-
//#region src/channels/plugins/onboarding/signal.ts
|
|
4005
|
-
const channel = "signal";
|
|
4006
|
-
const MIN_E164_DIGITS = 5;
|
|
4007
|
-
const MAX_E164_DIGITS = 15;
|
|
4008
|
-
const DIGITS_ONLY = /^\d+$/;
|
|
4009
|
-
const INVALID_SIGNAL_ACCOUNT_ERROR = "Invalid E.164 phone number (must start with + and country code, e.g. +15555550123)";
|
|
4010
|
-
function normalizeSignalAccountInput(value) {
|
|
4011
|
-
const trimmed = value?.trim();
|
|
4012
|
-
if (!trimmed) return null;
|
|
4013
|
-
const digits = normalizeE164(trimmed).slice(1);
|
|
4014
|
-
if (!DIGITS_ONLY.test(digits)) return null;
|
|
4015
|
-
if (digits.length < MIN_E164_DIGITS || digits.length > MAX_E164_DIGITS) return null;
|
|
4016
|
-
return `+${digits}`;
|
|
4017
|
-
}
|
|
4018
|
-
function isUuidLike(value) {
|
|
4019
|
-
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
4020
|
-
}
|
|
4021
|
-
function parseSignalAllowFromEntries(raw) {
|
|
4022
|
-
return parseOnboardingEntriesAllowingWildcard(raw, (entry) => {
|
|
4023
|
-
if (entry.toLowerCase().startsWith("uuid:")) {
|
|
4024
|
-
const id = entry.slice(5).trim();
|
|
4025
|
-
if (!id) return { error: "Invalid uuid entry" };
|
|
4026
|
-
return { value: `uuid:${id}` };
|
|
4027
|
-
}
|
|
4028
|
-
if (isUuidLike(entry)) return { value: `uuid:${entry}` };
|
|
4029
|
-
const normalized = normalizeSignalAccountInput(entry);
|
|
4030
|
-
if (!normalized) return { error: `Invalid entry: ${entry}` };
|
|
4031
|
-
return { value: normalized };
|
|
4032
|
-
});
|
|
4033
|
-
}
|
|
4034
|
-
async function promptSignalAllowFrom(params) {
|
|
4035
|
-
return promptParsedAllowFromForScopedChannel({
|
|
4036
|
-
cfg: params.cfg,
|
|
4037
|
-
channel: "signal",
|
|
4038
|
-
accountId: params.accountId,
|
|
4039
|
-
defaultAccountId: resolveDefaultSignalAccountId(params.cfg),
|
|
4040
|
-
prompter: params.prompter,
|
|
4041
|
-
noteTitle: "Signal allowlist",
|
|
4042
|
-
noteLines: [
|
|
4043
|
-
"Allowlist Signal DMs by sender id.",
|
|
4044
|
-
"Examples:",
|
|
4045
|
-
"- +15555550123",
|
|
4046
|
-
"- uuid:123e4567-e89b-12d3-a456-426614174000",
|
|
4047
|
-
"Multiple entries: comma-separated.",
|
|
4048
|
-
`Docs: ${formatDocsLink("/signal", "signal")}`
|
|
4049
|
-
],
|
|
4050
|
-
message: "Signal allowFrom (E.164 or uuid)",
|
|
4051
|
-
placeholder: "+15555550123, uuid:123e4567-e89b-12d3-a456-426614174000",
|
|
4052
|
-
parseEntries: parseSignalAllowFromEntries,
|
|
4053
|
-
getExistingAllowFrom: ({ cfg, accountId }) => {
|
|
4054
|
-
return resolveSignalAccount({
|
|
4055
|
-
cfg,
|
|
4056
|
-
accountId
|
|
4057
|
-
}).config.allowFrom ?? [];
|
|
4058
|
-
}
|
|
4059
|
-
});
|
|
4060
|
-
}
|
|
4061
|
-
const dmPolicy = {
|
|
4062
|
-
label: "Signal",
|
|
4063
|
-
channel,
|
|
4064
|
-
policyKey: "channels.signal.dmPolicy",
|
|
4065
|
-
allowFromKey: "channels.signal.allowFrom",
|
|
4066
|
-
getCurrent: (cfg) => cfg.channels?.signal?.dmPolicy ?? "pairing",
|
|
4067
|
-
setPolicy: (cfg, policy) => setChannelDmPolicyWithAllowFrom({
|
|
4068
|
-
cfg,
|
|
4069
|
-
channel: "signal",
|
|
4070
|
-
dmPolicy: policy
|
|
4071
|
-
}),
|
|
4072
|
-
promptAllowFrom: promptSignalAllowFrom
|
|
4073
|
-
};
|
|
4074
|
-
const signalOnboardingAdapter = {
|
|
4075
|
-
channel,
|
|
4076
|
-
uiSetup: {
|
|
4077
|
-
title: "Signal",
|
|
4078
|
-
detail: "signal-cli account.",
|
|
4079
|
-
notes: [
|
|
4080
|
-
"Install signal-cli if it is missing.",
|
|
4081
|
-
"Set the Signal bot number in E.164 format.",
|
|
4082
|
-
"Link device with: signal-cli link -n \"FasedAgent\"",
|
|
4083
|
-
"Scan the QR in Signal -> Linked Devices."
|
|
4084
|
-
],
|
|
4085
|
-
fields: [{
|
|
4086
|
-
label: "Signal number",
|
|
4087
|
-
path: [
|
|
4088
|
-
"channels",
|
|
4089
|
-
"signal",
|
|
4090
|
-
"account"
|
|
4091
|
-
],
|
|
4092
|
-
placeholder: "+15551234567"
|
|
4093
|
-
}, {
|
|
4094
|
-
label: "CLI path",
|
|
4095
|
-
path: [
|
|
4096
|
-
"channels",
|
|
4097
|
-
"signal",
|
|
4098
|
-
"cliPath"
|
|
4099
|
-
],
|
|
4100
|
-
placeholder: "signal-cli"
|
|
4101
|
-
}]
|
|
4102
|
-
},
|
|
4103
|
-
getStatus: async ({ cfg }) => {
|
|
4104
|
-
const configured = listSignalAccountIds(cfg).some((accountId) => resolveSignalAccount({
|
|
4105
|
-
cfg,
|
|
4106
|
-
accountId
|
|
4107
|
-
}).configured);
|
|
4108
|
-
const signalCliPath = cfg.channels?.signal?.cliPath ?? "signal-cli";
|
|
4109
|
-
const signalCliDetected = await detectBinary(signalCliPath);
|
|
4110
|
-
return {
|
|
4111
|
-
channel,
|
|
4112
|
-
configured,
|
|
4113
|
-
statusLines: [`Signal: ${configured ? "configured" : "needs setup"}`, `signal-cli: ${signalCliDetected ? "found" : "missing"} (${signalCliPath})`],
|
|
4114
|
-
selectionHint: signalCliDetected ? "signal-cli found" : "signal-cli missing",
|
|
4115
|
-
quickstartScore: signalCliDetected ? 1 : 0
|
|
4116
|
-
};
|
|
4117
|
-
},
|
|
4118
|
-
configure: async ({ cfg, runtime, prompter, accountOverrides, shouldPromptAccountIds, options }) => {
|
|
4119
|
-
const defaultSignalAccountId = resolveDefaultSignalAccountId(cfg);
|
|
4120
|
-
const signalAccountId = await resolveAccountIdForConfigure({
|
|
4121
|
-
cfg,
|
|
4122
|
-
prompter,
|
|
4123
|
-
label: "Signal",
|
|
4124
|
-
accountOverride: accountOverrides.signal,
|
|
4125
|
-
shouldPromptAccountIds,
|
|
4126
|
-
listAccountIds: listSignalAccountIds,
|
|
4127
|
-
defaultAccountId: defaultSignalAccountId
|
|
4128
|
-
});
|
|
4129
|
-
let next = cfg;
|
|
4130
|
-
const accountConfig = resolveSignalAccount({
|
|
4131
|
-
cfg: next,
|
|
4132
|
-
accountId: signalAccountId
|
|
4133
|
-
}).config;
|
|
4134
|
-
let resolvedCliPath = accountConfig.cliPath ?? "signal-cli";
|
|
4135
|
-
let cliDetected = await detectBinary(resolvedCliPath);
|
|
4136
|
-
if (options?.allowSignalInstall) {
|
|
4137
|
-
if (await prompter.confirm({
|
|
4138
|
-
message: cliDetected ? "signal-cli detected. Reinstall/update now?" : "signal-cli not found. Install now?",
|
|
4139
|
-
initialValue: !cliDetected
|
|
4140
|
-
})) try {
|
|
4141
|
-
const result = await installSignalCli(runtime);
|
|
4142
|
-
if (result.ok && result.cliPath) {
|
|
4143
|
-
cliDetected = true;
|
|
4144
|
-
resolvedCliPath = result.cliPath;
|
|
4145
|
-
await prompter.note(`Installed signal-cli at ${result.cliPath}`, "Signal");
|
|
4146
|
-
} else if (!result.ok) await prompter.note(result.error ?? "signal-cli install failed.", "Signal");
|
|
4147
|
-
} catch (err) {
|
|
4148
|
-
await prompter.note(`signal-cli install failed: ${String(err)}`, "Signal");
|
|
4149
|
-
}
|
|
4150
|
-
}
|
|
4151
|
-
if (!cliDetected) await prompter.note("signal-cli not found. Install it, then rerun this step or set channels.signal.cliPath.", "Signal");
|
|
4152
|
-
let account = accountConfig.account ?? "";
|
|
4153
|
-
if (account) {
|
|
4154
|
-
const normalizedExisting = normalizeSignalAccountInput(account);
|
|
4155
|
-
if (!normalizedExisting) {
|
|
4156
|
-
await prompter.note("Existing Signal account isn't a valid E.164 number. Please enter it again.", "Signal");
|
|
4157
|
-
account = "";
|
|
4158
|
-
} else {
|
|
4159
|
-
account = normalizedExisting;
|
|
4160
|
-
if (!await prompter.confirm({
|
|
4161
|
-
message: `Signal account set (${account}). Keep it?`,
|
|
4162
|
-
initialValue: true
|
|
4163
|
-
})) account = "";
|
|
4164
|
-
}
|
|
4165
|
-
}
|
|
4166
|
-
if (!account) account = normalizeSignalAccountInput(String(await prompter.text({
|
|
4167
|
-
message: "Signal bot number (E.164)",
|
|
4168
|
-
validate: (value) => normalizeSignalAccountInput(String(value ?? "")) ? void 0 : INVALID_SIGNAL_ACCOUNT_ERROR
|
|
4169
|
-
}))) ?? "";
|
|
4170
|
-
if (account) next = patchChannelConfigForAccount({
|
|
4171
|
-
cfg: next,
|
|
4172
|
-
channel: "signal",
|
|
4173
|
-
accountId: signalAccountId,
|
|
4174
|
-
patch: {
|
|
4175
|
-
account,
|
|
4176
|
-
cliPath: resolvedCliPath ?? "signal-cli"
|
|
4177
|
-
}
|
|
4178
|
-
});
|
|
4179
|
-
await prompter.note([
|
|
4180
|
-
"Link device with: signal-cli link -n \"FasedAgent\"",
|
|
4181
|
-
"Scan QR in Signal → Linked Devices",
|
|
4182
|
-
`Then run: ${formatCliCommand("fased gateway call channels.status --params '{\"probe\":true}'")}`,
|
|
4183
|
-
`Docs: ${formatDocsLink("/signal", "signal")}`
|
|
4184
|
-
].join("\n"), "Signal next steps");
|
|
4185
|
-
return {
|
|
4186
|
-
cfg: next,
|
|
4187
|
-
accountId: signalAccountId
|
|
4188
|
-
};
|
|
4189
|
-
},
|
|
4190
|
-
dmPolicy,
|
|
4191
|
-
disable: (cfg) => setOnboardingChannelEnabled(cfg, channel, false)
|
|
4192
|
-
};
|
|
4193
|
-
|
|
4194
|
-
//#endregion
|
|
4195
|
-
//#region src/channels/plugins/status-issues/bluebubbles.ts
|
|
4196
|
-
function readBlueBubblesAccountStatus(value) {
|
|
4197
|
-
if (!isRecord(value)) return null;
|
|
4198
|
-
return {
|
|
4199
|
-
accountId: value.accountId,
|
|
4200
|
-
enabled: value.enabled,
|
|
4201
|
-
configured: value.configured,
|
|
4202
|
-
running: value.running,
|
|
4203
|
-
baseUrl: value.baseUrl,
|
|
4204
|
-
lastError: value.lastError,
|
|
4205
|
-
probe: value.probe
|
|
4206
|
-
};
|
|
4207
|
-
}
|
|
4208
|
-
function readBlueBubblesProbeResult(value) {
|
|
4209
|
-
if (!isRecord(value)) return null;
|
|
4210
|
-
return {
|
|
4211
|
-
ok: typeof value.ok === "boolean" ? value.ok : void 0,
|
|
4212
|
-
status: typeof value.status === "number" ? value.status : null,
|
|
4213
|
-
error: asString(value.error) ?? null
|
|
4214
|
-
};
|
|
4215
|
-
}
|
|
4216
|
-
function collectBlueBubblesStatusIssues(accounts) {
|
|
4217
|
-
return collectIssuesForEnabledAccounts({
|
|
4218
|
-
accounts,
|
|
4219
|
-
readAccount: readBlueBubblesAccountStatus,
|
|
4220
|
-
collectIssues: ({ account, accountId, issues }) => {
|
|
4221
|
-
const configured = account.configured === true;
|
|
4222
|
-
const running = account.running === true;
|
|
4223
|
-
const lastError = asString(account.lastError);
|
|
4224
|
-
const probe = readBlueBubblesProbeResult(account.probe);
|
|
4225
|
-
if (!configured) {
|
|
4226
|
-
issues.push({
|
|
4227
|
-
channel: "bluebubbles",
|
|
4228
|
-
accountId,
|
|
4229
|
-
kind: "config",
|
|
4230
|
-
message: "Not configured (missing serverUrl or password).",
|
|
4231
|
-
fix: "Run: fased channels add bluebubbles --http-url <server-url> --password <password>"
|
|
4232
|
-
});
|
|
4233
|
-
return;
|
|
4234
|
-
}
|
|
4235
|
-
if (probe && probe.ok === false) {
|
|
4236
|
-
const errorDetail = probe.error ? `: ${probe.error}` : probe.status ? ` (HTTP ${probe.status})` : "";
|
|
4237
|
-
issues.push({
|
|
4238
|
-
channel: "bluebubbles",
|
|
4239
|
-
accountId,
|
|
4240
|
-
kind: "runtime",
|
|
4241
|
-
message: `BlueBubbles server unreachable${errorDetail}`,
|
|
4242
|
-
fix: "Check that the BlueBubbles server is running and accessible. Verify serverUrl and password in your config."
|
|
4243
|
-
});
|
|
4244
|
-
}
|
|
4245
|
-
if (running && lastError) issues.push({
|
|
4246
|
-
channel: "bluebubbles",
|
|
4247
|
-
accountId,
|
|
4248
|
-
kind: "runtime",
|
|
4249
|
-
message: `Channel error: ${lastError}`,
|
|
4250
|
-
fix: "Check gateway logs for details. If the webhook is failing, verify the webhook URL is configured in BlueBubbles server settings."
|
|
4251
|
-
});
|
|
4252
|
-
}
|
|
4253
|
-
});
|
|
4254
|
-
}
|
|
4255
|
-
|
|
4256
|
-
//#endregion
|
|
4257
|
-
//#region src/line/config-schema.ts
|
|
4258
|
-
const DmPolicySchema$1 = z.enum([
|
|
4259
|
-
"open",
|
|
4260
|
-
"allowlist",
|
|
4261
|
-
"pairing",
|
|
4262
|
-
"disabled"
|
|
4263
|
-
]);
|
|
4264
|
-
const GroupPolicySchema$1 = z.enum([
|
|
4265
|
-
"open",
|
|
4266
|
-
"allowlist",
|
|
4267
|
-
"disabled"
|
|
4268
|
-
]);
|
|
4269
|
-
const LineCommonConfigSchema = z.object({
|
|
4270
|
-
enabled: z.boolean().optional(),
|
|
4271
|
-
channelAccessToken: z.string().optional(),
|
|
4272
|
-
channelSecret: z.string().optional(),
|
|
4273
|
-
tokenFile: z.string().optional(),
|
|
4274
|
-
secretFile: z.string().optional(),
|
|
4275
|
-
name: z.string().optional(),
|
|
4276
|
-
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
4277
|
-
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
4278
|
-
dmPolicy: DmPolicySchema$1.optional().default("pairing"),
|
|
4279
|
-
groupPolicy: GroupPolicySchema$1.optional().default("allowlist"),
|
|
4280
|
-
responsePrefix: z.string().optional(),
|
|
4281
|
-
mediaMaxMb: z.number().optional(),
|
|
4282
|
-
webhookPath: z.string().optional()
|
|
4283
|
-
});
|
|
4284
|
-
const LineGroupConfigSchema = z.object({
|
|
4285
|
-
enabled: z.boolean().optional(),
|
|
4286
|
-
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
4287
|
-
requireMention: z.boolean().optional(),
|
|
4288
|
-
systemPrompt: z.string().optional(),
|
|
4289
|
-
skills: z.array(z.string()).optional()
|
|
4290
|
-
}).strict();
|
|
4291
|
-
const LineAccountConfigSchema = LineCommonConfigSchema.extend({ groups: z.record(z.string(), LineGroupConfigSchema.optional()).optional() }).strict();
|
|
4292
|
-
const LineConfigSchema = LineCommonConfigSchema.extend({
|
|
4293
|
-
accounts: z.record(z.string(), LineAccountConfigSchema.optional()).optional(),
|
|
4294
|
-
groups: z.record(z.string(), LineGroupConfigSchema.optional()).optional()
|
|
4295
|
-
}).strict();
|
|
4296
|
-
|
|
4297
|
-
//#endregion
|
|
4298
|
-
export { ACPX_PUSH_TEST_METHOD, ACPX_PUSH_TEST_WRAPPER_ID, AcpRuntimeError, BLUEBUBBLES_ACTIONS, BLUEBUBBLES_ACTION_NAMES, BLUEBUBBLES_GROUP_ACTIONS, BlockStreamingCoalesceSchema, CHANNEL_MESSAGE_ACTION_NAMES, DEFAULT_ACCOUNT_ID, DEFAULT_FEDERATION_BASE_URL, DEFAULT_GROUP_HISTORY_LIMIT, DEFAULT_VIDEO_GENERATION_DURATION_SECONDS, DEFAULT_VIDEO_GENERATION_TIMEOUT_MS, DEFAULT_VIDEO_RESOLUTION_TO_SIZE, DEFAULT_WEBHOOK_BODY_TIMEOUT_MS, DEFAULT_WEBHOOK_MAX_BODY_BYTES, DM_GROUP_ACCESS_REASON, DmConfigSchema, DmPolicySchema, GROUP_POLICY_BLOCKED_LABEL, GoogleChatConfigSchema, GroupPolicySchema, IMessageConfigSchema, LineConfigSchema, MSTeamsConfigSchema, MarkdownConfigSchema, MarkdownTableModeSchema, PAIRING_APPROVED_MESSAGE, ReplyRuntimeConfigSchemaShape, RequestBodyLimitError, SILENT_REPLY_TOKEN, SignalConfigSchema, SsrFBlockedError, ToolPolicySchema, TtsAutoSchema, TtsConfigSchema, TtsModeSchema, TtsProviderSchema, acquireFileLock, addWildcardAllowFrom, applyAccountNameToChannelSection, approveDevicePairing, buildAgentMediaPayload, buildBaseAccountStatusSnapshot, buildBaseChannelStatusSummary, buildChannelConfigSchema, buildChannelKeyCandidates, buildCommandsListResult, buildDashscopeVideoGenerationInput, buildDashscopeVideoGenerationParameters, buildHostnameAllowlistPolicyFromSuffixAllowlist, buildMediaPayload, buildModelCatalogStatus, buildOauthProviderAuthResult, buildPendingHistoryContextFromMap, buildTokenChannelStatusSummary, canonicalizeRealtimeTranscriptionProviderId, canonicalizeRealtimeVoiceProviderId, chunkTextForOutbound, clamp, clearHistoryEntries, clearHistoryEntriesIfEnabled, collectBlueBubblesStatusIssues, collectStatusIssuesFromLastError, createActionCard, createActionGate, createDedupeCache, createDefaultChannelRuntimeState, createImageCard, createInfoCard, createListCard, createLoggerBackedRuntime, createNormalizedOutboundDeliverer, createPersistentDedupe, createReceiptCard, createReplyPrefixContext, createReplyPrefixOptions, createScopedPairingAccess, createTypingCallbacks, deleteAccountFromConfigSection, detectMime, downloadDashscopeGeneratedVideos, emitDiagnosticEvent, emptyPluginConfigSchema, escapeRegExp, evaluateSenderGroupAccess, evictOldHistoryKeys, executeAcpxPushTestRequest, extensionForMime, extractDashscopeVideoUrls, extractOriginalFilename, extractToolSend, fetchWithBearerAuthScopeFallback, fetchWithSsrFGuard, formatAllowFromLowercase, formatAllowlistMatchMeta, formatDocsLink, formatErrorMessage, formatInboundFromLabel, formatLocationText, formatPairingApproveHint, formatTextWithAttachmentLinks, getAcpStatusSnapshot, getChatChannelMeta, getFileExtension, getGatewayUpdateStatus, getPublicGatewayIdentity, getRealtimeTranscriptionProvider, getRealtimeVoiceProvider, getStatusSummary, hasMarkdownToConvert, imessageOnboardingAdapter, installRequestBodyLimitGuard, isAllowedParsedChatSender, isBlockedHostname, isBlockedHostnameOrIp, isDangerousNameMatchingEnabled, isDiagnosticsEnabled, isHttpsUrlAllowedByHostnameSuffixAllowlist, isNormalizedSenderAllowed, isPrivateIpAddress, isRequestBodyLimitError, isSilentReplyText, isTruthyEnvValue, isWSL2Sync, isWSLEnv, isWSLSync, issuePairingChallenge, jsonResult, listAgentIds, listDevicePairing, listIMessageAccountIds, listLineAccountIds, listRealtimeTranscriptionProviders, listRealtimeVoiceProviders, listSignalAccountIds, loadGatewayModelCatalog, loadWebMedia, logAckFailure, logInboundDrop, logTypingFailure, looksLikeIMessageTargetId, looksLikeSignalTargetId, mergeAllowFromEntries, mergeAllowlist, migrateBaseNameToDefaultAccount, missingTargetError, normalizeAccountId, normalizeAllowFrom, normalizeChannelSlug, normalizeE164, normalizeHandle, normalizeHostnameSuffixAllowlist, normalizeIMessageMessagingTarget, normalizeAccountId$1 as normalizeLineAccountId, normalizePluginHttpPath, normalizeRealtimeTranscriptionProviderId, normalizeRealtimeVoiceProviderId, normalizeSignalMessagingTarget, normalizeWebhookPath, onDiagnosticEvent, optionalStringEnum, parseChatAllowTargetPrefixes, parseChatTargetPrefixesOrThrow, pollDashscopeVideoTaskUntilComplete, processLineMessage, promptAccountId, promptChannelAccessConfig, pushHandlers, rawDataToString, readJsonBodyWithLimit, readJsonFileWithFallback, readNumberParam, readReactionParams, readRequestBodyWithLimit, readStoreAllowFromForDmPolicy, readStringParam, recordInboundSession, recordPendingHistoryEntry, recordPendingHistoryEntryIfEnabled, redactSensitiveText, registerAcpRuntimeBackend, registerLogTransport, registerPluginHttpRoute, registerWebhookTarget, rejectDevicePairing, rejectNonPostWebhookRequest, removeAckReactionAfterReply, requestBodyErrorToText, requireOpenAllowFrom, resolveAckReaction, resolveAgentPublicOrigin, resolveAllowlistMatchSimple, resolveAllowlistProviderRuntimeGroupPolicy, resolveBlueBubblesGroupRequireMention, resolveBlueBubblesGroupToolPolicy, resolveChannelAccountConfigBasePath, resolveChannelEntryMatch, resolveChannelEntryMatchWithFallback, resolveChannelMediaMaxBytes, resolveControlCommandGate, resolveDefaultAgentId, resolveDefaultGroupPolicy, resolveDefaultIMessageAccountId, resolveDefaultLineAccountId, resolveDefaultSignalAccountId, resolveDmGroupAccessWithCommandGate, resolveDmGroupAccessWithLists, resolveEffectiveAllowFromLists, resolveEffectiveToolInventory, resolveFederationBaseUrl, resolveFederationHandle, resolveGatewayBindUrl, resolveGoogleChatGroupRequireMention, resolveGoogleChatGroupToolPolicy, resolveIMessageAccount, resolveIMessageGroupRequireMention, resolveIMessageGroupToolPolicy, resolveLineAccount, resolveMentionGating, resolveMentionGatingWithBypass, resolveNestedAllowlistDecision, resolveOpenProviderRuntimeGroupPolicy, resolveOutboundMediaUrls, resolvePreferredFasedAgentTmpDir, resolveSenderCommandAuthorization, resolveServicePrefixedAllowTarget, resolveServicePrefixedTarget, resolveSignalAccount, resolveSingleWebhookTarget, resolveSingleWebhookTargetAsync, resolveTailnetHostWithRunner, resolveThreadSessionKeys, resolveToolsBySender, resolveVideoGenerationReferenceUrls, resolveWebhookPath, resolveWebhookTargets, runEmbeddedPiAgent, runFederationAutoConnectOnce, runPluginCommandWithTimeout, safeParseJson, sendMediaWithLeadingCaption, setAccountEnabledInConfigSection, shouldAckReaction, shouldAckReactionForWhatsApp, signalOnboardingAdapter, sleep, startFederationAutoConnect, stringEnum, stripAnsi, stripMarkdown, summarizeMapping, toLocationContext, unregisterAcpRuntimeBackend, waitForAbortSignal, warnMissingProviderGroupPolicyFallbackOnce, withFileLock, withTempDownloadPath, writeJsonFileAtomically };
|
|
13
|
+
import "./exec-DQ8j4Fm3.js";
|
|
14
|
+
import "./accounts-DI5226U8.js";
|
|
15
|
+
import { C as resolveNestedAllowlistDecision, S as resolveChannelEntryMatchWithFallback, b as normalizeChannelSlug, x as resolveChannelEntryMatch, y as buildChannelKeyCandidates } from "./allow-list-BYeKk0Sy.js";
|
|
16
|
+
import { n as redactSensitiveText } from "./redact-Bm8vu_ju.js";
|
|
17
|
+
import "./github-copilot-token-CUK__1s1.js";
|
|
18
|
+
import "./retry-DstNBhDW.js";
|
|
19
|
+
import "./paths-CTZ2RS_E.js";
|
|
20
|
+
import { a as deleteAccountFromConfigSection, c as formatPairingApproveHint, n as applyAccountNameToChannelSection, o as setAccountEnabledInConfigSection, r as migrateBaseNameToDefaultAccount, s as buildChannelConfigSchema, t as PAIRING_APPROVED_MESSAGE, u as emptyPluginConfigSchema } from "./pairing-message-CZBahREg.js";
|
|
21
|
+
import "./ip-C70a-YlD.js";
|
|
22
|
+
import { a as isBlockedHostnameOrIp, i as isBlockedHostname, o as isPrivateIpAddress, t as SsrFBlockedError } from "./ssrf-BZG8Ztod.js";
|
|
23
|
+
import { t as fetchWithSsrFGuard } from "./fetch-guard-BuYrl6q2.js";
|
|
24
|
+
import { n as redactSensitiveUrlLikeString, t as redactSensitiveUrl } from "./redact-sensitive-url-dPvnFFsK.js";
|
|
25
|
+
import { d as parseChatTargetPrefixesOrThrow, f as resolveServicePrefixedAllowTarget, g as isNormalizedSenderAllowed, h as isAllowedParsedChatSender, m as formatAllowFromLowercase, p as resolveServicePrefixedTarget, u as parseChatAllowTargetPrefixes } from "./send-CUGEhiUa.js";
|
|
26
|
+
import { A as resolveDefaultGroupPolicy, C as buildBaseChannelStatusSummary, D as formatDocsLink, E as createDefaultChannelRuntimeState, M as warnMissingProviderGroupPolicyFallbackOnce, O as GROUP_POLICY_BLOCKED_LABEL, S as buildBaseAccountStatusSnapshot, T as collectStatusIssuesFromLastError, j as resolveOpenProviderRuntimeGroupPolicy, k as resolveAllowlistProviderRuntimeGroupPolicy, r as mergeAllowFromEntries, t as addWildcardAllowFrom, u as promptAccountId, w as buildTokenChannelStatusSummary } from "./helpers-BCZ9d3fl.js";
|
|
27
|
+
import { D as formatLocationText, O as toLocationContext, n as isWSLEnv, r as isWSLSync, t as isWSL2Sync } from "./wsl-JwmI6Zu8.js";
|
|
28
|
+
import { o as readJsonFileWithFallback, s as writeJsonFileAtomically } from "./pairing-store-D7I1Qmiw.js";
|
|
29
|
+
import "./chunk-CSUjIQSx.js";
|
|
30
|
+
import { n as resolveGatewayBindUrl, r as runPluginCommandWithTimeout, t as resolveTailnetHostWithRunner } from "./tailscale-status-ClsquRGs.js";
|
|
31
|
+
import { a as resolveAckReaction, n as createReplyPrefixOptions, t as createReplyPrefixContext } from "./reply-prefix-Bqzm6DC1.js";
|
|
32
|
+
import { i as isSilentReplyText, n as SILENT_REPLY_TOKEN } from "./tokens-BvtlOzVP.js";
|
|
33
|
+
import { i as rejectDevicePairing, r as listDevicePairing, t as approveDevicePairing } from "./device-pairing-D2Yq-_U6.js";
|
|
34
|
+
import "./pairing-token-N_6Jbt1L.js";
|
|
35
|
+
import { h as onDiagnosticEvent, m as isDiagnosticsEnabled, p as emitDiagnosticEvent } from "./diagnostic-Dfn1iERm.js";
|
|
36
|
+
import "./diagnostic-session-state-HO94DMou.js";
|
|
37
|
+
import "./optional-runtime-dependency-BEBP4VFJ.js";
|
|
38
|
+
import { c as detectMime, l as extensionForMime, u as getFileExtension } from "./image-ops-B-QR_M9Z.js";
|
|
39
|
+
import { C as resolveFederationHandle, S as resolveFederationBaseUrl, b as normalizeHandle, x as resolveAgentPublicOrigin, y as DEFAULT_FEDERATION_BASE_URL } from "./wallet-status-CQI89c_6.js";
|
|
40
|
+
import "./pi-model-discovery-C5klknQZ.js";
|
|
41
|
+
import "./runtime-dependency-C5rJfC0u.js";
|
|
42
|
+
import { t as rawDataToString } from "./ws-CocpWGhl.js";
|
|
43
|
+
import "./path-alias-guards-Dg3gU-MA.js";
|
|
44
|
+
import "./fs-safe-BG-VzVkL.js";
|
|
45
|
+
import "./markdown-tables-B1Tx7c77.js";
|
|
46
|
+
import { n as loadWebMedia } from "./media-BwsqooWC.js";
|
|
47
|
+
import "./send-Bd3Z-jAd.js";
|
|
48
|
+
import { r as extractOriginalFilename } from "./outbound-attachment-L1q40kiu.js";
|
|
49
|
+
import "./fetch-Cu-ahkie.js";
|
|
50
|
+
import "./thread-bindings.state-ssxdG7xV.js";
|
|
51
|
+
import "./memory-search-C3C0bD2H.js";
|
|
52
|
+
import "./manager-Dhq3SXpO.js";
|
|
53
|
+
import "./query-expansion-Zr9qQixm.js";
|
|
54
|
+
import { a as jsonResult, c as readReactionParams, d as readStringParam, n as createActionGate, s as readNumberParam } from "./common-Cjm8psuW.js";
|
|
55
|
+
import "./commands-registry-DJ0pfCcY.js";
|
|
56
|
+
import "./skill-commands-BybEjJ1_.js";
|
|
57
|
+
import "./directory-live-DatqU-eS.js";
|
|
58
|
+
import "./targets-DskkhJAp.js";
|
|
59
|
+
import "./sandbox-paths-lj7_EVuA.js";
|
|
60
|
+
import { n as missingTargetError } from "./target-errors-C_SVKoEv.js";
|
|
61
|
+
import "./resolve-route-B7vZ3nKp.js";
|
|
62
|
+
import "./render-Bwu_WxwA.js";
|
|
63
|
+
import "./tables-CVDOT5dX.js";
|
|
64
|
+
import { t as promptChannelAccessConfig } from "./channel-access-B1KciKmw.js";
|
|
65
|
+
import "./shared-clhhbWN3.js";
|
|
66
|
+
|
|
67
|
+
export { ACPX_PUSH_TEST_METHOD, ACPX_PUSH_TEST_WRAPPER_ID, AcpRuntimeError, BLUEBUBBLES_ACTIONS, BLUEBUBBLES_ACTION_NAMES, BLUEBUBBLES_GROUP_ACTIONS, BlockStreamingCoalesceSchema, CHANNEL_MESSAGE_ACTION_NAMES, DEFAULT_ACCOUNT_ID, DEFAULT_FEDERATION_BASE_URL, DEFAULT_GROUP_HISTORY_LIMIT, DEFAULT_VIDEO_GENERATION_DURATION_SECONDS, DEFAULT_VIDEO_GENERATION_TIMEOUT_MS, DEFAULT_VIDEO_RESOLUTION_TO_SIZE, DEFAULT_WEBHOOK_BODY_TIMEOUT_MS, DEFAULT_WEBHOOK_MAX_BODY_BYTES, DM_GROUP_ACCESS_REASON, DmConfigSchema, DmPolicySchema, GROUP_POLICY_BLOCKED_LABEL, GoogleChatConfigSchema, GroupPolicySchema, IMessageConfigSchema, LineConfigSchema, MSTeamsConfigSchema, MarkdownConfigSchema, MarkdownTableModeSchema, PAIRING_APPROVED_MESSAGE, ReplyRuntimeConfigSchemaShape, RequestBodyLimitError, SILENT_REPLY_TOKEN, SignalConfigSchema, SsrFBlockedError, ToolPolicySchema, TtsAutoSchema, TtsConfigSchema, TtsModeSchema, TtsProviderSchema, acquireFileLock, addWildcardAllowFrom, applyAccountNameToChannelSection, approveDevicePairing, buildAgentMediaPayload, buildBaseAccountStatusSnapshot, buildBaseChannelStatusSummary, buildChannelConfigSchema, buildChannelKeyCandidates, buildCommandsListResult, buildDashscopeVideoGenerationInput, buildDashscopeVideoGenerationParameters, buildHostnameAllowlistPolicyFromSuffixAllowlist, buildMediaPayload, buildModelCatalogStatus, buildOauthProviderAuthResult, buildPendingHistoryContextFromMap, buildTokenChannelStatusSummary, canonicalizeRealtimeTranscriptionProviderId, canonicalizeRealtimeVoiceProviderId, chunkTextForOutbound, clamp, clearHistoryEntries, clearHistoryEntriesIfEnabled, collectBlueBubblesStatusIssues, collectStatusIssuesFromLastError, createActionCard, createActionGate, createDedupeCache, createDefaultChannelRuntimeState, createImageCard, createInfoCard, createListCard, createLoggerBackedRuntime, createNormalizedOutboundDeliverer, createPersistentDedupe, createReceiptCard, createReplyPrefixContext, createReplyPrefixOptions, createScopedPairingAccess, createTypingCallbacks, deleteAccountFromConfigSection, detectMime, downloadDashscopeGeneratedVideos, emitDiagnosticEvent, emptyPluginConfigSchema, escapeRegExp, evaluateSenderGroupAccess, evictOldHistoryKeys, executeAcpxPushTestRequest, extensionForMime, extractDashscopeVideoUrls, extractOriginalFilename, extractToolSend, fetchWithBearerAuthScopeFallback, fetchWithSsrFGuard, formatAllowFromLowercase, formatAllowlistMatchMeta, formatDocsLink, formatErrorMessage, formatInboundFromLabel, formatLocationText, formatPairingApproveHint, formatTextWithAttachmentLinks, getAcpStatusSnapshot, getChatChannelMeta, getFileExtension, getGatewayUpdateStatus, getPublicGatewayIdentity, getRealtimeTranscriptionProvider, getRealtimeVoiceProvider, getStatusSummary, hasMarkdownToConvert, imessageOnboardingAdapter, installRequestBodyLimitGuard, isAllowedParsedChatSender, isBlockedHostname, isBlockedHostnameOrIp, isDangerousNameMatchingEnabled, isDiagnosticsEnabled, isHttpsUrlAllowedByHostnameSuffixAllowlist, isNormalizedSenderAllowed, isPrivateIpAddress, isRequestBodyLimitError, isSilentReplyText, isTruthyEnvValue, isWSL2Sync, isWSLEnv, isWSLSync, issuePairingChallenge, jsonResult, listAgentIds, listDevicePairing, listIMessageAccountIds, listLineAccountIds, listRealtimeTranscriptionProviders, listRealtimeVoiceProviders, listSignalAccountIds, loadGatewayModelCatalog, loadWebMedia, logAckFailure, logInboundDrop, logTypingFailure, looksLikeIMessageTargetId, looksLikeSignalTargetId, mergeAllowFromEntries, mergeAllowlist, migrateBaseNameToDefaultAccount, missingTargetError, normalizeAccountId, normalizeAllowFrom, normalizeChannelSlug, normalizeE164, normalizeHandle, normalizeHostnameSuffixAllowlist, normalizeIMessageMessagingTarget, normalizeAccountId$1 as normalizeLineAccountId, normalizePluginHttpPath, normalizeRealtimeTranscriptionProviderId, normalizeRealtimeVoiceProviderId, normalizeSignalMessagingTarget, normalizeWebhookPath, onDiagnosticEvent, optionalStringEnum, parseChatAllowTargetPrefixes, parseChatTargetPrefixesOrThrow, pollDashscopeVideoTaskUntilComplete, processLineMessage, promptAccountId, promptChannelAccessConfig, pushHandlers, rawDataToString, readJsonBodyWithLimit, readJsonFileWithFallback, readNumberParam, readReactionParams, readRequestBodyWithLimit, readStoreAllowFromForDmPolicy, readStringParam, recordInboundSession, recordPendingHistoryEntry, recordPendingHistoryEntryIfEnabled, redactSensitiveText, redactSensitiveUrl, redactSensitiveUrlLikeString, registerAcpRuntimeBackend, registerLogTransport, registerPluginHttpRoute, registerWebhookTarget, rejectDevicePairing, rejectNonPostWebhookRequest, removeAckReactionAfterReply, requestBodyErrorToText, requireOpenAllowFrom, resolveAckReaction, resolveAgentPublicOrigin, resolveAllowlistMatchSimple, resolveAllowlistProviderRuntimeGroupPolicy, resolveBlueBubblesGroupRequireMention, resolveBlueBubblesGroupToolPolicy, resolveChannelAccountConfigBasePath, resolveChannelEntryMatch, resolveChannelEntryMatchWithFallback, resolveChannelMediaMaxBytes, resolveControlCommandGate, resolveDefaultAgentId, resolveDefaultGroupPolicy, resolveDefaultIMessageAccountId, resolveDefaultLineAccountId, resolveDefaultSignalAccountId, resolveDmGroupAccessWithCommandGate, resolveDmGroupAccessWithLists, resolveEffectiveAllowFromLists, resolveEffectiveToolInventory, resolveFederationBaseUrl, resolveFederationHandle, resolveGatewayBindUrl, resolveGoogleChatGroupRequireMention, resolveGoogleChatGroupToolPolicy, resolveIMessageAccount, resolveIMessageGroupRequireMention, resolveIMessageGroupToolPolicy, resolveLineAccount, resolveMentionGating, resolveMentionGatingWithBypass, resolveNestedAllowlistDecision, resolveOpenProviderRuntimeGroupPolicy, resolveOutboundMediaUrls, resolvePreferredFasedAgentTmpDir, resolveSenderCommandAuthorization, resolveServicePrefixedAllowTarget, resolveServicePrefixedTarget, resolveSignalAccount, resolveSingleWebhookTarget, resolveSingleWebhookTargetAsync, resolveTailnetHostWithRunner, resolveThreadSessionKeys, resolveToolsBySender, resolveVideoGenerationReferenceUrls, resolveWebhookPath, resolveWebhookTargets, runEmbeddedPiAgent, runFederationAutoConnectOnce, runPluginCommandWithTimeout, safeParseJson, sendMediaWithLeadingCaption, setAccountEnabledInConfigSection, shouldAckReaction, shouldAckReactionForWhatsApp, signalOnboardingAdapter, sleep, startFederationAutoConnect, stringEnum, stripAnsi, stripMarkdown, summarizeMapping, toLocationContext, unregisterAcpRuntimeBackend, waitForAbortSignal, warnMissingProviderGroupPolicyFallbackOnce, withFileLock, withTempDownloadPath, writeJsonFileAtomically };
|