synabun 2.1.0 → 2.2.1
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 +139 -0
- package/README.md +1 -1
- package/docs/whatsapp.md +11 -11
- package/lib/client-config-repair.js +13 -9
- package/lib/external-tools.js +6 -5
- package/lib/update-snapshot.js +77 -3
- package/lib/virustotal.mjs +12 -1
- package/mcp-server/dist/index.js +1 -1
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
- package/mcp-server/dist/services/budget-judgments.js +5 -3
- package/mcp-server/dist/services/budget-judgments.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +2 -2
- package/mcp-server/dist/services/identity.js +2 -2
- package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
- package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-sessions.js +28 -2
- package/mcp-server/dist/services/memory-sessions.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +2 -1
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
- package/mcp-server/dist/services/server-instructions.js +2 -1
- package/mcp-server/dist/services/server-instructions.js.map +1 -1
- package/mcp-server/dist/services/tool-catalog.js +1 -1
- package/mcp-server/dist/services/tool-catalog.js.map +1 -1
- package/mcp-server/dist/services/tool-list.d.ts +1 -1
- package/mcp-server/dist/services/tool-list.d.ts.map +1 -1
- package/mcp-server/dist/services/tool-runtime.d.ts +2 -2
- package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -1
- package/mcp-server/dist/services/tool-runtime.js +1 -1
- package/mcp-server/dist/services/tool-runtime.js.map +1 -1
- package/mcp-server/dist/services/typesafe-config.js +1 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +8 -8
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +10 -9
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/choice.d.ts +6 -0
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +18 -13
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +2 -2
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +9 -6
- package/mcp-server/dist/tools/morelogin.js.map +1 -1
- package/mcp-server/dist/tools/skill-tools.d.ts +8 -8
- package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/skill-tools.js +11 -8
- package/mcp-server/dist/tools/skill-tools.js.map +1 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/src/index.ts +1 -1
- package/mcp-server/src/services/budget-judgments.ts +5 -3
- package/mcp-server/src/services/identity.ts +2 -2
- package/mcp-server/src/services/memory-sessions.ts +24 -3
- package/mcp-server/src/services/neural-interface.ts +2 -1
- package/mcp-server/src/services/server-instructions.ts +2 -1
- package/mcp-server/src/services/tool-catalog.ts +1 -1
- package/mcp-server/src/services/tool-list.ts +1 -1
- package/mcp-server/src/services/tool-runtime.ts +2 -2
- package/mcp-server/src/services/typesafe-config.ts +1 -1
- package/mcp-server/src/tools/agents.ts +10 -9
- package/mcp-server/src/tools/choice.ts +17 -12
- package/mcp-server/src/tools/git-tools.ts +2 -2
- package/mcp-server/src/tools/morelogin.ts +6 -4
- package/mcp-server/src/tools/skill-tools.ts +11 -8
- package/neural-interface/i18n/en.json +504 -39
- package/neural-interface/i18n/pt-BR.json +504 -39
- package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
- package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
- package/neural-interface/lib/antigravity/acp-connection.js +299 -0
- package/neural-interface/lib/antigravity/bypass.js +100 -0
- package/neural-interface/lib/antigravity/catalog.js +273 -0
- package/neural-interface/lib/antigravity/home.js +260 -0
- package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
- package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
- package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
- package/neural-interface/lib/antigravity/policy.js +74 -0
- package/neural-interface/lib/antigravity/tool-call.js +178 -0
- package/neural-interface/lib/antigravity/transcripts.js +252 -0
- package/neural-interface/lib/antigravity/usage.js +43 -0
- package/neural-interface/lib/assistant-api.js +1 -0
- package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
- package/neural-interface/lib/assistant-brains/codex.js +6 -1
- package/neural-interface/lib/assistant-budget-planner.js +7 -3
- package/neural-interface/lib/assistant-budget-split.js +42 -15
- package/neural-interface/lib/assistant-catalog.js +76 -9
- package/neural-interface/lib/assistant-config.js +6 -3
- package/neural-interface/lib/assistant-dispatch.js +211 -50
- package/neural-interface/lib/assistant-envelope.js +153 -1
- package/neural-interface/lib/assistant-evidence.js +80 -0
- package/neural-interface/lib/assistant-hidden-models.js +7 -5
- package/neural-interface/lib/assistant-persona.js +16 -11
- package/neural-interface/lib/assistant-plan-permissions.js +15 -0
- package/neural-interface/lib/assistant-router.js +237 -35
- package/neural-interface/lib/assistant-runtime.js +158 -12
- package/neural-interface/lib/assistant-task-prompt.js +10 -8
- package/neural-interface/lib/assistant-tools.js +2 -2
- package/neural-interface/lib/assistant-usage.js +29 -0
- package/neural-interface/lib/backup-service.js +6 -1
- package/neural-interface/lib/browser-guard-cdp.js +5 -4
- package/neural-interface/lib/browser-network-guard.js +274 -82
- package/neural-interface/lib/browser-tool-policy.js +12 -7
- package/neural-interface/lib/claude-agent-bridge.js +7 -0
- package/neural-interface/lib/claude-history-cache.js +210 -0
- package/neural-interface/lib/claude-model-catalog.js +5 -1
- package/neural-interface/lib/cli-install-api.js +1 -1
- package/neural-interface/lib/cli-install.js +160 -31
- package/neural-interface/lib/cli-resolver.js +9 -2
- package/neural-interface/lib/cli-update-planner.js +25 -15
- package/neural-interface/lib/codex-rollout-reader.js +52 -0
- package/neural-interface/lib/codex-worker-app-server.js +25 -6
- package/neural-interface/lib/editor-git-api.js +3 -20
- package/neural-interface/lib/exec-loop-command.js +9 -2
- package/neural-interface/lib/file-explorer-api.js +74 -40
- package/neural-interface/lib/file-explorer-watch.js +9 -3
- package/neural-interface/lib/git-bash/install.js +52 -6
- package/neural-interface/lib/git-bash/manager.js +59 -17
- package/neural-interface/lib/git-bash/pins.js +12 -0
- package/neural-interface/lib/git-read.js +2 -2
- package/neural-interface/lib/git-status-runner.js +390 -0
- package/neural-interface/lib/git-status-watchdog.js +40 -0
- package/neural-interface/lib/greeting/budgets.js +1 -0
- package/neural-interface/lib/greeting/engine.js +2 -2
- package/neural-interface/lib/greeting/sources.js +10 -12
- package/neural-interface/lib/greeting/writer.js +1 -1
- package/neural-interface/lib/mcp-client-config.js +37 -6
- package/neural-interface/lib/mcp-external-servers.js +40 -7
- package/neural-interface/lib/mcp-installer.js +2 -1
- package/neural-interface/lib/memory-session-capture.js +17 -3
- package/neural-interface/lib/morelogin.js +147 -25
- package/neural-interface/lib/native-loop-providers.js +423 -23
- package/neural-interface/lib/native-loop-runtime.js +23 -2
- package/neural-interface/lib/opencode-native-boundary.js +165 -22
- package/neural-interface/lib/owner-authority.js +1 -1
- package/neural-interface/lib/path-confine.js +12 -9
- package/neural-interface/lib/peer-identity.js +80 -6
- package/neural-interface/lib/pty-host/core.js +28 -1
- package/neural-interface/lib/pty-host/lifetime.js +12 -0
- package/neural-interface/lib/pty-host/manager.js +12 -1
- package/neural-interface/lib/remote-policy.js +1 -1
- package/neural-interface/lib/request-gate.js +3 -0
- package/neural-interface/lib/rulesets/api.js +6 -5
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +89 -10
- package/neural-interface/lib/rulesets/render.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +64 -1
- package/neural-interface/lib/skills/catalog.js +5 -1
- package/neural-interface/lib/skills/creation-brief.js +1 -1
- package/neural-interface/lib/skills/creation-sources.js +19 -1
- package/neural-interface/lib/skills/deploy.js +9 -4
- package/neural-interface/lib/skills/draft-confinement.js +3 -1
- package/neural-interface/lib/skills/eval-confinement.js +3 -1
- package/neural-interface/lib/skills/eval-trace.js +16 -0
- package/neural-interface/lib/skills/export.js +5 -2
- package/neural-interface/lib/skills/known-bridge-digests.json +58 -4
- package/neural-interface/lib/skills/ownership.js +1 -1
- package/neural-interface/lib/skills/roots.js +10 -3
- package/neural-interface/lib/skills/runtime-checks.js +6 -3
- package/neural-interface/lib/skills/service.js +15 -7
- package/neural-interface/lib/skills/telemetry.js +6 -0
- package/neural-interface/lib/synabun-version.js +1 -1
- package/neural-interface/lib/system-activity.js +1 -1
- package/neural-interface/lib/whatsapp/brain.js +7 -3
- package/neural-interface/lib/whatsapp/cards.js +22 -9
- package/neural-interface/lib/whatsapp/config.js +1 -1
- package/neural-interface/lib/worker-confinement.js +77 -1
- package/neural-interface/lib/worker-question.js +74 -0
- package/neural-interface/lib/worker-resume.js +13 -4
- package/neural-interface/lib/worker-sandbox.js +46 -16
- package/neural-interface/package-lock.json +18 -2
- package/neural-interface/package.json +2 -1
- package/neural-interface/public/onboarding.html +1 -1
- package/neural-interface/public/shared/agent-runtime-options.js +89 -11
- package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
- package/neural-interface/public/shared/agy/agy-client.js +266 -0
- package/neural-interface/public/shared/agy/agy-commands.js +120 -0
- package/neural-interface/public/shared/agy/agy-copy.js +326 -0
- package/neural-interface/public/shared/agy/agy-diff.js +161 -0
- package/neural-interface/public/shared/agy/agy-events.js +469 -0
- package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
- package/neural-interface/public/shared/agy/agy-options.js +232 -0
- package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
- package/neural-interface/public/shared/agy/agy-render.js +392 -0
- package/neural-interface/public/shared/agy/agy-setup.js +149 -0
- package/neural-interface/public/shared/agy/agy-styles.js +604 -0
- package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
- package/neural-interface/public/shared/api.js +4 -4
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +3 -4
- package/neural-interface/public/shared/assistant/asst-api.js +15 -3
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +56 -9
- package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
- package/neural-interface/public/shared/assistant/asst-computer.js +26 -2
- package/neural-interface/public/shared/assistant/asst-control.js +48 -0
- package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +6 -1
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +38 -5
- package/neural-interface/public/shared/assistant/asst-motion-gate.js +80 -0
- package/neural-interface/public/shared/assistant/asst-panel.js +46 -9
- package/neural-interface/public/shared/assistant/asst-render.js +112 -18
- package/neural-interface/public/shared/assistant/asst-route.js +237 -57
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
- package/neural-interface/public/shared/assistant/asst-scene-styles.js +55 -23
- package/neural-interface/public/shared/assistant/asst-scenes.js +279 -80
- package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +4 -4
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +51 -7
- package/neural-interface/public/shared/assistant/asst-slash.js +8 -2
- package/neural-interface/public/shared/assistant/asst-state.js +76 -9
- package/neural-interface/public/shared/assistant/asst-style-cache.js +44 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +58 -4
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +34 -7
- package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
- package/neural-interface/public/shared/cdx/cdx-output.js +26 -3
- package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
- package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
- package/neural-interface/public/shared/cdx/cdx-protocol.js +11 -10
- package/neural-interface/public/shared/cdx/cdx-render.js +10 -9
- package/neural-interface/public/shared/cdx/cdx-requests.js +11 -1
- package/neural-interface/public/shared/cdx/cdx-styles.js +4 -0
- package/neural-interface/public/shared/cdx/cdx-tabs.js +75 -5
- package/neural-interface/public/shared/cli-install-controller.js +218 -0
- package/neural-interface/public/shared/cli-install-view.js +7 -4
- package/neural-interface/public/shared/cli-status.js +9 -8
- package/neural-interface/public/shared/constants.js +3 -2
- package/neural-interface/public/shared/context-menu-model.js +1 -0
- package/neural-interface/public/shared/cp/cp-permissions.js +12 -0
- package/neural-interface/public/shared/cp/cp-restore.js +162 -0
- package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
- package/neural-interface/public/shared/desktop-bridge.js +17 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
- package/neural-interface/public/shared/desktop-titlebar.js +13 -4
- package/neural-interface/public/shared/desktop-update-view.js +1 -0
- package/neural-interface/public/shared/explorer/fx-api.js +4 -4
- package/neural-interface/public/shared/explorer/fx-links.js +3 -3
- package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +48 -16
- package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
- package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
- package/neural-interface/public/shared/html-shell.js +21 -4
- package/neural-interface/public/shared/loading-teardown.js +34 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +6 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +28 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +17 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +6 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +101 -68
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +46 -3
- package/neural-interface/public/shared/provider-icons.js +4 -2
- package/neural-interface/public/shared/settings/settings-rules.js +2 -2
- package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +1 -1
- package/neural-interface/public/shared/settings/settings-skill-folders.js +2 -2
- package/neural-interface/public/shared/showcase-privacy.js +6 -1
- package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
- package/neural-interface/public/shared/skills/ss-creation.js +6 -3
- package/neural-interface/public/shared/skills/ss-dialog.js +5 -2
- package/neural-interface/public/shared/skills/ss-lint.js +9 -1
- package/neural-interface/public/shared/skills/ss-profiles.js +44 -6
- package/neural-interface/public/shared/skills/ss-review-view.js +2 -2
- package/neural-interface/public/shared/skills/studio/sx-actions.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-create-ai.js +2 -1
- package/neural-interface/public/shared/skills/studio/sx-dom.js +2 -1
- package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
- package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
- package/neural-interface/public/shared/skills/studio/sx-inspector.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-install-flow.js +9 -6
- package/neural-interface/public/shared/skills/studio/sx-install.js +20 -3
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +144 -35
- package/neural-interface/public/shared/skills/studio/sx-library.js +6 -2
- package/neural-interface/public/shared/skills/studio/sx-main.js +11 -2
- package/neural-interface/public/shared/skills/studio/sx-review-view.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-subject.js +7 -6
- package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +6 -5
- package/neural-interface/public/shared/state.js +1 -1
- package/neural-interface/public/shared/storage-retry.js +47 -0
- package/neural-interface/public/shared/storage.js +25 -4
- package/neural-interface/public/shared/streaming-markdown.js +58 -0
- package/neural-interface/public/shared/styles.css +362 -35
- package/neural-interface/public/shared/synabun-mascot.js +27 -8
- package/neural-interface/public/shared/synabun-ticker.js +147 -12
- package/neural-interface/public/shared/text-cache.js +29 -0
- package/neural-interface/public/shared/tool-list-picker.js +2 -2
- package/neural-interface/public/shared/tool-list-view.js +2 -2
- package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
- package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
- package/neural-interface/public/shared/ui-assistant.js +2 -0
- package/neural-interface/public/shared/ui-automation-studio.js +20 -12
- package/neural-interface/public/shared/ui-claude-panel.js +374 -134
- package/neural-interface/public/shared/ui-cli-banner.js +23 -167
- package/neural-interface/public/shared/ui-file-explorer.js +48 -6
- package/neural-interface/public/shared/ui-file-links.js +2 -2
- package/neural-interface/public/shared/ui-games.js +2 -2
- package/neural-interface/public/shared/ui-judgments.js +1 -1
- package/neural-interface/public/shared/ui-keybinds.js +8 -2
- package/neural-interface/public/shared/ui-link.js +3 -3
- package/neural-interface/public/shared/ui-loading.js +5 -4
- package/neural-interface/public/shared/ui-menubar.js +2 -2
- package/neural-interface/public/shared/ui-native-loop-router.js +4 -0
- package/neural-interface/public/shared/ui-navbar.js +45 -0
- package/neural-interface/public/shared/ui-notifications.js +1 -0
- package/neural-interface/public/shared/ui-resume.js +9 -7
- package/neural-interface/public/shared/ui-schedules-studio.js +41 -10
- package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
- package/neural-interface/public/shared/ui-sessions.js +22 -12
- package/neural-interface/public/shared/ui-settings.js +90 -65
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -3
- package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
- package/neural-interface/public/shared/ui-sidepanel-windows.js +3 -3
- package/neural-interface/public/shared/ui-terminal.js +146 -10
- package/neural-interface/public/shared/ui-tool-updates.js +3 -3
- package/neural-interface/public/shared/ui-tooltip.js +71 -46
- package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
- package/neural-interface/public/shared/ui-whiteboard.js +1 -1
- package/neural-interface/public/shared/ui-worker-end.js +1 -1
- package/neural-interface/public/shared/whatsapp/wa-view.js +2 -2
- package/neural-interface/public/shared/worker-questions.js +34 -0
- package/neural-interface/public/sidepanel-session.html +1 -1
- package/neural-interface/server.js +731 -466
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +5 -5
- package/neural-interface/templates/rulesets/manifest.json +1 -1
- package/npm-shrinkwrap.json +18 -2
- package/package.json +2 -1
- package/skills/skill-forge/SKILL.md +4 -4
- package/skills/synabun/SKILL.md +1 -1
- package/skills/synabun/modules/schedule.md +3 -3
- /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
|
@@ -10,7 +10,9 @@ const object = value => value && typeof value === 'object' && !Array.isArray(val
|
|
|
10
10
|
const canonical = path => { try { return realpathSync(path); } catch { return resolve(path); } };
|
|
11
11
|
const claudeProject = (user, cwd) => user.projects?.[canonical(cwd)] || user.projects?.[resolve(cwd)] || {};
|
|
12
12
|
const external = name => name !== 'SynaBun';
|
|
13
|
-
export const EXTERNAL_SERVER_LIMITATIONS = Object.freeze({ codex: 'Plugin-provided MCP servers cannot be enumerated from config.toml', 'claude-code': 'claude.ai connectors are unavailable under strict MCP configuration' });
|
|
13
|
+
export const EXTERNAL_SERVER_LIMITATIONS = Object.freeze({ codex: 'Plugin-provided MCP servers cannot be enumerated from config.toml', 'claude-code': 'claude.ai connectors are unavailable under strict MCP configuration', antigravity: 'A project\'s own .agents/mcp_config.json still loads into an Antigravity session; managed brains, workers and restricted panel sessions refuse enabled project servers before starting agy' });
|
|
14
|
+
/** Where Antigravity keeps its MCP servers: { mcpServers: { name: { command, args, env, disabled } | { serverUrl, headers, disabled } } }. */
|
|
15
|
+
export function antigravityMcpConfigPath(home = homedir()) { return join(home, '.gemini', 'config', 'mcp_config.json'); }
|
|
14
16
|
export const externalServerName = name => typeof name === 'string' && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/.test(name) && external(name);
|
|
15
17
|
function read(path, fallback = null) {
|
|
16
18
|
try { return readFileSync(path, 'utf8'); }
|
|
@@ -144,7 +146,7 @@ export function claudePluginDefinitions(pluginRoot, manifest = document(join(plu
|
|
|
144
146
|
function claudeEntry(entry) { return structuredClone(entry); }
|
|
145
147
|
|
|
146
148
|
/** This is the only filesystem discovery boundary; definitions never escape through servers/catalogView. */
|
|
147
|
-
export function externalServerInventory({ provider, cwd = process.cwd(), codexHome, home = homedir(), dataHome, registry, opencodeConfig, readProject = true, env = process.env } = {}) {
|
|
149
|
+
export function externalServerInventory({ provider, cwd = process.cwd(), codexHome, home = homedir(), dataHome, registry, opencodeConfig, antigravityConfigPath = antigravityMcpConfigPath(home), readProject = true, env = process.env } = {}) {
|
|
148
150
|
const reg = registry || (dataHome ? document(join(dataHome, 'data', 'mcp-registry.json')) : {});
|
|
149
151
|
const access = agentAccessFrom(reg), definitions = new Map(), rows = new Map();
|
|
150
152
|
function add(name, entry, source, deliverable = true, reason = null, kind = null) {
|
|
@@ -172,6 +174,13 @@ export function externalServerInventory({ provider, cwd = process.cwd(), codexHo
|
|
|
172
174
|
for (const [name, entry] of Object.entries(mcpServersOf(base))) add(name, entry, 'user');
|
|
173
175
|
if (env.OPENCODE_CONFIG) for (const [name, entry] of Object.entries(mcpServersOf(document(resolve(env.OPENCODE_CONFIG))))) add(name, entry, 'user-override');
|
|
174
176
|
if (readProject) for (const path of projectOpenCodeConfigs(cwd)) for (const [name, entry] of Object.entries(mcpServersOf(document(path)))) add(name, entry, 'project');
|
|
177
|
+
} else if (provider === 'antigravity') {
|
|
178
|
+
// A remote server names its address `serverUrl` there; `disabled: true` turns it off in Antigravity itself.
|
|
179
|
+
for (const [name, entry] of Object.entries(object(document(antigravityConfigPath).mcpServers))) {
|
|
180
|
+
const own = object(entry);
|
|
181
|
+
const url = typeof own.serverUrl === 'string' ? own.serverUrl : own.url;
|
|
182
|
+
add(name, url !== undefined && own.url === undefined ? { ...own, url } : own, 'user', true, null, url !== undefined ? 'http' : 'stdio');
|
|
183
|
+
}
|
|
175
184
|
} else if (provider === 'claude-code') {
|
|
176
185
|
const configDir = env.CLAUDE_CONFIG_DIR || join(home, '.claude');
|
|
177
186
|
const settings = { ...document(join(configDir, 'settings.json')), ...document(join(cwd, '.claude', 'settings.json')), ...document(join(cwd, '.claude', 'settings.local.json')) };
|
|
@@ -247,8 +256,8 @@ export function selectExternalServers(tools, inventory) {
|
|
|
247
256
|
}
|
|
248
257
|
export function externalCatalog(options = {}) {
|
|
249
258
|
const rows = new Map();
|
|
250
|
-
for (const provider of ['codex', 'opencode', 'claude-code']) for (const row of discoverExternalServers({ ...options, provider })) {
|
|
251
|
-
if (!rows.has(row.name)) rows.set(row.name, { ref: `mcp:${row.name}`, label: row.name, selectable: row.selectable, providers: { codex: 'absent', opencode: 'absent', 'claude-code': 'absent' }, blocked: row.blocked });
|
|
259
|
+
for (const provider of ['codex', 'opencode', 'claude-code', 'antigravity']) for (const row of discoverExternalServers({ ...options, provider })) {
|
|
260
|
+
if (!rows.has(row.name)) rows.set(row.name, { ref: `mcp:${row.name}`, label: row.name, selectable: row.selectable, providers: { codex: 'absent', opencode: 'absent', 'claude-code': 'absent', antigravity: 'absent' }, blocked: row.blocked });
|
|
252
261
|
rows.get(row.name).providers[provider] = row.blocked ? 'blocked' : row.deliverable ? 'deliverable' : 'unavailable';
|
|
253
262
|
}
|
|
254
263
|
return [...rows.values()].sort((a, b) => a.ref.localeCompare(b.ref));
|
|
@@ -310,14 +319,38 @@ export async function waitForClaudeMcpScope(q, names, { timeoutMs = 30000 } = {}
|
|
|
310
319
|
}
|
|
311
320
|
throw Object.assign(new Error('Claude MCP scope could not be enforced before the first turn'), { code: 'MCP_SCOPE_UNENFORCED' });
|
|
312
321
|
}
|
|
322
|
+
/** Values shorter than this are never redacted: a short or common word would be erased from ordinary output. */
|
|
323
|
+
export const MIN_MCP_SECRET_LENGTH = 8;
|
|
313
324
|
export function redactMcpValues(value, values) {
|
|
314
|
-
if (typeof value === 'string') { for (const secret of [...new Set(values)].filter(
|
|
325
|
+
if (typeof value === 'string') { for (const secret of [...new Set(values)].filter(item => typeof item === 'string' && item.length >= MIN_MCP_SECRET_LENGTH).sort((a, b) => b.length - a.length)) value = value.split(secret).join('[MCP value]'); return value; }
|
|
315
326
|
if (Array.isArray(value)) return value.map(item => redactMcpValues(item, values));
|
|
316
327
|
if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, redactMcpValues(item, values)]));
|
|
317
328
|
return value;
|
|
318
329
|
}
|
|
319
330
|
|
|
320
|
-
|
|
331
|
+
const SECRET_QUERY_NAME = /token|key|secret|auth|pass|pwd|sig|credential|session|code/i;
|
|
332
|
+
/** The secrets a URL carries (userinfo, credential-named query values), never the URL itself. */
|
|
333
|
+
export function urlSecretValues(url) {
|
|
334
|
+
if (typeof url !== 'string' || !url) return [];
|
|
335
|
+
let parsed; try { parsed = new URL(url); } catch { return []; }
|
|
336
|
+
const out = [];
|
|
337
|
+
for (const part of [parsed.username, parsed.password]) if (part) { out.push(part); try { out.push(decodeURIComponent(part)); } catch {} }
|
|
338
|
+
for (const [name, value] of parsed.searchParams) if (value && SECRET_QUERY_NAME.test(name)) { out.push(value); out.push(encodeURIComponent(value)); }
|
|
339
|
+
return out;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* The real secrets of MCP server definitions: env / environment and header values, authToken, oauth strings
|
|
344
|
+
* and the secrets inside a URL. Never command, args, cwd, type or a whole URL, and never SynaBun's own entry
|
|
345
|
+
* (its headers name the project, terminal and role: ordinary words). `${VAR}` / `{env:VAR}` references are
|
|
346
|
+
* expanded through `env` when given. Values under MIN_MCP_SECRET_LENGTH are dropped.
|
|
347
|
+
*/
|
|
348
|
+
export function mcpSecretValues(servers, { env = null, exclude = ['SynaBun'] } = {}) {
|
|
321
349
|
const strings = value => typeof value === 'string' ? [value] : Array.isArray(value) ? value.flatMap(strings) : value && typeof value === 'object' ? Object.values(value).flatMap(strings) : [];
|
|
322
|
-
|
|
350
|
+
const expand = value => env ? value.replace(/\{env:([A-Za-z_][A-Za-z0-9_]*)\}|\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (whole, a, b) => env[a || b] ?? whole) : value;
|
|
351
|
+
const values = Object.entries(servers || {}).filter(([name]) => !exclude.includes(name)).flatMap(([, entry]) => entry && typeof entry === 'object' ? [
|
|
352
|
+
...['env', 'environment', 'headers', 'oauth', 'authToken'].flatMap(field => strings(entry[field])).flatMap(value => { const bare = /^(?:Bearer|Basic|Token|Bot)\s+(\S.*)$/i.exec(value); return bare ? [value, bare[1]] : [value]; }),
|
|
353
|
+
...urlSecretValues(entry.url),
|
|
354
|
+
] : []).map(expand);
|
|
355
|
+
return [...new Set(values)].filter(value => value.length >= MIN_MCP_SECRET_LENGTH);
|
|
323
356
|
}
|
|
@@ -422,7 +422,8 @@ function scanHints(repoPath) {
|
|
|
422
422
|
const hints = [];
|
|
423
423
|
if (existsSync(join(repoPath, '.claude-plugin', 'plugin.json'))) hints.push('claude-plugin');
|
|
424
424
|
if (existsSync(join(repoPath, '.codex'))) hints.push('codex');
|
|
425
|
-
|
|
425
|
+
// Antigravity's plugins: its own manifests, and the Gemini CLI extensions `agy plugin install` imports.
|
|
426
|
+
if (['gemini-extension.json', join('.antigravity-plugin', 'plugin.json'), join('.gemini-plugin', 'plugin.json')].some((file) => existsSync(join(repoPath, file)))) hints.push('antigravity');
|
|
426
427
|
if (existsSync(join(repoPath, '.cursor'))) hints.push('cursor');
|
|
427
428
|
if (existsSync(join(repoPath, '.windsurf'))) hints.push('windsurf');
|
|
428
429
|
return hints;
|
|
@@ -4,10 +4,24 @@ import { detectProject } from '../../mcp-server/dist/config.js';
|
|
|
4
4
|
import { normalizeMemoryEvents, ingestMemoryEvents } from '../../mcp-server/dist/services/memory-sessions.js';
|
|
5
5
|
import { maintenancePaused } from './memory-timers.js';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/** The hosts whose transcripts are captured from a file (OpenCode's rows are taken in session-cache-builder.js). */
|
|
8
|
+
export const CAPTURE_HOSTS=Object.freeze(['claude-code','codex','antigravity']);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* `size`: the transcript's size now, when the caller knows it. A file already read to that size is left as it is
|
|
12
|
+
* (a pass over every Antigravity conversation would otherwise mark each one pending again).
|
|
13
|
+
*/
|
|
14
|
+
export function queueMemoryCapture({host,sessionId,filePath,project,cwd,size=null}) {
|
|
15
|
+
if(!filePath || !sessionId || !CAPTURE_HOSTS.includes(host))return;
|
|
16
|
+
if(size!=null) {
|
|
17
|
+
const row=getDb().prepare('SELECT file_path,byte_offset,status,cwd FROM memory_capture_files WHERE host=? AND session_id=?').get(host,sessionId);
|
|
18
|
+
if(row && row.status==='complete' && row.file_path===filePath && Number(row.byte_offset)===Number(size) && (row.cwd || null)===(cwd || null))return;
|
|
19
|
+
}
|
|
20
|
+
// An Antigravity conversation whose folder agy named only later is read again from its start: its events take the
|
|
21
|
+
// project (each step keeps its source id, so nothing is stored twice).
|
|
9
22
|
getDb().prepare(`INSERT INTO memory_capture_files(host,session_id,file_path,project,cwd) VALUES(?,?,?,?,?)
|
|
10
|
-
ON CONFLICT(host,session_id) DO UPDATE SET file_path=excluded.file_path,project=excluded.project,cwd=excluded.cwd,status='pending',error=NULL
|
|
23
|
+
ON CONFLICT(host,session_id) DO UPDATE SET file_path=excluded.file_path,project=excluded.project,cwd=excluded.cwd,status='pending',error=NULL,
|
|
24
|
+
byte_offset=CASE WHEN excluded.host='antigravity' AND memory_capture_files.cwd IS NOT excluded.cwd THEN 0 ELSE memory_capture_files.byte_offset END`)
|
|
11
25
|
.run(host,sessionId,filePath,cwd ? detectProject(cwd) : project || 'global',cwd || null);
|
|
12
26
|
}
|
|
13
27
|
|
|
@@ -47,7 +47,8 @@ export function isMoreLoginInstalled() {
|
|
|
47
47
|
} else if (process.platform === 'win32') {
|
|
48
48
|
const { LOCALAPPDATA, ProgramFiles } = process.env;
|
|
49
49
|
const PF86 = process.env['ProgramFiles(x86)'];
|
|
50
|
-
if (
|
|
50
|
+
if (process.env.MORELOGIN_APP_PATH) candidates.push(process.env.MORELOGIN_APP_PATH);
|
|
51
|
+
if (LOCALAPPDATA) candidates.push(`${LOCALAPPDATA}\\Programs\\MoreLogin\\MoreLogin.exe`, `${LOCALAPPDATA}\\Programs\\morelogin\\MoreLogin.exe`, `${LOCALAPPDATA}\\MoreLogin\\MoreLogin.exe`);
|
|
51
52
|
if (ProgramFiles) candidates.push(`${ProgramFiles}\\MoreLogin\\MoreLogin.exe`);
|
|
52
53
|
if (PF86) candidates.push(`${PF86}\\MoreLogin\\MoreLogin.exe`);
|
|
53
54
|
} else {
|
|
@@ -100,40 +101,147 @@ function buildHeaders(opts = {}) {
|
|
|
100
101
|
|
|
101
102
|
/**
|
|
102
103
|
* Low-level POST to the MoreLogin local API. Returns the `data` payload.
|
|
103
|
-
* Throws with a `.code` of MORELOGIN_UNREACHABLE | MORELOGIN_HTTP | MORELOGIN_API
|
|
104
|
+
* Throws with a `.code` of MORELOGIN_UNREACHABLE | MORELOGIN_HTTP | MORELOGIN_API | MORELOGIN_SHAPE
|
|
105
|
+
* (answered 2xx with a body that is not JSON).
|
|
106
|
+
*/
|
|
107
|
+
// Loopback hosts to try, in order. MoreLogin's local server may listen on IPv6 only
|
|
108
|
+
// (an Electron server bound to "localhost" resolves to ::1 first on Windows), so a
|
|
109
|
+
// refused 127.0.0.1 is retried on [::1]. The host that answered is remembered.
|
|
110
|
+
const LOOPBACK_HOSTS = ['127.0.0.1', '[::1]'];
|
|
111
|
+
let _preferredHost = LOOPBACK_HOSTS[0];
|
|
112
|
+
let _lastAnswerAt = 0; // when the local API last answered anything (any status, any body)
|
|
113
|
+
export function _resetMoreLoginHostForTests() { _preferredHost = LOOPBACK_HOSTS[0]; _lastAnswerAt = 0; }
|
|
114
|
+
export function _preferredMoreLoginHostForTests() { return _preferredHost; }
|
|
115
|
+
|
|
116
|
+
/** Whether the local API answered within the last `withinMs` (no request is made). */
|
|
117
|
+
export function moreLoginApiAnsweredRecently(withinMs = 120000) {
|
|
118
|
+
return _lastAnswerAt > 0 && Date.now() - _lastAnswerAt <= withinMs;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Cheap, synchronous "is MoreLogin here": the exe is in a known folder (or
|
|
123
|
+
* MORELOGIN_APP_PATH) or its local API answered recently. Callers that need a
|
|
124
|
+
* fresh answer probe first (probeMoreLoginApi).
|
|
125
|
+
*/
|
|
126
|
+
export function isMoreLoginPresent(withinMs = 120000) {
|
|
127
|
+
let exe = false;
|
|
128
|
+
try { exe = isMoreLoginInstalled().installed; } catch { exe = false; }
|
|
129
|
+
return exe || moreLoginApiAnsweredRecently(withinMs);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Error codes that prove the connection never opened, so the request was never
|
|
133
|
+
// delivered and the other loopback host may be tried even for a call that changes
|
|
134
|
+
// state (create, start). Anything else (a reset after sending, a malformed answer)
|
|
135
|
+
// may have reached MoreLogin: it is thrown, never sent a second time.
|
|
136
|
+
const NEVER_CONNECTED_CODES = new Set([
|
|
137
|
+
'ECONNREFUSED', 'EHOSTUNREACH', 'ENETUNREACH', 'EADDRNOTAVAIL', 'EAFNOSUPPORT', 'ENOTFOUND',
|
|
138
|
+
'EAI_AGAIN', 'UND_ERR_CONNECT_TIMEOUT', 'UND_ERR_CONNECT',
|
|
139
|
+
]);
|
|
140
|
+
export function _neverConnected(err) {
|
|
141
|
+
for (let e = err, i = 0; e && i < 4; e = e.cause, i++) {
|
|
142
|
+
if (NEVER_CONNECTED_CODES.has(e.code)) return true;
|
|
143
|
+
if (Array.isArray(e.errors) && e.errors.some(x => NEVER_CONNECTED_CODES.has(x?.code))) return true;
|
|
144
|
+
}
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* One deadline for the whole call (opts.timeout, default 15 s): each loopback host
|
|
150
|
+
* gets what is left of it, so two hosts never double the budget. The other host is
|
|
151
|
+
* tried while time remains when the first one provably never connected; a timeout
|
|
152
|
+
* is tried on the other host only for a read (opts.idempotent), since a timed-out
|
|
153
|
+
* write may have been received.
|
|
104
154
|
*/
|
|
105
155
|
async function request(path, body, opts = {}) {
|
|
106
156
|
const port = opts.port || getMoreLoginPort(opts.savedCfg);
|
|
107
|
-
const
|
|
157
|
+
const fetchImpl = opts.fetch || fetch;
|
|
158
|
+
const idempotent = opts.idempotent ?? (path === '/api/env/page' || path === '/api/env/status');
|
|
159
|
+
const budget = opts.timeout || 15000;
|
|
160
|
+
const deadline = Date.now() + budget;
|
|
161
|
+
const hosts = [_preferredHost, ...LOOPBACK_HOSTS.filter(h => h !== _preferredHost)];
|
|
108
162
|
let res;
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
163
|
+
let lastErr = null;
|
|
164
|
+
for (const host of hosts) {
|
|
165
|
+
const left = deadline - Date.now();
|
|
166
|
+
if (left <= 0) break;
|
|
167
|
+
try {
|
|
168
|
+
res = await fetchImpl(`http://${host}:${port}${path}`, {
|
|
169
|
+
method: 'POST',
|
|
170
|
+
headers: buildHeaders(opts),
|
|
171
|
+
body: JSON.stringify(body || {}),
|
|
172
|
+
signal: AbortSignal.timeout(left),
|
|
173
|
+
});
|
|
174
|
+
_preferredHost = host;
|
|
175
|
+
_lastAnswerAt = Date.now();
|
|
176
|
+
break;
|
|
177
|
+
} catch (err) {
|
|
178
|
+
lastErr = err;
|
|
179
|
+
const timedOut = err?.name === 'TimeoutError' || err?.name === 'AbortError';
|
|
180
|
+
const retry = _neverConnected(err) || (timedOut && idempotent);
|
|
181
|
+
if (!retry) break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (!res) {
|
|
185
|
+
const err = lastErr;
|
|
186
|
+
const reason = !err ? `no time left of ${budget} ms` : (err?.cause?.code || err?.code || err?.message || err);
|
|
187
|
+
const e = new Error(`MoreLogin API unreachable on :${port} (${reason})`);
|
|
118
188
|
e.code = 'MORELOGIN_UNREACHABLE';
|
|
119
189
|
throw e;
|
|
120
190
|
}
|
|
121
191
|
let json = null;
|
|
192
|
+
let unparsable = false;
|
|
122
193
|
try { json = await res.json(); }
|
|
123
|
-
catch { try { await res.body?.cancel(); } catch { /* ignore */ } }
|
|
194
|
+
catch { unparsable = true; try { await res.body?.cancel(); } catch { /* ignore */ } }
|
|
124
195
|
if (!res.ok) {
|
|
125
|
-
const
|
|
196
|
+
const detail = json && (json.msg || json.message);
|
|
197
|
+
const e = new Error(`MoreLogin API ${path} → HTTP ${res.status}${detail ? ` (${detail})` : ''}${res.status === 401 || res.status === 403 ? ' (set the API ID and API Key from MoreLogin → Settings → API in SynaBun Settings → MoreLogin)' : ''}`);
|
|
126
198
|
e.code = 'MORELOGIN_HTTP';
|
|
127
199
|
e.status = res.status;
|
|
128
200
|
throw e;
|
|
129
201
|
}
|
|
130
202
|
if (json && json.code !== undefined && json.code !== 0) {
|
|
131
|
-
const
|
|
203
|
+
const msg = json.msg || json.message || `code ${json.code}`;
|
|
204
|
+
const e = new Error(`MoreLogin API ${path}: ${msg}${looksLikeAuthError(json.code, msg) ? ' (set the API ID and API Key from MoreLogin → Settings → API in SynaBun Settings → MoreLogin)' : ''}`);
|
|
132
205
|
e.code = 'MORELOGIN_API';
|
|
133
206
|
e.apiCode = json.code;
|
|
134
207
|
throw e;
|
|
135
208
|
}
|
|
136
|
-
|
|
209
|
+
if (unparsable || !json || typeof json !== 'object') {
|
|
210
|
+
const type = res.headers?.get?.('content-type') || 'no content type';
|
|
211
|
+
const e = new Error(`MoreLogin API ${path} answered HTTP ${res.status} with a body that is not JSON (${type}); is something else listening on port ${port}?`);
|
|
212
|
+
e.code = 'MORELOGIN_SHAPE';
|
|
213
|
+
throw e;
|
|
214
|
+
}
|
|
215
|
+
return json.data;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function looksLikeAuthError(code, msg) {
|
|
219
|
+
return /auth|sign|api.?key|api.?id|nonce|unauthori[sz]ed|forbidden|permission|token/i.test(String(msg || ''))
|
|
220
|
+
|| code === 401 || code === 403;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const PROFILE_LIST_KEYS = ['dataList', 'list', 'records', 'rows'];
|
|
224
|
+
|
|
225
|
+
/** Profile rows across response shapes: data.dataList | data.list | data.records | data.rows | data (array). A non-empty array wins over an empty one. */
|
|
226
|
+
export function profileRows(data) {
|
|
227
|
+
if (Array.isArray(data)) return data;
|
|
228
|
+
if (!data || typeof data !== 'object') return [];
|
|
229
|
+
const arrays = PROFILE_LIST_KEYS.map(k => data[k]).filter(Array.isArray);
|
|
230
|
+
return arrays.find(a => a.length) || arrays[0] || [];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Whether a profile-page answer has a shape SynaBun reads (an array, a known list key, or a total). */
|
|
234
|
+
export function hasProfileShape(data) {
|
|
235
|
+
if (Array.isArray(data)) return true;
|
|
236
|
+
if (!data || typeof data !== 'object') return false;
|
|
237
|
+
return PROFILE_LIST_KEYS.some(k => Array.isArray(data[k])) || (data.total != null && Number.isFinite(Number(data.total)));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function shapeError(data) {
|
|
241
|
+
const keys = data && typeof data === 'object' ? Object.keys(data).slice(0, 8).join(', ') : typeof data;
|
|
242
|
+
const e = new Error(`MoreLogin API /api/env/page answered in a shape SynaBun does not read (data: ${keys || 'empty'}); a newer MoreLogin may have changed its API`);
|
|
243
|
+
e.code = 'MORELOGIN_SHAPE';
|
|
244
|
+
return e;
|
|
137
245
|
}
|
|
138
246
|
|
|
139
247
|
/**
|
|
@@ -171,19 +279,29 @@ function normalizeProfile(e = {}) {
|
|
|
171
279
|
/** List browser profiles (environments). Returns normalized rows. */
|
|
172
280
|
export async function listProfiles(opts = {}) {
|
|
173
281
|
const data = await request('/api/env/page', { pageNo: opts.pageNo || 1, pageSize: opts.pageSize || 100 }, opts);
|
|
174
|
-
|
|
175
|
-
return
|
|
282
|
+
if (!hasProfileShape(data)) throw shapeError(data);
|
|
283
|
+
return profileRows(data).map(normalizeProfile).filter(p => p.id);
|
|
176
284
|
}
|
|
177
285
|
|
|
178
|
-
/**
|
|
286
|
+
/**
|
|
287
|
+
* Lightweight reachability probe. Never throws — returns {running, count}.
|
|
288
|
+
* `running` means the local API answered at all: an answer that is an HTTP or API
|
|
289
|
+
* error (signature required, API disabled, ...) still proves MoreLogin is up, and its
|
|
290
|
+
* reason is returned in `apiError` instead of being reported as "not running".
|
|
291
|
+
*/
|
|
179
292
|
export async function probeMoreLoginApi(opts = {}) {
|
|
180
293
|
try {
|
|
181
294
|
const data = await request('/api/env/page', { pageNo: 1, pageSize: 1 }, { ...opts, timeout: opts.timeout || 4000 });
|
|
182
|
-
|
|
183
|
-
|
|
295
|
+
if (!hasProfileShape(data)) throw shapeError(data);
|
|
296
|
+
const total = data && !Array.isArray(data) && data.total != null ? Number(data.total) : profileRows(data).length;
|
|
184
297
|
return { running: true, count: Number.isFinite(total) ? total : 0 };
|
|
185
298
|
} catch (err) {
|
|
186
|
-
|
|
299
|
+
const answered = err?.code === 'MORELOGIN_HTTP' || err?.code === 'MORELOGIN_API' || err?.code === 'MORELOGIN_SHAPE';
|
|
300
|
+
return {
|
|
301
|
+
running: answered, count: 0,
|
|
302
|
+
error: err?.message || String(err), errCode: err?.code || null,
|
|
303
|
+
...(answered ? { apiError: err?.message || String(err) } : {}),
|
|
304
|
+
};
|
|
187
305
|
}
|
|
188
306
|
}
|
|
189
307
|
|
|
@@ -221,13 +339,17 @@ export async function getMoreLoginStatus(opts = {}) {
|
|
|
221
339
|
const port = getMoreLoginPort(opts.savedCfg);
|
|
222
340
|
const probe = await probeMoreLoginApi({ ...opts, port });
|
|
223
341
|
return {
|
|
224
|
-
|
|
342
|
+
// An answering local API is proof of an install, wherever it lives (a custom
|
|
343
|
+
// install folder on Windows is not among the known paths).
|
|
344
|
+
installed: inst.installed || probe.running,
|
|
225
345
|
appPath: inst.path,
|
|
226
346
|
port,
|
|
227
347
|
running: probe.running,
|
|
348
|
+
apiError: probe.apiError || null,
|
|
228
349
|
profileCount: probe.count || 0,
|
|
229
350
|
signed: !!((opts.appId || process.env.MORELOGIN_API_ID) && (opts.secretKey || process.env.MORELOGIN_SECRET)),
|
|
230
|
-
|
|
351
|
+
// Transport failures only: an API that answered with an error is running, and says why in apiError.
|
|
352
|
+
error: probe.running ? null : (probe.error || null),
|
|
231
353
|
};
|
|
232
354
|
}
|
|
233
355
|
|
|
@@ -259,7 +381,7 @@ export async function startProfile(envId, opts = {}) {
|
|
|
259
381
|
try {
|
|
260
382
|
data = await request('/api/env/start', { envId: String(envId), ...(opts.startArgs || {}) }, opts);
|
|
261
383
|
} catch (err) {
|
|
262
|
-
if (err?.code === 'MORELOGIN_UNREACHABLE' || err?.code === 'MORELOGIN_HTTP') throw err;
|
|
384
|
+
if (err?.code === 'MORELOGIN_UNREACHABLE' || err?.code === 'MORELOGIN_HTTP' || err?.code === 'MORELOGIN_SHAPE') throw err;
|
|
263
385
|
// MORELOGIN_API (e.g. "already running") → fall through to status recovery.
|
|
264
386
|
}
|
|
265
387
|
let port = extractDebugPort(data);
|