@geminixiang/mikan 1.0.0-beta.76 → 1.0.0-beta.78
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 +39 -0
- package/dist/adapters/commands/login.d.ts.map +1 -1
- package/dist/adapters/commands/login.js +0 -9
- package/dist/adapters/commands/login.js.map +1 -1
- package/dist/adapters/commands/manifest.d.ts +0 -19
- package/dist/adapters/commands/manifest.d.ts.map +1 -1
- package/dist/adapters/commands/manifest.js +0 -34
- package/dist/adapters/commands/manifest.js.map +1 -1
- package/dist/adapters/commands/model.d.ts +0 -5
- package/dist/adapters/commands/model.d.ts.map +1 -1
- package/dist/adapters/commands/model.js +0 -5
- package/dist/adapters/commands/model.js.map +1 -1
- package/dist/adapters/commands/new.d.ts.map +1 -1
- package/dist/adapters/commands/new.js +0 -2
- package/dist/adapters/commands/new.js.map +1 -1
- package/dist/adapters/commands/registry.d.ts +0 -1
- package/dist/adapters/commands/registry.d.ts.map +1 -1
- package/dist/adapters/commands/registry.js +0 -8
- package/dist/adapters/commands/registry.js.map +1 -1
- package/dist/adapters/commands/session-view.d.ts.map +1 -1
- package/dist/adapters/commands/session-view.js +0 -2
- package/dist/adapters/commands/session-view.js.map +1 -1
- package/dist/adapters/commands/types.d.ts +0 -29
- package/dist/adapters/commands/types.d.ts.map +1 -1
- package/dist/adapters/commands/types.js.map +1 -1
- package/dist/adapters/commands/utils.d.ts +0 -3
- package/dist/adapters/commands/utils.d.ts.map +1 -1
- package/dist/adapters/commands/utils.js +0 -3
- package/dist/adapters/commands/utils.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts +0 -4
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +5 -41
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/components.d.ts +0 -23
- package/dist/adapters/discord/components.d.ts.map +1 -1
- package/dist/adapters/discord/components.js +0 -23
- package/dist/adapters/discord/components.js.map +1 -1
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +1 -11
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/discord/format.d.ts +0 -1
- package/dist/adapters/discord/format.d.ts.map +1 -1
- package/dist/adapters/discord/format.js +0 -37
- package/dist/adapters/discord/format.js.map +1 -1
- package/dist/adapters/github/bot.d.ts +0 -44
- package/dist/adapters/github/bot.d.ts.map +1 -1
- package/dist/adapters/github/bot.js +1 -120
- package/dist/adapters/github/bot.js.map +1 -1
- package/dist/adapters/github/client.d.ts +0 -57
- package/dist/adapters/github/client.d.ts.map +1 -1
- package/dist/adapters/github/client.js +0 -59
- package/dist/adapters/github/client.js.map +1 -1
- package/dist/adapters/github/context.d.ts.map +1 -1
- package/dist/adapters/github/context.js +1 -8
- package/dist/adapters/github/context.js.map +1 -1
- package/dist/adapters/github/github-ops.d.ts +0 -55
- package/dist/adapters/github/github-ops.d.ts.map +1 -1
- package/dist/adapters/github/github-ops.js +0 -59
- package/dist/adapters/github/github-ops.js.map +1 -1
- package/dist/adapters/github/ids.d.ts +0 -27
- package/dist/adapters/github/ids.d.ts.map +1 -1
- package/dist/adapters/github/ids.js +0 -29
- package/dist/adapters/github/ids.js.map +1 -1
- package/dist/adapters/github/repo.d.ts +0 -25
- package/dist/adapters/github/repo.d.ts.map +1 -1
- package/dist/adapters/github/repo.js +0 -49
- package/dist/adapters/github/repo.js.map +1 -1
- package/dist/adapters/github/tool-pack.d.ts +0 -10
- package/dist/adapters/github/tool-pack.d.ts.map +1 -1
- package/dist/adapters/github/tool-pack.js +0 -10
- package/dist/adapters/github/tool-pack.js.map +1 -1
- package/dist/adapters/github/tools/checks.d.ts +0 -6
- package/dist/adapters/github/tools/checks.d.ts.map +1 -1
- package/dist/adapters/github/tools/checks.js +0 -7
- package/dist/adapters/github/tools/checks.js.map +1 -1
- package/dist/adapters/github/tools/issue.d.ts +0 -5
- package/dist/adapters/github/tools/issue.d.ts.map +1 -1
- package/dist/adapters/github/tools/issue.js +0 -5
- package/dist/adapters/github/tools/issue.js.map +1 -1
- package/dist/adapters/github/tools/pr.d.ts +0 -7
- package/dist/adapters/github/tools/pr.d.ts.map +1 -1
- package/dist/adapters/github/tools/pr.js +0 -7
- package/dist/adapters/github/tools/pr.js.map +1 -1
- package/dist/adapters/github/tools/read.d.ts +0 -6
- package/dist/adapters/github/tools/read.d.ts.map +1 -1
- package/dist/adapters/github/tools/read.js +0 -8
- package/dist/adapters/github/tools/read.js.map +1 -1
- package/dist/adapters/github/tools/review-reply.d.ts +0 -5
- package/dist/adapters/github/tools/review-reply.d.ts.map +1 -1
- package/dist/adapters/github/tools/review-reply.js +0 -5
- package/dist/adapters/github/tools/review-reply.js.map +1 -1
- package/dist/adapters/github/tools/sync.d.ts +0 -6
- package/dist/adapters/github/tools/sync.d.ts.map +1 -1
- package/dist/adapters/github/tools/sync.js +0 -6
- package/dist/adapters/github/tools/sync.js.map +1 -1
- package/dist/adapters/github/types.d.ts +0 -88
- package/dist/adapters/github/types.d.ts.map +1 -1
- package/dist/adapters/github/types.js.map +1 -1
- package/dist/adapters/github/webhook.d.ts +0 -13
- package/dist/adapters/github/webhook.d.ts.map +1 -1
- package/dist/adapters/github/webhook.js +0 -16
- package/dist/adapters/github/webhook.js.map +1 -1
- package/dist/adapters/index.d.ts +3 -4
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/intake.d.ts +0 -17
- package/dist/adapters/intake.d.ts.map +1 -1
- package/dist/adapters/intake.js +6 -32
- package/dist/adapters/intake.js.map +1 -1
- package/dist/adapters/markdown-tables.d.ts +0 -10
- package/dist/adapters/markdown-tables.d.ts.map +1 -1
- package/dist/adapters/markdown-tables.js +1 -42
- package/dist/adapters/markdown-tables.js.map +1 -1
- package/dist/adapters/progressive-renderer.d.ts.map +1 -1
- package/dist/adapters/progressive-renderer.js +3 -25
- package/dist/adapters/progressive-renderer.js.map +1 -1
- package/dist/adapters/shared.d.ts +0 -57
- package/dist/adapters/shared.d.ts.map +1 -1
- package/dist/adapters/shared.js +0 -71
- package/dist/adapters/shared.js.map +1 -1
- package/dist/adapters/slack/assistant.d.ts +0 -49
- package/dist/adapters/slack/assistant.d.ts.map +1 -1
- package/dist/adapters/slack/assistant.js +0 -97
- package/dist/adapters/slack/assistant.js.map +1 -1
- package/dist/adapters/slack/auto-reply-context.d.ts +0 -7
- package/dist/adapters/slack/auto-reply-context.d.ts.map +1 -1
- package/dist/adapters/slack/auto-reply-context.js +0 -7
- package/dist/adapters/slack/auto-reply-context.js.map +1 -1
- package/dist/adapters/slack/blocks.d.ts +0 -9
- package/dist/adapters/slack/blocks.d.ts.map +1 -1
- package/dist/adapters/slack/blocks.js +0 -20
- package/dist/adapters/slack/blocks.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +0 -116
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +7 -231
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +1 -3
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/slack/jev-context.d.ts +0 -13
- package/dist/adapters/slack/jev-context.d.ts.map +1 -1
- package/dist/adapters/slack/jev-context.js +0 -12
- package/dist/adapters/slack/jev-context.js.map +1 -1
- package/dist/adapters/slack/markdown.d.ts +0 -1
- package/dist/adapters/slack/markdown.d.ts.map +1 -1
- package/dist/adapters/slack/markdown.js +0 -5
- package/dist/adapters/slack/markdown.js.map +1 -1
- package/dist/adapters/slack/persona.d.ts +0 -9
- package/dist/adapters/slack/persona.d.ts.map +1 -1
- package/dist/adapters/slack/persona.js +0 -9
- package/dist/adapters/slack/persona.js.map +1 -1
- package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
- package/dist/adapters/slack/response-lifecycle.js +1 -45
- package/dist/adapters/slack/response-lifecycle.js.map +1 -1
- package/dist/adapters/slack/session.d.ts +2 -1
- package/dist/adapters/slack/session.d.ts.map +1 -1
- package/dist/adapters/slack/session.js +2 -2
- package/dist/adapters/slack/session.js.map +1 -1
- package/dist/adapters/slack/stream-limits.d.ts +0 -25
- package/dist/adapters/slack/stream-limits.d.ts.map +1 -1
- package/dist/adapters/slack/stream-limits.js +0 -34
- package/dist/adapters/slack/stream-limits.js.map +1 -1
- package/dist/adapters/slack/task-intent.d.ts +0 -13
- package/dist/adapters/slack/task-intent.d.ts.map +1 -1
- package/dist/adapters/slack/task-intent.js +0 -7
- package/dist/adapters/slack/task-intent.js.map +1 -1
- package/dist/adapters/slack/task-status.d.ts +0 -1
- package/dist/adapters/slack/task-status.d.ts.map +1 -1
- package/dist/adapters/slack/task-status.js +1 -9
- package/dist/adapters/slack/task-status.js.map +1 -1
- package/dist/adapters/slack/tool-pack.d.ts +0 -6
- package/dist/adapters/slack/tool-pack.d.ts.map +1 -1
- package/dist/adapters/slack/tool-pack.js +0 -6
- package/dist/adapters/slack/tool-pack.js.map +1 -1
- package/dist/adapters/slack/tools/blockkit.d.ts.map +1 -1
- package/dist/adapters/slack/tools/blockkit.js +0 -11
- package/dist/adapters/slack/tools/blockkit.js.map +1 -1
- package/dist/adapters/slack/types.d.ts +0 -19
- package/dist/adapters/slack/types.d.ts.map +1 -1
- package/dist/adapters/slack/types.js.map +1 -1
- package/dist/adapters/slack/update-diagnostics.d.ts +0 -1
- package/dist/adapters/slack/update-diagnostics.d.ts.map +1 -1
- package/dist/adapters/slack/update-diagnostics.js +1 -7
- package/dist/adapters/slack/update-diagnostics.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts +0 -6
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +1 -50
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +1 -8
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/adapters/types.d.ts +0 -57
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/adapters/types.js.map +1 -1
- package/dist/adapters/web/admin/client-assets.d.ts +4 -0
- package/dist/adapters/web/admin/client-assets.d.ts.map +1 -0
- package/dist/adapters/web/admin/client-assets.js +1765 -0
- package/dist/adapters/web/admin/client-assets.js.map +1 -0
- package/dist/adapters/web/admin/portal.d.ts +0 -10
- package/dist/adapters/web/admin/portal.d.ts.map +1 -1
- package/dist/adapters/web/admin/portal.js +6 -1909
- package/dist/adapters/web/admin/portal.js.map +1 -1
- package/dist/adapters/web/admin/types.d.ts +0 -3
- package/dist/adapters/web/admin/types.d.ts.map +1 -1
- package/dist/adapters/web/admin/types.js.map +1 -1
- package/dist/adapters/web/login/oauth.d.ts.map +1 -1
- package/dist/adapters/web/login/oauth.js +3 -7
- package/dist/adapters/web/login/oauth.js.map +1 -1
- package/dist/adapters/web/login/portal.d.ts +0 -10
- package/dist/adapters/web/login/portal.d.ts.map +1 -1
- package/dist/adapters/web/login/portal.js +4 -37
- package/dist/adapters/web/login/portal.js.map +1 -1
- package/dist/adapters/web/login/types.d.ts +0 -5
- package/dist/adapters/web/login/types.d.ts.map +1 -1
- package/dist/adapters/web/login/types.js.map +1 -1
- package/dist/adapters/web/portal-shell.d.ts +0 -12
- package/dist/adapters/web/portal-shell.d.ts.map +1 -1
- package/dist/adapters/web/portal-shell.js +8 -25
- package/dist/adapters/web/portal-shell.js.map +1 -1
- package/dist/adapters/web/server.d.ts +0 -1
- package/dist/adapters/web/server.d.ts.map +1 -1
- package/dist/adapters/web/server.js +0 -2
- package/dist/adapters/web/server.js.map +1 -1
- package/dist/adapters/web/session-view/portal.d.ts.map +1 -1
- package/dist/adapters/web/session-view/portal.js +13 -24
- package/dist/adapters/web/session-view/portal.js.map +1 -1
- package/dist/adapters/web/session-view/types.d.ts.map +1 -1
- package/dist/adapters/web/session-view/types.js.map +1 -1
- package/dist/adapters/web/token-store.d.ts +0 -2
- package/dist/adapters/web/token-store.d.ts.map +1 -1
- package/dist/adapters/web/token-store.js +0 -2
- package/dist/adapters/web/token-store.js.map +1 -1
- package/dist/cli/arg-grammar.d.ts +0 -3
- package/dist/cli/arg-grammar.d.ts.map +1 -1
- package/dist/cli/arg-grammar.js +0 -3
- package/dist/cli/arg-grammar.js.map +1 -1
- package/dist/cli/boot.d.ts.map +1 -1
- package/dist/cli/boot.js +3 -12
- package/dist/cli/boot.js.map +1 -1
- package/dist/cli/download.d.ts.map +1 -1
- package/dist/cli/download.js +2 -6
- package/dist/cli/download.js.map +1 -1
- package/dist/cli/office.d.ts.map +1 -1
- package/dist/cli/office.js +0 -31
- package/dist/cli/office.js.map +1 -1
- package/dist/cli/onboard.d.ts +0 -2
- package/dist/cli/onboard.d.ts.map +1 -1
- package/dist/cli/onboard.js +0 -30
- package/dist/cli/onboard.js.map +1 -1
- package/dist/cli/process-lifecycle.d.ts +0 -5
- package/dist/cli/process-lifecycle.d.ts.map +1 -1
- package/dist/cli/process-lifecycle.js +0 -6
- package/dist/cli/process-lifecycle.js.map +1 -1
- package/dist/cli/sandbox.d.ts +3 -0
- package/dist/cli/sandbox.d.ts.map +1 -0
- package/dist/cli/sandbox.js +79 -0
- package/dist/cli/sandbox.js.map +1 -0
- package/dist/cli/sessions.d.ts.map +1 -1
- package/dist/cli/sessions.js +0 -11
- package/dist/cli/sessions.js.map +1 -1
- package/dist/cli/types.d.ts +2 -6
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/types.js.map +1 -1
- package/dist/env-manifest.d.ts +0 -21
- package/dist/env-manifest.d.ts.map +1 -1
- package/dist/env-manifest.js +0 -11
- package/dist/env-manifest.js.map +1 -1
- package/dist/events/index.d.ts +0 -51
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +0 -42
- package/dist/events/index.js.map +1 -1
- package/dist/events/scheduler.d.ts +0 -8
- package/dist/events/scheduler.d.ts.map +1 -1
- package/dist/events/scheduler.js +0 -8
- package/dist/events/scheduler.js.map +1 -1
- package/dist/file-guards.d.ts +0 -15
- package/dist/file-guards.d.ts.map +1 -1
- package/dist/file-guards.js +2 -26
- package/dist/file-guards.js.map +1 -1
- package/dist/harness/execution-env.d.ts +0 -15
- package/dist/harness/execution-env.d.ts.map +1 -1
- package/dist/harness/execution-env.js +4 -55
- package/dist/harness/execution-env.js.map +1 -1
- package/dist/harness/http.d.ts +0 -12
- package/dist/harness/http.d.ts.map +1 -1
- package/dist/harness/http.js +0 -34
- package/dist/harness/http.js.map +1 -1
- package/dist/harness/index.d.ts +4 -7
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +4 -7
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/jev.d.ts +0 -28
- package/dist/harness/jev.d.ts.map +1 -1
- package/dist/harness/jev.js +0 -45
- package/dist/harness/jev.js.map +1 -1
- package/dist/harness/mcp-result.d.ts +20 -0
- package/dist/harness/mcp-result.d.ts.map +1 -0
- package/dist/harness/mcp-result.js +171 -0
- package/dist/harness/mcp-result.js.map +1 -0
- package/dist/harness/mcp.d.ts +0 -19
- package/dist/harness/mcp.d.ts.map +1 -1
- package/dist/harness/mcp.js +7 -66
- package/dist/harness/mcp.js.map +1 -1
- package/dist/harness/models.d.ts +0 -19
- package/dist/harness/models.d.ts.map +1 -1
- package/dist/harness/models.js +0 -52
- package/dist/harness/models.js.map +1 -1
- package/dist/harness/open-connector.d.ts +0 -12
- package/dist/harness/open-connector.d.ts.map +1 -1
- package/dist/harness/open-connector.js +0 -22
- package/dist/harness/open-connector.js.map +1 -1
- package/dist/harness/presenter.d.ts +1 -0
- package/dist/harness/presenter.d.ts.map +1 -1
- package/dist/harness/presenter.js +16 -66
- package/dist/harness/presenter.js.map +1 -1
- package/dist/harness/prompt.d.ts +0 -11
- package/dist/harness/prompt.d.ts.map +1 -1
- package/dist/harness/prompt.js +1 -18
- package/dist/harness/prompt.js.map +1 -1
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +8 -28
- package/dist/harness/runner.js.map +1 -1
- package/dist/harness/session.d.ts +11 -28
- package/dist/harness/session.d.ts.map +1 -1
- package/dist/harness/session.js +100 -38
- package/dist/harness/session.js.map +1 -1
- package/dist/harness/skills.d.ts +0 -22
- package/dist/harness/skills.d.ts.map +1 -1
- package/dist/harness/skills.js +0 -20
- package/dist/harness/skills.js.map +1 -1
- package/dist/harness/subagent-profiles.d.ts +0 -4
- package/dist/harness/subagent-profiles.d.ts.map +1 -1
- package/dist/harness/subagent-profiles.js +0 -29
- package/dist/harness/subagent-profiles.js.map +1 -1
- package/dist/harness/subagent.d.ts +0 -25
- package/dist/harness/subagent.d.ts.map +1 -1
- package/dist/harness/subagent.js +1 -70
- package/dist/harness/subagent.js.map +1 -1
- package/dist/harness/tools/event.d.ts.map +1 -1
- package/dist/harness/tools/event.js +0 -9
- package/dist/harness/tools/event.js.map +1 -1
- package/dist/harness/tools/generate-image.d.ts +0 -6
- package/dist/harness/tools/generate-image.d.ts.map +1 -1
- package/dist/harness/tools/generate-image.js.map +1 -1
- package/dist/harness/tools/host-fn-tool.d.ts +0 -17
- package/dist/harness/tools/host-fn-tool.d.ts.map +1 -1
- package/dist/harness/tools/host-fn-tool.js +0 -16
- package/dist/harness/tools/host-fn-tool.js.map +1 -1
- package/dist/harness/tools/index.d.ts +1 -7
- package/dist/harness/tools/index.d.ts.map +1 -1
- package/dist/harness/tools/index.js +3 -14
- package/dist/harness/tools/index.js.map +1 -1
- package/dist/harness/tools/jev-browser.d.ts +4 -94
- package/dist/harness/tools/jev-browser.d.ts.map +1 -1
- package/dist/harness/tools/jev-browser.js +203 -92
- package/dist/harness/tools/jev-browser.js.map +1 -1
- package/dist/harness/tools/jev.d.ts +0 -8
- package/dist/harness/tools/jev.d.ts.map +1 -1
- package/dist/harness/tools/jev.js +0 -11
- package/dist/harness/tools/jev.js.map +1 -1
- package/dist/harness/tools/pi-tools.d.ts +1 -11
- package/dist/harness/tools/pi-tools.d.ts.map +1 -1
- package/dist/harness/tools/pi-tools.js +1 -30
- package/dist/harness/tools/pi-tools.js.map +1 -1
- package/dist/harness/tools/react.d.ts +0 -5
- package/dist/harness/tools/react.d.ts.map +1 -1
- package/dist/harness/tools/react.js +0 -5
- package/dist/harness/tools/react.js.map +1 -1
- package/dist/harness/tools/secret-redaction.d.ts +0 -25
- package/dist/harness/tools/secret-redaction.d.ts.map +1 -1
- package/dist/harness/tools/secret-redaction.js +3 -36
- package/dist/harness/tools/secret-redaction.js.map +1 -1
- package/dist/harness/tools/subagent.d.ts +0 -34
- package/dist/harness/tools/subagent.d.ts.map +1 -1
- package/dist/harness/tools/subagent.js +0 -92
- package/dist/harness/tools/subagent.js.map +1 -1
- package/dist/harness/tools/task.d.ts +0 -1
- package/dist/harness/tools/task.d.ts.map +1 -1
- package/dist/harness/tools/task.js +0 -1
- package/dist/harness/tools/task.js.map +1 -1
- package/dist/harness/tools/types.d.ts +0 -16
- package/dist/harness/tools/types.d.ts.map +1 -1
- package/dist/harness/tools/types.js.map +1 -1
- package/dist/harness/types.d.ts +1 -77
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +0 -13
- package/dist/log.js.map +1 -1
- package/dist/main.js +7 -52
- package/dist/main.js.map +1 -1
- package/dist/memory-capture/index.d.ts +16 -0
- package/dist/memory-capture/index.d.ts.map +1 -0
- package/dist/memory-capture/index.js +194 -0
- package/dist/memory-capture/index.js.map +1 -0
- package/dist/memory-capture/types.d.ts +27 -0
- package/dist/memory-capture/types.d.ts.map +1 -0
- package/dist/memory-capture/types.js.map +1 -0
- package/dist/observability/index.d.ts +0 -9
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +0 -9
- package/dist/observability/index.js.map +1 -1
- package/dist/observability/instrument.js +0 -5
- package/dist/observability/instrument.js.map +1 -1
- package/dist/observability/otel.js +0 -1
- package/dist/observability/otel.js.map +1 -1
- package/dist/observability/sentry.d.ts +0 -18
- package/dist/observability/sentry.d.ts.map +1 -1
- package/dist/observability/sentry.js +0 -36
- package/dist/observability/sentry.js.map +1 -1
- package/dist/observability/types.d.ts +1 -10
- package/dist/observability/types.d.ts.map +1 -1
- package/dist/observability/types.js.map +1 -1
- package/dist/office/index.d.ts +0 -76
- package/dist/office/index.d.ts.map +1 -1
- package/dist/office/index.js +4 -154
- package/dist/office/index.js.map +1 -1
- package/dist/office/projection.d.ts +0 -29
- package/dist/office/projection.d.ts.map +1 -1
- package/dist/office/projection.js +5 -41
- package/dist/office/projection.js.map +1 -1
- package/dist/office/types.d.ts +0 -66
- package/dist/office/types.d.ts.map +1 -1
- package/dist/office/types.js.map +1 -1
- package/dist/runtime/conversation-runtime.d.ts.map +1 -1
- package/dist/runtime/conversation-runtime.js +22 -83
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/session-lifecycle.d.ts +0 -10
- package/dist/runtime/session-lifecycle.d.ts.map +1 -1
- package/dist/runtime/session-lifecycle.js +0 -12
- package/dist/runtime/session-lifecycle.js.map +1 -1
- package/dist/runtime/types.d.ts +2 -29
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sandbox/cloudflare.d.ts.map +1 -1
- package/dist/sandbox/cloudflare.js +2 -1
- package/dist/sandbox/cloudflare.js.map +1 -1
- package/dist/sandbox/container.d.ts.map +1 -1
- package/dist/sandbox/container.js +21 -15
- package/dist/sandbox/container.js.map +1 -1
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +0 -2
- package/dist/sandbox/host.js.map +1 -1
- package/dist/sandbox/identity.d.ts +0 -28
- package/dist/sandbox/identity.d.ts.map +1 -1
- package/dist/sandbox/identity.js +0 -28
- package/dist/sandbox/identity.js.map +1 -1
- package/dist/sandbox/index.d.ts +0 -18
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +2 -19
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/layout.d.ts +7 -0
- package/dist/sandbox/layout.d.ts.map +1 -0
- package/dist/sandbox/layout.js +13 -0
- package/dist/sandbox/layout.js.map +1 -0
- package/dist/sandbox/provisioner.d.ts +24 -65
- package/dist/sandbox/provisioner.d.ts.map +1 -1
- package/dist/sandbox/provisioner.js +213 -125
- package/dist/sandbox/provisioner.js.map +1 -1
- package/dist/sandbox/types.d.ts +0 -39
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/sandbox/types.js.map +1 -1
- package/dist/sandbox/utils.d.ts +2 -5
- package/dist/sandbox/utils.d.ts.map +1 -1
- package/dist/sandbox/utils.js +31 -31
- package/dist/sandbox/utils.js.map +1 -1
- package/dist/sessions/chat-history-sync.d.ts +0 -6
- package/dist/sessions/chat-history-sync.d.ts.map +1 -1
- package/dist/sessions/chat-history-sync.js +1 -33
- package/dist/sessions/chat-history-sync.js.map +1 -1
- package/dist/sessions/history-line.d.ts +0 -20
- package/dist/sessions/history-line.d.ts.map +1 -1
- package/dist/sessions/history-line.js +0 -20
- package/dist/sessions/history-line.js.map +1 -1
- package/dist/sessions/migrate-common.d.ts +0 -15
- package/dist/sessions/migrate-common.d.ts.map +1 -1
- package/dist/sessions/migrate-common.js +0 -19
- package/dist/sessions/migrate-common.js.map +1 -1
- package/dist/sessions/migrate-pi-084.d.ts.map +1 -1
- package/dist/sessions/migrate-pi-084.js +0 -7
- package/dist/sessions/migrate-pi-084.js.map +1 -1
- package/dist/sessions/migrate-v3.d.ts +0 -7
- package/dist/sessions/migrate-v3.d.ts.map +1 -1
- package/dist/sessions/migrate-v3.js +1 -67
- package/dist/sessions/migrate-v3.js.map +1 -1
- package/dist/sessions/session-key.d.ts +3 -17
- package/dist/sessions/session-key.d.ts.map +1 -1
- package/dist/sessions/session-key.js +8 -35
- package/dist/sessions/session-key.js.map +1 -1
- package/dist/sessions/session-store.d.ts +1 -7
- package/dist/sessions/session-store.d.ts.map +1 -1
- package/dist/sessions/session-store.js +8 -50
- package/dist/sessions/session-store.js.map +1 -1
- package/dist/sessions/store.d.ts +0 -60
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +0 -113
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/types.d.ts +0 -17
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/sessions/types.js.map +1 -1
- package/dist/settings/apply.d.ts +0 -6
- package/dist/settings/apply.d.ts.map +1 -1
- package/dist/settings/apply.js +0 -29
- package/dist/settings/apply.js.map +1 -1
- package/dist/settings/index.d.ts +0 -53
- package/dist/settings/index.d.ts.map +1 -1
- package/dist/settings/index.js +0 -77
- package/dist/settings/index.js.map +1 -1
- package/dist/settings/migrate.d.ts +0 -5
- package/dist/settings/migrate.d.ts.map +1 -1
- package/dist/settings/migrate.js +0 -17
- package/dist/settings/migrate.js.map +1 -1
- package/dist/types.d.ts +9 -159
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vault/index.d.ts +2 -36
- package/dist/vault/index.d.ts.map +1 -1
- package/dist/vault/index.js +135 -65
- package/dist/vault/index.js.map +1 -1
- package/dist/vault/types.d.ts +0 -15
- package/dist/vault/types.d.ts.map +1 -1
- package/dist/vault/types.js.map +1 -1
- package/package.json +1 -1
- package/dist/dream/index.d.ts +0 -20
- package/dist/dream/index.d.ts.map +0 -1
- package/dist/dream/index.js +0 -264
- package/dist/dream/index.js.map +0 -1
- package/dist/dream/types.d.ts +0 -30
- package/dist/dream/types.d.ts.map +0 -1
- package/dist/dream/types.js.map +0 -1
- /package/dist/{dream → memory-capture}/types.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-assets.js","sourceRoot":"","sources":["../../../../src/adapters/web/admin/client-assets.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,wBAAwB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2sCrC,CAAC;AAEJ,MAAM,CAAC,MAAM,sBAAsB,GAAG;;GAEnC,CAAC;AAEJ,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAohB9B,CAAC","sourcesContent":["export const adminViewFunctionsScript = ` let activeConversationKey = defaultConversationKey;\n function scopeOf(key) {\n const sep = key.indexOf(':');\n return { platform: key.slice(0, sep), conversationId: key.slice(sep + 1) };\n }\n function scopeQuery() {\n const scope = scopeOf(activeConversationKey);\n return 'conversationId=' + encodeURIComponent(scope.conversationId) +\n '&platform=' + encodeURIComponent(scope.platform);\n }\n function scopeBody() {\n const scope = scopeOf(activeConversationKey);\n return { conversationId: scope.conversationId, platform: scope.platform };\n }\n let availableModels = [];\n let modelsLoaded = false;\n let mcpPresets = [];\n let mcpServersByScope = { conversation: {}, global: {} };\n let pendingMcpInstall = null;\n\n\n function escHtml(str) {\n return String(str).replace(/[&<>\"']/g, (c) => (\n {'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]\n ));\n }\n const escAttr = escHtml;\n async function copyToClipboard(text) {\n try { await navigator.clipboard.writeText(text); } catch { prompt('Copy this link:', text); }\n }\n document.addEventListener('click', (event) => {\n const target = event.target instanceof Element\n ? event.target.closest('[data-admin-action]')\n : null;\n if (!target) return;\n switch (target.dataset.adminAction) {\n case 'preview-file':\n void previewFile(target.dataset.filePath || '');\n break;\n case 'copy-link':\n void copyToClipboard(target.dataset.copyText || '');\n break;\n case 'delete-event':\n void deleteEvent(target.dataset.eventName || '', target);\n break;\n case 'select-conversation':\n setActiveConversation(target.dataset.conversationId || '');\n switchTab('conversation');\n break;\n case 'toggle-timeline-filter':\n toggleTimelineFilter(target.dataset.filterKey || '');\n break;\n }\n });\n async function apiGet(path) {\n const url = path + (path.includes('?') ? '&' : '?') + 'token=' + encodeURIComponent(adminToken);\n const r = await fetch(url);\n const data = await r.json();\n if (!r.ok) throw new Error(data.error || ('HTTP ' + r.status));\n return data;\n }\n async function apiPost(path, body) {\n const r = await fetch(path, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ token: adminToken, ...body }),\n });\n const data = await r.json();\n if (!r.ok) throw new Error(data.error || ('HTTP ' + r.status));\n return data;\n }\n async function loadModels() {\n try {\n const data = await apiGet('/admin/api/models');\n availableModels = Array.isArray(data.models) ? data.models : [];\n } catch (err) {\n availableModels = [];\n } finally {\n modelsLoaded = true;\n }\n }\n function modelRef(provider, model) {\n return provider && model ? provider + '/' + model : '';\n }\n function parseModelRef(value) {\n const slash = value.indexOf('/');\n if (slash <= 0 || slash === value.length - 1) return { provider: '', model: '' };\n return { provider: value.slice(0, slash), model: value.slice(slash + 1) };\n }\n function renderModelOptions(currentProvider, currentModel) {\n const current = modelRef(currentProvider, currentModel);\n const seen = new Set();\n const groups = {\n available: [],\n unverified: [],\n };\n if (current) {\n seen.add(current);\n groups.available.push('<option value=\"' + escAttr(current) + '\">' + escHtml(current + ' (current)') + '</option>');\n }\n for (const model of availableModels) {\n const ref = modelRef(model.provider, model.id);\n if (!ref || seen.has(ref)) continue;\n seen.add(ref);\n const details = [model.name && model.name !== model.id ? model.name : '', model.reasoning ? 'thinking' : '', Array.isArray(model.input) && model.input.includes('image') ? 'image' : '']\n .filter(Boolean)\n .join(' · ');\n const option = '<option value=\"' + escAttr(ref) + '\">' + escHtml(details ? ref + ' — ' + details : ref) + '</option>';\n if (model.status === 'unverified') groups.unverified.push(option);\n else groups.available.push(option);\n }\n const sections = [];\n if (groups.available.length > 0) sections.push('<optgroup label=\"Available\">' + groups.available.join('') + '</optgroup>');\n if (groups.unverified.length > 0) sections.push('<optgroup label=\"Configured but unverified\">' + groups.unverified.join('') + '</optgroup>');\n if (sections.length === 0) {\n return '<option value=\"\">No available models</option>';\n }\n return sections.join('');\n }\n\n\n const scopeBtns = document.querySelectorAll('.rail-scope-btn');\n const tabPanels = document.querySelectorAll('.tab-panel');\n const railNavConversation = document.getElementById('rail-nav-conversation');\n const railNavGlobal = document.getElementById('rail-nav-global');\n\n function switchTab(tabId) {\n scopeBtns.forEach((btn) => {\n const active = btn.dataset.tab === tabId;\n btn.classList.toggle('active', active);\n btn.setAttribute('aria-selected', active ? 'true' : 'false');\n });\n tabPanels.forEach((panel) => panel.classList.toggle('active', panel.id === 'panel-' + tabId));\n railNavConversation.hidden = tabId !== 'conversation';\n railNavGlobal.hidden = tabId !== 'global';\n if (tabId === 'global') initGlobal();\n const firstPane = (tabId === 'global' ? railNavGlobal : railNavConversation).querySelector('.rail-link');\n if (firstPane) selectPane(tabId, firstPane.dataset.pane);\n }\n scopeBtns.forEach((btn) => btn.addEventListener('click', () => switchTab(btn.dataset.tab)));\n\n\n function paneLoader(key) {\n switch (key) {\n case 'settings': return loadSettings;\n case 'workspace': return loadWorkspace;\n case 'skills': return loadSkills;\n case 'mcp': return loadMcpServers;\n case 'events': return loadConversationEvents;\n case 'g-overview': return loadAllConversations;\n case 'g-usage': return loadTokenUsage;\n case 'g-settings': return loadGlobalSettings;\n case 'g-mcp': return loadMcpServers;\n case 'g-skills': return loadGlobalSkills;\n case 'g-events': return loadEvents;\n default: return undefined;\n }\n }\n const paneLoaded = new Set();\n\n function ensurePaneLoaded(key) {\n if (paneLoaded.has(key)) return;\n const loader = paneLoader(key);\n if (!loader) return;\n paneLoaded.add(key);\n loader();\n }\n\n function selectPane(scope, key) {\n const nav = scope === 'global' ? railNavGlobal : railNavConversation;\n const panel = document.getElementById(scope === 'global' ? 'panel-global' : 'panel-conversation');\n nav.querySelectorAll('.rail-link').forEach((btn) => {\n const active = btn.dataset.pane === key;\n btn.classList.toggle('active', active);\n btn.setAttribute('aria-current', active ? 'page' : 'false');\n });\n panel.querySelectorAll('.pane').forEach((pane) => {\n pane.classList.toggle('active', pane.dataset.pane === key);\n });\n ensurePaneLoaded(key);\n }\n\n function initRailNav() {\n document.querySelectorAll('.rail-link').forEach((btn) => {\n btn.addEventListener('click', () => {\n const nav = btn.closest('.rail-nav');\n const scope = nav.dataset.scope;\n selectPane(scope, btn.dataset.pane);\n });\n });\n selectPane('conversation', 'settings');\n }\n\n\n async function initConvSwitcher() {\n const sel = document.getElementById('conv-switcher');\n try {\n const data = await apiGet('/admin/api/conversations');\n sel.innerHTML = data.conversations.map((c) => {\n const key = c.platform + ':' + c.conversationId;\n const label = (c.label || c.conversationId) + (c.running ? ' (running)' : '');\n const selected = key === defaultConversationKey ? ' selected' : '';\n return '<option value=\"' + escAttr(key) + '\"' + selected + '>' + escHtml(label) + '</option>';\n }).join('');\n sel.addEventListener('change', () => setActiveConversation(sel.value));\n } catch (err) {\n }\n }\n\n function setActiveConversation(key) {\n activeConversationKey = key;\n const sel = document.getElementById('conv-switcher');\n if (sel && sel.value !== key) sel.value = key;\n const activeLink = railNavConversation.querySelector('.rail-link.active');\n ['settings', 'workspace', 'skills', 'mcp', 'events'].forEach((key) => paneLoaded.delete(key));\n if (activeLink) ensurePaneLoaded(activeLink.dataset.pane);\n openLogin(true);\n openSessionView(true);\n }\n\n\n const loadSettings = () => loadSettingsPanel('settings-content', () => '/admin/api/conversation-state?' + scopeQuery(), renderSettings);\n const loadGlobalSettings = () => loadSettingsPanel('global-settings-content', () => '/admin/api/settings/global', renderGlobalSettings);\n\n async function loadSettingsPanel(id, path, render) {\n const container = document.getElementById(id);\n container.innerHTML = '<div class=\"loading-msg\">Loading…</div>';\n if (!modelsLoaded) await loadModels();\n try {\n const data = await apiGet(path());\n container.innerHTML = render(data);\n } catch (err) {\n container.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n function renderConfigCard(title, content, action, buttonLabel, resultId) {\n return '<div class=\"config-block\"><h3 class=\"card-subtitle\">' + title + '</h3>' + content +\n '<button class=\"primary-action-btn\" onclick=\"' + action + '(this)\">' + buttonLabel + '</button>' +\n '<div id=\"' + resultId + '\" class=\"inline-result\" style=\"display:none\"></div></div>';\n }\n\n function renderOptions(choices, selected) {\n return choices.map(([value, label]) =>\n '<option value=\"' + escAttr(value) + '\"' + (selected === value ? ' selected' : '') + '>' + escHtml(label) + '</option>'\n ).join('');\n }\n function renderThinkingOptions(value) {\n return renderOptions(['off','minimal','low','medium','high','xhigh','max'].map((t) => [t, t]), value);\n }\n function renderReplyOptions(slack) {\n return renderOptions(['top-level','thread'].map((m) => [m, m]), (slack && slack.replyMode) || 'top-level');\n }\n function renderSettings(data) {\n const thinkingOpts = renderThinkingOptions(data.thinkingLevel);\n const replyModeOpts = renderReplyOptions(data.slack);\n const globalReplyMode = (data.slack && data.slack.globalReplyMode) || 'top-level';\n const globalModel = [data.globalProvider, data.globalModel].filter(Boolean).join('/');\n const globalModelLabel = globalModel + (data.globalThinkingLevel ? ':' + data.globalThinkingLevel : '');\n return [\n '<div class=\"config-grid\">',\n renderConfigCard('Model', [\n '<div class=\"config-row config-row-stack\"><label>Model</label><select id=\"m-model-ref\">' + renderModelOptions(data.provider, data.model) + '</select></div>',\n '<div class=\"config-row\"><label>Thinking</label><select id=\"m-thinking\">' + thinkingOpts + '</select></div>',\n '<p class=\"muted-note\">Global default: ' + escHtml(globalModelLabel) + '</p>',\n ].join(''), 'saveModel', 'Save model', 'model-save-result'),\n\n renderVisibilityCard(data),\n renderConfigCard('Slack', [\n '<div class=\"config-row\"><label>Reply mode</label><select id=\"m-slack-reply-mode\">' + replyModeOpts + '</select></div>',\n '<p class=\"muted-note\">Global default: ' + escHtml(globalReplyMode) + '</p>',\n ].join(''), 'saveSlack', 'Save Slack', 'slack-save-result'),\n '</div>',\n ].join('');\n }\n\n async function saveModel(btn) {\n const selectedModel = parseModelRef(document.getElementById('m-model-ref').value.trim());\n const provider = selectedModel.provider;\n const model = selectedModel.model;\n const thinkingLevel = document.getElementById('m-thinking').value;\n const result = document.getElementById('model-save-result');\n if (!provider || !model) {\n result.style.display = 'block'; result.className = 'inline-result err';\n result.textContent = 'Provider and model are required';\n return;\n }\n await saveConversationSetting(btn, result, 'model', { provider, model, thinkingLevel }, 'Save model');\n }\n\n async function saveSlack(btn) {\n const replyMode = document.getElementById('m-slack-reply-mode').value;\n const result = document.getElementById('slack-save-result');\n await saveConversationSetting(btn, result, 'slack', { replyMode }, 'Save Slack');\n }\n\n function renderVisibilityCard(data) {\n const hidden = data.officeVisibilityOverride === 'private';\n const canChoose = data.officeVisibilitySource === 'platform' ? data.officeVisibility === 'public' : hidden;\n if (!canChoose) {\n const why = data.officeVisibilitySource === 'platform'\n ? 'This is a DM, a private channel, or a conversation on a platform other than Slack, so its files are hidden from every other office. The platform decides this; it cannot be opened up here.'\n : 'Slack has not reported what kind of conversation this is yet, so it is treated as hidden until it does.';\n return '<div class=\"config-block\"><h3 class=\"card-subtitle\">Who can see the files in this office</h3>' +\n '<p><strong>Hidden</strong> — only this office.</p><p class=\"muted-note\">' + why + '</p></div>';\n }\n return renderConfigCard('Who can see the files in this office', [\n '<div class=\"config-row\"><label><input type=\"checkbox\" id=\"m-visibility\"' + (hidden ? ' checked' : '') + '> Hide the files in this channel from other offices</label></div>',\n '<p>' + (hidden\n ? '<strong>Hidden</strong> — other offices cannot read the files in this channel, and it can read shared MEMORY.md and skills but not change them.'\n : '<strong>Shared</strong> — every other office can read the files in this channel (read-only), and this channel may update shared MEMORY.md and skills.') + '</p>',\n '<p class=\"muted-note\">Slack says this is a public channel, which is why sharing is the default. Hiding is the only change allowed; nothing can be made more visible than Slack allows.</p>',\n ].join(''), 'saveVisibility', 'Save', 'mount-save-result');\n }\n\n async function saveVisibility(btn) {\n const visibility = document.getElementById('m-visibility').checked ? 'private' : 'default';\n const result = document.getElementById('mount-save-result');\n await saveConversationSetting(btn, result, 'visibility', { visibility }, 'Save visibility', loadSettings);\n }\n\n function saveConversationSetting(btn, result, setting, values, label, onSaved) {\n return saveSetting(btn, result, 'conversations/' + setting, { ...scopeBody(), ...values }, label, onSaved);\n }\n\n async function saveSetting(btn, result, path, values, label, onSaved) {\n btn.disabled = true; btn.textContent = 'Saving…'; result.style.display = 'none';\n try {\n await apiPost('/admin/api/' + path, values);\n result.style.display = 'block'; result.className = 'inline-result ok'; result.textContent = 'Saved ✓';\n if (onSaved) onSaved();\n } catch (err) {\n result.style.display = 'block'; result.className = 'inline-result err'; result.textContent = err.message;\n } finally {\n btn.disabled = false; btn.textContent = label;\n }\n }\n\n\n async function loadWorkspace() {\n const treeEl = document.getElementById('workspace-tree');\n const previewEl = document.getElementById('workspace-preview');\n treeEl.innerHTML = '<div class=\"loading-msg\">Loading…</div>';\n previewEl.innerHTML = '<div class=\"placeholder-msg\">Click a file to preview</div>';\n try {\n const data = await apiGet('/admin/api/workspace/tree?' + scopeQuery());\n if (!data.tree) {\n treeEl.innerHTML = '<div class=\"empty-state\">No files</div>';\n return;\n }\n treeEl.innerHTML = '<ul class=\"tree-root\">' + renderTreeChildren(data.tree) + '</ul>';\n } catch (err) {\n treeEl.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n function renderTreeChildren(node) {\n if (node.type === 'file') {\n return '<li><button class=\"tree-file\" data-admin-action=\"preview-file\" data-file-path=\"' + escAttr(node.path) + '\">' + escHtml(node.name) + '</button></li>';\n }\n if (!node.children || node.children.length === 0) {\n return '<li><span class=\"tree-dir empty\">' + escHtml(node.name || '.') + '/</span></li>';\n }\n const inner = node.children.map((c) =>\n c.type === 'file'\n ? '<li><button class=\"tree-file\" data-admin-action=\"preview-file\" data-file-path=\"' + escAttr(c.path) + '\">' + escHtml(c.name) + '</button></li>'\n : '<li><details open><summary class=\"tree-dir\">' + escHtml(c.name) + '/</summary><ul>' + renderTreeChildren(c) + '</ul></details></li>'\n ).join('');\n return inner;\n }\n\n function renderPreviewFileResult(previewEl, label, data) {\n if (data.binary) {\n previewEl.innerHTML = '<div class=\"preview-meta\">' + escHtml(label) + ' · ' + data.size + ' bytes · binary</div><div class=\"placeholder-msg\">Binary file — preview not available</div>';\n return;\n }\n previewEl.innerHTML =\n '<div class=\"preview-meta\">' + escHtml(label) + ' · ' + data.size + ' bytes</div>' +\n '<pre class=\"preview-body\">' + escHtml(data.content || '') + '</pre>';\n }\n\n async function previewFile(path) {\n const previewEl = document.getElementById('workspace-preview');\n previewEl.innerHTML = '<div class=\"loading-msg\">Loading ' + escHtml(path) + '…</div>';\n try {\n const data = await apiGet('/admin/api/workspace/file?' + scopeQuery() + '&path=' + encodeURIComponent(path));\n renderPreviewFileResult(previewEl, path, data);\n } catch (err) {\n previewEl.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n\n async function loadScopePanels(prefix, endpoint, render) {\n const convEl = document.getElementById(prefix + '-conv-content');\n const globalEl = document.getElementById(prefix + '-global-content');\n if (convEl) convEl.innerHTML = '<div class=\"loading-msg\">Loading…</div>';\n if (globalEl) globalEl.innerHTML = '<div class=\"loading-msg\">Loading…</div>';\n try {\n const data = await apiGet('/admin/api/' + endpoint + '?' + scopeQuery());\n render(data, convEl, globalEl);\n } catch (err) {\n if (convEl) convEl.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n if (globalEl) globalEl.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n\n function mcpMessage(scope, text, kind) {\n const el = document.getElementById(scope === 'global' ? 'mcp-global-msg' : 'mcp-conv-msg');\n if (!el) return;\n el.style.display = 'block';\n el.className = 'status-msg status-msg-' + kind;\n el.textContent = text;\n }\n\n function mcpTransport(server) {\n return server.command\n ? server.command + (server.args && server.args.length ? ' ' + server.args.join(' ') : '')\n : (server.url || '');\n }\n\n function renderMcpServer(scope, name, server) {\n const transport = (server.command ? 'stdio: ' : 'http: ') + mcpTransport(server);\n const keys = [];\n if (server.envKeys && server.envKeys.length) keys.push('env: ' + server.envKeys.join(', '));\n if (server.headerKeys && server.headerKeys.length) keys.push('headers: ' + server.headerKeys.join(', '));\n return '<article class=\"mcp-preset mcp-installed-card\">' +\n '<div class=\"mcp-preset-top\">' +\n '<span class=\"mcp-preset-category\">' + (server.command ? 'STDIO' : 'HTTP') + '</span>' +\n (server.disabled ? '<span class=\"mcp-badge mcp-badge-warn\">disabled</span>' : '<span class=\"mcp-badge mcp-badge-ok\">enabled</span>') +\n '</div>' +\n '<h3>' + escHtml(name) + '</h3>' +\n '<p class=\"mcp-preset-meta mcp-installed-transport\">' + escHtml(transport) + '</p>' +\n (keys.length ? '<div class=\"mcp-preset-meta\">' + escHtml(keys.join(' · ')) + '</div>' : '') +\n '<div class=\"mcp-preset-actions\">' +\n '<button class=\"mcp-btn\" data-mcp-action=\"test\" data-mcp-scope=\"' + scope + '\" data-mcp-name=\"' + escAttr(name) + '\">Test</button>' +\n '<button class=\"mcp-btn\" data-mcp-action=\"toggle\" data-mcp-scope=\"' + scope + '\" data-mcp-name=\"' + escAttr(name) + '\">' + (server.disabled ? 'Enable' : 'Disable') + '</button>' +\n '<button class=\"mcp-btn mcp-btn-danger\" data-mcp-action=\"remove\" data-mcp-scope=\"' + scope + '\" data-mcp-name=\"' + escAttr(name) + '\">Remove</button>' +\n '</div>' +\n '</article>';\n }\n\n function renderMcpPreset(scope, preset, servers) {\n const installed = Boolean(servers[preset.serverName]);\n const transport = preset.server.command ? 'Runs on host' : 'Remote service';\n return '<article class=\"mcp-preset\">' +\n '<div class=\"mcp-preset-top\">' +\n '<span class=\"mcp-preset-category\">' + escHtml(preset.category) + '</span>' +\n (installed ? '<span class=\"mcp-badge mcp-badge-ok\">Installed here</span>' : '') +\n '</div>' +\n '<h3>' + escHtml(preset.name) + '</h3>' +\n '<p>' + escHtml(preset.description) + '</p>' +\n '<div class=\"mcp-preset-meta\">' + escHtml(transport) + ' · <code>' + escHtml(preset.serverName) + '</code></div>' +\n '<div class=\"mcp-preset-actions\">' +\n '<a href=\"' + escAttr(preset.sourceUrl) + '\" target=\"_blank\" rel=\"noopener\">Source ↗</a>' +\n '<button class=\"primary-action-btn\" data-mcp-action=\"preset\" data-mcp-scope=\"' + scope + '\" data-mcp-preset=\"' + escAttr(preset.id) + '\">' + (installed ? 'Review' : 'Install') + '</button>' +\n '</div>' +\n '</article>';\n }\n\n function renderMcpScope(el, scope, servers) {\n const presets = mcpPresets.map((preset) => renderMcpPreset(scope, preset, servers)).join('');\n const rows = Object.entries(servers).map(([name, server]) => renderMcpServer(scope, name, server)).join('');\n el.innerHTML =\n '<div class=\"mcp-market-head\"><div><h3>Explore presets</h3><p>Reviewed recipes that install into this scope.</p></div><span>' + mcpPresets.length + ' available</span></div>' +\n '<div class=\"mcp-preset-grid\">' + presets + '</div>' +\n '<details class=\"mcp-installed\" open><summary>Installed in this scope</summary>' +\n '<div class=\"mcp-preset-grid\">' +\n rows +\n '<button type=\"button\" class=\"mcp-add-card\" data-mcp-action=\"open-custom\" data-mcp-scope=\"' + scope + '\">' +\n '<span class=\"mcp-add-card-plus\">+</span><span>新增 server</span>' +\n '</button>' +\n '</div>' +\n '</details>';\n }\n\n function renderMcpGuidedForm(scope) {\n return '<div class=\"mcp-guided-grid\">' +\n '<label class=\"mcp-field\"><span>Server 名稱</span><input id=\"mcp-' + scope + '-g-name\" class=\"form-input mcp-json\" placeholder=\"例如 github\" autocomplete=\"off\" /></label>' +\n '<div class=\"mcp-transport-toggle\">' +\n '<label><input type=\"radio\" name=\"mcp-' + scope + '-g-transport\" value=\"stdio\" checked /> 本機指令 (stdio)</label>' +\n '<label><input type=\"radio\" name=\"mcp-' + scope + '-g-transport\" value=\"http\" /> 遠端服務 (HTTP)</label>' +\n '</div>' +\n '<div id=\"mcp-' + scope + '-g-stdio\" class=\"mcp-transport-fields\">' +\n '<label class=\"mcp-field\"><span>指令</span><input id=\"mcp-' + scope + '-g-command\" class=\"form-input mcp-json\" placeholder=\"npx\" autocomplete=\"off\" /></label>' +\n '<label class=\"mcp-field\"><span>參數(以逗號分隔)</span><input id=\"mcp-' + scope + '-g-args\" class=\"form-input mcp-json\" placeholder=\"-y, @modelcontextprotocol/server-github\" autocomplete=\"off\" /></label>' +\n '</div>' +\n '<div id=\"mcp-' + scope + '-g-http\" class=\"mcp-transport-fields\" style=\"display:none\">' +\n '<label class=\"mcp-field\"><span>URL</span><input id=\"mcp-' + scope + '-g-url\" class=\"form-input mcp-json\" placeholder=\"https://mcp.example.com/mcp\" autocomplete=\"off\" /></label>' +\n '</div>' +\n '<div class=\"mcp-kv-block\">' +\n '<div class=\"mcp-kv-head\"><span id=\"mcp-' + scope + '-g-kv-label\">環境變數 (env)</span><button type=\"button\" class=\"mcp-btn\" data-mcp-kv-add=\"' + scope + '\">+ 新增一列</button></div>' +\n '<div id=\"mcp-' + scope + '-g-kv\" class=\"mcp-kv-rows\"></div>' +\n '</div>' +\n '</div>';\n }\n\n function renderMcpKvRow() {\n return '<div class=\"mcp-kv-row\">' +\n '<input class=\"form-input mcp-json\" placeholder=\"KEY\" data-kv-key autocomplete=\"off\" />' +\n '<input class=\"form-input mcp-json\" type=\"password\" placeholder=\"value\" data-kv-value autocomplete=\"off\" />' +\n '<button type=\"button\" class=\"mcp-btn mcp-btn-danger\" data-mcp-kv-remove>移除</button>' +\n '</div>';\n }\n\n function addMcpGuidedRow(scope) {\n const container = document.getElementById('mcp-' + scope + '-g-kv');\n if (container) container.insertAdjacentHTML('beforeend', renderMcpKvRow());\n }\n\n function updateMcpGuidedTransport(scope) {\n const checked = document.querySelector('input[name=\"mcp-' + scope + '-g-transport\"]:checked');\n const transport = checked ? checked.value : 'stdio';\n const stdioEl = document.getElementById('mcp-' + scope + '-g-stdio');\n const httpEl = document.getElementById('mcp-' + scope + '-g-http');\n const kvLabel = document.getElementById('mcp-' + scope + '-g-kv-label');\n if (stdioEl) stdioEl.style.display = transport === 'stdio' ? '' : 'none';\n if (httpEl) httpEl.style.display = transport === 'http' ? '' : 'none';\n if (kvLabel) kvLabel.textContent = transport === 'stdio' ? '環境變數 (env)' : 'HTTP Headers';\n }\n\n let pendingMcpCustomScope = null;\n\n function openMcpCustomDialog(scope) {\n pendingMcpCustomScope = scope;\n const content = document.getElementById('mcp-custom-dialog-content');\n content.innerHTML =\n '<div class=\"mcp-add-mode\">' +\n '<button type=\"button\" class=\"mcp-mode-btn active\" data-mcp-mode=\"guided\">引導式表單</button>' +\n '<button type=\"button\" class=\"mcp-mode-btn\" data-mcp-mode=\"json\">貼上 JSON</button>' +\n '</div>' +\n '<div id=\"mcp-custom-guided-panel\" class=\"mcp-panel\">' + renderMcpGuidedForm('custom') + '</div>' +\n '<div id=\"mcp-custom-json-panel\" class=\"mcp-panel\" style=\"display:none\">' +\n '<p class=\"mcp-manual-hint\">貼上 MCP server 文件給的 <code>mcpServers</code> JSON,原樣保存到這個 scope。remote 走 Streamable HTTP,local 走 stdio。</p>' +\n '<textarea id=\"mcp-custom-json\" class=\"form-input mcp-json\" spellcheck=\"false\" rows=\"9\" placeholder=\"' + escAttr(MCP_JSON_PLACEHOLDER) + '\"></textarea>' +\n '</div>';\n const dialogError = document.getElementById('mcp-custom-dialog-error');\n dialogError.style.display = 'none';\n dialogError.textContent = '';\n addMcpGuidedRow('custom');\n document.getElementById('mcp-custom-dialog').showModal();\n }\n\n function closeMcpCustomDialog() {\n pendingMcpCustomScope = null;\n document.getElementById('mcp-custom-dialog').close();\n }\n\n function switchMcpCustomMode(mode) {\n const guidedEl = document.getElementById('mcp-custom-guided-panel');\n const jsonEl = document.getElementById('mcp-custom-json-panel');\n if (guidedEl) guidedEl.style.display = mode === 'guided' ? '' : 'none';\n if (jsonEl) jsonEl.style.display = mode === 'json' ? '' : 'none';\n document.querySelectorAll('#mcp-custom-dialog-content [data-mcp-mode]').forEach((b) => {\n b.classList.toggle('active', b.dataset.mcpMode === mode);\n });\n }\n\n async function submitMcpCustomDialog(btn) {\n const scope = pendingMcpCustomScope;\n if (!scope) return;\n const dialogError = document.getElementById('mcp-custom-dialog-error');\n dialogError.style.display = 'none';\n dialogError.textContent = '';\n const jsonMode = document.getElementById('mcp-custom-json-panel').style.display !== 'none';\n let json;\n if (jsonMode) {\n const raw = document.getElementById('mcp-custom-json').value.trim();\n if (!raw) { dialogError.textContent = '請貼上 mcpServers JSON'; dialogError.style.display = 'block'; return; }\n json = raw;\n } else {\n const nameEl = document.getElementById('mcp-custom-g-name');\n const name = nameEl ? nameEl.value.trim() : '';\n if (!name) { dialogError.textContent = '請輸入 server 名稱'; dialogError.style.display = 'block'; return; }\n const checked = document.querySelector('input[name=\"mcp-custom-g-transport\"]:checked');\n const transport = checked ? checked.value : 'stdio';\n const kv = {};\n document.querySelectorAll('#mcp-custom-g-kv .mcp-kv-row').forEach((row) => {\n const keyInput = row.querySelector('[data-kv-key]');\n const valueInput = row.querySelector('[data-kv-value]');\n const key = keyInput ? keyInput.value.trim() : '';\n if (key) kv[key] = valueInput ? valueInput.value : '';\n });\n let entry;\n if (transport === 'stdio') {\n const commandEl = document.getElementById('mcp-custom-g-command');\n const command = commandEl ? commandEl.value.trim() : '';\n if (!command) { dialogError.textContent = '請輸入指令'; dialogError.style.display = 'block'; return; }\n const argsEl = document.getElementById('mcp-custom-g-args');\n const argsRaw = argsEl ? argsEl.value.trim() : '';\n const args = argsRaw ? argsRaw.split(',').map((s) => s.trim()).filter(Boolean) : [];\n entry = Object.assign({ command: command }, args.length ? { args: args } : {}, Object.keys(kv).length ? { env: kv } : {});\n } else {\n const urlEl = document.getElementById('mcp-custom-g-url');\n const url = urlEl ? urlEl.value.trim() : '';\n if (!url) { dialogError.textContent = '請輸入 URL'; dialogError.style.display = 'block'; return; }\n entry = Object.assign({ url: url }, Object.keys(kv).length ? { headers: kv } : {});\n }\n json = JSON.stringify({ mcpServers: { [name]: entry } });\n }\n btn.disabled = true;\n btn.textContent = '儲存中…';\n try {\n const data = await apiPost('/admin/api/mcp-servers/mutate', {\n action: 'import', scope: scope, json: json, ...scopeBody(),\n });\n const results = Array.isArray(data.results) ? data.results : [];\n const failed = results.filter((r) => r.error).length;\n closeMcpCustomDialog();\n mcpMessage(scope, failed ? '已儲存,但 ' + failed + ' 個 server 連線失敗,見上方選項維護。' : '已儲存並連線成功。', failed ? 'err' : 'ok');\n await loadMcpServers();\n } catch (err) {\n dialogError.textContent = err.message;\n dialogError.style.display = 'block';\n } finally {\n btn.disabled = false;\n btn.textContent = '新增並測試連線';\n }\n }\n\n const MCP_JSON_PLACEHOLDER = JSON.stringify({\n mcpServers: {\n browserless: {\n type: 'http',\n url: 'https://mcp.browserless.io/mcp',\n headers: { Authorization: 'Bearer YOUR_API_TOKEN' },\n },\n },\n }, null, 2);\n\n const loadMcpServers = () => loadScopePanels('mcp', 'mcp-servers', (data, convEl, globalEl) => {\n mcpPresets = Array.isArray(data.presets) ? data.presets : [];\n mcpServersByScope = { conversation: data.conversation || {}, global: data.global || {} };\n if (convEl) renderMcpScope(convEl, 'conversation', mcpServersByScope.conversation);\n if (globalEl) renderMcpScope(globalEl, 'global', mcpServersByScope.global);\n });\n\n async function mutateMcpServer(scope, action, name, extra) {\n mcpMessage(scope, action === 'test' ? '連線測試中…' : '儲存並測試連線中…', 'busy');\n try {\n const data = await apiPost('/admin/api/mcp-servers/mutate', {\n action: action,\n scope: scope,\n ...(name ? { name: name } : {}),\n ...(extra || {}),\n ...scopeBody(),\n });\n const results = Array.isArray(data.results) ? data.results : [];\n const failed = results.filter((r) => r.error).length;\n if (action === 'test') {\n mcpMessage(scope, failed ? '✗ ' + name + ': ' + results[0].error : '✓ ' + name + ': ' + results[0].tools + ' tool(s)', failed ? 'err' : 'ok');\n return;\n }\n if (action === 'remove' || action === 'toggle') {\n mcpMessage(scope, '完成。新設定在下一次回應生效。', 'ok');\n } else {\n mcpMessage(scope, failed ? '已儲存,但 ' + failed + ' 個 server 連線失敗,見下方錯誤。' : '已儲存並連線成功。', failed ? 'err' : 'ok');\n }\n await loadMcpServers();\n } catch (err) {\n mcpMessage(scope, err.message, 'err');\n }\n }\n\n function openMcpPreset(scope, presetId) {\n const preset = mcpPresets.find((item) => item.id === presetId);\n if (!preset) return;\n pendingMcpInstall = { scope: scope, preset: preset };\n const local = Boolean(preset.server.command);\n const installed = Boolean(mcpServersByScope[scope][preset.serverName]);\n const credentials = preset.credentials.map((credential, index) =>\n '<label class=\"mcp-credential\"><span>' + escHtml(credential.label) + (credential.required ? ' *' : '') + '</span>' +\n '<input data-mcp-credential=\"' + index + '\" type=\"' + (credential.secret ? 'password' : 'text') + '\" autocomplete=\"off\" />' +\n '<small>' + escHtml(credential.description) + '</small></label>'\n ).join('');\n document.getElementById('mcp-dialog-title').textContent = preset.name;\n const dialogError = document.getElementById('mcp-dialog-error');\n dialogError.style.display = 'none';\n dialogError.textContent = '';\n document.getElementById('mcp-dialog-content').innerHTML =\n '<p class=\"mcp-dialog-desc\">' + escHtml(preset.description) + '</p>' +\n '<div class=\"mcp-install-preview\"><span>' + (local ? 'Host command' : 'Remote endpoint') + '</span><code>' + escHtml(mcpTransport(preset.server)) + '</code></div>' +\n '<div class=\"mcp-security-note ' + (local ? 'local' : 'remote') + '\">' +\n (local\n ? '<strong>Host code execution.</strong> This command runs outside the conversation sandbox with the mikan process user permissions.'\n : '<strong>External service.</strong> Tool calls and selected data will be sent to this remote origin.') +\n '</div>' +\n (installed ? '<div class=\"mcp-replace-note\">Installing again replaces the existing <code>' + escHtml(preset.serverName) + '</code> entry in this scope.</div>' : '') +\n (credentials || '<p class=\"mcp-no-credentials\">No credentials required.</p>') +\n '<p class=\"mcp-secret-note\">These values are stored in host-private settings and are not shown to the model or sandbox.</p>' +\n '<a class=\"mcp-setup-link\" href=\"' + escAttr(preset.setupUrl) + '\" target=\"_blank\" rel=\"noopener\">Setup documentation ↗</a>';\n document.getElementById('mcp-install-dialog').showModal();\n }\n\n function closeMcpInstall() {\n pendingMcpInstall = null;\n document.getElementById('mcp-install-dialog').close();\n }\n\n async function installMcpPreset(btn) {\n if (!pendingMcpInstall) return;\n const { scope, preset } = pendingMcpInstall;\n const credentials = {};\n document.querySelectorAll('[data-mcp-credential]').forEach((input) => {\n const descriptor = preset.credentials[Number(input.dataset.mcpCredential)];\n if (descriptor) credentials[descriptor.key] = input.value;\n });\n btn.disabled = true;\n btn.textContent = 'Installing…';\n try {\n const data = await apiPost('/admin/api/mcp-servers/mutate', {\n action: 'install', scope: scope, presetId: preset.id, credentials: credentials, ...scopeBody(),\n });\n closeMcpInstall();\n const result = (Array.isArray(data.results) ? data.results : [])[0];\n if (result && result.error) mcpMessage(scope, '✗ ' + preset.name + ' saved but failed to connect: ' + result.error, 'err');\n else mcpMessage(scope, '✓ ' + preset.name + ' installed, ' + (result ? result.tools : 0) + ' tool(s).', 'ok');\n await loadMcpServers();\n } catch (err) {\n const dialogError = document.getElementById('mcp-dialog-error');\n dialogError.textContent = err.message;\n dialogError.style.display = 'block';\n } finally {\n btn.disabled = false;\n btn.textContent = 'Install preset';\n }\n }\n\n document.addEventListener('click', (event) => {\n const modeBtn = event.target.closest('#mcp-custom-dialog-content [data-mcp-mode]');\n if (modeBtn) { switchMcpCustomMode(modeBtn.dataset.mcpMode); return; }\n const kvAddBtn = event.target.closest('[data-mcp-kv-add]');\n if (kvAddBtn) { addMcpGuidedRow(kvAddBtn.dataset.mcpKvAdd); return; }\n const kvRemoveBtn = event.target.closest('[data-mcp-kv-remove]');\n if (kvRemoveBtn) { kvRemoveBtn.closest('.mcp-kv-row').remove(); return; }\n const transportRadio = event.target.closest('input[name^=\"mcp-\"][name$=\"-g-transport\"]');\n if (transportRadio) {\n const match = /^mcp-(.+)-g-transport$/.exec(transportRadio.name);\n if (match) updateMcpGuidedTransport(match[1]);\n return;\n }\n const btn = event.target.closest('[data-mcp-action]');\n if (!btn) return;\n if (btn.dataset.mcpAction === 'open-custom') { openMcpCustomDialog(btn.dataset.mcpScope); return; }\n if (btn.dataset.mcpAction === 'preset') { openMcpPreset(btn.dataset.mcpScope, btn.dataset.mcpPreset); return; }\n void mutateMcpServer(btn.dataset.mcpScope, btn.dataset.mcpAction, btn.dataset.mcpName);\n });\n\n let skillsCache = [];\n\n function renderSkillsInto(containerId, skills, allowedSource) {\n const container = document.getElementById(containerId);\n if (!container) return;\n const filtered = allowedSource ? skills.filter((s) => s.source === allowedSource) : skills;\n if (filtered.length === 0) {\n container.innerHTML = '<div class=\"empty-state\">No skills available</div>';\n return;\n }\n container.innerHTML = '<div class=\"skills-list\">' +\n filtered.map((s) =>\n '<div class=\"skill-row\">' +\n '<button class=\"skill-row-btn\" data-skill-source=\"' + escAttr(s.source) + '\" data-skill-directory=\"' + escAttr(s.directory) + '\" data-skill-name=\"' + escAttr(s.name) + '\">' +\n '<div class=\"skill-name\">' + escHtml(s.name) + '<span class=\"skill-source skill-source-' + s.source + '\">' + s.source + '</span></div>' +\n (s.description ? '<div class=\"skill-desc\">' + escHtml(s.description) + '</div>' : '') +\n '</button>' +\n '<div class=\"skill-row-actions\">' +\n '<button class=\"mcp-btn\" data-skill-edit-source=\"' + escAttr(s.source) + '\" data-skill-edit-directory=\"' + escAttr(s.directory) + '\">Edit</button>' +\n '<button class=\"mcp-btn mcp-btn-danger\" data-skill-delete-source=\"' + escAttr(s.source) + '\" data-skill-delete-directory=\"' + escAttr(s.directory) + '\" data-skill-delete-name=\"' + escAttr(s.name) + '\">Delete</button>' +\n '</div>' +\n '</div>'\n ).join('') + '</div>';\n }\n\n async function loadSkills() {\n const container = document.getElementById('skills-content');\n const previewEl = document.getElementById('skills-preview');\n container.innerHTML = '<div class=\"loading-msg\">Loading…</div>';\n if (previewEl) previewEl.innerHTML = '<div class=\"placeholder-msg\">Click a skill to preview SKILL.md</div>';\n try {\n const data = await apiGet('/admin/api/skills?' + scopeQuery());\n skillsCache = Array.isArray(data.skills) ? data.skills : [];\n renderSkillsInto('skills-content', skillsCache);\n renderSkillsInto('global-skills-content', skillsCache, 'global');\n } catch (err) {\n container.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n const loadGlobalSkills = loadSkills;\n\n async function previewSkillInto(previewId, source, directory, name) {\n const previewEl = document.getElementById(previewId);\n if (!previewEl) return;\n if (!source || !directory) {\n previewEl.innerHTML = '<div class=\"err-msg\">Missing skill source or directory</div>';\n return;\n }\n previewEl.innerHTML = '<div class=\"loading-msg\">Loading ' + escHtml(name || directory) + '…</div>';\n try {\n const data = await apiGet('/admin/api/skills/file?' + scopeQuery() + '&source=' + encodeURIComponent(source) + '&directory=' + encodeURIComponent(directory));\n renderPreviewFileResult(previewEl, source + '/' + directory + '/SKILL.md', data);\n } catch (err) {\n previewEl.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n async function deleteSkill(source, directory, name) {\n if (!confirm('Delete skill \"' + (name || directory) + '\" (' + source + ')?')) return;\n try {\n await apiPost('/admin/api/skills/mutate', { action: 'delete', source: source, directory: directory, ...scopeBody() });\n await loadSkills();\n } catch (err) {\n alert(err.message);\n }\n }\n\n function bindSkillListEvents(containerId, previewId) {\n const el = document.getElementById(containerId);\n if (!el) return;\n el.addEventListener('click', (event) => {\n const previewBtn = event.target.closest('[data-skill-source]');\n if (previewBtn) {\n previewSkillInto(previewId, previewBtn.dataset.skillSource, previewBtn.dataset.skillDirectory, previewBtn.dataset.skillName);\n return;\n }\n const editBtn = event.target.closest('[data-skill-edit-source]');\n if (editBtn) {\n const skill = skillsCache.find((s) => s.source === editBtn.dataset.skillEditSource && s.directory === editBtn.dataset.skillEditDirectory);\n if (skill) void openSkillDialog(skill.source, skill);\n return;\n }\n const deleteBtn = event.target.closest('[data-skill-delete-source]');\n if (deleteBtn) {\n void deleteSkill(deleteBtn.dataset.skillDeleteSource, deleteBtn.dataset.skillDeleteDirectory, deleteBtn.dataset.skillDeleteName);\n }\n });\n }\n bindSkillListEvents('skills-content', 'skills-preview');\n bindSkillListEvents('global-skills-content', 'global-skills-preview');\n\n let editingSkill = null;\n let pendingSkillScope = null;\n\n async function openSkillDialog(scope, existing) {\n editingSkill = existing ? { source: existing.source, directory: existing.directory } : null;\n document.getElementById('skill-dialog-eyebrow').textContent = scope === 'global' ? 'Global skill' : 'Conversation skill';\n document.getElementById('skill-dialog-title').textContent = existing ? 'Edit skill' : 'New skill';\n const dirInput = document.getElementById('skill-dialog-directory');\n dirInput.value = existing ? existing.directory : '';\n dirInput.disabled = Boolean(existing);\n document.getElementById('skill-dialog-name').value = existing ? existing.name : '';\n document.getElementById('skill-dialog-description').value = existing ? existing.description : '';\n const contentEl = document.getElementById('skill-dialog-content');\n contentEl.value = '';\n const dialogError = document.getElementById('skill-dialog-error');\n dialogError.style.display = 'none';\n dialogError.textContent = '';\n pendingSkillScope = scope;\n if (existing) {\n contentEl.value = 'Loading…';\n try {\n const data = await apiGet('/admin/api/skills/file?' + scopeQuery() + '&source=' + encodeURIComponent(existing.source) + '&directory=' + encodeURIComponent(existing.directory));\n contentEl.value = data.binary ? '' : (data.content || '').replace(/^---[\\\\s\\\\S]*?---\\\\n?/, '').trim();\n } catch (err) {\n contentEl.value = '';\n dialogError.textContent = 'Failed to load current content: ' + err.message;\n dialogError.style.display = 'block';\n }\n }\n document.getElementById('skill-dialog').showModal();\n }\n\n function closeSkillDialog() {\n editingSkill = null;\n pendingSkillScope = null;\n document.getElementById('skill-dialog').close();\n }\n\n async function submitSkillDialog(btn) {\n const dialogError = document.getElementById('skill-dialog-error');\n dialogError.style.display = 'none';\n dialogError.textContent = '';\n const directory = document.getElementById('skill-dialog-directory').value.trim();\n const name = document.getElementById('skill-dialog-name').value.trim() || directory;\n const description = document.getElementById('skill-dialog-description').value.trim();\n const content = document.getElementById('skill-dialog-content').value;\n if (!directory) { dialogError.textContent = 'Directory is required'; dialogError.style.display = 'block'; return; }\n if (!description) { dialogError.textContent = 'Description is required'; dialogError.style.display = 'block'; return; }\n const source = editingSkill ? editingSkill.source : pendingSkillScope;\n btn.disabled = true;\n btn.textContent = 'Saving…';\n try {\n await apiPost('/admin/api/skills/mutate', {\n action: 'save', source: source, directory: directory, name: name, description: description, content: content, ...scopeBody(),\n });\n closeSkillDialog();\n await loadSkills();\n } catch (err) {\n dialogError.textContent = err.message;\n dialogError.style.display = 'block';\n } finally {\n btn.disabled = false;\n btn.textContent = 'Save';\n }\n }\n\n\n const openLogin = (silent) => openPortalLink('vault', 'login', silent);\n const openSessionView = (silent) => openPortalLink('session', 'session', silent);\n\n async function openPortalLink(resultId, kind, silent) {\n const result = document.getElementById(resultId + '-link-result');\n if (silent) { result.style.display = 'none'; return; }\n result.style.display = 'block'; result.className = 'link-result loading'; result.textContent = 'Generating link…';\n try {\n const data = await apiPost('/admin/api/conversations/' + kind + '-link', scopeBody());\n result.className = 'link-result ok';\n result.innerHTML =\n (kind === 'login' ? '<span class=\"link-vault\">vault: <code>' + escHtml(data.vaultId) + '</code></span>' : '') +\n '<a href=\"' + escAttr(data.url) + '\" target=\"_blank\" rel=\"noopener\">' + escHtml(data.url) + '</a>' +\n '<button class=\"copy-link-btn\" data-admin-action=\"copy-link\" data-copy-text=\"' + escAttr(data.url) + '\">Copy</button>';\n window.open(data.url, '_blank', 'noopener');\n } catch (err) {\n result.className = 'link-result err'; result.textContent = err.message;\n }\n }\n\n\n const loadConversationEvents = () => loadEventList(true);\n const loadEvents = () => loadEventList(false);\n\n async function loadEventList(conversation) {\n const container = document.getElementById(conversation ? 'events-content' : 'global-events-content');\n if (!container) return;\n container.innerHTML = '<div class=\"loading-msg\">Loading…</div>';\n try {\n const data = await apiGet(conversation ? '/admin/api/conversations/events?' + scopeQuery() : '/admin/api/events');\n if (data.events.length === 0) {\n container.innerHTML = '<div class=\"empty-state\">' + (conversation ? '沒有關聯此對話的 event' : 'No events scheduled') + '</div>';\n return;\n }\n container.innerHTML = '<div class=\"events-list\">' +\n data.events.map((e) => renderEventRow(e, conversation)).join('') + '</div>';\n } catch (err) {\n container.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n function renderEventRow(e, allowDelete) {\n const meta = [e.type, e.platform, e.conversationId, e.schedule || e.at]\n .filter(Boolean).map(escHtml).join(' · ');\n const preview = e.text ? '<div class=\"event-text\">' + escHtml(e.text.length > 240 ? e.text.slice(0, 237) + '…' : e.text) + '</div>' : '';\n const deleteBtn = allowDelete\n ? '<button class=\"event-delete-btn\" data-admin-action=\"delete-event\" data-event-name=\"' + escAttr(e.name) + '\">Delete</button>'\n : '';\n return '<div class=\"event-row\">' +\n '<div class=\"event-row-top\">' +\n '<div class=\"event-name\"><code>' + escHtml(e.name) + '</code></div>' +\n deleteBtn +\n '</div>' +\n '<div class=\"event-meta\">' + meta + '</div>' +\n preview +\n '</div>';\n }\n\n async function deleteEvent(name, btn) {\n if (!confirm('Delete event \"' + name + '\"?')) return;\n btn.disabled = true; btn.textContent = 'Deleting…';\n try {\n await apiPost('/admin/api/conversations/events/delete', {\n ...scopeBody(), name,\n });\n await loadConversationEvents();\n } catch (err) {\n btn.disabled = false; btn.textContent = 'Delete';\n alert(err.message);\n }\n }\n\n\n function initGlobal() {}\n\n async function loadAllConversations() {\n const container = document.getElementById('all-conv-content');\n container.innerHTML = '<div class=\"loading-msg\">Loading…</div>';\n try {\n const data = await apiGet('/admin/api/conversations');\n if (data.conversations.length === 0) {\n container.innerHTML = '<div class=\"empty-state\">No conversations found</div>';\n return;\n }\n container.innerHTML = '<div class=\"conv-list\">' + data.conversations.map((c) => {\n const last = c.lastActivityAt ? new Date(c.lastActivityAt).toLocaleString() : '—';\n return '<button class=\"conv-row-btn\" data-admin-action=\"select-conversation\" data-conversation-id=\"' + escAttr(c.conversationId) + '\">' +\n '<span class=\"conv-id\">' + escHtml(c.label || c.conversationId) + '</span>' +\n (c.running ? '<span class=\"status-pill running\">running</span>' : '') +\n '<span class=\"conv-last\">' + escHtml(last) + '</span>' +\n '</button>';\n }).join('') + '</div>';\n } catch (err) {\n container.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n async function loadSessionUsage() {\n const container = document.getElementById('session-usage-content');\n container.innerHTML = '<div class=\"loading-msg\">Loading…</div>';\n try {\n const data = await apiGet('/admin/api/session-usage');\n if (data.sessions.length === 0) {\n container.innerHTML = '<div class=\"empty-state\">No token usage found</div>';\n return;\n }\n container.innerHTML = '<div class=\"usage-table-wrap\"><table class=\"usage-table\"><thead><tr><th>#</th><th>Channel</th><th>Session</th><th>Updated</th><th>Input</th><th>Output</th><th>Cache Read</th><th>Cache Write</th><th>Total</th><th>Cost</th></tr></thead><tbody>' +\n data.sessions.map((s, i) => '<tr>' +\n '<td>' + (i + 1) + '</td>' +\n '<td>' + escHtml(s.label || s.conversationId) + '</td>' +\n '<td><code>' + escHtml(s.fileName) + '</code></td>' +\n '<td>' + escHtml(new Date(s.updatedAt).toLocaleString()) + '</td>' +\n '<td>' + fmtNum(s.input) + '</td>' +\n '<td>' + fmtNum(s.output) + '</td>' +\n '<td>' + fmtNum(s.cacheRead) + '</td>' +\n '<td>' + fmtNum(s.cacheWrite) + '</td>' +\n '<td><strong>' + fmtNum(s.total) + '</strong></td>' +\n '<td>' + (s.cost > 0 ? '$' + Number(s.cost).toFixed(4) : '—') + '</td>' +\n '</tr>').join('') + '</tbody></table></div>';\n } catch (err) {\n container.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n function fmtNum(value) {\n return Number(value || 0).toLocaleString('en-US');\n }\n\n let timelineConvLoaded = false;\n let timelineData = null;\n let timelineFilter = null;\n async function ensureTimelineConvOptions() {\n if (timelineConvLoaded) return;\n const sel = document.getElementById('timeline-conv');\n const prev = sel.value;\n const data = await apiGet('/admin/api/conversations');\n if (!data.conversations.length) {\n sel.innerHTML = '<option value=\"\">No conversations</option>';\n timelineConvLoaded = true;\n return;\n }\n const want = prev || defaultConversationKey;\n sel.innerHTML = data.conversations.map((c) => {\n const key = c.platform + ':' + c.conversationId;\n return '<option value=\"' + escAttr(key) + '\"' +\n (key === want ? ' selected' : '') + '>' +\n escHtml(c.label || c.conversationId) + '</option>';\n }).join('');\n timelineConvLoaded = true;\n }\n\n async function loadTokenUsage() {\n loadSessionUsage();\n timelineConvLoaded = false;\n await loadUsageTimeline();\n }\n\n async function loadUsageTimeline() {\n const container = document.getElementById('usage-timeline-content');\n try {\n await ensureTimelineConvOptions();\n const conv = document.getElementById('timeline-conv').value;\n if (!conv) {\n container.innerHTML = '<div class=\"empty-state\">No conversations found</div>';\n return;\n }\n container.innerHTML = '<div class=\"loading-msg\">Loading…</div>';\n const convScope = scopeOf(conv);\n const data = await apiGet('/admin/api/conversation-usage?conversationId=' +\n encodeURIComponent(convScope.conversationId) +\n '&platform=' + encodeURIComponent(convScope.platform));\n timelineData = data;\n container.innerHTML = renderUsageTimeline(data);\n } catch (err) {\n container.innerHTML = '<div class=\"err-msg\">' + escHtml(err.message) + '</div>';\n }\n }\n\n function tlCard(label, value) {\n return '<div class=\"tl-card\"><div class=\"tl-card-label\">' + label +\n '</div><div class=\"tl-card-value\">' + value + '</div></div>';\n }\n\n const TL_SERIES = [\n { key: 'cacheRead', seg: 'tl-cache-read', sw: 'sw-cache-read', label: 'Cache read' },\n { key: 'cacheWrite', seg: 'tl-cache-write', sw: 'sw-cache-write', label: 'Cache write' },\n { key: 'input', seg: 'tl-input', sw: 'sw-input', label: 'Input' },\n { key: 'output', seg: 'tl-output', sw: 'sw-output', label: 'Output' },\n ];\n\n function toggleTimelineFilter(key) {\n timelineFilter = timelineFilter === key ? null : key;\n if (timelineData) {\n document.getElementById('usage-timeline-content').innerHTML = renderUsageTimeline(timelineData);\n }\n }\n\n function renderUsageTimeline(data) {\n const buckets = data.buckets || [];\n const totals = data.totals || { total: 0, cost: 0, cacheRead: 0 };\n const cacheHit = totals.total > 0 ? Math.round((totals.cacheRead / totals.total) * 100) : 0;\n const cards = '<div class=\"tl-cards\">' +\n tlCard('Total cost', totals.cost > 0 ? '$' + Number(totals.cost).toFixed(4) : '—') +\n tlCard('Total tokens', fmtNum(totals.total)) +\n tlCard('Cache hit', cacheHit + '%') +\n '</div>';\n\n if (totals.total === 0) {\n const emptyNote = data.hasOlder\n ? '<div class=\"tl-note\">No usage in the last 14 days · earlier activity exists</div>'\n : '<div class=\"empty-state\">No token usage in the last 14 days</div>';\n return cards + emptyNote;\n }\n\n const active = TL_SERIES.find((s) => s.key === timelineFilter) || null;\n const valueOf = (b) => active ? (b[active.key] || 0) : b.total;\n const max = Math.max(1, ...buckets.map(valueOf));\n const px = (v) => Math.round((v / max) * 180);\n\n const legend = '<div class=\"tl-legend\">' + TL_SERIES.map((s) => {\n const cls = 'tl-legend-item' +\n (active && active.key === s.key ? ' active' : (active ? ' dim' : ''));\n return '<span class=\"' + cls + '\" data-admin-action=\"toggle-timeline-filter\" data-filter-key=\"' + escAttr(s.key) + '\">' +\n '<i class=\"sw ' + s.sw + '\"></i>' + s.label + '</span>';\n }).join('') + '</div>';\n\n const bars = buckets.map((b) => {\n const val = valueOf(b);\n const tip = active\n ? b.date + ' · ' + active.label + ': ' + fmtNum(b[active.key] || 0) + ' tokens'\n : b.date + ' · ' + fmtNum(b.total) + ' tokens' +\n (b.cost > 0 ? ' · $' + Number(b.cost).toFixed(4) : '');\n let inner;\n if (val <= 0) {\n inner = '<span class=\"tl-empty\"></span>';\n } else if (active) {\n inner = '<span class=\"tl-seg ' + active.seg + '\" style=\"height:' + px(val) + 'px\"></span>';\n } else {\n inner = '<span class=\"tl-seg tl-output\" style=\"height:' + px(b.output) + 'px\"></span>' +\n '<span class=\"tl-seg tl-input\" style=\"height:' + px(b.input) + 'px\"></span>' +\n '<span class=\"tl-seg tl-cache-write\" style=\"height:' + px(b.cacheWrite) + 'px\"></span>' +\n '<span class=\"tl-seg tl-cache-read\" style=\"height:' + px(b.cacheRead) + 'px\"></span>';\n }\n return '<div class=\"tl-bar\">' +\n '<span class=\"tl-tip\">' + escHtml(tip) + '</span>' +\n '<div class=\"tl-fill\">' + inner + '</div>' +\n '</div>';\n }).join('');\n\n const axis = buckets.length\n ? '<div class=\"tl-axis\"><span>' + escHtml(buckets[0].date.slice(5)) +\n '</span><span>' + escHtml(buckets[buckets.length - 1].date.slice(5)) + '</span></div>'\n : '';\n const note = data.hasOlder\n ? '<div class=\"tl-note\">Showing last 14 days · earlier activity not shown</div>'\n : '<div class=\"tl-note\">Showing last 14 days</div>';\n const peak = '<div class=\"tl-peak\" style=\"bottom:180px\"><span class=\"tl-peak-label\">' +\n fmtNum(max) + ' tokens</span></div>';\n return cards + legend + '<div class=\"tl-chart\">' + peak + bars + '</div>' + axis + note;\n }\n\n function renderGlobalSettings(data) {\n const thinkingOpts = renderThinkingOptions(data.thinkingLevel);\n const replyModeOpts = renderReplyOptions(data.slack);\n return [\n '<div class=\"config-grid\">',\n renderConfigCard('Default model', [\n '<div class=\"config-row config-row-stack\"><label>Model</label><select id=\"g-model-ref\">' + renderModelOptions(data.provider, data.model) + '</select></div>',\n '<div class=\"config-row\"><label>Thinking</label><select id=\"g-thinking\">' + thinkingOpts + '</select></div>',\n ].join(''), 'saveGlobalModel', 'Save model', 'g-model-result'),\n renderConfigCard('Sandbox limits', [\n '<div class=\"config-row\"><label>CPUs</label><input id=\"g-cpus\" placeholder=\"0.5\" value=\"' + escAttr(data.sandboxCpus || '') + '\"></div>',\n '<div class=\"config-row\"><label>Memory</label><input id=\"g-mem\" placeholder=\"1g\" value=\"' + escAttr(data.sandboxMemory || '') + '\"></div>',\n '<div class=\"config-row\"><label>Boost CPUs</label><input id=\"g-bcpus\" placeholder=\"2\" value=\"' + escAttr(data.sandboxBoostCpus || '') + '\"></div>',\n '<div class=\"config-row\"><label>Boost Mem</label><input id=\"g-bmem\" placeholder=\"4g\" value=\"' + escAttr(data.sandboxBoostMemory || '') + '\"></div>',\n ].join(''), 'saveGlobalSandbox', 'Save sandbox', 'g-sandbox-result'),\n renderConfigCard('Slack', [\n '<div class=\"config-row\"><label>Reply mode</label><select id=\"g-slack-reply-mode\">' + replyModeOpts + '</select></div>',\n ].join(''), 'saveGlobalSlack', 'Save Slack', 'g-slack-result'),\n '</div>',\n ].join('');\n }\n\n async function saveGlobalModel(btn) {\n const selectedModel = parseModelRef(document.getElementById('g-model-ref').value.trim());\n const provider = selectedModel.provider;\n const model = selectedModel.model;\n const thinkingLevel = document.getElementById('g-thinking').value;\n const result = document.getElementById('g-model-result');\n if (!provider || !model) {\n result.style.display = 'block'; result.className = 'inline-result err';\n result.textContent = 'Provider and model are required'; return;\n }\n btn.disabled = true; btn.textContent = 'Saving…'; result.style.display = 'none';\n try {\n await apiPost('/admin/api/settings/model', { provider, model, thinkingLevel });\n result.style.display = 'block'; result.className = 'inline-result ok'; result.textContent = 'Saved ✓';\n } catch (err) {\n result.style.display = 'block'; result.className = 'inline-result err'; result.textContent = err.message;\n } finally {\n btn.disabled = false; btn.textContent = 'Save model';\n }\n }\n\n async function saveGlobalSandbox(btn) {\n const cpus = document.getElementById('g-cpus').value.trim();\n const memory = document.getElementById('g-mem').value.trim();\n const boostCpus = document.getElementById('g-bcpus').value.trim();\n const boostMemory = document.getElementById('g-bmem').value.trim();\n const result = document.getElementById('g-sandbox-result');\n await saveSetting(btn, result, 'settings/sandbox', { cpus, memory, boostCpus, boostMemory }, 'Save sandbox');\n }\n\n async function saveGlobalSlack(btn) {\n const replyMode = document.getElementById('g-slack-reply-mode').value;\n const result = document.getElementById('g-slack-result');\n await saveSetting(btn, result, 'settings/slack', { replyMode }, 'Save Slack');\n }\n\n `;\n\nexport const adminViewStartupScript = ` initConvSwitcher();\n initRailNav();\n `;\n\nexport const adminViewStyles = `\n\n .shell { max-width: 1180px; }\n\n\n\n .settings-shell {\n display: grid;\n grid-template-columns: 216px 1fr;\n align-items: start;\n gap: 22px;\n }\n\n .settings-rail {\n position: sticky;\n top: 28px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .rail-scope {\n display: flex;\n padding: 3px;\n border: 1px solid var(--border);\n border-radius: 11px;\n background: rgba(0,0,0,0.03);\n gap: 2px;\n }\n .rail-scope-btn {\n flex: 1;\n padding: 7px 8px;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: var(--muted);\n font: 600 0.72rem/1.2 'DM Sans', sans-serif;\n letter-spacing: 0.01em;\n cursor: pointer;\n transition: background 140ms, color 140ms;\n }\n .rail-scope-btn:hover { color: var(--text); }\n .rail-scope-btn.active { background: var(--surface); color: var(--text); box-shadow: 0 1px 3px rgba(0,0,0,0.1); }\n .rail-scope-btn:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }\n\n .rail-nav { display: flex; flex-direction: column; gap: 1px; }\n .rail-nav[hidden] { display: none; }\n .rail-link {\n display: flex; align-items: center; gap: 10px;\n padding: 8px 10px; border: none; border-radius: 9px;\n background: transparent; color: var(--muted); text-align: left;\n font: 500 0.86rem/1.2 'DM Sans', sans-serif; cursor: pointer;\n transition: background 120ms, color 120ms;\n }\n .rail-link svg { flex-shrink: 0; opacity: 0.75; }\n .rail-link:hover { background: rgba(0,0,0,0.045); color: var(--text); }\n .rail-link:hover svg { opacity: 1; }\n .rail-link.active {\n background: var(--text); color: #fafafa; font-weight: 600;\n }\n .rail-link.active svg { opacity: 1; }\n .rail-link:focus-visible { outline: 2px solid var(--text); outline-offset: 1px; }\n\n .settings-panels { min-width: 0; }\n .tab-panel { display: none; flex-direction: column; gap: 0; }\n .tab-panel.active { display: flex; }\n\n\n\n .pane { display: none; padding: 30px 0; border-bottom: 1px solid var(--border); }\n .pane:last-child { border-bottom: none; }\n .pane.active { display: block; animation: pane-in 180ms ease; }\n @keyframes pane-in { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: none; } }\n\n .pane-head {\n display: flex; align-items: flex-start; justify-content: space-between;\n gap: 16px; margin-bottom: 22px; flex-wrap: wrap;\n }\n .pane-title {\n font-family: 'Lora', Georgia, serif; font-size: 1.32rem; font-weight: 600;\n letter-spacing: -0.01em; line-height: 1.25; margin-bottom: 5px;\n }\n .pane-desc { color: var(--muted); font-size: 0.88rem; line-height: 1.55; max-width: 52ch; }\n .pane-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }\n .pane-body { display: flex; flex-direction: column; gap: 14px; }\n\n .card-desc { color: var(--muted); font-size: 0.9rem; line-height: 1.55; margin-bottom: 12px; }\n\n .link-result {\n margin-top: 12px; padding: 10px 14px; border-radius: 10px;\n display: flex; gap: 10px; align-items: center; flex-wrap: wrap;\n font-size: 0.84rem;\n }\n .link-result.ok { background: var(--ok-bg); border: 1px solid var(--ok-border); }\n .link-result.err { background: var(--err-bg); border: 1px solid var(--err-border); color: var(--err-text); }\n .link-result.loading { background: rgba(0,0,0,0.025); border: 1px solid var(--border); color: var(--muted); }\n .link-result a {\n color: var(--ok-text);\n font-family: 'JetBrains Mono', ui-monospace, monospace;\n font-size: 0.78rem; word-break: break-all; flex: 1; min-width: 0;\n }\n .link-vault { color: var(--muted); font-size: 0.78rem; flex-shrink: 0; }\n .copy-link-btn {\n padding: 5px 12px; border: 1px solid var(--ok-border); border-radius: 7px;\n background: rgba(255,255,255,0.7); color: var(--ok-text);\n font: 500 0.78rem/1.2 'DM Sans', sans-serif;\n cursor: pointer; flex-shrink: 0;\n }\n\n .config-grid {\n display: grid; grid-template-columns: 1fr 1fr; gap: 18px;\n }\n .config-block { display: flex; flex-direction: column; gap: 10px; }\n\n .config-block .primary-action-btn { align-self: flex-start; min-width: 132px; justify-content: center; }\n .config-row { display: grid; grid-template-columns: 110px 1fr; gap: 10px; align-items: center; }\n .config-row.config-row-stack { grid-template-columns: 1fr; }\n .config-row label { font-size: 0.82rem; color: var(--muted); }\n .config-row input, .config-row select, .config-row textarea {\n padding: 7px 10px; border: 1px solid var(--border); border-radius: 8px;\n font-family: inherit; font-size: 0.84rem; width: 100%;\n }\n .config-row textarea {\n font-family: 'JetBrains Mono', ui-monospace, monospace;\n resize: vertical;\n }\n .toggle { display: inline-flex; align-items: center; gap: 8px; font-size: 0.84rem; }\n\n .inline-result {\n padding: 8px 12px; border-radius: 8px; font-size: 0.82rem; margin-top: 4px;\n }\n .inline-result.ok { background: var(--ok-bg); color: var(--ok-text); border: 1px solid var(--ok-border); }\n .inline-result.err { background: var(--err-bg); color: var(--err-text); border: 1px solid var(--err-border); }\n\n .refresh-btn {\n flex-shrink: 0; padding: 6px 12px;\n border: 1px solid var(--border); border-radius: 10px;\n background: rgba(0,0,0,0.025); color: var(--muted);\n font: 500 0.8rem/1.2 'DM Sans', sans-serif; cursor: pointer;\n transition: background 120ms, color 120ms;\n }\n .refresh-btn:hover { background: rgba(0,0,0,0.06); color: var(--text); }\n\n\n\n .workspace-split {\n display: grid; grid-template-columns: 260px 1fr; gap: 14px;\n min-height: 360px;\n }\n .workspace-tree {\n border: 1px solid var(--border); border-radius: 12px; padding: 10px;\n background: rgba(0,0,0,0.02); overflow: auto; max-height: 480px;\n font-family: 'JetBrains Mono', ui-monospace, monospace;\n font-size: 0.78rem;\n }\n .workspace-tree ul { list-style: none; padding-left: 12px; margin: 0; }\n .workspace-tree .tree-root { padding-left: 0; }\n .workspace-tree details { margin: 1px 0; }\n .workspace-tree summary { cursor: pointer; padding: 2px 4px; border-radius: 4px; }\n .workspace-tree summary:hover { background: rgba(0,0,0,0.05); }\n .tree-dir { color: var(--text); font-weight: 600; }\n .tree-dir.empty { color: var(--subtle); font-weight: 400; }\n .tree-file {\n display: block; width: 100%; text-align: left;\n background: transparent; border: none; cursor: pointer;\n padding: 2px 4px; border-radius: 4px;\n font-family: inherit; font-size: inherit; color: var(--muted);\n }\n .tree-file:hover { background: rgba(0,0,0,0.05); color: var(--text); }\n\n .workspace-preview {\n border: 1px solid var(--border); border-radius: 12px;\n background: #fff; padding: 12px; overflow: auto; max-height: 480px;\n }\n .preview-meta {\n font-size: 0.74rem; color: var(--subtle);\n margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px solid var(--border);\n font-family: 'JetBrains Mono', ui-monospace, monospace;\n }\n .preview-body {\n margin: 0; white-space: pre-wrap; word-break: break-word;\n font-family: 'JetBrains Mono', ui-monospace, monospace;\n font-size: 0.78rem; color: var(--text);\n }\n .placeholder-msg { color: var(--subtle); font-size: 0.86rem; padding: 24px 8px; text-align: center; }\n\n\n\n .skills-list { display: flex; flex-direction: column; gap: 8px; }\n .skill-row {\n padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px;\n background: rgba(0,0,0,0.02);\n display: flex; flex-direction: column; align-items: stretch; gap: 6px;\n }\n .skill-row-btn {\n flex: 1 1 auto; min-width: 0; text-align: left; cursor: pointer; font-family: inherit;\n background: transparent; border: none; padding: 0;\n }\n .skill-row-btn:hover .skill-name { color: var(--accent); }\n .skill-row-actions { display: flex; gap: 6px; flex-shrink: 0; justify-content: flex-end; }\n .skill-name {\n font-weight: 650; font-size: 0.9rem; color: var(--text);\n display: flex; align-items: center; gap: 8px; flex-wrap: wrap;\n }\n .skill-source {\n padding: 1px 8px; border-radius: 999px; font-size: 0.7rem;\n font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;\n }\n .skill-source-global { background: rgba(59,130,246,0.1); color: #1d4ed8; }\n .skill-source-conversation { background: rgba(217,119,6,0.1); color: var(--accent); }\n .skill-desc { color: var(--muted); font-size: 0.82rem; margin-top: 4px; line-height: 1.5; }\n\n\n\n .form-input {\n flex: 1 1 240px; min-width: 0; padding: 8px 10px;\n border: 1px solid var(--border); border-radius: 8px; background: var(--card);\n font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 0.8rem; color: var(--text);\n }\n .status-msg {\n padding: 8px 12px; border-radius: 8px; margin-bottom: 12px;\n font-size: 0.82rem; line-height: 1.5; word-break: break-word;\n }\n .status-msg-ok { background: rgba(22,163,74,0.1); color: #15803d; }\n .status-msg-err { background: rgba(220,38,38,0.1); color: #b91c1c; }\n .status-msg-busy { background: rgba(0,0,0,0.05); color: var(--muted); }\n .mcp-btn {\n padding: 4px 10px; border: 1px solid var(--border); border-radius: 7px;\n background: var(--card); color: var(--text); font-size: 0.76rem; cursor: pointer;\n }\n .mcp-btn:hover { background: rgba(0,0,0,0.05); }\n .mcp-btn-danger { color: #b91c1c; }\n .mcp-badge {\n padding: 1px 8px; border-radius: 999px; font-size: 0.7rem;\n font-weight: 600; letter-spacing: 0.03em;\n }\n .mcp-badge-ok { background: rgba(22,163,74,0.1); color: #15803d; }\n .mcp-badge-warn { background: rgba(217,119,6,0.12); color: var(--accent); }\n\n .mcp-market-head {\n display: flex; align-items: end; justify-content: space-between; gap: 16px;\n margin-bottom: 12px;\n }\n .mcp-market-head h3 { margin: 0; font-size: 1rem; }\n .mcp-market-head p { margin: 3px 0 0; color: var(--muted); font-size: 0.8rem; }\n .mcp-market-head > span {\n color: var(--subtle); font: 500 0.72rem/1.2 'JetBrains Mono', ui-monospace, monospace;\n }\n .mcp-preset-grid {\n display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px;\n }\n .mcp-preset {\n min-width: 0; padding: 14px; border: 1px solid var(--border); border-radius: 12px;\n background:\n linear-gradient(145deg, rgba(255,255,255,0.85), rgba(247,245,238,0.72)),\n var(--card);\n display: flex; flex-direction: column; min-height: 190px;\n }\n .mcp-preset-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }\n .mcp-preset-category {\n color: var(--accent); font-size: 0.66rem; font-weight: 700;\n letter-spacing: 0.09em; text-transform: uppercase;\n }\n .mcp-preset h3 { margin: 14px 0 5px; font-size: 1.06rem; letter-spacing: -0.02em; }\n .mcp-preset p { margin: 0; color: var(--muted); font-size: 0.8rem; line-height: 1.5; }\n .mcp-preset-meta {\n margin-top: 12px; color: var(--subtle); font-size: 0.72rem;\n }\n .mcp-preset-meta code { color: var(--text); background: transparent; }\n .mcp-preset-actions {\n margin-top: auto; padding-top: 16px; display: flex; align-items: center;\n justify-content: space-between; gap: 10px;\n }\n .mcp-preset-actions a, .mcp-setup-link {\n color: var(--muted); font-size: 0.76rem; text-decoration: none;\n }\n .mcp-preset-actions a:hover, .mcp-setup-link:hover { color: var(--text); text-decoration: underline; }\n .mcp-preset-actions .primary-action-btn { padding: 6px 12px; }\n .mcp-installed {\n margin-top: 14px; border-top: 1px solid var(--border); padding-top: 12px;\n }\n .mcp-installed > summary {\n cursor: pointer; color: var(--muted); font-size: 0.8rem; font-weight: 600;\n margin-bottom: 10px;\n }\n .mcp-installed-card { min-height: 150px; }\n .mcp-installed-transport {\n font-family: 'JetBrains Mono', ui-monospace, monospace; word-break: break-all;\n }\n .mcp-add-card {\n min-width: 0; min-height: 190px; border: 1.5px dashed var(--border); border-radius: 12px;\n background: transparent; color: var(--muted); font-size: 0.86rem; font-weight: 600;\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n gap: 8px; cursor: pointer;\n }\n .mcp-add-card:hover { background: rgba(0,0,0,0.03); color: var(--text); border-color: var(--text); }\n .mcp-add-card-plus { font-size: 1.8rem; line-height: 1; font-weight: 400; }\n .mcp-manual-hint { color: var(--muted); font-size: 0.8rem; margin: 0 0 10px; line-height: 1.5; }\n .mcp-add-mode { display: flex; gap: 8px; margin: 0 0 12px; }\n .mcp-mode-btn {\n padding: 6px 12px; border: 1px solid var(--border); border-radius: 999px;\n background: var(--card); font-size: 0.8rem; cursor: pointer; color: var(--muted);\n }\n .mcp-mode-btn.active { background: var(--text); color: #fafafa; border-color: var(--text); }\n .mcp-guided-grid { display: grid; gap: 12px; }\n .mcp-field { display: grid; gap: 5px; }\n .mcp-field span { font-size: 0.78rem; font-weight: 650; color: var(--muted); }\n .mcp-transport-toggle { display: flex; gap: 16px; font-size: 0.85rem; align-items: center; }\n .mcp-transport-toggle label { display: flex; align-items: center; gap: 6px; cursor: pointer; }\n .mcp-transport-fields { display: grid; gap: 12px; }\n .mcp-kv-block { display: grid; gap: 8px; }\n .mcp-kv-head { display: flex; align-items: center; justify-content: space-between; }\n .mcp-kv-head span { font-size: 0.78rem; font-weight: 650; color: var(--muted); }\n .mcp-kv-rows { display: grid; gap: 6px; }\n .mcp-kv-row { display: flex; gap: 8px; }\n .mcp-kv-row .form-input { flex: 1 1 auto; }\n .mcp-json {\n width: 100%; box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.8rem; line-height: 1.45; resize: vertical;\n }\n .mcp-verify { margin-top: 10px; display: grid; gap: 6px; }\n .mcp-verify .inline-result { word-break: break-word; }\n\n .mcp-dialog {\n width: min(620px, calc(100vw - 28px)); max-height: calc(100vh - 40px);\n border: 1px solid var(--border); border-radius: 18px; padding: 22px;\n color: var(--text); background: #fbfaf6; box-shadow: 0 28px 90px rgba(18,18,16,0.24);\n\n position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); margin: 0;\n }\n .mcp-dialog::backdrop { background: rgba(20,20,18,0.5); backdrop-filter: blur(3px); }\n .mcp-dialog-head {\n display: flex; align-items: flex-start; justify-content: space-between; gap: 18px;\n padding-bottom: 14px; border-bottom: 1px solid var(--border);\n }\n .mcp-dialog-head .card-title { margin: 4px 0 0; }\n .mcp-dialog-close {\n border: 0; background: transparent; color: var(--muted); cursor: pointer;\n font-size: 1.6rem; line-height: 1; padding: 2px 5px;\n }\n .mcp-dialog-desc { color: var(--muted); line-height: 1.55; font-size: 0.88rem; }\n .mcp-install-preview {\n display: grid; gap: 6px; padding: 12px; border-radius: 10px;\n background: #1d211f; color: #f5f2e9;\n }\n .mcp-install-preview span {\n color: #aeb8b1; font-size: 0.66rem; font-weight: 700;\n letter-spacing: 0.08em; text-transform: uppercase;\n }\n .mcp-install-preview code {\n color: #f5f2e9; background: transparent; word-break: break-all; font-size: 0.78rem;\n }\n .mcp-security-note, .mcp-replace-note {\n margin-top: 12px; padding: 10px 12px; border-radius: 9px;\n font-size: 0.78rem; line-height: 1.5;\n }\n .mcp-security-note.local {\n background: rgba(220,38,38,0.08); border: 1px solid rgba(185,28,28,0.18); color: #991b1b;\n }\n .mcp-security-note.remote {\n background: rgba(59,130,246,0.08); border: 1px solid rgba(29,78,216,0.16); color: #1e40af;\n }\n .mcp-replace-note { background: rgba(217,119,6,0.1); color: #92400e; }\n .mcp-credential { display: grid; gap: 5px; margin-top: 14px; }\n .mcp-credential span { font-size: 0.78rem; font-weight: 650; }\n .mcp-credential input {\n width: 100%; padding: 9px 10px; border: 1px solid var(--border); border-radius: 8px;\n background: #fff; color: var(--text); font: 0.8rem/1.3 'JetBrains Mono', ui-monospace, monospace;\n }\n .mcp-credential small, .mcp-secret-note, .mcp-no-credentials {\n color: var(--subtle); font-size: 0.72rem; line-height: 1.45;\n }\n .mcp-secret-note { margin: 14px 0 8px; }\n .mcp-no-credentials { margin: 14px 0 0; }\n #mcp-dialog-error, #mcp-custom-dialog-error { margin-top: 14px; }\n .mcp-dialog-actions {\n display: flex; justify-content: flex-end; gap: 8px; margin-top: 20px;\n padding-top: 14px; border-top: 1px solid var(--border);\n }\n\n\n\n .events-list { display: flex; flex-direction: column; gap: 8px; }\n .event-row {\n padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px;\n background: rgba(0,0,0,0.02);\n }\n .event-row-top {\n display: flex; align-items: center; justify-content: space-between;\n gap: 10px;\n }\n .event-name { min-width: 0; flex: 1; word-break: break-all; }\n .event-name code { font-size: 0.82rem; background: transparent; padding: 0; }\n .event-meta { font-size: 0.74rem; color: var(--muted); margin-top: 3px; }\n .event-text {\n font-size: 0.82rem; color: var(--text); margin-top: 6px;\n font-family: 'JetBrains Mono', ui-monospace, monospace;\n white-space: pre-wrap; word-break: break-word;\n }\n .event-delete-btn {\n flex-shrink: 0; padding: 4px 10px;\n border-radius: 7px; border: 1px solid rgba(185, 28, 28, 0.18);\n background: rgba(0,0,0,0.03); color: var(--err-text);\n font: 500 0.76rem/1.2 'DM Sans', sans-serif; cursor: pointer;\n }\n .event-delete-btn:hover:not(:disabled) {\n background: var(--err-bg); border-color: rgba(185, 28, 28, 0.28);\n }\n .event-delete-btn:disabled { opacity: 0.5; cursor: wait; }\n\n\n\n .conv-list { display: flex; flex-direction: column; gap: 6px; }\n .conv-row-btn {\n display: flex; align-items: center; gap: 12px;\n padding: 10px 14px; border: 1px solid var(--border); border-radius: 10px;\n background: rgba(0,0,0,0.02); cursor: pointer; text-align: left;\n transition: background 120ms, border-color 120ms;\n }\n .conv-row-btn:hover { background: rgba(0,0,0,0.05); border-color: rgba(0,0,0,0.14); }\n .conv-id { flex: 1; font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 0.84rem; }\n .conv-last { color: var(--subtle); font-size: 0.78rem; }\n\n .usage-table-wrap { overflow-x: auto; }\n .usage-table { width: 100%; border-collapse: collapse; font-size: 0.78rem; }\n .usage-table th, .usage-table td {\n padding: 8px 10px; border-bottom: 1px solid var(--border);\n text-align: left; white-space: nowrap;\n }\n .usage-table th {\n color: var(--subtle); font-size: 0.68rem;\n text-transform: uppercase; letter-spacing: 0.08em;\n }\n .usage-table code { font-size: 0.72rem; }\n\n .timeline-controls {\n display: flex; flex-wrap: wrap; gap: 18px; align-items: center; margin-bottom: 16px;\n }\n .timeline-controls label {\n display: flex; align-items: center; gap: 8px;\n font-size: 0.76rem; color: var(--muted);\n }\n .timeline-controls select {\n padding: 6px 10px; border: 1px solid var(--border); border-radius: 8px;\n background: #fff; color: var(--text); font-size: 0.8rem; max-width: 240px;\n }\n .tl-note { margin-top: 8px; font-size: 0.72rem; color: var(--subtle); }\n .tl-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 16px; }\n .tl-card { background: rgba(0,0,0,0.025); border-radius: 10px; padding: 10px 12px; }\n .tl-card-label { font-size: 0.73rem; color: var(--muted); margin-bottom: 4px; }\n .tl-card-value { font-size: 1.35rem; font-weight: 600; color: var(--text); }\n .tl-legend { display: flex; gap: 16px; font-size: 0.73rem; color: var(--muted); margin-bottom: 10px; }\n .tl-legend span { display: inline-flex; align-items: center; gap: 6px; }\n .tl-legend-item { cursor: pointer; transition: opacity 100ms; }\n .tl-legend-item:hover { text-decoration: underline; }\n .tl-legend-item.active { color: var(--text); font-weight: 600; text-decoration: underline; }\n .tl-legend-item.dim { opacity: 0.4; }\n .sw { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }\n .sw-cache-read { background: rgba(0,0,0,0.18); }\n .sw-cache-write { background: #3b6fb0; }\n .sw-input { background: var(--accent); }\n .sw-output { background: var(--ok-text); }\n .tl-chart {\n position: relative;\n display: flex; align-items: flex-end; gap: 6px;\n height: 216px; border-bottom: 1px solid var(--border);\n }\n .tl-peak {\n position: absolute; left: 0; right: 0; height: 0;\n border-top: 1px dashed var(--accent); pointer-events: none;\n }\n .tl-peak-label {\n position: absolute; left: 0; top: -15px;\n font-size: 0.7rem; color: var(--accent); white-space: nowrap;\n }\n .tl-bar {\n position: relative; flex: 1; min-width: 0; height: 180px;\n display: flex; flex-direction: column; justify-content: flex-end;\n border-radius: 6px 6px 0 0; transition: background 80ms ease;\n }\n .tl-bar:hover { background: rgba(0,0,0,0.06); }\n .tl-bar:hover .tl-seg { opacity: 0.55; }\n .tl-fill {\n display: flex; flex-direction: column; justify-content: flex-end;\n border-radius: 3px 3px 0 0; overflow: hidden;\n }\n .tl-tip {\n position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%);\n margin-bottom: 6px; padding: 5px 9px; border-radius: 6px;\n background: var(--text); color: #fafafa; font-size: 0.7rem; line-height: 1.35;\n white-space: nowrap; opacity: 0; pointer-events: none; z-index: 5;\n }\n .tl-bar:hover .tl-tip { opacity: 1; }\n .tl-seg { display: block; width: 100%; }\n .tl-seg.tl-output { background: var(--ok-text); }\n .tl-seg.tl-input { background: var(--accent); }\n .tl-seg.tl-cache-write { background: #3b6fb0; }\n .tl-seg.tl-cache-read { background: rgba(0,0,0,0.18); }\n .tl-empty { display: block; width: 100%; height: 3px; background: var(--border); }\n .tl-axis { display: flex; justify-content: space-between; margin-top: 6px; font-size: 0.7rem; color: var(--subtle); }\n\n .status-pill {\n display: inline-flex; padding: 2px 9px; border-radius: 999px;\n font-size: 0.7rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;\n }\n .status-pill.running { background: var(--ok-bg); color: var(--ok-text); border: 1px solid var(--ok-border); }\n\n @media (max-width: 860px) {\n .settings-shell { grid-template-columns: 1fr; }\n .settings-rail { position: static; }\n\n .rail-nav {\n flex-direction: row; flex-wrap: nowrap; gap: 6px;\n overflow-x: auto; padding-bottom: 6px;\n\n min-width: 0; max-width: 100%;\n scrollbar-width: thin; -webkit-overflow-scrolling: touch;\n }\n .settings-rail { min-width: 0; }\n .rail-nav[hidden] { display: none; }\n .rail-link { flex: 0 0 auto; padding: 7px 11px; }\n .rail-link span { white-space: nowrap; }\n }\n\n @media (max-width: 640px) {\n .config-grid { grid-template-columns: 1fr; }\n .config-row { grid-template-columns: 1fr; gap: 4px; }\n .workspace-split { grid-template-columns: 1fr; }\n .workspace-tree, .workspace-preview { max-height: 260px; }\n .mcp-preset-grid { grid-template-columns: 1fr; }\n .mcp-dialog { padding: 18px; }\n }\n`;\n"]}
|
|
@@ -21,11 +21,6 @@ interface AdminConversationScope {
|
|
|
21
21
|
conversationId: string;
|
|
22
22
|
error?: string;
|
|
23
23
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Admin scope is a full office address. The platform defaults to the token's
|
|
26
|
-
* (an admin invoked from Slack browses Slack offices); cross-platform targets
|
|
27
|
-
* name theirs explicitly. Identity validation is the address factory's.
|
|
28
|
-
*/
|
|
29
24
|
export declare function resolveConversationScope(requestedId: string, requestedPlatform: string, token: AdminToken): AdminConversationScope;
|
|
30
25
|
interface SkillEntry {
|
|
31
26
|
name: string;
|
|
@@ -35,10 +30,5 @@ interface SkillEntry {
|
|
|
35
30
|
directory: string;
|
|
36
31
|
}
|
|
37
32
|
export declare function readSkillsFromDir(skillsDir: string, source: SkillEntry["source"]): SkillEntry[];
|
|
38
|
-
/**
|
|
39
|
-
* List events through the owning store, whose payloads are validated by the
|
|
40
|
-
* event-format module (files that fail validation stay visible with a null
|
|
41
|
-
* payload so operators can delete them).
|
|
42
|
-
*/
|
|
43
33
|
export declare function listOfficeEvents(store: EventStore): Promise<EventSummary[]>;
|
|
44
34
|
//# sourceMappingURL=portal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../../../src/adapters/web/admin/portal.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAKjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../../../src/adapters/web/admin/portal.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAKjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAM7C,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,qBAAa,uBAAwB,SAAQ,kBAAkB,CAAC,UAAU,CAAC;IACzE,MAAM,CAAC,IAAI,EAAE;QACX,QAAQ,EAAE,YAAY,CAAC;QACvB,cAAc,EAAE,MAAM,CAAC;QACvB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,GAAG,UAAU,CAUb;CACF;AAoCD,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAUpD,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,aAAa,GACtB,OAAO,CAAC,OAAO,CAAC,CA6BlB;AAuGD,UAAU,sBAAsB;IAC9B,OAAO,EAAE,aAAa,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,iBAAiB,EAAE,MAAM,EACzB,KAAK,EAAE,UAAU,GAChB,sBAAsB,CAexB;AAm7BD,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,QAAQ,GAAG,cAAc,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAmBD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,UAAU,EAAE,CAuB/F;AAoVD,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAmBjF"}
|