synabun 2.0.5 → 2.0.9
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/CHANGELOG.md +78 -0
- package/README.md +2 -1
- package/THIRD-PARTY-LICENSES.md +13 -0
- package/docs/whatsapp.md +151 -16
- package/hooks/claude-code/post-plan.mjs +6 -30
- package/hooks/claude-code/session-start.mjs +1 -4
- package/hooks/claude-code/shared.mjs +112 -75
- package/lib/child-path.js +303 -0
- package/lib/claude-hooks.js +2 -1
- package/lib/data-home-migration.js +2 -0
- package/lib/desktop-app.js +265 -0
- package/lib/external-tools.js +7 -3
- package/lib/log-redact.js +102 -0
- package/lib/package-processes.js +670 -0
- package/lib/packaged-runtime.js +138 -11
- package/lib/runtime-dependencies.js +225 -0
- package/mcp-server/dist/http.d.ts +3 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +15 -4
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +9 -6
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/services/browser-bench.d.ts +1 -1
- package/mcp-server/dist/services/browser-bench.d.ts.map +1 -1
- package/mcp-server/dist/services/browser-bench.js +1 -1
- package/mcp-server/dist/services/browser-bench.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
- package/mcp-server/dist/services/budget-judgments.js +153 -0
- package/mcp-server/dist/services/budget-judgments.js.map +1 -0
- package/mcp-server/dist/services/categories.d.ts +35 -2
- package/mcp-server/dist/services/categories.d.ts.map +1 -1
- package/mcp-server/dist/services/categories.js +77 -33
- package/mcp-server/dist/services/categories.js.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +41 -4
- package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
- package/mcp-server/dist/services/desktop-bench.d.ts +7 -7
- package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -1
- package/mcp-server/dist/services/desktop-bench.js +1 -1
- package/mcp-server/dist/services/desktop-bench.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +11 -0
- package/mcp-server/dist/services/identity.d.ts.map +1 -1
- package/mcp-server/dist/services/identity.js +24 -0
- package/mcp-server/dist/services/identity.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +13 -0
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +34 -2
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
- package/mcp-server/dist/services/typesafe-config.js +4 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +11 -8
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +35 -14
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/bluesky.js +1 -1
- package/mcp-server/dist/tools/bluesky.js.map +1 -1
- package/mcp-server/dist/tools/browser-advanced.d.ts +1 -1
- package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-advanced.js +1 -1
- package/mcp-server/dist/tools/browser-advanced.js.map +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-batch.js +1 -1
- package/mcp-server/dist/tools/browser-batch.js.map +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.d.ts +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.js +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -1
- package/mcp-server/dist/tools/browser-interact.d.ts +1 -1
- package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-interact.js +1 -1
- package/mcp-server/dist/tools/browser-interact.js.map +1 -1
- package/mcp-server/dist/tools/browser-navigate.d.ts +1 -1
- package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-navigate.js +1 -1
- package/mcp-server/dist/tools/browser-navigate.js.map +1 -1
- package/mcp-server/dist/tools/browser-observe.d.ts +1 -1
- package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-observe.js +1 -1
- package/mcp-server/dist/tools/browser-observe.js.map +1 -1
- package/mcp-server/dist/tools/card-tools.d.ts +1 -1
- package/mcp-server/dist/tools/card-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/card-tools.js +1 -1
- package/mcp-server/dist/tools/card-tools.js.map +1 -1
- package/mcp-server/dist/tools/category.d.ts +1 -1
- package/mcp-server/dist/tools/category.d.ts.map +1 -1
- package/mcp-server/dist/tools/category.js +5 -1
- package/mcp-server/dist/tools/category.js.map +1 -1
- package/mcp-server/dist/tools/choice.d.ts +1 -1
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +1 -1
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/computer.d.ts +1 -1
- package/mcp-server/dist/tools/computer.d.ts.map +1 -1
- package/mcp-server/dist/tools/computer.js +1 -1
- package/mcp-server/dist/tools/computer.js.map +1 -1
- package/mcp-server/dist/tools/discord-channel.d.ts +1 -1
- package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-channel.js +1 -1
- package/mcp-server/dist/tools/discord-channel.js.map +1 -1
- package/mcp-server/dist/tools/discord-guild.d.ts +1 -1
- package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-guild.js +1 -1
- package/mcp-server/dist/tools/discord-guild.js.map +1 -1
- package/mcp-server/dist/tools/discord-member.d.ts +1 -1
- package/mcp-server/dist/tools/discord-member.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-member.js +1 -1
- package/mcp-server/dist/tools/discord-member.js.map +1 -1
- package/mcp-server/dist/tools/discord-message.d.ts +1 -1
- package/mcp-server/dist/tools/discord-message.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-message.js +1 -1
- package/mcp-server/dist/tools/discord-message.js.map +1 -1
- package/mcp-server/dist/tools/discord-onboarding.d.ts +1 -1
- package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-onboarding.js +1 -1
- package/mcp-server/dist/tools/discord-onboarding.js.map +1 -1
- package/mcp-server/dist/tools/discord-role.d.ts +1 -1
- package/mcp-server/dist/tools/discord-role.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-role.js +1 -1
- package/mcp-server/dist/tools/discord-role.js.map +1 -1
- package/mcp-server/dist/tools/discord-thread.d.ts +1 -1
- package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-thread.js +1 -1
- package/mcp-server/dist/tools/discord-thread.js.map +1 -1
- package/mcp-server/dist/tools/discord-webhook.d.ts +1 -1
- package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-webhook.js +1 -1
- package/mcp-server/dist/tools/discord-webhook.js.map +1 -1
- package/mcp-server/dist/tools/fb-groups.d.ts +1 -1
- package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -1
- package/mcp-server/dist/tools/fb-groups.js +1 -1
- package/mcp-server/dist/tools/fb-groups.js.map +1 -1
- package/mcp-server/dist/tools/forget.d.ts +1 -1
- package/mcp-server/dist/tools/forget.d.ts.map +1 -1
- package/mcp-server/dist/tools/forget.js +1 -1
- package/mcp-server/dist/tools/forget.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.d.ts +1 -1
- package/mcp-server/dist/tools/git-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +1 -1
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- package/mcp-server/dist/tools/gsc/experience.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/experience.js +1 -1
- package/mcp-server/dist/tools/gsc/experience.js.map +1 -1
- package/mcp-server/dist/tools/gsc/indexing.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/indexing.js +1 -1
- package/mcp-server/dist/tools/gsc/indexing.js.map +1 -1
- package/mcp-server/dist/tools/gsc/inspect.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/inspect.js +1 -1
- package/mcp-server/dist/tools/gsc/inspect.js.map +1 -1
- package/mcp-server/dist/tools/gsc/navigate.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/navigate.js +1 -1
- package/mcp-server/dist/tools/gsc/navigate.js.map +1 -1
- package/mcp-server/dist/tools/gsc/performance.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/performance.js +1 -1
- package/mcp-server/dist/tools/gsc/performance.js.map +1 -1
- package/mcp-server/dist/tools/gsc/settings.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/settings.js +1 -1
- package/mcp-server/dist/tools/gsc/settings.js.map +1 -1
- package/mcp-server/dist/tools/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/image-tools.js +1 -1
- package/mcp-server/dist/tools/image-tools.js.map +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -1
- package/mcp-server/dist/tools/loop.d.ts +1 -1
- package/mcp-server/dist/tools/loop.d.ts.map +1 -1
- package/mcp-server/dist/tools/loop.js +1 -1
- package/mcp-server/dist/tools/loop.js.map +1 -1
- package/mcp-server/dist/tools/memories.d.ts +1 -1
- package/mcp-server/dist/tools/memories.d.ts.map +1 -1
- package/mcp-server/dist/tools/memories.js +1 -1
- package/mcp-server/dist/tools/memories.js.map +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +1 -1
- package/mcp-server/dist/tools/morelogin.js.map +1 -1
- package/mcp-server/dist/tools/profile.d.ts +1 -1
- package/mcp-server/dist/tools/profile.d.ts.map +1 -1
- package/mcp-server/dist/tools/profile.js +1 -1
- package/mcp-server/dist/tools/profile.js.map +1 -1
- package/mcp-server/dist/tools/recall.d.ts +1 -1
- package/mcp-server/dist/tools/recall.d.ts.map +1 -1
- package/mcp-server/dist/tools/recall.js +1 -1
- package/mcp-server/dist/tools/recall.js.map +1 -1
- package/mcp-server/dist/tools/reflect.d.ts +1 -1
- package/mcp-server/dist/tools/reflect.d.ts.map +1 -1
- package/mcp-server/dist/tools/reflect.js +1 -1
- package/mcp-server/dist/tools/reflect.js.map +1 -1
- package/mcp-server/dist/tools/remember.d.ts +1 -1
- package/mcp-server/dist/tools/remember.d.ts.map +1 -1
- package/mcp-server/dist/tools/remember.js +13 -11
- package/mcp-server/dist/tools/remember.js.map +1 -1
- package/mcp-server/dist/tools/restore.d.ts +1 -1
- package/mcp-server/dist/tools/restore.d.ts.map +1 -1
- package/mcp-server/dist/tools/restore.js +1 -1
- package/mcp-server/dist/tools/restore.js.map +1 -1
- package/mcp-server/dist/tools/style-guide-tools.d.ts +1 -1
- package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/style-guide-tools.js +1 -1
- package/mcp-server/dist/tools/style-guide-tools.js.map +1 -1
- package/mcp-server/dist/tools/sync.d.ts +1 -1
- package/mcp-server/dist/tools/sync.d.ts.map +1 -1
- package/mcp-server/dist/tools/sync.js +1 -1
- package/mcp-server/dist/tools/sync.js.map +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.d.ts +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.js +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -1
- package/mcp-server/dist/tools/utils.d.ts +1 -1
- package/mcp-server/dist/tools/utils.d.ts.map +1 -1
- package/mcp-server/dist/tools/utils.js +1 -1
- package/mcp-server/dist/tools/utils.js.map +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
- package/mcp-server/dist/tools/whiteboard-tools.d.ts +3 -3
- package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
- package/mcp-server/dist/tools/youtube/analytics.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/analytics.js +1 -1
- package/mcp-server/dist/tools/youtube/analytics.js.map +1 -1
- package/mcp-server/dist/tools/youtube/download.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/download.js +1 -1
- package/mcp-server/dist/tools/youtube/download.js.map +1 -1
- package/mcp-server/dist/tools/youtube/media.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/media.js +1 -1
- package/mcp-server/dist/tools/youtube/media.js.map +1 -1
- package/mcp-server/dist/tools/youtube/metadata.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/metadata.js +1 -1
- package/mcp-server/dist/tools/youtube/metadata.js.map +1 -1
- package/mcp-server/dist/tools/youtube/navigate.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/navigate.js +1 -1
- package/mcp-server/dist/tools/youtube/navigate.js.map +1 -1
- package/mcp-server/dist/tools/youtube/playlist.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/playlist.js +1 -1
- package/mcp-server/dist/tools/youtube/playlist.js.map +1 -1
- package/mcp-server/dist/tools/youtube/schedule.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/schedule.js +1 -1
- package/mcp-server/dist/tools/youtube/schedule.js.map +1 -1
- package/mcp-server/dist/tools/youtube/source.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/source.js +1 -1
- package/mcp-server/dist/tools/youtube/source.js.map +1 -1
- package/mcp-server/dist/tools/youtube/upload.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/upload.js +4 -9
- package/mcp-server/dist/tools/youtube/upload.js.map +1 -1
- package/mcp-server/dist/tui.js +18 -5
- package/mcp-server/dist/tui.js.map +1 -1
- package/mcp-server/dist/version.d.ts +5 -0
- package/mcp-server/dist/version.d.ts.map +1 -0
- package/mcp-server/dist/version.js +21 -0
- package/mcp-server/dist/version.js.map +1 -0
- package/mcp-server/package-lock.json +3 -3
- package/mcp-server/package.json +3 -3
- package/mcp-server/run.mjs +15 -9
- package/mcp-server/src/http.ts +18 -3
- package/mcp-server/src/index.ts +9 -6
- package/mcp-server/src/services/browser-bench.ts +1 -1
- package/mcp-server/src/services/budget-judgments.ts +183 -0
- package/mcp-server/src/services/categories.ts +77 -32
- package/mcp-server/src/services/codex-config-heal.ts +35 -4
- package/mcp-server/src/services/desktop-bench.ts +1 -1
- package/mcp-server/src/services/identity.ts +27 -0
- package/mcp-server/src/services/neural-interface.ts +34 -2
- package/mcp-server/src/services/typesafe-config.ts +4 -1
- package/mcp-server/src/tools/agents.ts +34 -15
- package/mcp-server/src/tools/bluesky.ts +1 -1
- package/mcp-server/src/tools/browser-advanced.ts +1 -1
- package/mcp-server/src/tools/browser-batch.ts +1 -1
- package/mcp-server/src/tools/browser-cheatsheet.ts +1 -1
- package/mcp-server/src/tools/browser-interact.ts +1 -1
- package/mcp-server/src/tools/browser-navigate.ts +1 -1
- package/mcp-server/src/tools/browser-observe.ts +1 -1
- package/mcp-server/src/tools/card-tools.ts +1 -1
- package/mcp-server/src/tools/category.ts +6 -1
- package/mcp-server/src/tools/choice.ts +1 -1
- package/mcp-server/src/tools/computer.ts +1 -1
- package/mcp-server/src/tools/discord-channel.ts +1 -1
- package/mcp-server/src/tools/discord-guild.ts +1 -1
- package/mcp-server/src/tools/discord-member.ts +1 -1
- package/mcp-server/src/tools/discord-message.ts +1 -1
- package/mcp-server/src/tools/discord-onboarding.ts +1 -1
- package/mcp-server/src/tools/discord-role.ts +1 -1
- package/mcp-server/src/tools/discord-thread.ts +1 -1
- package/mcp-server/src/tools/discord-webhook.ts +1 -1
- package/mcp-server/src/tools/fb-groups.ts +1 -1
- package/mcp-server/src/tools/forget.ts +1 -1
- package/mcp-server/src/tools/git-tools.ts +1 -1
- package/mcp-server/src/tools/gsc/experience.ts +1 -1
- package/mcp-server/src/tools/gsc/indexing.ts +1 -1
- package/mcp-server/src/tools/gsc/inspect.ts +1 -1
- package/mcp-server/src/tools/gsc/navigate.ts +1 -1
- package/mcp-server/src/tools/gsc/performance.ts +1 -1
- package/mcp-server/src/tools/gsc/settings.ts +1 -1
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/leonardo-browser-tools.ts +1 -1
- package/mcp-server/src/tools/loop.ts +1 -1
- package/mcp-server/src/tools/memories.ts +1 -1
- package/mcp-server/src/tools/morelogin.ts +1 -1
- package/mcp-server/src/tools/profile.ts +1 -1
- package/mcp-server/src/tools/recall.ts +1 -1
- package/mcp-server/src/tools/reflect.ts +1 -1
- package/mcp-server/src/tools/remember.ts +13 -12
- package/mcp-server/src/tools/restore.ts +1 -1
- package/mcp-server/src/tools/style-guide-tools.ts +1 -1
- package/mcp-server/src/tools/sync.ts +1 -1
- package/mcp-server/src/tools/tictactoe-tools.ts +1 -1
- package/mcp-server/src/tools/utils.ts +1 -1
- package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
- package/mcp-server/src/tools/whiteboard-tools.ts +1 -1
- package/mcp-server/src/tools/youtube/analytics.ts +1 -1
- package/mcp-server/src/tools/youtube/download.ts +1 -1
- package/mcp-server/src/tools/youtube/media.ts +1 -1
- package/mcp-server/src/tools/youtube/metadata.ts +1 -1
- package/mcp-server/src/tools/youtube/navigate.ts +1 -1
- package/mcp-server/src/tools/youtube/playlist.ts +1 -1
- package/mcp-server/src/tools/youtube/schedule.ts +1 -1
- package/mcp-server/src/tools/youtube/source.ts +1 -1
- package/mcp-server/src/tools/youtube/upload.ts +4 -12
- package/mcp-server/src/tui.ts +24 -10
- package/mcp-server/src/version.ts +22 -0
- package/neural-interface/i18n/en.json +645 -57
- package/neural-interface/i18n/pt-BR.json +644 -56
- package/neural-interface/lib/account-usage.js +163 -0
- package/neural-interface/lib/agent-sandbox-elevation.js +102 -0
- package/neural-interface/lib/agent-sandbox-setting.js +57 -0
- package/neural-interface/lib/assistant-api.js +274 -52
- package/neural-interface/lib/assistant-brains/claude.js +15 -2
- package/neural-interface/lib/assistant-brains/codex.js +3 -0
- package/neural-interface/lib/assistant-brains/opencode.js +17 -4
- package/neural-interface/lib/assistant-budget-planner.js +390 -0
- package/neural-interface/lib/assistant-budget-split.js +408 -0
- package/neural-interface/lib/assistant-budget.js +94 -95
- package/neural-interface/lib/assistant-config.js +62 -14
- package/neural-interface/lib/assistant-dispatch.js +432 -125
- package/neural-interface/lib/assistant-jev.js +26 -3
- package/neural-interface/lib/assistant-persona.js +68 -11
- package/neural-interface/lib/assistant-plan-permissions.js +8 -1
- package/neural-interface/lib/assistant-route-accounts.js +14 -0
- package/neural-interface/lib/assistant-route-gate-codex.js +3 -1
- package/neural-interface/lib/assistant-route-gate.js +19 -3
- package/neural-interface/lib/assistant-router.js +373 -42
- package/neural-interface/lib/assistant-runtime.js +312 -48
- package/neural-interface/lib/assistant-user-writer.js +4 -2
- package/neural-interface/lib/augmented-path.js +317 -53
- package/neural-interface/lib/claude-accounts.js +342 -31
- package/neural-interface/lib/claude-agent-bridge.js +90 -3
- package/neural-interface/lib/claude-executable.js +6 -1
- package/neural-interface/lib/claude-model-catalog.js +3 -3
- package/neural-interface/lib/claude-panel-auth.js +221 -0
- package/neural-interface/lib/claude-profile-api-key.js +62 -0
- package/neural-interface/lib/cli-install-api.js +137 -0
- package/neural-interface/lib/cli-install.js +797 -0
- package/neural-interface/lib/cli-path.js +130 -0
- package/neural-interface/lib/cli-resolver.js +744 -0
- package/neural-interface/lib/cli-update-planner.js +143 -7
- package/neural-interface/lib/codex-account-auth.js +180 -0
- package/neural-interface/lib/codex-accounts.js +502 -0
- package/neural-interface/lib/codex-capabilities.js +2 -1
- package/neural-interface/lib/codex-compat-rpc.js +8 -2
- package/neural-interface/lib/codex-mcp-approvals.js +5 -1
- package/neural-interface/lib/codex-panel-auth.js +62 -0
- package/neural-interface/lib/codex-session-settings.js +1 -1
- package/neural-interface/lib/codex-settings-schema.js +122 -0
- package/neural-interface/lib/desktop/config.js +3 -0
- package/neural-interface/lib/exec-loop-command.js +3 -1
- package/neural-interface/lib/git-bash/bzip2.js +339 -0
- package/neural-interface/lib/git-bash/claude-settings.js +73 -0
- package/neural-interface/lib/git-bash/extract-worker.js +34 -0
- package/neural-interface/lib/git-bash/install.js +529 -0
- package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
- package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
- package/neural-interface/lib/git-bash/manager.js +268 -0
- package/neural-interface/lib/git-bash/pins.js +206 -0
- package/neural-interface/lib/git-bash/pins.json +34 -0
- package/neural-interface/lib/git-bash/resolver.js +194 -0
- package/neural-interface/lib/git-read.js +34 -2
- package/neural-interface/lib/install-details.js +163 -0
- package/neural-interface/lib/mcp-installer.js +14 -2
- package/neural-interface/lib/native-loop-providers.js +22 -4
- package/neural-interface/lib/native-loop-runtime.js +9 -4
- package/neural-interface/lib/os-path.js +319 -0
- package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
- package/neural-interface/lib/provider-panel-terminal-auth.js +96 -0
- package/neural-interface/lib/pty-host/core.js +2 -1
- package/neural-interface/lib/pty-host/manager.js +27 -2
- package/neural-interface/lib/remote-policy.js +20 -11
- package/neural-interface/lib/session-indexer.js +3 -1
- package/neural-interface/lib/session-title-generator.js +6 -3
- package/neural-interface/lib/system-about.js +340 -0
- package/neural-interface/lib/system-activity.js +66 -0
- package/neural-interface/lib/terminal-env.js +11 -3
- package/neural-interface/lib/terminal-path.js +55 -0
- package/neural-interface/lib/virtual-socket.js +12 -1
- package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
- package/neural-interface/lib/whatsapp/bridge.js +78 -34
- package/neural-interface/lib/whatsapp/commands.js +13 -14
- package/neural-interface/lib/whatsapp/config.js +134 -20
- package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
- package/neural-interface/lib/whatsapp/host-core.js +168 -35
- package/neural-interface/lib/whatsapp/inbound.js +5 -3
- package/neural-interface/lib/whatsapp/local-file.js +524 -0
- package/neural-interface/lib/whatsapp/manager.js +92 -32
- package/neural-interface/lib/whatsapp/paths.js +11 -1
- package/neural-interface/lib/whatsapp/protocol.js +65 -5
- package/neural-interface/lib/whatsapp/redact.js +63 -0
- package/neural-interface/lib/whatsapp/service.js +343 -91
- package/neural-interface/lib/whatsapp/transcription.js +463 -0
- package/neural-interface/package-lock.json +2 -2
- package/neural-interface/package.json +1 -1
- package/neural-interface/public/index.html +2 -0
- package/neural-interface/public/index2d.html +2 -0
- package/neural-interface/public/offline.html +16 -1
- package/neural-interface/public/onboarding.html +96 -2
- package/neural-interface/public/shared/account-i18n.js +7 -0
- package/neural-interface/public/shared/account-menu.js +542 -0
- package/neural-interface/public/shared/account-usage.js +79 -0
- package/neural-interface/public/shared/account-view.js +336 -0
- package/neural-interface/public/shared/agent-runtime-options.js +28 -8
- package/neural-interface/public/shared/assistant/asst-account-menu.js +298 -0
- package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
- package/neural-interface/public/shared/assistant/asst-api.js +39 -2
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +288 -32
- package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
- package/neural-interface/public/shared/assistant/asst-mascot.js +126 -7
- package/neural-interface/public/shared/assistant/asst-menu.js +219 -80
- package/neural-interface/public/shared/assistant/asst-model-chip.js +135 -0
- package/neural-interface/public/shared/assistant/asst-panel.js +87 -16
- package/neural-interface/public/shared/assistant/asst-render.js +38 -7
- package/neural-interface/public/shared/assistant/asst-route.js +72 -14
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +45 -6
- package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
- package/neural-interface/public/shared/assistant/asst-state.js +1 -1
- package/neural-interface/public/shared/assistant/asst-styles.js +160 -29
- package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
- package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
- package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
- package/neural-interface/public/shared/cdx/cdx-panel.js +49 -108
- package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +172 -111
- package/neural-interface/public/shared/cdx/cdx-tabs.js +264 -664
- package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
- package/neural-interface/public/shared/claude-account-view.js +3 -0
- package/neural-interface/public/shared/cli-install-view.js +392 -0
- package/neural-interface/public/shared/cli-status.js +282 -22
- package/neural-interface/public/shared/cp/cp-events.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +46 -0
- package/neural-interface/public/shared/desktop-bridge.js +39 -0
- package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
- package/neural-interface/public/shared/desktop-titlebar.js +153 -0
- package/neural-interface/public/shared/desktop-update-view.js +462 -0
- package/neural-interface/public/shared/editor/editor-panel.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +1 -1
- package/neural-interface/public/shared/git-bash-view.js +145 -0
- package/neural-interface/public/shared/html-shell.js +1 -0
- package/neural-interface/public/shared/i18n.js +19 -1
- package/neural-interface/public/shared/install-details-view.js +86 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +36 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +17 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +169 -2
- package/neural-interface/public/shared/pwa-install-page.js +3 -1
- package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
- package/neural-interface/public/shared/settings/settings-about.js +254 -0
- package/neural-interface/public/shared/settings/settings-ia.js +15 -0
- package/neural-interface/public/shared/showcase-privacy.js +490 -0
- package/neural-interface/public/shared/storage.js +30 -3
- package/neural-interface/public/shared/styles.css +281 -32
- package/neural-interface/public/shared/synabun-mascot.js +48 -2
- package/neural-interface/public/shared/ui-claude-panel.js +262 -217
- package/neural-interface/public/shared/ui-cli-banner.js +355 -0
- package/neural-interface/public/shared/ui-explorer.js +2 -0
- package/neural-interface/public/shared/ui-git-bash.js +124 -0
- package/neural-interface/public/shared/ui-judgments.js +10 -1
- package/neural-interface/public/shared/ui-loading.js +30 -6
- package/neural-interface/public/shared/ui-menubar.js +13 -3
- package/neural-interface/public/shared/ui-navbar.js +3 -1
- package/neural-interface/public/shared/ui-provider-auth.js +104 -0
- package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
- package/neural-interface/public/shared/ui-sessions.js +9 -3
- package/neural-interface/public/shared/ui-settings.js +55 -3
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
- package/neural-interface/public/shared/ui-sync.js +8 -0
- package/neural-interface/public/shared/ui-terminal.js +17 -0
- package/neural-interface/public/shared/ui-tool-updates.js +5 -0
- package/neural-interface/public/shared/ui-update.js +598 -7
- package/neural-interface/public/shared/ui-whatsapp.js +21 -6
- package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
- package/neural-interface/public/shared/worker-end.js +2 -0
- package/neural-interface/public/sw.js +1 -1
- package/neural-interface/public/variant/2d/main.js +10 -1
- package/neural-interface/public/variant/3d/main.js +10 -1
- package/neural-interface/scripts/rebuild-pty.js +14 -2
- package/neural-interface/server.js +1384 -679
- package/npm-shrinkwrap.json +4424 -0
- package/package.json +57 -2
- package/postinstall.js +61 -47
- package/preuninstall.js +8 -35
- package/setup.js +80 -47
- package/skills/synabun/modules/idea.md +1 -1
- package/skills/synabun/modules/schedule.md +1 -1
- package/updater.cmd +2 -2
- package/updater.mjs +97 -3
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
// Main-process side of the WhatsApp Link: forks host.js — the only process
|
|
2
2
|
// that loads Baileys or reads the WhatsApp credentials — speaks protocol.js
|
|
3
|
-
// over IPC, respawns it
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// over IPC, and respawns it whenever it dies, for as long as it is wanted:
|
|
4
|
+
// 1 / 5 / 15 / 60 / 300 s, never more than 300 s, the ladder starting over
|
|
5
|
+
// once a host ran for 10 minutes. host.state 'held' is only a host speaking
|
|
6
|
+
// another IPC protocol (an update installed while SynaBun ran): it is killed
|
|
7
|
+
// and stays down until start() again. mode 'inproc'
|
|
8
|
+
// (SYNABUN_WHATSAPP_HOST=inproc) runs host-core inside this process instead:
|
|
9
|
+
// tests and a kill switch.
|
|
6
10
|
//
|
|
7
11
|
// Same shape as lib/pty-host/manager.js and lib/desktop/manager.js, with two
|
|
8
12
|
// deliberate differences: the child gets an ALLOWLISTED environment (no API
|
|
@@ -10,15 +14,19 @@
|
|
|
10
14
|
// redacted before it reaches a log (never inherited).
|
|
11
15
|
//
|
|
12
16
|
// Every method resolves; none rejects. Failures are {ok:false, code, message}.
|
|
13
|
-
//
|
|
14
|
-
//
|
|
17
|
+
// The run intent (connect({purpose:'run'}), or a fresh link) is kept here
|
|
18
|
+
// before the request leaves, so a host that is still starting, or one that
|
|
19
|
+
// crashed with the request in flight, gets it anyway: every ready replays the
|
|
20
|
+
// last configure, then connect({purpose:'run'}) while the intent stands. Only
|
|
21
|
+
// NOT_LINKED, a terminal state or fatal event of the host (TERMINAL_CONN,
|
|
22
|
+
// DESIRE_KILLERS), stop(), disconnect() and logout() drop it.
|
|
15
23
|
|
|
16
24
|
import { fork } from 'node:child_process';
|
|
17
25
|
import { mkdirSync } from 'node:fs';
|
|
18
26
|
import { fileURLToPath } from 'node:url';
|
|
19
|
-
import { CONN, ERR, EV, LIMITS, OP, PROTOCOL_VERSION, pickPrefs, validateEvent } from './protocol.js';
|
|
27
|
+
import { ADDRESS_KEYS, CONN, ERR, EV, LIMITS, OP, PROTOCOL_VERSION, pickPrefs, validateEvent } from './protocol.js';
|
|
20
28
|
import { resolveWhatsAppPaths } from './paths.js';
|
|
21
|
-
import { redactWa } from './redact.js';
|
|
29
|
+
import { redactLogLine, redactWa } from './redact.js';
|
|
22
30
|
|
|
23
31
|
const HOST_PATH = fileURLToPath(new URL('./host.js', import.meta.url));
|
|
24
32
|
|
|
@@ -77,7 +85,10 @@ export function hostExecArgv(parent = process.execArgv, { nodeVersion = process.
|
|
|
77
85
|
|
|
78
86
|
const PUBLIC_EVENTS = new Set(['state', 'qr', 'pairing_code', 'linked', 'owner_bound', 'inbound', 'ignored', 'throttled', 'fatal']);
|
|
79
87
|
const TERMINAL_CONN = new Set([CONN.LOGGED_OUT, CONN.REPLACED, CONN.FORBIDDEN, CONN.UNLINKED, CONN.LINK_EXPIRED, CONN.ERROR, CONN.LOCKED]);
|
|
80
|
-
|
|
88
|
+
// Fatal events that end the run intent: a logout (401, or a session WhatsApp calls invalid), an
|
|
89
|
+
// account refused while linking, an owner never confirmed, a session store held by another process.
|
|
90
|
+
// A saved session never stops on 440, 403 or a generic 500: the host retries those.
|
|
91
|
+
const DESIRE_KILLERS = new Set(['LOGGED_OUT', 'FORBIDDEN', 'CONFIRM_TIMEOUT', 'LOCKED']);
|
|
81
92
|
const LINE_BURST = 60;
|
|
82
93
|
const LINE_WINDOW_MS = 60_000;
|
|
83
94
|
|
|
@@ -114,11 +125,12 @@ export function createWhatsAppManager({
|
|
|
114
125
|
log = () => {},
|
|
115
126
|
now = () => Date.now(),
|
|
116
127
|
respawnBackoffMs = [1000, 5000, 15000, 60000, 300000],
|
|
117
|
-
|
|
118
|
-
crashWindowMs = 15 * 60_000,
|
|
128
|
+
respawnMaxMs = 300_000, // no respawn waits longer, whatever the ladder says
|
|
119
129
|
stableMs = 10 * 60_000,
|
|
120
130
|
requestTimeoutMs = 15_000,
|
|
121
131
|
sendTimeoutMs = 90_000,
|
|
132
|
+
// send_file: a read of up to 100 MiB and its upload. Past it the delivery is uncertain (never retried).
|
|
133
|
+
sendFileTimeoutMs = 120_000,
|
|
122
134
|
readyTimeoutMs = 20_000,
|
|
123
135
|
env = process.env,
|
|
124
136
|
platform = process.platform,
|
|
@@ -135,7 +147,6 @@ export function createWhatsAppManager({
|
|
|
135
147
|
let core = null;
|
|
136
148
|
let token = null; // identity of the current host (child or inproc core)
|
|
137
149
|
let ready = false;
|
|
138
|
-
let hadReady = false;
|
|
139
150
|
let hostState = 'stopped';
|
|
140
151
|
let hostPid = null;
|
|
141
152
|
let restarts = 0;
|
|
@@ -143,10 +154,10 @@ export function createWhatsAppManager({
|
|
|
143
154
|
let heldReason = null;
|
|
144
155
|
let launchError = null; // why the current launch failed before its ready event
|
|
145
156
|
let configured = Promise.resolve(); // the configure replayed on ready
|
|
146
|
-
let exitTimes = [];
|
|
147
157
|
let failIdx = 0;
|
|
148
158
|
let startedAt = 0;
|
|
149
159
|
let respawnTimer = null;
|
|
160
|
+
let respawnAt = null; // when the next host starts, while hostState is 'backoff'
|
|
150
161
|
let readyTimer = null;
|
|
151
162
|
let stoppedByUser = true;
|
|
152
163
|
let lastPrefs = null;
|
|
@@ -175,8 +186,11 @@ export function createWhatsAppManager({
|
|
|
175
186
|
const conn = { ...connCache };
|
|
176
187
|
if (hostState === 'held') conn.state = CONN.HELD;
|
|
177
188
|
else if (hostState === 'stopped') conn.state = CONN.IDLE;
|
|
189
|
+
// host.retryInMs: how long until the next host starts. While the link is wanted, conn says
|
|
190
|
+
// "reconnecting" with the same wait (set when the respawn was scheduled, like the host's own retryInMs).
|
|
191
|
+
const retryInMs = hostState === 'backoff' && respawnAt !== null ? Math.max(0, respawnAt - now()) : null;
|
|
178
192
|
return {
|
|
179
|
-
host: { state: hostState, pid: hostPid, restarts, lastError },
|
|
193
|
+
host: { state: hostState, pid: hostPid, restarts, lastError, retryInMs },
|
|
180
194
|
conn,
|
|
181
195
|
runtime: { ...runtimeInfo },
|
|
182
196
|
};
|
|
@@ -225,7 +239,7 @@ export function createWhatsAppManager({
|
|
|
225
239
|
function request(op, fields = {}, { timeoutMs = requestTimeoutMs } = {}) {
|
|
226
240
|
return new Promise((resolve) => {
|
|
227
241
|
if (hostState === 'held') {
|
|
228
|
-
resolve(fail(ERR.HELD, heldReason || lastError || 'the WhatsApp host
|
|
242
|
+
resolve(fail(ERR.HELD, heldReason || lastError || 'the WhatsApp host speaks another protocol version'));
|
|
229
243
|
return;
|
|
230
244
|
}
|
|
231
245
|
if (hostState === 'stopped') {
|
|
@@ -280,6 +294,7 @@ export function createWhatsAppManager({
|
|
|
280
294
|
|
|
281
295
|
// ── Host → manager ──
|
|
282
296
|
|
|
297
|
+
/** An incompatible host (another IPC protocol): no respawn until start() again. */
|
|
283
298
|
function hold(reason) {
|
|
284
299
|
heldReason = reason;
|
|
285
300
|
lastError = reason;
|
|
@@ -287,6 +302,7 @@ export function createWhatsAppManager({
|
|
|
287
302
|
desired = null;
|
|
288
303
|
clearTimeout(respawnTimer);
|
|
289
304
|
respawnTimer = null;
|
|
305
|
+
respawnAt = null;
|
|
290
306
|
failAll(ERR.HELD, reason);
|
|
291
307
|
resolveReadyWaiters(false);
|
|
292
308
|
say(`[whatsapp] host held: ${reason}`, 'warn');
|
|
@@ -315,19 +331,17 @@ export function createWhatsAppManager({
|
|
|
315
331
|
ready = true;
|
|
316
332
|
hostPid = m.pid;
|
|
317
333
|
hostState = 'running';
|
|
334
|
+
respawnAt = null;
|
|
318
335
|
runtimeInfo = { ...m.runtime };
|
|
319
336
|
connCache = { ...m.conn };
|
|
320
|
-
|
|
321
|
-
hadReady = true;
|
|
337
|
+
// Every ready, the first one included: the configure first, then the run intent.
|
|
322
338
|
configured = lastPrefs
|
|
323
339
|
? request(OP.CONFIGURE, { prefs: lastPrefs }).then((r) => { if (!r.ok) say(`[whatsapp] configure failed: ${r.message}`, 'warn'); })
|
|
324
340
|
: Promise.resolve();
|
|
341
|
+
// A connect that waited for this host carries the intent itself.
|
|
342
|
+
const connectWaiting = queue.some((q) => q.op === OP.CONNECT && pending.has(q.reqId));
|
|
325
343
|
flushQueue();
|
|
326
|
-
if (
|
|
327
|
-
request(OP.CONNECT, { ...desired }).then((r) => {
|
|
328
|
-
if (!r.ok) say(`[whatsapp] reconnect after a host restart failed: ${r.code}`, 'warn');
|
|
329
|
-
});
|
|
330
|
-
}
|
|
344
|
+
if (desired && !connectWaiting) resumeRun(source);
|
|
331
345
|
resolveReadyWaiters(true);
|
|
332
346
|
emitState();
|
|
333
347
|
return;
|
|
@@ -348,7 +362,8 @@ export function createWhatsAppManager({
|
|
|
348
362
|
emitState();
|
|
349
363
|
return;
|
|
350
364
|
case EV.LOG:
|
|
351
|
-
|
|
365
|
+
// send_file's audit line keeps its path legible (redact.js fileAuditLine checks its exact shape).
|
|
366
|
+
say(`[whatsapp-host] ${redactLogLine(m.message)}`, m.level === 'error' ? 'error' : m.level === 'warn' ? 'warn' : 'info');
|
|
352
367
|
return;
|
|
353
368
|
case EV.FATAL:
|
|
354
369
|
if (DESIRE_KILLERS.has(m.code)) desired = null;
|
|
@@ -368,6 +383,19 @@ export function createWhatsAppManager({
|
|
|
368
383
|
}
|
|
369
384
|
}
|
|
370
385
|
|
|
386
|
+
/** After `configured`, connect the wanted link on the host that just became ready (`me`). */
|
|
387
|
+
function resumeRun(me) {
|
|
388
|
+
configured.then(() => {
|
|
389
|
+
if (token !== me || !ready || stoppedByUser || !desired) return;
|
|
390
|
+
const intent = desired;
|
|
391
|
+
request(OP.CONNECT, { ...intent }).then((r) => {
|
|
392
|
+
if (r.ok) return;
|
|
393
|
+
if (r.code === ERR.NOT_LINKED && desired === intent) desired = null;
|
|
394
|
+
say(`[whatsapp] connecting after the host started failed: ${r.code}`, 'warn');
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
371
399
|
// ── Lifecycle ──
|
|
372
400
|
|
|
373
401
|
function allowLine() {
|
|
@@ -404,6 +432,9 @@ export function createWhatsAppManager({
|
|
|
404
432
|
function launch() {
|
|
405
433
|
clearTimeout(respawnTimer);
|
|
406
434
|
respawnTimer = null;
|
|
435
|
+
respawnAt = null;
|
|
436
|
+
// The respawn wait is over: a host starting now has no "next try" to show.
|
|
437
|
+
if (connCache.retryInMs !== null) connCache = { ...connCache, retryInMs: null };
|
|
407
438
|
ready = false;
|
|
408
439
|
hostPid = null;
|
|
409
440
|
launchError = null;
|
|
@@ -492,21 +523,18 @@ export function createWhatsAppManager({
|
|
|
492
523
|
emitState();
|
|
493
524
|
return;
|
|
494
525
|
}
|
|
526
|
+
// A crash is never final: respawn on the ladder, for as long as the host is wanted.
|
|
495
527
|
const reason = `exited (code ${code}, signal ${signal})`;
|
|
496
528
|
lastError = launchError || `the WhatsApp host ${reason}`;
|
|
497
529
|
launchError = null;
|
|
498
530
|
const t = now();
|
|
499
|
-
exitTimes = exitTimes.filter((x) => t - x < crashWindowMs);
|
|
500
|
-
exitTimes.push(t);
|
|
501
|
-
if (exitTimes.length >= crashLimit) {
|
|
502
|
-
hold(`the WhatsApp host crashed ${exitTimes.length} times in ${Math.round(crashWindowMs / 60_000)} minutes`);
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
531
|
if (startedAt && t - startedAt >= stableMs) failIdx = 0;
|
|
506
|
-
const delay = respawnBackoffMs[Math.min(failIdx, respawnBackoffMs.length - 1)] ?? 0;
|
|
532
|
+
const delay = Math.min(respawnMaxMs, Math.max(0, respawnBackoffMs[Math.min(failIdx, respawnBackoffMs.length - 1)] ?? 0));
|
|
507
533
|
failIdx += 1;
|
|
508
534
|
restarts += 1;
|
|
509
535
|
hostState = 'backoff';
|
|
536
|
+
respawnAt = t + delay;
|
|
537
|
+
if (desired) connCache = { ...connCache, retryInMs: delay };
|
|
510
538
|
say(`[whatsapp] host ${reason}; restarting in ${delay} ms`, 'warn');
|
|
511
539
|
emitState();
|
|
512
540
|
respawnTimer = setTimeout(() => {
|
|
@@ -517,12 +545,16 @@ export function createWhatsAppManager({
|
|
|
517
545
|
|
|
518
546
|
// ── Public API ──
|
|
519
547
|
|
|
520
|
-
|
|
548
|
+
/**
|
|
549
|
+
* Start the host (at once, also while a respawn is pending). `resume: false` drops the run intent
|
|
550
|
+
* first: a host started to log out (Unlink) must not reconnect the link on its ready.
|
|
551
|
+
*/
|
|
552
|
+
async function start(prefs, { resume = true } = {}) {
|
|
521
553
|
if (prefs !== undefined) lastPrefs = { ...(lastPrefs || {}), ...pickPrefs(prefs) };
|
|
554
|
+
if (!resume) desired = null;
|
|
522
555
|
stoppedByUser = false;
|
|
523
556
|
if (hostState === 'held') {
|
|
524
557
|
heldReason = null;
|
|
525
|
-
exitTimes = [];
|
|
526
558
|
failIdx = 0;
|
|
527
559
|
hostState = 'stopped';
|
|
528
560
|
}
|
|
@@ -544,8 +576,13 @@ export function createWhatsAppManager({
|
|
|
544
576
|
const fields = { purpose };
|
|
545
577
|
if (connMode) fields.mode = connMode;
|
|
546
578
|
if (link) fields.link = { method: link.method === 'code' ? 'code' : 'qr', ...(link.phone ? { phone: String(link.phone) } : {}) };
|
|
579
|
+
// The intent before the request: a host that is not ready yet, or that dies with the request
|
|
580
|
+
// in flight, connects on its next ready. Only "nothing is linked" ends it here. A host that was
|
|
581
|
+
// stopped or is held gets no ready of its own: whatever starts it next asks for what it wants.
|
|
582
|
+
const intent = purpose === 'run' && hostState !== 'stopped' && hostState !== 'held' ? { purpose: 'run', ...(connMode ? { mode: connMode } : {}) } : null;
|
|
583
|
+
if (intent) desired = intent;
|
|
547
584
|
const res = await request(OP.CONNECT, fields);
|
|
548
|
-
if (res.ok &&
|
|
585
|
+
if (intent && !res.ok && res.code === ERR.NOT_LINKED && desired === intent) desired = null;
|
|
549
586
|
return res;
|
|
550
587
|
}
|
|
551
588
|
|
|
@@ -554,6 +591,7 @@ export function createWhatsAppManager({
|
|
|
554
591
|
desired = null;
|
|
555
592
|
clearTimeout(respawnTimer);
|
|
556
593
|
respawnTimer = null;
|
|
594
|
+
respawnAt = null;
|
|
557
595
|
clearTimeout(readyTimer);
|
|
558
596
|
readyTimer = null;
|
|
559
597
|
resolveReadyWaiters(false);
|
|
@@ -595,6 +633,7 @@ export function createWhatsAppManager({
|
|
|
595
633
|
desired = null;
|
|
596
634
|
clearTimeout(respawnTimer);
|
|
597
635
|
respawnTimer = null;
|
|
636
|
+
respawnAt = null;
|
|
598
637
|
clearTimeout(readyTimer);
|
|
599
638
|
readyTimer = null;
|
|
600
639
|
if (child) { try { child.kill('SIGTERM'); } catch {} }
|
|
@@ -623,6 +662,27 @@ export function createWhatsAppManager({
|
|
|
623
662
|
if (typeof text !== 'string') return Promise.resolve(fail(ERR.BAD_REQUEST, 'text must be a string'));
|
|
624
663
|
return request(OP.SEND, { text, ...(replyTo ? { replyTo } : {}) }, { timeoutMs: sendTimeoutMs });
|
|
625
664
|
},
|
|
665
|
+
/**
|
|
666
|
+
* A local file to the owner (send_file): exactly one request, never retried here or anywhere,
|
|
667
|
+
* and never queued for a host that is not up yet (HOST_UNAVAILABLE: nothing was sent). A TIMEOUT,
|
|
668
|
+
* or a host that exits with the request in flight, may come after WhatsApp took the file: those
|
|
669
|
+
* answers carry `uncertain: true`. Only { path, caption?, as? } travel: an address key is
|
|
670
|
+
* ADDRESS_FORBIDDEN, any other key BAD_REQUEST.
|
|
671
|
+
*/
|
|
672
|
+
sendFile: async (fields = {}) => {
|
|
673
|
+
if (!isPlainObject(fields)) return fail(ERR.BAD_REQUEST, 'send_file takes { path, caption?, as? }');
|
|
674
|
+
const address = ADDRESS_KEYS.find((key) => Object.hasOwn(fields, key));
|
|
675
|
+
if (address) return fail(ERR.ADDRESS_FORBIDDEN, `send_file never takes an address ('${address}')`);
|
|
676
|
+
const extra = Object.keys(fields).find((key) => !['path', 'caption', 'as'].includes(key));
|
|
677
|
+
if (extra) return fail(ERR.BAD_REQUEST, `send_file has an unknown field '${extra.slice(0, 40)}'`);
|
|
678
|
+
if (!ready) return fail(ERR.HOST_UNAVAILABLE, 'the WhatsApp host is not running: nothing was sent');
|
|
679
|
+
const out = { path: fields.path };
|
|
680
|
+
if (fields.caption !== undefined && fields.caption !== null) out.caption = fields.caption;
|
|
681
|
+
if (fields.as !== undefined && fields.as !== null) out.as = fields.as;
|
|
682
|
+
const result = await request(OP.SEND_FILE, out, { timeoutMs: sendFileTimeoutMs });
|
|
683
|
+
if (!result.ok && (result.code === ERR.TIMEOUT || result.code === ERR.HOST_UNAVAILABLE)) result.uncertain = true;
|
|
684
|
+
return result;
|
|
685
|
+
},
|
|
626
686
|
react: (id, name) => request(OP.REACT, { id, reaction: name ?? null }, { timeoutMs: sendTimeoutMs }),
|
|
627
687
|
presence: (state) => request(OP.PRESENCE, { state }),
|
|
628
688
|
markRead: (ids) => request(OP.READ, { ids: Array.isArray(ids) ? ids.slice(0, LIMITS.MAX_READ_IDS) : [] }),
|
|
@@ -9,8 +9,16 @@
|
|
|
9
9
|
// session to a second machine (that ends in connectionReplaced).
|
|
10
10
|
// runtimeDir the Baileys runtime, installed on demand from the pinned
|
|
11
11
|
// lockfile in ./connector (never in a SynaBun package.json).
|
|
12
|
+
// transcriptionDir voice notes (transcription.js): where the user puts
|
|
13
|
+
// whisper.cpp (bin/), FFmpeg (bin/) and the model, plus the
|
|
14
|
+
// private scratch folder of each note. A sibling of runtimeDir:
|
|
15
|
+
// outside every backup. SynaBun never downloads anything here.
|
|
12
16
|
// stagingDir where the installer builds the next runtime before swapping.
|
|
13
17
|
// logsDir SynaBun's shared log folder.
|
|
18
|
+
// dataHome DATA_HOME itself: a file the Assistant sends from WhatsApp is
|
|
19
|
+
// never taken from it or from waHome, save SynaBun's generated
|
|
20
|
+
// media (local-file.js protectedPlaces); SynaBun's backup
|
|
21
|
+
// archives are refused by name wherever the backup folder is.
|
|
14
22
|
//
|
|
15
23
|
// Pure: no filesystem access, so callers and tests can resolve any platform.
|
|
16
24
|
|
|
@@ -31,7 +39,7 @@ export function isInside(candidate, parent, platform = process.platform) {
|
|
|
31
39
|
/**
|
|
32
40
|
* @param {{dataHome: string, env?: Record<string,string|undefined>, platform?: string, extraBackupRoots?: string[]}} opts
|
|
33
41
|
* `extraBackupRoots`: folders a backup copies besides BACKUP_DATA_DIRS (server.js' additional entries).
|
|
34
|
-
* @returns {{waHome:string, defaultWaHome:string, authDir:string, runtimeDir:string, stagingDir:string, logsDir:string,
|
|
42
|
+
* @returns {{dataHome:string, waHome:string, defaultWaHome:string, authDir:string, runtimeDir:string, transcriptionDir:string, stagingDir:string, logsDir:string,
|
|
35
43
|
* installLogPath:string, installLockPath:string, npmCacheDir:string, backupRoots:string[], warnings:string[]}}
|
|
36
44
|
*/
|
|
37
45
|
export function resolveWhatsAppPaths({ dataHome, env = process.env, platform = process.platform, extraBackupRoots = [] } = {}) {
|
|
@@ -66,10 +74,12 @@ export function resolveWhatsAppPaths({ dataHome, env = process.env, platform = p
|
|
|
66
74
|
|
|
67
75
|
const runtimeRoot = p.join(home, 'runtime');
|
|
68
76
|
return {
|
|
77
|
+
dataHome: home,
|
|
69
78
|
waHome,
|
|
70
79
|
defaultWaHome,
|
|
71
80
|
authDir: p.join(waHome, 'auth'),
|
|
72
81
|
runtimeDir: p.join(runtimeRoot, 'whatsapp'),
|
|
82
|
+
transcriptionDir: p.join(runtimeRoot, 'whatsapp-transcription'),
|
|
73
83
|
stagingDir: p.join(runtimeRoot, 'whatsapp.staging'),
|
|
74
84
|
logsDir: p.join(dataDir, 'logs'),
|
|
75
85
|
installLogPath: p.join(dataDir, 'whatsapp', 'install.log'),
|
|
@@ -9,13 +9,15 @@
|
|
|
9
9
|
// 'inbound'|'ignored'|'throttled'|'fatal'|'log', ...}
|
|
10
10
|
//
|
|
11
11
|
// Addressing: no op that makes the host talk to WhatsApp carries an address.
|
|
12
|
-
// send/react/presence/read always reach the bound owner (sendToOwner
|
|
13
|
-
// host-core.js is the only sendMessage call site) and validateOp rejects any
|
|
12
|
+
// send/send_file/react/presence/read always reach the bound owner (sendToOwner
|
|
13
|
+
// in host-core.js is the only sendMessage call site) and validateOp rejects any
|
|
14
14
|
// address-shaped key on them. Events never carry a JID or a phone number: the
|
|
15
15
|
// schemas below are closed, so a stray field fails validation instead of
|
|
16
16
|
// leaking. The single exception is the claim_start reply, whose wa.me link
|
|
17
17
|
// holds the dedicated account's own number for the local settings tab.
|
|
18
18
|
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
|
|
19
21
|
export const PROTOCOL_VERSION = 1;
|
|
20
22
|
|
|
21
23
|
export const OP = Object.freeze({
|
|
@@ -28,6 +30,8 @@ export const OP = Object.freeze({
|
|
|
28
30
|
CLAIM_CANCEL: 'claim_cancel',
|
|
29
31
|
OWNER_RESET: 'owner_reset',
|
|
30
32
|
SEND: 'send',
|
|
33
|
+
// A local file to the owner as a WhatsApp attachment (local-file.js reads and checks it in the host).
|
|
34
|
+
SEND_FILE: 'send_file',
|
|
31
35
|
REACT: 'react',
|
|
32
36
|
PRESENCE: 'presence',
|
|
33
37
|
READ: 'read',
|
|
@@ -52,7 +56,12 @@ export const EV = Object.freeze({
|
|
|
52
56
|
LOG: 'log',
|
|
53
57
|
});
|
|
54
58
|
|
|
55
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* conn.state values. `held` is set by the manager (a host speaking another
|
|
61
|
+
* IPC protocol: an update installed while SynaBun ran), never by the host.
|
|
62
|
+
* The host no longer stops in `replaced` or `forbidden` for a saved session
|
|
63
|
+
* (440 and 403 are retried: CONN_WARNINGS); only a link attempt still ends there.
|
|
64
|
+
*/
|
|
56
65
|
export const CONN = Object.freeze({
|
|
57
66
|
IDLE: 'idle',
|
|
58
67
|
UNLINKED: 'unlinked',
|
|
@@ -91,6 +100,10 @@ export const ERR = Object.freeze({
|
|
|
91
100
|
PAUSED: 'PAUSED',
|
|
92
101
|
THROTTLED: 'THROTTLED',
|
|
93
102
|
TOO_LONG: 'TOO_LONG',
|
|
103
|
+
// send_file (local-file.js): a protected location, over the size limit, or not a readable regular file.
|
|
104
|
+
FILE_FORBIDDEN: 'FILE_FORBIDDEN',
|
|
105
|
+
FILE_TOO_LARGE: 'FILE_TOO_LARGE',
|
|
106
|
+
FILE_INVALID: 'FILE_INVALID',
|
|
94
107
|
BAD_TARGET: 'BAD_TARGET',
|
|
95
108
|
BAD_PHONE: 'BAD_PHONE',
|
|
96
109
|
CLAIM_INACTIVE: 'CLAIM_INACTIVE',
|
|
@@ -103,15 +116,46 @@ export const ERR = Object.freeze({
|
|
|
103
116
|
INTERNAL: 'INTERNAL',
|
|
104
117
|
});
|
|
105
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Disconnect codes a saved session keeps retrying at the longest delay while
|
|
121
|
+
* Settings shows a warning: 440 connectionReplaced (another copy uses the
|
|
122
|
+
* session) and 403 forbidden (the account is refused). The host picks the
|
|
123
|
+
* delay by this table (baileys-adapter.js classifyDisconnect), the service the
|
|
124
|
+
* warning, from conn.lastDisconnect.code.
|
|
125
|
+
*/
|
|
126
|
+
export const CONN_WARNINGS = Object.freeze({ 440: 'replaced', 403: 'forbidden' });
|
|
127
|
+
|
|
106
128
|
export const LIMITS = Object.freeze({
|
|
107
129
|
MAX_TEXT: 3500,
|
|
108
130
|
MAX_INBOUND_TEXT: 8192,
|
|
109
131
|
MAX_IMAGE_BYTES: 5242880,
|
|
110
132
|
MAX_IMAGES: 4,
|
|
111
133
|
MAX_READ_IDS: 20,
|
|
134
|
+
// Voice notes (transcription.js): declared and actual size, declared and decoded duration.
|
|
135
|
+
MAX_AUDIO_BYTES: 20 * 1024 * 1024,
|
|
136
|
+
MAX_AUDIO_SECONDS: 600,
|
|
137
|
+
// send_file (local-file.js): the path and caption the op takes; a photo, video or audio file above
|
|
138
|
+
// MAX_FILE_MEDIA_BYTES goes as a document; nothing above MAX_FILE_BYTES is read.
|
|
139
|
+
MAX_FILE_PATH: 2048,
|
|
140
|
+
MAX_FILE_CAPTION: 1024,
|
|
141
|
+
MAX_FILE_MEDIA_BYTES: 16 * 1024 * 1024,
|
|
142
|
+
MAX_FILE_BYTES: 100 * 1024 * 1024,
|
|
112
143
|
});
|
|
113
144
|
|
|
114
|
-
/**
|
|
145
|
+
/** send_file `as`: auto (by extension), or the form asked for (a native form must match the extension). */
|
|
146
|
+
export const FILE_SEND_AS = Object.freeze(['auto', 'document', 'image', 'video', 'audio']);
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The text of a transcribed voice note starts with this marker, then the
|
|
150
|
+
* transcript (one line), then "\n" and the caption when there is one. The
|
|
151
|
+
* inbound message also carries `voice: true`: spoken words are an ordinary
|
|
152
|
+
* message, never a command, an ALLOW code or the answer to an approval.
|
|
153
|
+
*/
|
|
154
|
+
export const VOICE_MARKER = '[Voice note transcript]';
|
|
155
|
+
/** transcriptionLanguage: 'auto' (whisper.cpp detects it) or a 2-3 letter lowercase language code. */
|
|
156
|
+
export const TRANSCRIPTION_LANGUAGE_RE = /^(?:auto|[a-z]{2,3})$/;
|
|
157
|
+
|
|
158
|
+
/** Keys that name a WhatsApp recipient. None may appear on send/send_file/react/presence/read. */
|
|
115
159
|
export const ADDRESS_KEYS = Object.freeze(['jid', 'to', 'remoteJid', 'chat', 'number', 'participant', 'recipient']);
|
|
116
160
|
|
|
117
161
|
/** Status reactions the bridge may set on an owner message (null clears it). */
|
|
@@ -127,7 +171,7 @@ export const MODES = Object.freeze(['self', 'dedicated']);
|
|
|
127
171
|
export const SELF_TRIGGERS = Object.freeze(['all', 'prefix']);
|
|
128
172
|
export const DEFAULT_PREFIX = 'sb';
|
|
129
173
|
|
|
130
|
-
const ADDRESSLESS_OPS = new Set([OP.SEND, OP.REACT, OP.PRESENCE, OP.READ]);
|
|
174
|
+
const ADDRESSLESS_OPS = new Set([OP.SEND, OP.SEND_FILE, OP.REACT, OP.PRESENCE, OP.READ]);
|
|
131
175
|
const MSG_ID_RE = /^[A-Za-z0-9._:+/=-]{1,128}$/;
|
|
132
176
|
const PREFIX_RE = /^[^\s]{1,16}$/;
|
|
133
177
|
|
|
@@ -180,10 +224,21 @@ function checkObject(v, spec) {
|
|
|
180
224
|
|
|
181
225
|
// ── Prefs (configure) ──
|
|
182
226
|
|
|
227
|
+
// A tool path the user set in Settings: '' (found by itself) or an absolute path. Whether the file
|
|
228
|
+
// exists and runs is checked where it is used (transcription.js), right before it runs.
|
|
229
|
+
const toolPath = () => (v, p) => (!p || v === undefined ? null
|
|
230
|
+
: typeof v !== 'string' || v.length > 4096 || /[\u0000-\u001f]/.test(v) || (v !== '' && !path.isAbsolute(v)) ? "must be '' or an absolute path" : null);
|
|
231
|
+
|
|
183
232
|
const PREFS_SPEC = {
|
|
184
233
|
mode: t.optOneOf(MODES),
|
|
185
234
|
selfTrigger: t.optOneOf(SELF_TRIGGERS),
|
|
186
235
|
prefix: (v, p) => (!p || v === undefined ? null : typeof v !== 'string' || !PREFIX_RE.test(v) ? 'must be 1-16 non-space characters' : null),
|
|
236
|
+
// Voice notes (transcription.js); a host that was never configured transcribes nothing.
|
|
237
|
+
transcriptionEnabled: t.optBool(),
|
|
238
|
+
transcriptionLanguage: (v, p) => (!p || v === undefined ? null : typeof v !== 'string' || !TRANSCRIPTION_LANGUAGE_RE.test(v) ? "must be 'auto' or a 2-3 letter language code" : null),
|
|
239
|
+
transcriptionEnginePath: toolPath(),
|
|
240
|
+
transcriptionModelPath: toolPath(),
|
|
241
|
+
transcriptionFfmpegPath: toolPath(),
|
|
187
242
|
};
|
|
188
243
|
export const PREF_KEYS = Object.freeze(Object.keys(PREFS_SPEC));
|
|
189
244
|
|
|
@@ -212,6 +267,7 @@ const OP_SPECS = {
|
|
|
212
267
|
[OP.CLAIM_CANCEL]: {},
|
|
213
268
|
[OP.OWNER_RESET]: {},
|
|
214
269
|
[OP.SEND]: { text: t.str(LIMITS.MAX_TEXT * 4), replyTo: (v, p) => (!p || v === undefined || v === null ? null : typeof v !== 'string' || !MSG_ID_RE.test(v) ? 'must be a message id' : null) },
|
|
270
|
+
[OP.SEND_FILE]: { path: t.str(LIMITS.MAX_FILE_PATH), caption: t.optStr(LIMITS.MAX_FILE_CAPTION), as: t.optOneOf(FILE_SEND_AS) },
|
|
215
271
|
[OP.REACT]: { id: t.msgId(), reaction: (v, p) => (!p ? 'is required' : v === null || Object.hasOwn(REACTIONS, v) ? null : `must be one of ${Object.keys(REACTIONS).join('|')} or null`) },
|
|
216
272
|
[OP.PRESENCE]: { state: t.oneOf(['composing', 'paused']) },
|
|
217
273
|
[OP.READ]: {
|
|
@@ -294,6 +350,10 @@ export const INBOUND_SPEC = {
|
|
|
294
350
|
owner: (v, p) => (!p ? 'is required' : v === true ? null : 'must be true'),
|
|
295
351
|
// Delivered after an offline spell (messages.upsert 'append'); `ts` says when it was sent.
|
|
296
352
|
backlog: t.optBool(),
|
|
353
|
+
// A voice note transcribed on this computer: `text` is VOICE_MARKER + the transcript (+ "\n" + the caption).
|
|
354
|
+
voice: t.optBool(),
|
|
355
|
+
// …of an audio file, not a note recorded in WhatsApp: its words may be anyone's (the turn is capped like a picture's).
|
|
356
|
+
audioFile: t.optBool(),
|
|
297
357
|
};
|
|
298
358
|
|
|
299
359
|
const EVENT_SPECS = {
|
|
@@ -121,6 +121,69 @@ export function redactWa(value) {
|
|
|
121
121
|
return text;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* A local file path for the send_file audit line and its Activity row: credentials, QR payloads,
|
|
126
|
+
* JIDs, codes and phone numbers are redacted as in redactWa, but not the key-shape heuristics
|
|
127
|
+
* (HEX_RE, B64_RE), which read most real paths (`/Users/<you>/App2/…`, a run id folder) as key
|
|
128
|
+
* material and would leave nothing of the path to audit. Paths only: never for message text.
|
|
129
|
+
*/
|
|
130
|
+
export function redactFilePath(value) {
|
|
131
|
+
let text = typeof value === 'string' ? value : String(value ?? '');
|
|
132
|
+
if (!text) return '';
|
|
133
|
+
text = redactCredentials(text);
|
|
134
|
+
text = text.replace(QR_URL_RE, '[qr]').replace(QR_REF_RE, '[qr]');
|
|
135
|
+
text = text.replace(JID_RE, (_m, user, server) => tagFor(user, server));
|
|
136
|
+
text = text.replace(ALLOW_CODE_RE, (_m, word) => `${word} [code]`);
|
|
137
|
+
text = text.replace(CLAIM_CODE_RE, '[code]').replace(PAIRING_DASH_RE, '[code]');
|
|
138
|
+
text = text.replace(NUMBER_RE, (run) => (DATE_LIKE_RE.test(run) ? run : '[number]'));
|
|
139
|
+
return text.slice(0, 2048);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// A run of key-shaped characters inside a path, between separators, dashes, underscores, dots and spaces.
|
|
143
|
+
const PATH_KEY_RUN_RE = /[A-Za-z0-9+=]{24,}/g;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* A path the Assistant asked for and SynaBun refused (its Activity row): redactFilePath, and any run of
|
|
147
|
+
* 24 or more hex or key-shaped characters between the path's separators, dashes, underscores, dots and
|
|
148
|
+
* spaces masked too, because the text came from the model and need not name a file at all. Ordinary
|
|
149
|
+
* names stay readable (their words are short); a real file's canonical path keeps redactFilePath alone.
|
|
150
|
+
*/
|
|
151
|
+
export function redactRequestedPath(value) {
|
|
152
|
+
return redactFilePath(value).replace(PATH_KEY_RUN_RE, (run) => (/^[0-9a-fA-F]+$/.test(run) || looksLikeKey(run) ? '[key]' : run));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const FILE_AUDIT_PREFIX = '[whatsapp] file send ';
|
|
156
|
+
const FILE_AUDIT_TYPES = new Set(['document', 'image', 'video', 'audio']);
|
|
157
|
+
const MASKED_CHAT_RE = /^(?:••••|••\d{2}|••••\d{4}|linked id ••[A-Za-z0-9]{0,2})$/;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The send_file audit line (host-core.js noteFileSend), checked field by field: exactly
|
|
161
|
+
* { path, size, chat, type } with a masked chat and a known form, the path through
|
|
162
|
+
* redactFilePath. → the line, or null for anything else (which gets redactWa like every log line).
|
|
163
|
+
*/
|
|
164
|
+
export function fileAuditLine(message) {
|
|
165
|
+
const text = typeof message === 'string' ? message : '';
|
|
166
|
+
if (!text.startsWith(FILE_AUDIT_PREFIX)) return null;
|
|
167
|
+
let entry;
|
|
168
|
+
try { entry = JSON.parse(text.slice(FILE_AUDIT_PREFIX.length)); } catch { return null; }
|
|
169
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return null;
|
|
170
|
+
const keys = Object.keys(entry);
|
|
171
|
+
if (keys.length !== 4 || keys.join(',') !== 'path,size,chat,type') return null;
|
|
172
|
+
if (typeof entry.path !== 'string' || !Number.isSafeInteger(entry.size) || entry.size < 0 || typeof entry.chat !== 'string' || !MASKED_CHAT_RE.test(entry.chat) || !FILE_AUDIT_TYPES.has(entry.type)) return null;
|
|
173
|
+
return `${FILE_AUDIT_PREFIX}${JSON.stringify({ path: redactFilePath(entry.path), size: entry.size, chat: entry.chat, type: entry.type })}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* One log line on its way to SynaBun's log (manager.js, service.js): redactWa, except a send_file
|
|
178
|
+
* audit line (after the host's "[whatsapp-host] " tag or not), which fileAuditLine checks and keeps.
|
|
179
|
+
*/
|
|
180
|
+
export function redactLogLine(message) {
|
|
181
|
+
const text = typeof message === 'string' ? message : String(message ?? '');
|
|
182
|
+
const tag = text.startsWith('[whatsapp-host] ') ? '[whatsapp-host] ' : '';
|
|
183
|
+
const audit = fileAuditLine(text.slice(tag.length));
|
|
184
|
+
return audit ? `${tag}${audit}` : redactWa(text);
|
|
185
|
+
}
|
|
186
|
+
|
|
124
187
|
/** A phone number for display: never more than the last four digits. */
|
|
125
188
|
export function maskNumber(digits) {
|
|
126
189
|
const d = String(digits ?? '').replace(/\D/g, '');
|