@geminixiang/mikan 1.0.0-beta.8 → 1.0.0-beta.80
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 +1149 -0
- package/README.md +98 -38
- package/dist/adapters/commands/admin.d.ts.map +1 -0
- package/dist/{commands → adapters/commands}/admin.js +12 -9
- package/dist/adapters/commands/admin.js.map +1 -0
- package/dist/{commands → adapters/commands}/auto-reply.d.ts +0 -3
- package/dist/adapters/commands/auto-reply.d.ts.map +1 -0
- package/dist/adapters/commands/auto-reply.js +47 -0
- package/dist/adapters/commands/auto-reply.js.map +1 -0
- package/dist/adapters/commands/login.d.ts +6 -0
- package/dist/adapters/commands/login.d.ts.map +1 -0
- package/dist/adapters/commands/login.js +158 -0
- package/dist/adapters/commands/login.js.map +1 -0
- package/dist/adapters/commands/manifest.d.ts +17 -0
- package/dist/adapters/commands/manifest.d.ts.map +1 -0
- package/dist/adapters/commands/manifest.js +121 -0
- package/dist/adapters/commands/manifest.js.map +1 -0
- package/dist/adapters/commands/model.d.ts +9 -0
- package/dist/adapters/commands/model.d.ts.map +1 -0
- package/dist/adapters/commands/model.js +136 -0
- package/dist/adapters/commands/model.js.map +1 -0
- package/dist/adapters/commands/new.d.ts.map +1 -0
- package/dist/adapters/commands/new.js +39 -0
- package/dist/adapters/commands/new.js.map +1 -0
- package/dist/adapters/commands/registry.d.ts +4 -0
- package/dist/adapters/commands/registry.d.ts.map +1 -0
- package/dist/adapters/commands/registry.js +35 -0
- package/dist/adapters/commands/registry.js.map +1 -0
- package/dist/{commands → adapters/commands}/sandbox.d.ts +0 -1
- package/dist/adapters/commands/sandbox.d.ts.map +1 -0
- package/dist/adapters/commands/sandbox.js +125 -0
- package/dist/adapters/commands/sandbox.js.map +1 -0
- package/dist/adapters/commands/session-view.d.ts.map +1 -0
- package/dist/{commands → adapters/commands}/session-view.js +21 -16
- package/dist/adapters/commands/session-view.js.map +1 -0
- package/dist/adapters/commands/types.d.ts +105 -0
- package/dist/adapters/commands/types.d.ts.map +1 -0
- package/dist/adapters/commands/types.js.map +1 -0
- package/dist/adapters/commands/utils.d.ts +11 -0
- package/dist/adapters/commands/utils.d.ts.map +1 -0
- package/dist/{commands → adapters/commands}/utils.js +15 -1
- package/dist/adapters/commands/utils.js.map +1 -0
- package/dist/adapters/discord/bot.d.ts +10 -17
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +232 -297
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/components.d.ts +4 -0
- package/dist/adapters/discord/components.d.ts.map +1 -0
- package/dist/adapters/discord/components.js +9 -0
- package/dist/adapters/discord/components.js.map +1 -0
- package/dist/adapters/discord/context.d.ts +3 -7
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +55 -219
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/discord/format.d.ts +2 -0
- package/dist/adapters/discord/format.d.ts.map +1 -0
- package/dist/adapters/discord/format.js +47 -0
- package/dist/adapters/discord/format.js.map +1 -0
- package/dist/adapters/discord/types.d.ts +90 -1
- package/dist/adapters/discord/types.d.ts.map +1 -1
- package/dist/adapters/discord/types.js.map +1 -1
- package/dist/adapters/github/bot.d.ts +25 -9
- package/dist/adapters/github/bot.d.ts.map +1 -1
- package/dist/adapters/github/bot.js +326 -154
- package/dist/adapters/github/bot.js.map +1 -1
- package/dist/adapters/github/client.d.ts +35 -23
- package/dist/adapters/github/client.d.ts.map +1 -1
- package/dist/adapters/github/client.js +121 -28
- package/dist/adapters/github/client.js.map +1 -1
- package/dist/adapters/github/context.d.ts +3 -8
- package/dist/adapters/github/context.d.ts.map +1 -1
- package/dist/adapters/github/context.js +64 -131
- package/dist/adapters/github/context.js.map +1 -1
- package/dist/adapters/github/github-ops.d.ts +22 -0
- package/dist/adapters/github/github-ops.d.ts.map +1 -0
- package/dist/adapters/github/github-ops.js +277 -0
- package/dist/adapters/github/github-ops.js.map +1 -0
- package/dist/adapters/github/ids.d.ts +2 -23
- package/dist/adapters/github/ids.d.ts.map +1 -1
- package/dist/adapters/github/ids.js +6 -36
- package/dist/adapters/github/ids.js.map +1 -1
- package/dist/adapters/github/repo.d.ts +8 -0
- package/dist/adapters/github/repo.d.ts.map +1 -0
- package/dist/adapters/github/repo.js +92 -0
- package/dist/adapters/github/repo.js.map +1 -0
- package/dist/adapters/github/tool-pack.d.ts +4 -0
- package/dist/adapters/github/tool-pack.d.ts.map +1 -0
- package/dist/adapters/github/tool-pack.js +45 -0
- package/dist/adapters/github/tool-pack.js.map +1 -0
- package/dist/adapters/github/tools/checks.d.ts +14 -0
- package/dist/adapters/github/tools/checks.d.ts.map +1 -0
- package/dist/adapters/github/tools/checks.js +80 -0
- package/dist/adapters/github/tools/checks.js.map +1 -0
- package/dist/adapters/github/tools/issue.d.ts +18 -0
- package/dist/adapters/github/tools/issue.d.ts.map +1 -0
- package/dist/adapters/github/tools/issue.js +44 -0
- package/dist/adapters/github/tools/issue.js.map +1 -0
- package/dist/adapters/github/tools/pr.d.ts +17 -0
- package/dist/adapters/github/tools/pr.d.ts.map +1 -0
- package/dist/adapters/github/tools/pr.js +42 -0
- package/dist/adapters/github/tools/pr.js.map +1 -0
- package/dist/adapters/github/tools/read.d.ts +17 -0
- package/dist/adapters/github/tools/read.d.ts.map +1 -0
- package/dist/adapters/github/tools/read.js +134 -0
- package/dist/adapters/github/tools/read.js.map +1 -0
- package/dist/adapters/github/tools/review-reply.d.ts +14 -0
- package/dist/adapters/github/tools/review-reply.d.ts.map +1 -0
- package/dist/adapters/github/tools/review-reply.js +34 -0
- package/dist/adapters/github/tools/review-reply.js.map +1 -0
- package/dist/adapters/github/tools/sync.d.ts +13 -0
- package/dist/adapters/github/tools/sync.d.ts.map +1 -0
- package/dist/adapters/github/tools/sync.js +29 -0
- package/dist/adapters/github/tools/sync.js.map +1 -0
- package/dist/adapters/github/types.d.ts +197 -18
- 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 +6 -0
- package/dist/adapters/github/webhook.d.ts.map +1 -0
- package/dist/adapters/github/webhook.js +62 -0
- package/dist/adapters/github/webhook.js.map +1 -0
- package/dist/adapters/intake.d.ts +4 -10
- package/dist/adapters/intake.d.ts.map +1 -1
- package/dist/adapters/intake.js +74 -31
- package/dist/adapters/intake.js.map +1 -1
- package/dist/adapters/markdown-tables.d.ts +8 -0
- package/dist/adapters/markdown-tables.d.ts.map +1 -0
- package/dist/adapters/markdown-tables.js +111 -0
- package/dist/adapters/markdown-tables.js.map +1 -0
- package/dist/{platform-messages.d.ts → adapters/messages.d.ts} +3 -2
- package/dist/adapters/messages.d.ts.map +1 -0
- package/dist/{platform-messages.js → adapters/messages.js} +4 -1
- package/dist/adapters/messages.js.map +1 -0
- package/dist/adapters/progressive-renderer.d.ts +7 -0
- package/dist/adapters/progressive-renderer.d.ts.map +1 -0
- package/dist/adapters/progressive-renderer.js +423 -0
- package/dist/adapters/progressive-renderer.js.map +1 -0
- package/dist/adapters/shared.d.ts +24 -53
- package/dist/adapters/shared.d.ts.map +1 -1
- package/dist/adapters/shared.js +140 -91
- package/dist/adapters/shared.js.map +1 -1
- package/dist/adapters/slack/assistant.d.ts +23 -0
- package/dist/adapters/slack/assistant.d.ts.map +1 -0
- package/dist/adapters/slack/assistant.js +109 -0
- package/dist/adapters/slack/assistant.js.map +1 -0
- package/dist/adapters/slack/auto-reply-context.d.ts +14 -0
- package/dist/adapters/slack/auto-reply-context.d.ts.map +1 -0
- package/dist/adapters/slack/auto-reply-context.js +21 -0
- package/dist/adapters/slack/auto-reply-context.js.map +1 -0
- package/dist/adapters/slack/blocks.d.ts +5 -0
- package/dist/adapters/slack/blocks.d.ts.map +1 -1
- package/dist/adapters/slack/blocks.js +84 -148
- package/dist/adapters/slack/blocks.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +51 -39
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +892 -567
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts +3 -9
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +8 -17
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/slack/jev-context.d.ts +17 -0
- package/dist/adapters/slack/jev-context.d.ts.map +1 -0
- package/dist/adapters/slack/jev-context.js +57 -0
- package/dist/adapters/slack/jev-context.js.map +1 -0
- package/dist/adapters/slack/markdown.d.ts +2 -0
- package/dist/adapters/slack/markdown.d.ts.map +1 -0
- package/dist/adapters/slack/markdown.js +43 -0
- package/dist/adapters/slack/markdown.js.map +1 -0
- package/dist/adapters/slack/persona.d.ts +6 -0
- package/dist/adapters/slack/persona.d.ts.map +1 -0
- package/dist/adapters/slack/persona.js +13 -0
- package/dist/adapters/slack/persona.js.map +1 -0
- package/dist/adapters/slack/response-lifecycle.d.ts +3 -4
- package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
- package/dist/adapters/slack/response-lifecycle.js +221 -347
- package/dist/adapters/slack/response-lifecycle.js.map +1 -1
- package/dist/adapters/slack/session.d.ts +3 -2
- package/dist/adapters/slack/session.d.ts.map +1 -1
- package/dist/adapters/slack/session.js +9 -13
- package/dist/adapters/slack/session.js.map +1 -1
- package/dist/adapters/slack/stream-limits.d.ts +10 -0
- package/dist/adapters/slack/stream-limits.d.ts.map +1 -0
- package/dist/adapters/slack/stream-limits.js +22 -0
- package/dist/adapters/slack/stream-limits.js.map +1 -0
- package/dist/adapters/slack/task-intent.d.ts +19 -0
- package/dist/adapters/slack/task-intent.d.ts.map +1 -0
- package/dist/adapters/slack/task-intent.js +65 -0
- package/dist/adapters/slack/task-intent.js.map +1 -0
- package/dist/adapters/slack/task-status.d.ts +6 -0
- package/dist/adapters/slack/task-status.d.ts.map +1 -0
- package/dist/adapters/slack/task-status.js +100 -0
- package/dist/adapters/slack/task-status.js.map +1 -0
- package/dist/adapters/slack/tool-pack.d.ts +4 -0
- package/dist/adapters/slack/tool-pack.d.ts.map +1 -0
- package/dist/adapters/slack/tool-pack.js +40 -0
- package/dist/adapters/slack/tool-pack.js.map +1 -0
- package/dist/adapters/slack/tools/blockkit.d.ts +17 -0
- package/dist/adapters/slack/tools/blockkit.d.ts.map +1 -0
- package/dist/adapters/slack/tools/blockkit.js +98 -0
- package/dist/adapters/slack/tools/blockkit.js.map +1 -0
- package/dist/adapters/slack/types.d.ts +91 -5
- 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 +13 -0
- package/dist/adapters/slack/update-diagnostics.d.ts.map +1 -0
- package/dist/adapters/slack/update-diagnostics.js +128 -0
- package/dist/adapters/slack/update-diagnostics.js.map +1 -0
- package/dist/adapters/telegram/bot.d.ts +8 -18
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +114 -232
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts +3 -7
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +47 -191
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/adapters/telegram/types.d.ts +24 -1
- package/dist/adapters/telegram/types.d.ts.map +1 -1
- package/dist/adapters/telegram/types.js.map +1 -1
- package/dist/adapters/types.d.ts +89 -16
- 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 +1766 -0
- package/dist/adapters/web/admin/client-assets.js.map +1 -0
- package/dist/adapters/web/admin/portal.d.ts +27 -0
- package/dist/adapters/web/admin/portal.d.ts.map +1 -0
- package/dist/adapters/web/admin/portal.js +1506 -0
- package/dist/adapters/web/admin/portal.js.map +1 -0
- package/dist/{web → adapters/web}/admin/provider-models.d.ts +1 -1
- package/dist/adapters/web/admin/provider-models.d.ts.map +1 -0
- package/dist/{web → adapters/web}/admin/provider-models.js +1 -1
- package/dist/adapters/web/admin/provider-models.js.map +1 -0
- package/dist/adapters/web/admin/types.d.ts +56 -0
- package/dist/adapters/web/admin/types.d.ts.map +1 -0
- package/dist/adapters/web/admin/types.js.map +1 -0
- package/dist/adapters/web/login/oauth.d.ts +4 -0
- package/dist/adapters/web/login/oauth.d.ts.map +1 -0
- package/dist/{web → adapters/web}/login/oauth.js +61 -100
- package/dist/adapters/web/login/oauth.js.map +1 -0
- package/dist/adapters/web/login/portal.d.ts +10 -0
- package/dist/adapters/web/login/portal.d.ts.map +1 -0
- package/dist/{web → adapters/web}/login/portal.js +169 -159
- package/dist/adapters/web/login/portal.js.map +1 -0
- package/dist/{web → adapters/web}/login/types.d.ts +1 -17
- package/dist/adapters/web/login/types.d.ts.map +1 -0
- package/dist/adapters/web/login/types.js.map +1 -0
- package/dist/adapters/web/portal-shell.d.ts +9 -0
- package/dist/adapters/web/portal-shell.d.ts.map +1 -0
- package/dist/{portal-shell.js → adapters/web/portal-shell.js} +73 -11
- package/dist/adapters/web/portal-shell.js.map +1 -0
- package/dist/adapters/web/server.d.ts +33 -0
- package/dist/adapters/web/server.d.ts.map +1 -0
- package/dist/{web → adapters/web}/server.js +15 -9
- package/dist/adapters/web/server.js.map +1 -0
- package/dist/adapters/web/session-view/portal.d.ts +19 -0
- package/dist/adapters/web/session-view/portal.d.ts.map +1 -0
- package/dist/{web → adapters/web}/session-view/portal.js +641 -181
- package/dist/adapters/web/session-view/portal.js.map +1 -0
- package/dist/{web → adapters/web}/session-view/types.d.ts +1 -6
- package/dist/adapters/web/session-view/types.d.ts.map +1 -0
- package/dist/adapters/web/session-view/types.js.map +1 -0
- package/dist/{web → adapters/web}/token-store.d.ts +0 -3
- package/dist/adapters/web/token-store.d.ts.map +1 -0
- package/dist/{web → adapters/web}/token-store.js +1 -3
- package/dist/adapters/web/token-store.js.map +1 -0
- package/dist/adapters/web/types.d.ts.map +1 -0
- package/dist/adapters/web/types.js.map +1 -0
- package/dist/cli/arg-grammar.d.ts +8 -0
- package/dist/cli/arg-grammar.d.ts.map +1 -0
- package/dist/cli/arg-grammar.js +47 -0
- package/dist/cli/arg-grammar.js.map +1 -0
- package/dist/cli/boot.d.ts +4 -0
- package/dist/cli/boot.d.ts.map +1 -0
- package/dist/cli/boot.js +81 -0
- package/dist/cli/boot.js.map +1 -0
- package/dist/cli/download.d.ts.map +1 -0
- package/dist/cli/download.js +84 -0
- package/dist/cli/download.js.map +1 -0
- package/dist/cli/office.d.ts +2 -0
- package/dist/cli/office.d.ts.map +1 -0
- package/dist/cli/office.js +151 -0
- package/dist/cli/office.js.map +1 -0
- package/dist/cli/onboard.d.ts +8 -0
- package/dist/cli/onboard.d.ts.map +1 -0
- package/dist/cli/onboard.js +195 -0
- package/dist/cli/onboard.js.map +1 -0
- package/dist/cli/process-lifecycle.d.ts +14 -0
- package/dist/cli/process-lifecycle.d.ts.map +1 -0
- package/dist/cli/process-lifecycle.js +61 -0
- package/dist/cli/process-lifecycle.js.map +1 -0
- package/dist/cli/sandbox.d.ts +3 -0
- package/dist/cli/sandbox.d.ts.map +1 -0
- package/dist/cli/sandbox.js +80 -0
- package/dist/cli/sandbox.js.map +1 -0
- package/dist/cli/sessions.d.ts +2 -0
- package/dist/cli/sessions.d.ts.map +1 -0
- package/dist/cli/sessions.js +78 -0
- package/dist/cli/sessions.js.map +1 -0
- package/dist/cli/types.d.ts +21 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/env-manifest.d.ts +17 -0
- package/dist/env-manifest.d.ts.map +1 -0
- package/dist/env-manifest.js +249 -0
- package/dist/env-manifest.js.map +1 -0
- package/dist/events/index.d.ts +122 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +299 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/scheduler.d.ts +25 -0
- package/dist/events/scheduler.d.ts.map +1 -0
- package/dist/events/scheduler.js +223 -0
- package/dist/events/scheduler.js.map +1 -0
- package/dist/file-guards.d.ts +13 -0
- package/dist/file-guards.d.ts.map +1 -0
- package/dist/file-guards.js +104 -0
- package/dist/file-guards.js.map +1 -0
- package/dist/harness/execution-env.d.ts +4 -0
- package/dist/harness/execution-env.d.ts.map +1 -0
- package/dist/harness/execution-env.js +245 -0
- package/dist/harness/execution-env.js.map +1 -0
- package/dist/harness/execution-resolver.d.ts +23 -0
- package/dist/harness/execution-resolver.d.ts.map +1 -0
- package/dist/harness/execution-resolver.js +156 -0
- package/dist/harness/execution-resolver.js.map +1 -0
- 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 +19 -17
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +12 -15
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/jev.d.ts +67 -0
- package/dist/harness/jev.d.ts.map +1 -0
- package/dist/harness/jev.js +135 -0
- package/dist/harness/jev.js.map +1 -0
- 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 +179 -0
- package/dist/harness/mcp-result.js.map +1 -0
- package/dist/harness/mcp.d.ts +17 -0
- package/dist/harness/mcp.d.ts.map +1 -0
- package/dist/harness/mcp.js +243 -0
- package/dist/harness/mcp.js.map +1 -0
- package/dist/harness/models.d.ts +2 -19
- package/dist/harness/models.d.ts.map +1 -1
- package/dist/harness/models.js +42 -94
- package/dist/harness/models.js.map +1 -1
- package/dist/harness/open-connector.d.ts +12 -0
- package/dist/harness/open-connector.d.ts.map +1 -0
- package/dist/harness/open-connector.js +160 -0
- package/dist/harness/open-connector.js.map +1 -0
- package/dist/harness/presenter.d.ts +11 -0
- package/dist/harness/presenter.d.ts.map +1 -0
- package/dist/harness/presenter.js +824 -0
- package/dist/harness/presenter.js.map +1 -0
- package/dist/harness/prompt.d.ts +18 -0
- package/dist/harness/prompt.d.ts.map +1 -0
- package/dist/harness/prompt.js +403 -0
- package/dist/harness/prompt.js.map +1 -0
- package/dist/harness/runner.d.ts +2 -123
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +547 -415
- package/dist/harness/runner.js.map +1 -1
- package/dist/harness/session.d.ts +98 -0
- package/dist/harness/session.d.ts.map +1 -0
- package/dist/harness/session.js +645 -0
- package/dist/harness/session.js.map +1 -0
- package/dist/harness/skills.d.ts +7 -41
- package/dist/harness/skills.d.ts.map +1 -1
- package/dist/harness/skills.js +101 -55
- package/dist/harness/skills.js.map +1 -1
- package/dist/harness/subagent-profiles.d.ts +3 -0
- package/dist/harness/subagent-profiles.d.ts.map +1 -0
- package/dist/harness/subagent-profiles.js +260 -0
- package/dist/harness/subagent-profiles.js.map +1 -0
- package/dist/harness/subagent.d.ts +39 -0
- package/dist/harness/subagent.d.ts.map +1 -0
- package/dist/harness/subagent.js +618 -0
- package/dist/harness/subagent.js.map +1 -0
- package/dist/harness/tool-args.d.ts +2 -0
- package/dist/harness/tool-args.d.ts.map +1 -0
- package/dist/harness/tool-args.js +20 -0
- package/dist/harness/tool-args.js.map +1 -0
- package/dist/harness/tools/attach.d.ts +13 -0
- package/dist/harness/tools/attach.d.ts.map +1 -0
- package/dist/{adapters/slack → harness}/tools/attach.js +2 -2
- package/dist/harness/tools/attach.js.map +1 -0
- package/dist/harness/tools/event.d.ts +28 -0
- package/dist/harness/tools/event.d.ts.map +1 -0
- package/dist/harness/tools/event.js +166 -0
- package/dist/harness/tools/event.js.map +1 -0
- package/dist/harness/tools/generate-image.d.ts +20 -0
- package/dist/harness/tools/generate-image.d.ts.map +1 -0
- package/dist/harness/tools/generate-image.js +68 -0
- package/dist/harness/tools/generate-image.js.map +1 -0
- package/dist/harness/tools/host-fn-tool.d.ts +14 -0
- package/dist/harness/tools/host-fn-tool.d.ts.map +1 -0
- package/dist/harness/tools/host-fn-tool.js +39 -0
- package/dist/harness/tools/host-fn-tool.js.map +1 -0
- package/dist/harness/tools/index.d.ts +34 -0
- package/dist/harness/tools/index.d.ts.map +1 -0
- package/dist/harness/tools/index.js +49 -0
- package/dist/harness/tools/index.js.map +1 -0
- package/dist/harness/tools/jev-browser.d.ts +42 -0
- package/dist/harness/tools/jev-browser.d.ts.map +1 -0
- package/dist/harness/tools/jev-browser.js +547 -0
- package/dist/harness/tools/jev-browser.js.map +1 -0
- package/dist/harness/tools/jev.d.ts +15 -0
- package/dist/harness/tools/jev.d.ts.map +1 -0
- package/dist/harness/tools/jev.js +98 -0
- package/dist/harness/tools/jev.js.map +1 -0
- package/dist/harness/tools/pi-tools.d.ts +8 -0
- package/dist/harness/tools/pi-tools.d.ts.map +1 -0
- package/dist/harness/tools/pi-tools.js +55 -0
- package/dist/harness/tools/pi-tools.js.map +1 -0
- package/dist/harness/tools/react.d.ts +11 -0
- package/dist/harness/tools/react.d.ts.map +1 -0
- package/dist/harness/tools/react.js +24 -0
- package/dist/harness/tools/react.js.map +1 -0
- package/dist/harness/tools/sandbox.d.ts +24 -0
- package/dist/harness/tools/sandbox.d.ts.map +1 -0
- package/dist/{tools → harness/tools}/sandbox.js +15 -9
- package/dist/harness/tools/sandbox.js.map +1 -0
- package/dist/harness/tools/secret-redaction.d.ts +9 -0
- package/dist/harness/tools/secret-redaction.d.ts.map +1 -0
- package/dist/harness/tools/secret-redaction.js +44 -0
- package/dist/harness/tools/secret-redaction.js.map +1 -0
- package/dist/harness/tools/subagent.d.ts +51 -0
- package/dist/harness/tools/subagent.d.ts.map +1 -0
- package/dist/harness/tools/subagent.js +581 -0
- package/dist/harness/tools/subagent.js.map +1 -0
- package/dist/harness/tools/task.d.ts +22 -0
- package/dist/harness/tools/task.d.ts.map +1 -0
- package/dist/harness/tools/task.js +80 -0
- package/dist/harness/tools/task.js.map +1 -0
- package/dist/harness/tools/types.d.ts +14 -0
- package/dist/harness/tools/types.d.ts.map +1 -0
- package/dist/harness/tools/types.js.map +1 -0
- package/dist/harness/types.d.ts +345 -29
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/index.d.ts +36 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -7
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts +0 -1
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +3 -13
- package/dist/log.js.map +1 -1
- package/dist/main.js +300 -217
- package/dist/main.js.map +1 -1
- package/dist/memory-capture/index.d.ts +15 -0
- package/dist/memory-capture/index.d.ts.map +1 -0
- package/dist/memory-capture/index.js +199 -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 +2 -0
- package/dist/memory-capture/types.js.map +1 -0
- package/dist/observability/index.d.ts +30 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +273 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/instrument.js +9 -6
- package/dist/observability/instrument.js.map +1 -1
- package/dist/observability/otel.d.ts +10 -0
- package/dist/observability/otel.d.ts.map +1 -0
- package/dist/observability/otel.js +182 -0
- package/dist/observability/otel.js.map +1 -0
- package/dist/observability/sentry.d.ts +32 -13
- package/dist/observability/sentry.d.ts.map +1 -1
- package/dist/observability/sentry.js +85 -58
- package/dist/observability/sentry.js.map +1 -1
- package/dist/observability/types.d.ts +30 -3
- package/dist/observability/types.d.ts.map +1 -1
- package/dist/observability/types.js.map +1 -1
- package/dist/office/index.d.ts +74 -0
- package/dist/office/index.d.ts.map +1 -0
- package/dist/office/index.js +971 -0
- package/dist/office/index.js.map +1 -0
- package/dist/office/projection.d.ts +11 -0
- package/dist/office/projection.d.ts.map +1 -0
- package/dist/office/projection.js +146 -0
- package/dist/office/projection.js.map +1 -0
- package/dist/office/types.d.ts +55 -0
- package/dist/office/types.d.ts.map +1 -0
- package/dist/office/types.js +2 -0
- package/dist/office/types.js.map +1 -0
- package/dist/runtime/conversation-runtime.d.ts +0 -1
- package/dist/runtime/conversation-runtime.d.ts.map +1 -1
- package/dist/runtime/conversation-runtime.js +375 -152
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/session-lifecycle.d.ts +66 -0
- package/dist/runtime/session-lifecycle.d.ts.map +1 -0
- package/dist/runtime/session-lifecycle.js +393 -0
- package/dist/runtime/session-lifecycle.js.map +1 -0
- package/dist/runtime/types.d.ts +41 -17
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sandbox/cloudflare.d.ts +4 -0
- package/dist/sandbox/cloudflare.d.ts.map +1 -1
- package/dist/sandbox/cloudflare.js +59 -55
- package/dist/sandbox/cloudflare.js.map +1 -1
- package/dist/sandbox/container.d.ts +3 -0
- package/dist/sandbox/container.d.ts.map +1 -1
- package/dist/sandbox/container.js +39 -20
- package/dist/sandbox/container.js.map +1 -1
- package/dist/sandbox/host.d.ts +3 -0
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +42 -35
- package/dist/sandbox/host.js.map +1 -1
- package/dist/sandbox/identity.d.ts +13 -0
- package/dist/sandbox/identity.d.ts.map +1 -0
- package/dist/sandbox/identity.js +49 -0
- package/dist/sandbox/identity.js.map +1 -0
- package/dist/sandbox/index.d.ts +5 -15
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +5 -51
- 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/{provisioner.d.ts → sandbox/provisioner.d.ts} +41 -4
- package/dist/sandbox/provisioner.d.ts.map +1 -0
- package/dist/sandbox/provisioner.js +971 -0
- package/dist/sandbox/provisioner.js.map +1 -0
- package/dist/sandbox/registry.d.ts +12 -0
- package/dist/sandbox/registry.d.ts.map +1 -0
- package/dist/sandbox/registry.js +93 -0
- package/dist/sandbox/registry.js.map +1 -0
- package/dist/sandbox/types.d.ts +23 -25
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/sandbox/types.js.map +1 -1
- package/dist/sandbox/utils.d.ts +21 -0
- package/dist/sandbox/utils.d.ts.map +1 -1
- package/dist/sandbox/utils.js +100 -8
- package/dist/sandbox/utils.js.map +1 -1
- package/dist/sessions/chat-history-sync.d.ts +17 -0
- package/dist/sessions/chat-history-sync.d.ts.map +1 -0
- package/dist/sessions/{agent-memory-file-manager.js → chat-history-sync.js} +224 -183
- package/dist/sessions/chat-history-sync.js.map +1 -0
- package/dist/sessions/history-line.d.ts +9 -0
- package/dist/sessions/history-line.d.ts.map +1 -0
- package/dist/sessions/history-line.js +25 -0
- package/dist/sessions/history-line.js.map +1 -0
- package/dist/sessions/migrate-common.d.ts +28 -0
- package/dist/sessions/migrate-common.d.ts.map +1 -0
- package/dist/sessions/migrate-common.js +80 -0
- package/dist/sessions/migrate-common.js.map +1 -0
- package/dist/sessions/migrate-pi-084.d.ts +7 -0
- package/dist/sessions/migrate-pi-084.d.ts.map +1 -0
- package/dist/sessions/migrate-pi-084.js +293 -0
- package/dist/sessions/migrate-pi-084.js.map +1 -0
- package/dist/sessions/migrate-v3.d.ts +7 -0
- package/dist/sessions/migrate-v3.d.ts.map +1 -0
- package/dist/sessions/migrate-v3.js +351 -0
- package/dist/sessions/migrate-v3.js.map +1 -0
- package/dist/sessions/session-key.d.ts +18 -0
- package/dist/sessions/session-key.d.ts.map +1 -0
- package/dist/sessions/session-key.js +88 -0
- package/dist/sessions/session-key.js.map +1 -0
- package/dist/sessions/session-store.d.ts +55 -0
- package/dist/sessions/session-store.d.ts.map +1 -0
- package/dist/sessions/session-store.js +635 -0
- package/dist/sessions/session-store.js.map +1 -0
- package/dist/sessions/store.d.ts +7 -61
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +126 -102
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/types.d.ts +59 -8
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/sessions/types.js +2 -1
- package/dist/sessions/types.js.map +1 -1
- package/dist/settings/apply.d.ts +9 -0
- package/dist/settings/apply.d.ts.map +1 -0
- package/dist/settings/apply.js +31 -0
- package/dist/settings/apply.js.map +1 -0
- package/dist/settings/index.d.ts +76 -0
- package/dist/settings/index.d.ts.map +1 -0
- package/dist/settings/index.js +351 -0
- package/dist/settings/index.js.map +1 -0
- package/dist/settings/migrate.d.ts +16 -0
- package/dist/settings/migrate.d.ts.map +1 -0
- package/dist/settings/migrate.js +70 -0
- package/dist/settings/migrate.js.map +1 -0
- package/dist/types.d.ts +231 -173
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -3
- package/dist/types.js.map +1 -1
- package/dist/unknown-values.d.ts +3 -0
- package/dist/unknown-values.d.ts.map +1 -0
- package/dist/unknown-values.js +7 -0
- package/dist/unknown-values.js.map +1 -0
- package/dist/vault/index.d.ts +35 -11
- package/dist/vault/index.d.ts.map +1 -1
- package/dist/vault/index.js +326 -115
- package/dist/vault/index.js.map +1 -1
- package/dist/vault/types.d.ts +5 -16
- package/dist/vault/types.d.ts.map +1 -1
- package/dist/vault/types.js.map +1 -1
- package/package.json +82 -41
- package/dist/adapter.d.ts +0 -2
- package/dist/adapter.d.ts.map +0 -1
- package/dist/adapter.js +0 -2
- package/dist/adapter.js.map +0 -1
- package/dist/adapters/slack/tools/attach.d.ts +0 -12
- package/dist/adapters/slack/tools/attach.d.ts.map +0 -1
- package/dist/adapters/slack/tools/attach.js.map +0 -1
- package/dist/adapters/streaming.d.ts +0 -18
- package/dist/adapters/streaming.d.ts.map +0 -1
- package/dist/adapters/streaming.js +0 -44
- package/dist/adapters/streaming.js.map +0 -1
- package/dist/adapters/telegram/html.d.ts +0 -3
- package/dist/adapters/telegram/html.d.ts.map +0 -1
- package/dist/adapters/telegram/html.js +0 -98
- package/dist/adapters/telegram/html.js.map +0 -1
- package/dist/agent-events.d.ts +0 -5
- package/dist/agent-events.d.ts.map +0 -1
- package/dist/agent-events.js +0 -26
- package/dist/agent-events.js.map +0 -1
- package/dist/agent.d.ts +0 -39
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js +0 -1291
- package/dist/agent.js.map +0 -1
- package/dist/cli/ext-git.d.ts +0 -22
- package/dist/cli/ext-git.d.ts.map +0 -1
- package/dist/cli/ext-git.js +0 -76
- package/dist/cli/ext-git.js.map +0 -1
- package/dist/cli/ext.d.ts +0 -2
- package/dist/cli/ext.d.ts.map +0 -1
- package/dist/cli/ext.js +0 -217
- package/dist/cli/ext.js.map +0 -1
- package/dist/commands/admin.d.ts.map +0 -1
- package/dist/commands/admin.js.map +0 -1
- package/dist/commands/auto-reply.d.ts.map +0 -1
- package/dist/commands/auto-reply.js +0 -76
- package/dist/commands/auto-reply.js.map +0 -1
- package/dist/commands/extensions.d.ts +0 -14
- package/dist/commands/extensions.d.ts.map +0 -1
- package/dist/commands/extensions.js +0 -55
- package/dist/commands/extensions.js.map +0 -1
- package/dist/commands/login.d.ts +0 -5
- package/dist/commands/login.d.ts.map +0 -1
- package/dist/commands/login.js +0 -115
- package/dist/commands/login.js.map +0 -1
- package/dist/commands/model.d.ts +0 -10
- package/dist/commands/model.d.ts.map +0 -1
- package/dist/commands/model.js +0 -102
- package/dist/commands/model.js.map +0 -1
- package/dist/commands/new.d.ts.map +0 -1
- package/dist/commands/new.js +0 -25
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/parse.d.ts +0 -7
- package/dist/commands/parse.d.ts.map +0 -1
- package/dist/commands/parse.js +0 -14
- package/dist/commands/parse.js.map +0 -1
- package/dist/commands/registry.d.ts +0 -5
- package/dist/commands/registry.d.ts.map +0 -1
- package/dist/commands/registry.js +0 -31
- package/dist/commands/registry.js.map +0 -1
- package/dist/commands/sandbox.d.ts.map +0 -1
- package/dist/commands/sandbox.js +0 -78
- package/dist/commands/sandbox.js.map +0 -1
- package/dist/commands/session-view.d.ts.map +0 -1
- package/dist/commands/session-view.js.map +0 -1
- package/dist/commands/types.d.ts +0 -66
- package/dist/commands/types.d.ts.map +0 -1
- package/dist/commands/types.js.map +0 -1
- package/dist/commands/utils.d.ts +0 -11
- package/dist/commands/utils.d.ts.map +0 -1
- package/dist/commands/utils.js.map +0 -1
- package/dist/config.d.ts +0 -78
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -450
- package/dist/config.js.map +0 -1
- package/dist/download.d.ts.map +0 -1
- package/dist/download.js +0 -89
- package/dist/download.js.map +0 -1
- package/dist/events.d.ts +0 -43
- package/dist/events.d.ts.map +0 -1
- package/dist/events.js +0 -456
- package/dist/events.js.map +0 -1
- package/dist/execution-resolver.d.ts +0 -21
- package/dist/execution-resolver.d.ts.map +0 -1
- package/dist/execution-resolver.js +0 -170
- package/dist/execution-resolver.js.map +0 -1
- package/dist/harness/auth.d.ts +0 -16
- package/dist/harness/auth.d.ts.map +0 -1
- package/dist/harness/auth.js +0 -86
- package/dist/harness/auth.js.map +0 -1
- package/dist/harness/extensions/loader.d.ts +0 -86
- package/dist/harness/extensions/loader.d.ts.map +0 -1
- package/dist/harness/extensions/loader.js +0 -525
- package/dist/harness/extensions/loader.js.map +0 -1
- package/dist/harness/extensions/registry.d.ts +0 -23
- package/dist/harness/extensions/registry.d.ts.map +0 -1
- package/dist/harness/extensions/registry.js +0 -45
- package/dist/harness/extensions/registry.js.map +0 -1
- package/dist/harness/extensions/types.d.ts +0 -248
- package/dist/harness/extensions/types.d.ts.map +0 -1
- package/dist/harness/extensions/types.js.map +0 -1
- package/dist/harness/session-store.d.ts +0 -77
- package/dist/harness/session-store.d.ts.map +0 -1
- package/dist/harness/session-store.js +0 -248
- package/dist/harness/session-store.js.map +0 -1
- package/dist/harness/settings.d.ts +0 -47
- package/dist/harness/settings.d.ts.map +0 -1
- package/dist/harness/settings.js +0 -29
- package/dist/harness/settings.js.map +0 -1
- package/dist/model-registry.d.ts +0 -4
- package/dist/model-registry.d.ts.map +0 -1
- package/dist/model-registry.js +0 -7
- package/dist/model-registry.js.map +0 -1
- package/dist/platform-messages.d.ts.map +0 -1
- package/dist/platform-messages.js.map +0 -1
- package/dist/portal-shell.d.ts +0 -4
- package/dist/portal-shell.d.ts.map +0 -1
- package/dist/portal-shell.js.map +0 -1
- package/dist/provisioner.d.ts.map +0 -1
- package/dist/provisioner.js +0 -517
- package/dist/provisioner.js.map +0 -1
- package/dist/runtime/agent-run-controller.d.ts +0 -31
- package/dist/runtime/agent-run-controller.d.ts.map +0 -1
- package/dist/runtime/agent-run-controller.js +0 -195
- package/dist/runtime/agent-run-controller.js.map +0 -1
- package/dist/sandbox/errors.d.ts +0 -6
- package/dist/sandbox/errors.d.ts.map +0 -1
- package/dist/sandbox/errors.js +0 -11
- package/dist/sandbox/errors.js.map +0 -1
- package/dist/sandbox/firecracker.d.ts +0 -15
- package/dist/sandbox/firecracker.d.ts.map +0 -1
- package/dist/sandbox/firecracker.js +0 -212
- package/dist/sandbox/firecracker.js.map +0 -1
- package/dist/sandbox/image.d.ts +0 -3
- package/dist/sandbox/image.d.ts.map +0 -1
- package/dist/sandbox/image.js +0 -27
- package/dist/sandbox/image.js.map +0 -1
- package/dist/sandbox/path-context.d.ts +0 -3
- package/dist/sandbox/path-context.d.ts.map +0 -1
- package/dist/sandbox/path-context.js +0 -20
- package/dist/sandbox/path-context.js.map +0 -1
- package/dist/sessions/agent-memory-file-manager.d.ts +0 -19
- package/dist/sessions/agent-memory-file-manager.d.ts.map +0 -1
- package/dist/sessions/agent-memory-file-manager.js.map +0 -1
- package/dist/sessions/metadata.d.ts +0 -3
- package/dist/sessions/metadata.d.ts.map +0 -1
- package/dist/sessions/metadata.js +0 -11
- package/dist/sessions/metadata.js.map +0 -1
- package/dist/sessions/policy.d.ts +0 -6
- package/dist/sessions/policy.d.ts.map +0 -1
- package/dist/sessions/policy.js +0 -24
- package/dist/sessions/policy.js.map +0 -1
- package/dist/store.d.ts +0 -46
- package/dist/store.d.ts.map +0 -1
- package/dist/store.js +0 -177
- package/dist/store.js.map +0 -1
- package/dist/tools/bash.d.ts +0 -10
- package/dist/tools/bash.d.ts.map +0 -1
- package/dist/tools/bash.js +0 -80
- package/dist/tools/bash.js.map +0 -1
- package/dist/tools/edit.d.ts +0 -11
- package/dist/tools/edit.d.ts.map +0 -1
- package/dist/tools/edit.js +0 -131
- package/dist/tools/edit.js.map +0 -1
- package/dist/tools/event.d.ts +0 -56
- package/dist/tools/event.d.ts.map +0 -1
- package/dist/tools/event.js +0 -252
- package/dist/tools/event.js.map +0 -1
- package/dist/tools/index.d.ts +0 -24
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js +0 -31
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/react.d.ts +0 -15
- package/dist/tools/react.d.ts.map +0 -1
- package/dist/tools/react.js +0 -40
- package/dist/tools/react.js.map +0 -1
- package/dist/tools/read.d.ts +0 -11
- package/dist/tools/read.d.ts.map +0 -1
- package/dist/tools/read.js +0 -134
- package/dist/tools/read.js.map +0 -1
- package/dist/tools/sandbox.d.ts +0 -22
- package/dist/tools/sandbox.d.ts.map +0 -1
- package/dist/tools/sandbox.js.map +0 -1
- package/dist/tools/truncate.d.ts +0 -33
- package/dist/tools/truncate.d.ts.map +0 -1
- package/dist/tools/truncate.js +0 -184
- package/dist/tools/truncate.js.map +0 -1
- package/dist/tools/types.d.ts +0 -54
- package/dist/tools/types.d.ts.map +0 -1
- package/dist/tools/types.js.map +0 -1
- package/dist/tools/write.d.ts +0 -10
- package/dist/tools/write.d.ts.map +0 -1
- package/dist/tools/write.js +0 -29
- package/dist/tools/write.js.map +0 -1
- package/dist/trigger.d.ts +0 -22
- package/dist/trigger.d.ts.map +0 -1
- package/dist/trigger.js +0 -100
- package/dist/trigger.js.map +0 -1
- package/dist/utils/date.d.ts +0 -10
- package/dist/utils/date.d.ts.map +0 -1
- package/dist/utils/date.js +0 -23
- package/dist/utils/date.js.map +0 -1
- package/dist/utils/env.d.ts +0 -3
- package/dist/utils/env.d.ts.map +0 -1
- package/dist/utils/env.js +0 -12
- package/dist/utils/env.js.map +0 -1
- package/dist/utils/file-guards.d.ts +0 -9
- package/dist/utils/file-guards.d.ts.map +0 -1
- package/dist/utils/file-guards.js +0 -59
- package/dist/utils/file-guards.js.map +0 -1
- package/dist/utils/fs-atomic.d.ts +0 -10
- package/dist/utils/fs-atomic.d.ts.map +0 -1
- package/dist/utils/fs-atomic.js +0 -45
- package/dist/utils/fs-atomic.js.map +0 -1
- package/dist/utils/html.d.ts +0 -2
- package/dist/utils/html.d.ts.map +0 -1
- package/dist/utils/html.js +0 -4
- package/dist/utils/html.js.map +0 -1
- package/dist/utils/http-body.d.ts +0 -10
- package/dist/utils/http-body.d.ts.map +0 -1
- package/dist/utils/http-body.js +0 -34
- package/dist/utils/http-body.js.map +0 -1
- package/dist/vault/routing.d.ts +0 -3
- package/dist/vault/routing.d.ts.map +0 -1
- package/dist/vault/routing.js +0 -13
- package/dist/vault/routing.js.map +0 -1
- package/dist/web/admin/portal.d.ts +0 -5
- package/dist/web/admin/portal.d.ts.map +0 -1
- package/dist/web/admin/portal.js +0 -2572
- package/dist/web/admin/portal.js.map +0 -1
- package/dist/web/admin/provider-models.d.ts.map +0 -1
- package/dist/web/admin/provider-models.js.map +0 -1
- package/dist/web/admin/store.d.ts +0 -13
- package/dist/web/admin/store.d.ts.map +0 -1
- package/dist/web/admin/store.js +0 -14
- package/dist/web/admin/store.js.map +0 -1
- package/dist/web/admin/types.d.ts +0 -30
- package/dist/web/admin/types.d.ts.map +0 -1
- package/dist/web/admin/types.js.map +0 -1
- package/dist/web/agent-events/portal.d.ts +0 -3
- package/dist/web/agent-events/portal.d.ts.map +0 -1
- package/dist/web/agent-events/portal.js +0 -18
- package/dist/web/agent-events/portal.js.map +0 -1
- package/dist/web/login/oauth.d.ts +0 -6
- package/dist/web/login/oauth.d.ts.map +0 -1
- package/dist/web/login/oauth.js.map +0 -1
- package/dist/web/login/portal.d.ts +0 -17
- package/dist/web/login/portal.d.ts.map +0 -1
- package/dist/web/login/portal.js.map +0 -1
- package/dist/web/login/store.d.ts +0 -8
- package/dist/web/login/store.d.ts.map +0 -1
- package/dist/web/login/store.js +0 -15
- package/dist/web/login/store.js.map +0 -1
- package/dist/web/login/types.d.ts.map +0 -1
- package/dist/web/login/types.js.map +0 -1
- package/dist/web/request.d.ts +0 -3
- package/dist/web/request.d.ts.map +0 -1
- package/dist/web/request.js +0 -13
- package/dist/web/request.js.map +0 -1
- package/dist/web/server.d.ts +0 -28
- package/dist/web/server.d.ts.map +0 -1
- package/dist/web/server.js.map +0 -1
- package/dist/web/session-view/command.d.ts +0 -4
- package/dist/web/session-view/command.d.ts.map +0 -1
- package/dist/web/session-view/command.js +0 -7
- package/dist/web/session-view/command.js.map +0 -1
- package/dist/web/session-view/portal.d.ts +0 -13
- package/dist/web/session-view/portal.d.ts.map +0 -1
- package/dist/web/session-view/portal.js.map +0 -1
- package/dist/web/session-view/service.d.ts +0 -6
- package/dist/web/session-view/service.d.ts.map +0 -1
- package/dist/web/session-view/service.js +0 -498
- package/dist/web/session-view/service.js.map +0 -1
- package/dist/web/session-view/store.d.ts +0 -8
- package/dist/web/session-view/store.d.ts.map +0 -1
- package/dist/web/session-view/store.js +0 -15
- package/dist/web/session-view/store.js.map +0 -1
- package/dist/web/session-view/types.d.ts.map +0 -1
- package/dist/web/session-view/types.js.map +0 -1
- package/dist/web/token-store.d.ts.map +0 -1
- package/dist/web/token-store.js.map +0 -1
- package/dist/web/types.d.ts.map +0 -1
- package/dist/web/types.js.map +0 -1
- /package/dist/{commands → adapters/commands}/admin.d.ts +0 -0
- /package/dist/{commands → adapters/commands}/new.d.ts +0 -0
- /package/dist/{commands → adapters/commands}/session-view.d.ts +0 -0
- /package/dist/{commands → adapters/commands}/types.js +0 -0
- /package/dist/{harness/extensions → adapters/web/admin}/types.js +0 -0
- /package/dist/{tools → adapters/web/login}/types.js +0 -0
- /package/dist/{web/admin → adapters/web/session-view}/types.js +0 -0
- /package/dist/{web → adapters/web}/types.d.ts +0 -0
- /package/dist/{web/login → adapters/web}/types.js +0 -0
- /package/dist/{download.d.ts → cli/download.d.ts} +0 -0
- /package/dist/{web/session-view → cli}/types.js +0 -0
- /package/dist/{web → harness/tools}/types.js +0 -0
|
@@ -0,0 +1,1506 @@
|
|
|
1
|
+
import { validateEventFilename } from "../../../events/index.js";
|
|
2
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, } from "node:fs";
|
|
3
|
+
import { basename, join, resolve as pathResolve, sep as pathSep } from "node:path";
|
|
4
|
+
import { atomicWritePrivateFile } from "../../../file-guards.js";
|
|
5
|
+
import { MikanModels } from "../../../harness/models.js";
|
|
6
|
+
import { parseFrontmatter, validateSkill } from "../../../harness/skills.js";
|
|
7
|
+
import { SessionStore } from "../../../sessions/session-store.js";
|
|
8
|
+
import { InMemoryTokenStore } from "../token-store.js";
|
|
9
|
+
import { adminViewFunctionsScript, adminViewStartupScript, adminViewStyles, } from "./client-assets.js";
|
|
10
|
+
const ADMIN_TOKEN_TTL_MS = 30 * 60 * 1000;
|
|
11
|
+
export class InMemoryAdminTokenStore extends InMemoryTokenStore {
|
|
12
|
+
create(args) {
|
|
13
|
+
this.deleteWhere((token) => token.platform === args.platform && token.platformUserId === args.platformUserId);
|
|
14
|
+
return this.createRecord(ADMIN_TOKEN_TTL_MS, {
|
|
15
|
+
platform: args.platform,
|
|
16
|
+
platformUserId: args.platformUserId,
|
|
17
|
+
platformUserName: args.platformUserName || undefined,
|
|
18
|
+
conversationId: args.conversationId,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
import { isThinkingLevel, loadOfficeVisibilityOverride, loadGlobalSettings, loadScopeMcpServers, resolveConversationSettings, } from "../../../settings/index.js";
|
|
23
|
+
import { findMcpPreset, listMcpPresets, materializeMcpPreset } from "../../../harness/mcp.js";
|
|
24
|
+
import { loadMcpTools } from "../../../harness/mcp.js";
|
|
25
|
+
import { isValidMcpServerName, parseStandardMcpServers, redactMcpUrl, } from "../../../harness/mcp.js";
|
|
26
|
+
import { applyConversationSettings, applyOfficeVisibility, applyGlobalSettings, } from "../../../settings/apply.js";
|
|
27
|
+
import { escapeHtml, jsonResponse as jsonRes, readJsonBody, renderPortalShell, } from "../portal-shell.js";
|
|
28
|
+
import { resolveExistingSessionFile } from "../session-view/portal.js";
|
|
29
|
+
import { PRODUCT_NAME } from "../../messages.js";
|
|
30
|
+
import { credentialAuthorizationKey } from "../../../sandbox/identity.js";
|
|
31
|
+
import { resolveWorkspaceProjection } from "../../../office/projection.js";
|
|
32
|
+
import { sharedVaultKey } from "../../../vault/index.js";
|
|
33
|
+
import { modelKey, resolveAdminModelAccessStatuses } from "./provider-models.js";
|
|
34
|
+
import { assertPlatformName, createOfficeAddress, listRegisteredOffices, sameOffice, } from "../../../office/index.js";
|
|
35
|
+
import { errorMessage } from "../../../unknown-values.js";
|
|
36
|
+
export async function handleAdminRequest(req, res, url, services) {
|
|
37
|
+
if (!url.pathname.startsWith("/admin"))
|
|
38
|
+
return false;
|
|
39
|
+
if (req.method === "GET" && url.pathname === "/admin") {
|
|
40
|
+
const provided = url.searchParams.get("token") ?? "";
|
|
41
|
+
const token = services.adminTokenStore.peek(provided);
|
|
42
|
+
if (!token) {
|
|
43
|
+
res.writeHead(403, { "Content-Type": "text/html; charset=utf-8" });
|
|
44
|
+
res.end(renderAdminErrorPage("Admin link is missing, invalid, or expired. Send `/admin` to the bot to get a fresh link."));
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
res.writeHead(200, {
|
|
48
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
49
|
+
"Cache-Control": "no-store",
|
|
50
|
+
});
|
|
51
|
+
res.end(renderAdminPage(token));
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (url.pathname.startsWith("/admin/api/")) {
|
|
55
|
+
await routeApiRequest(req, res, url, services);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
async function routeApiRequest(req, res, url, services) {
|
|
61
|
+
if (req.method === "GET") {
|
|
62
|
+
await routeGetApiRequest(res, url, services);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (req.method !== "POST") {
|
|
66
|
+
jsonRes(res, 405, { error: "Method not allowed" });
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
await routePostApiRequest(req, res, url, services);
|
|
70
|
+
}
|
|
71
|
+
async function routeGetApiRequest(res, url, services) {
|
|
72
|
+
const token = services.adminTokenStore.peek(url.searchParams.get("token") ?? "");
|
|
73
|
+
if (!token) {
|
|
74
|
+
jsonRes(res, 403, { error: "Unauthorized" });
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
switch (url.pathname) {
|
|
78
|
+
case "/admin/api/conversations":
|
|
79
|
+
return serveConversationsList(res, services);
|
|
80
|
+
case "/admin/api/session-usage":
|
|
81
|
+
return serveSessionUsage(res, services);
|
|
82
|
+
case "/admin/api/conversation-usage":
|
|
83
|
+
return serveConversationUsage(res, url, services);
|
|
84
|
+
case "/admin/api/conversation-state":
|
|
85
|
+
return serveConversationState(res, url, services, token);
|
|
86
|
+
case "/admin/api/settings/global":
|
|
87
|
+
return serveGlobalSettings(res);
|
|
88
|
+
case "/admin/api/models":
|
|
89
|
+
return serveModelsList(res);
|
|
90
|
+
case "/admin/api/workspace/tree":
|
|
91
|
+
return serveWorkspaceTree(res, url, services, token);
|
|
92
|
+
case "/admin/api/workspace/file":
|
|
93
|
+
return serveWorkspaceFile(res, url, services, token);
|
|
94
|
+
case "/admin/api/skills":
|
|
95
|
+
return serveSkillsList(res, url, services, token);
|
|
96
|
+
case "/admin/api/skills/file":
|
|
97
|
+
return serveSkillFile(res, url, services, token);
|
|
98
|
+
case "/admin/api/mcp-servers":
|
|
99
|
+
return serveMcpServersList(res, url, services, token);
|
|
100
|
+
case "/admin/api/events":
|
|
101
|
+
return serveEventsList(res, services);
|
|
102
|
+
case "/admin/api/conversations/events":
|
|
103
|
+
return serveConversationEventsList(res, url, services, token);
|
|
104
|
+
default:
|
|
105
|
+
jsonRes(res, 404, { error: "Not found" });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function routePostApiRequest(req, res, url, services) {
|
|
109
|
+
const bodyLimit = url.pathname === "/admin/api/skills/mutate" ? 256 * 1024 : 32 * 1024;
|
|
110
|
+
const body = await readJsonBody(req, res, bodyLimit);
|
|
111
|
+
if (!body)
|
|
112
|
+
return;
|
|
113
|
+
const token = services.adminTokenStore.peek(typeof body.token === "string" ? body.token : "");
|
|
114
|
+
if (!token) {
|
|
115
|
+
jsonRes(res, 403, { error: "Unauthorized" });
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
switch (url.pathname) {
|
|
119
|
+
case "/admin/api/conversations/model":
|
|
120
|
+
return serveConversationModelUpdate(res, body, services, token);
|
|
121
|
+
case "/admin/api/conversations/visibility":
|
|
122
|
+
return serveConversationVisibilityUpdate(res, body, services, token);
|
|
123
|
+
case "/admin/api/conversations/slack":
|
|
124
|
+
return serveConversationSlackUpdate(res, body, services, token);
|
|
125
|
+
case "/admin/api/conversations/session-link":
|
|
126
|
+
return serveConversationSessionLink(res, body, services, token);
|
|
127
|
+
case "/admin/api/conversations/login-link":
|
|
128
|
+
return serveConversationLoginLink(res, body, services, token);
|
|
129
|
+
case "/admin/api/conversations/events/delete":
|
|
130
|
+
return serveConversationEventDelete(res, body, services, token);
|
|
131
|
+
case "/admin/api/mcp-servers/mutate":
|
|
132
|
+
return serveMcpServerMutation(res, body, services, token);
|
|
133
|
+
case "/admin/api/skills/mutate":
|
|
134
|
+
return serveSkillMutation(res, body, services, token);
|
|
135
|
+
case "/admin/api/settings/model":
|
|
136
|
+
return serveGlobalModelUpdate(res, body, services);
|
|
137
|
+
case "/admin/api/settings/sandbox":
|
|
138
|
+
return serveGlobalSandboxUpdate(res, body, services);
|
|
139
|
+
case "/admin/api/settings/slack":
|
|
140
|
+
return serveGlobalSlackUpdate(res, body, services);
|
|
141
|
+
default:
|
|
142
|
+
jsonRes(res, 404, { error: "Not found" });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
export function resolveConversationScope(requestedId, requestedPlatform, token) {
|
|
146
|
+
const fallback = createOfficeAddress(token.platform, token.conversationId);
|
|
147
|
+
try {
|
|
148
|
+
const address = createOfficeAddress(requestedPlatform ? assertPlatformName(requestedPlatform) : token.platform, requestedId || token.conversationId);
|
|
149
|
+
return { address, conversationId: address.conversationId };
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return {
|
|
153
|
+
address: fallback,
|
|
154
|
+
conversationId: fallback.conversationId,
|
|
155
|
+
error: "Invalid conversation scope.",
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function resolveTargetConversation(body, token) {
|
|
160
|
+
const requested = typeof body.conversationId === "string" ? body.conversationId.trim() : "";
|
|
161
|
+
const platform = typeof body.platform === "string" ? body.platform.trim() : "";
|
|
162
|
+
return resolveConversationScope(requested, platform, token);
|
|
163
|
+
}
|
|
164
|
+
function requireAdminWorkspace(res, services) {
|
|
165
|
+
if (!services.workspace) {
|
|
166
|
+
jsonRes(res, 503, { error: "Working directory not available" });
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
return services.workspace;
|
|
170
|
+
}
|
|
171
|
+
function requireConversationWorkspace(res, body, services, token) {
|
|
172
|
+
const scope = resolveTargetConversation(body, token);
|
|
173
|
+
if (scope.error) {
|
|
174
|
+
jsonRes(res, 403, { error: scope.error });
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
178
|
+
return workspace ? { scope, workspace } : undefined;
|
|
179
|
+
}
|
|
180
|
+
function listAdminOffices(workspace) {
|
|
181
|
+
return listRegisteredOffices(workspace.stateDir)
|
|
182
|
+
.filter((office) => existsSync(workspace.office(office).dir))
|
|
183
|
+
.map((office) => createOfficeAddress(office.platform, office.conversationId))
|
|
184
|
+
.toSorted((a, b) => a.platform.localeCompare(b.platform) || a.conversationId.localeCompare(b.conversationId));
|
|
185
|
+
}
|
|
186
|
+
function conversationLastActivity(workspace, office) {
|
|
187
|
+
const dir = workspace.office(office).dir;
|
|
188
|
+
if (!existsSync(dir))
|
|
189
|
+
return null;
|
|
190
|
+
let latest = 0;
|
|
191
|
+
const visit = (path, depth) => {
|
|
192
|
+
if (depth > 3)
|
|
193
|
+
return;
|
|
194
|
+
let entries;
|
|
195
|
+
try {
|
|
196
|
+
entries = readdirSync(path, { withFileTypes: true });
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
for (const entry of entries) {
|
|
202
|
+
const full = join(path, entry.name);
|
|
203
|
+
if (entry.isDirectory()) {
|
|
204
|
+
visit(full, depth + 1);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
const stats = statSync(full);
|
|
209
|
+
if (stats.mtimeMs > latest)
|
|
210
|
+
latest = stats.mtimeMs;
|
|
211
|
+
}
|
|
212
|
+
catch { }
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
visit(dir, 0);
|
|
216
|
+
return latest > 0 ? latest : null;
|
|
217
|
+
}
|
|
218
|
+
function conversationDisplayLabel(services, office) {
|
|
219
|
+
const bot = services.botsByPlatform?.[office.platform];
|
|
220
|
+
const channel = bot?.getMessagingInfo().channels.find((c) => c.id === office.conversationId);
|
|
221
|
+
if (channel)
|
|
222
|
+
return `${office.platform}:#${channel.name}:${office.conversationId}`;
|
|
223
|
+
return `${office.platform}:${office.conversationId}`;
|
|
224
|
+
}
|
|
225
|
+
function serveConversationsList(res, services) {
|
|
226
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
227
|
+
if (!workspace)
|
|
228
|
+
return;
|
|
229
|
+
const running = services.runtime?.getRunningSessions() ?? [];
|
|
230
|
+
const conversations = listAdminOffices(workspace).map((office) => ({
|
|
231
|
+
platform: office.platform,
|
|
232
|
+
conversationId: office.conversationId,
|
|
233
|
+
label: conversationDisplayLabel(services, office),
|
|
234
|
+
running: running.some((session) => sameOffice(session.address, office)),
|
|
235
|
+
lastActivityAt: conversationLastActivity(workspace, office),
|
|
236
|
+
}));
|
|
237
|
+
jsonRes(res, 200, { conversations });
|
|
238
|
+
}
|
|
239
|
+
async function serveSessionUsage(res, services) {
|
|
240
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
241
|
+
if (!workspace)
|
|
242
|
+
return;
|
|
243
|
+
const usageLists = [];
|
|
244
|
+
for (const office of listAdminOffices(workspace)) {
|
|
245
|
+
usageLists.push(await listConversationSessionUsage(workspace, office, conversationDisplayLabel(services, office)));
|
|
246
|
+
}
|
|
247
|
+
const rows = usageLists
|
|
248
|
+
.flat()
|
|
249
|
+
.toSorted((a, b) => b.total - a.total)
|
|
250
|
+
.slice(0, 20);
|
|
251
|
+
jsonRes(res, 200, { sessions: rows });
|
|
252
|
+
}
|
|
253
|
+
async function listConversationSessionUsage(workspace, office, label) {
|
|
254
|
+
const sessionDir = workspace.office(office).sessionsDir;
|
|
255
|
+
let files;
|
|
256
|
+
try {
|
|
257
|
+
files = readdirSync(sessionDir, { withFileTypes: true })
|
|
258
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl"))
|
|
259
|
+
.map((entry) => entry.name);
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
const rows = [];
|
|
265
|
+
for (const name of files) {
|
|
266
|
+
rows.push(...(await readSessionUsage(join(sessionDir, name), office.conversationId, label)));
|
|
267
|
+
}
|
|
268
|
+
return rows;
|
|
269
|
+
}
|
|
270
|
+
async function readSessionUsage(sessionFile, conversationId, label) {
|
|
271
|
+
try {
|
|
272
|
+
const manager = await SessionStore.inspect(sessionFile);
|
|
273
|
+
const header = manager.getHeader();
|
|
274
|
+
const entries = await manager.getEntries();
|
|
275
|
+
const usage = entries.reduce((sum, entry) => {
|
|
276
|
+
if (entry.type !== "message" || entry.message.role !== "assistant")
|
|
277
|
+
return sum;
|
|
278
|
+
const message = entry.message;
|
|
279
|
+
const item = message.usage;
|
|
280
|
+
if (!item)
|
|
281
|
+
return sum;
|
|
282
|
+
sum.input += numberOrZero(item.input);
|
|
283
|
+
sum.output += numberOrZero(item.output);
|
|
284
|
+
sum.cacheRead += numberOrZero(item.cacheRead);
|
|
285
|
+
sum.cacheWrite += numberOrZero(item.cacheWrite);
|
|
286
|
+
sum.cost += numberOrZero(item.cost?.total);
|
|
287
|
+
return sum;
|
|
288
|
+
}, { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 });
|
|
289
|
+
const total = usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
290
|
+
if (total <= 0)
|
|
291
|
+
return [];
|
|
292
|
+
return [
|
|
293
|
+
{
|
|
294
|
+
conversationId,
|
|
295
|
+
label,
|
|
296
|
+
fileName: basename(sessionFile),
|
|
297
|
+
sessionId: header.id,
|
|
298
|
+
updatedAt: entries.length > 0 ? new Date(entries.at(-1).timestamp).toISOString() : header.timestamp,
|
|
299
|
+
...usage,
|
|
300
|
+
total,
|
|
301
|
+
},
|
|
302
|
+
];
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
return [];
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function numberOrZero(value) {
|
|
309
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
310
|
+
}
|
|
311
|
+
function localDayKey(d) {
|
|
312
|
+
const year = d.getFullYear();
|
|
313
|
+
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
314
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
315
|
+
return `${year}-${month}-${day}`;
|
|
316
|
+
}
|
|
317
|
+
function emptyBucket(date) {
|
|
318
|
+
return { date, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0, cost: 0 };
|
|
319
|
+
}
|
|
320
|
+
async function serveConversationUsage(res, url, services) {
|
|
321
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
322
|
+
if (!workspace)
|
|
323
|
+
return;
|
|
324
|
+
const conversationId = (url.searchParams.get("conversationId") ?? "").trim();
|
|
325
|
+
const platformParam = (url.searchParams.get("platform") ?? "").trim();
|
|
326
|
+
const office = listAdminOffices(workspace).find((candidate) => candidate.conversationId === conversationId &&
|
|
327
|
+
(!platformParam || candidate.platform === platformParam));
|
|
328
|
+
if (!office) {
|
|
329
|
+
jsonRes(res, 400, { error: "Unknown conversationId" });
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
const days = 14;
|
|
333
|
+
const today = new Date();
|
|
334
|
+
today.setHours(0, 0, 0, 0);
|
|
335
|
+
const buckets = new Map();
|
|
336
|
+
const order = [];
|
|
337
|
+
for (let i = days - 1; i >= 0; i--) {
|
|
338
|
+
const d = new Date(today);
|
|
339
|
+
d.setDate(today.getDate() - i);
|
|
340
|
+
const key = localDayKey(d);
|
|
341
|
+
order.push(key);
|
|
342
|
+
buckets.set(key, emptyBucket(key));
|
|
343
|
+
}
|
|
344
|
+
const cutoff = new Date(today);
|
|
345
|
+
cutoff.setDate(today.getDate() - (days - 1));
|
|
346
|
+
const flags = { hasOlder: false };
|
|
347
|
+
const sessionDir = workspace.office(office).sessionsDir;
|
|
348
|
+
try {
|
|
349
|
+
for (const entry of readdirSync(sessionDir, { withFileTypes: true })) {
|
|
350
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl"))
|
|
351
|
+
continue;
|
|
352
|
+
await accumulateSessionUsageByDay(join(sessionDir, entry.name), cutoff, buckets, flags);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
catch { }
|
|
356
|
+
const series = order.map((key) => buckets.get(key) ?? emptyBucket(key));
|
|
357
|
+
const totals = series.reduce((sum, b) => {
|
|
358
|
+
sum.input += b.input;
|
|
359
|
+
sum.output += b.output;
|
|
360
|
+
sum.cacheRead += b.cacheRead;
|
|
361
|
+
sum.cacheWrite += b.cacheWrite;
|
|
362
|
+
sum.total += b.total;
|
|
363
|
+
sum.cost += b.cost;
|
|
364
|
+
return sum;
|
|
365
|
+
}, emptyBucket(""));
|
|
366
|
+
jsonRes(res, 200, {
|
|
367
|
+
platform: office.platform,
|
|
368
|
+
conversationId,
|
|
369
|
+
label: conversationDisplayLabel(services, office),
|
|
370
|
+
days,
|
|
371
|
+
hasOlder: flags.hasOlder,
|
|
372
|
+
buckets: series,
|
|
373
|
+
totals: {
|
|
374
|
+
input: totals.input,
|
|
375
|
+
output: totals.output,
|
|
376
|
+
cacheRead: totals.cacheRead,
|
|
377
|
+
cacheWrite: totals.cacheWrite,
|
|
378
|
+
total: totals.total,
|
|
379
|
+
cost: totals.cost,
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
async function accumulateSessionUsageByDay(sessionFile, cutoff, buckets, flags) {
|
|
384
|
+
try {
|
|
385
|
+
const manager = await SessionStore.inspect(sessionFile);
|
|
386
|
+
for (const entry of await manager.getEntries()) {
|
|
387
|
+
if (entry.type !== "message" || entry.message.role !== "assistant")
|
|
388
|
+
continue;
|
|
389
|
+
const usage = entry.message.usage;
|
|
390
|
+
if (!usage || !entry.timestamp)
|
|
391
|
+
continue;
|
|
392
|
+
const when = new Date(entry.timestamp);
|
|
393
|
+
if (Number.isNaN(when.getTime()))
|
|
394
|
+
continue;
|
|
395
|
+
if (when < cutoff) {
|
|
396
|
+
flags.hasOlder = true;
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const bucket = buckets.get(localDayKey(when));
|
|
400
|
+
if (!bucket)
|
|
401
|
+
continue;
|
|
402
|
+
const input = numberOrZero(usage.input);
|
|
403
|
+
const output = numberOrZero(usage.output);
|
|
404
|
+
const cacheRead = numberOrZero(usage.cacheRead);
|
|
405
|
+
const cacheWrite = numberOrZero(usage.cacheWrite);
|
|
406
|
+
bucket.input += input;
|
|
407
|
+
bucket.output += output;
|
|
408
|
+
bucket.cacheRead += cacheRead;
|
|
409
|
+
bucket.cacheWrite += cacheWrite;
|
|
410
|
+
bucket.total += input + output + cacheRead + cacheWrite;
|
|
411
|
+
bucket.cost += numberOrZero(usage.cost?.total);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
catch { }
|
|
415
|
+
}
|
|
416
|
+
function serveConversationState(res, url, services, token) {
|
|
417
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
418
|
+
if (!workspace)
|
|
419
|
+
return;
|
|
420
|
+
const scope = resolveConversationFromQuery(url, token);
|
|
421
|
+
if (scope.error) {
|
|
422
|
+
jsonRes(res, 400, { error: scope.error });
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
const conversationId = scope.conversationId;
|
|
426
|
+
const office = workspace.office(scope.address);
|
|
427
|
+
const globalConfig = loadGlobalSettings();
|
|
428
|
+
const conversationConfig = resolveConversationSettings(office);
|
|
429
|
+
const conversationWorkspace = resolveWorkspaceProjection(office);
|
|
430
|
+
jsonRes(res, 200, {
|
|
431
|
+
conversationId,
|
|
432
|
+
provider: conversationConfig.provider,
|
|
433
|
+
model: conversationConfig.model,
|
|
434
|
+
thinkingLevel: conversationConfig.thinkingLevel,
|
|
435
|
+
globalProvider: globalConfig.provider,
|
|
436
|
+
globalModel: globalConfig.model,
|
|
437
|
+
globalThinkingLevel: globalConfig.thinkingLevel,
|
|
438
|
+
officeVisibility: conversationWorkspace.visibility,
|
|
439
|
+
officeVisibilitySource: conversationWorkspace.source,
|
|
440
|
+
officeVisibilityOverride: loadOfficeVisibilityOverride(office),
|
|
441
|
+
slack: {
|
|
442
|
+
replyMode: conversationConfig.slack?.replyMode ?? globalConfig.slack?.replyMode ?? "top-level",
|
|
443
|
+
globalReplyMode: globalConfig.slack?.replyMode ?? "top-level",
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
function serveGlobalSettings(res) {
|
|
448
|
+
try {
|
|
449
|
+
const config = loadGlobalSettings();
|
|
450
|
+
jsonRes(res, 200, {
|
|
451
|
+
provider: config.provider,
|
|
452
|
+
model: config.model,
|
|
453
|
+
thinkingLevel: config.thinkingLevel,
|
|
454
|
+
sandboxCpus: config.sandbox?.cpus ?? null,
|
|
455
|
+
sandboxMemory: config.sandbox?.memory ?? null,
|
|
456
|
+
sandboxBoostCpus: config.sandbox?.boost?.cpus ?? null,
|
|
457
|
+
sandboxBoostMemory: config.sandbox?.boost?.memory ?? null,
|
|
458
|
+
defaultSharedVault: config.sandbox?.defaultSharedVault ?? null,
|
|
459
|
+
slack: {
|
|
460
|
+
replyMode: config.slack?.replyMode ?? "top-level",
|
|
461
|
+
},
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
catch (err) {
|
|
465
|
+
jsonRes(res, 500, { error: errorMessage(err) });
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
async function serveModelsList(res) {
|
|
469
|
+
try {
|
|
470
|
+
const registry = MikanModels.create();
|
|
471
|
+
const availableModels = await registry.getAvailable();
|
|
472
|
+
const statuses = await resolveAdminModelAccessStatuses(registry, availableModels);
|
|
473
|
+
const models = availableModels.map((model) => ({
|
|
474
|
+
provider: model.provider,
|
|
475
|
+
id: model.id,
|
|
476
|
+
name: model.name ?? model.id,
|
|
477
|
+
reasoning: model.reasoning,
|
|
478
|
+
input: model.input,
|
|
479
|
+
contextWindow: model.contextWindow,
|
|
480
|
+
maxTokens: model.maxTokens,
|
|
481
|
+
status: statuses.get(modelKey(model.provider, model.id))?.status ?? "available",
|
|
482
|
+
}));
|
|
483
|
+
jsonRes(res, 200, { models });
|
|
484
|
+
}
|
|
485
|
+
catch (err) {
|
|
486
|
+
jsonRes(res, 500, { error: errorMessage(err) });
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
function serveConversationModelUpdate(res, body, services, token) {
|
|
490
|
+
const provider = typeof body.provider === "string" ? body.provider.trim() : "";
|
|
491
|
+
const model = typeof body.model === "string" ? body.model.trim() : "";
|
|
492
|
+
const thinkingLevel = isThinkingLevel(body.thinkingLevel) ? body.thinkingLevel : undefined;
|
|
493
|
+
if (!provider || !model) {
|
|
494
|
+
jsonRes(res, 400, { error: "Missing provider or model" });
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const target = requireConversationWorkspace(res, body, services, token);
|
|
498
|
+
if (!target)
|
|
499
|
+
return;
|
|
500
|
+
const { scope, workspace } = target;
|
|
501
|
+
try {
|
|
502
|
+
const result = applyConversationSettings(services.runtime, workspace.office(scope.address), {
|
|
503
|
+
provider,
|
|
504
|
+
model,
|
|
505
|
+
thinkingLevel,
|
|
506
|
+
});
|
|
507
|
+
if (!result.ok) {
|
|
508
|
+
jsonRes(res, 409, {
|
|
509
|
+
error: "Conversation has a running job; retry after it finishes (or /stop it).",
|
|
510
|
+
});
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
jsonRes(res, 200, { ok: true, runtimeSwitched: result.runtimeSwitched });
|
|
514
|
+
}
|
|
515
|
+
catch (err) {
|
|
516
|
+
jsonRes(res, 500, { error: errorMessage(err) });
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
function serveConversationVisibilityUpdate(res, body, services, token) {
|
|
520
|
+
if (body.visibility !== "private" && body.visibility !== "default") {
|
|
521
|
+
jsonRes(res, 400, { error: "visibility must be 'private' or 'default'" });
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
const target = requireConversationWorkspace(res, body, services, token);
|
|
525
|
+
if (!target)
|
|
526
|
+
return;
|
|
527
|
+
const { scope, workspace } = target;
|
|
528
|
+
try {
|
|
529
|
+
const result = applyOfficeVisibility(services.runtime, workspace.office(scope.address), body.visibility === "private" ? "private" : null);
|
|
530
|
+
if (!result.ok) {
|
|
531
|
+
jsonRes(res, 409, { error: "Conversation is busy; retry when the current run finishes" });
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
jsonRes(res, 200, { ok: true });
|
|
535
|
+
}
|
|
536
|
+
catch (err) {
|
|
537
|
+
jsonRes(res, 500, { error: errorMessage(err) });
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
function serveConversationSlackUpdate(res, body, services, token) {
|
|
541
|
+
const replyMode = body.replyMode;
|
|
542
|
+
if (replyMode !== "top-level" && replyMode !== "thread") {
|
|
543
|
+
jsonRes(res, 400, { error: "replyMode must be 'top-level' or 'thread'" });
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const target = requireConversationWorkspace(res, body, services, token);
|
|
547
|
+
if (!target)
|
|
548
|
+
return;
|
|
549
|
+
const { scope, workspace } = target;
|
|
550
|
+
respondWithSettingsUpdate(res, () => {
|
|
551
|
+
applyConversationSettings(services.runtime, workspace.office(scope.address), {
|
|
552
|
+
slack: { replyMode },
|
|
553
|
+
});
|
|
554
|
+
return { ok: true };
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
function serveConversationSessionLink(res, body, services, token) {
|
|
558
|
+
const target = requireConversationWorkspace(res, body, services, token);
|
|
559
|
+
if (!target)
|
|
560
|
+
return;
|
|
561
|
+
const { scope, workspace } = target;
|
|
562
|
+
if (!services.sessionViewTokenStore) {
|
|
563
|
+
jsonRes(res, 503, { error: "Session view token store not available" });
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
if (!services.portalBaseUrl) {
|
|
567
|
+
jsonRes(res, 503, {
|
|
568
|
+
error: "Portal URL not configured. Set MIKAN_LINK_URL to enable link generation.",
|
|
569
|
+
});
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
const sessionFile = resolveExistingSessionFile(workspace.office(scope.address).dir, scope.conversationId);
|
|
573
|
+
if (!sessionFile) {
|
|
574
|
+
jsonRes(res, 404, { error: "No session file found for this conversation" });
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
try {
|
|
578
|
+
const { token: viewToken } = services.sessionViewTokenStore.create({
|
|
579
|
+
platform: token.platform,
|
|
580
|
+
platformUserId: token.platformUserId,
|
|
581
|
+
conversationId: scope.conversationId,
|
|
582
|
+
sessionKey: scope.conversationId,
|
|
583
|
+
sessionFile,
|
|
584
|
+
platformUserName: token.platformUserName,
|
|
585
|
+
});
|
|
586
|
+
const url = `${services.portalBaseUrl}/session?token=${encodeURIComponent(viewToken)}`;
|
|
587
|
+
jsonRes(res, 200, { ok: true, url });
|
|
588
|
+
}
|
|
589
|
+
catch (err) {
|
|
590
|
+
jsonRes(res, 500, { error: errorMessage(err) });
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
function serveConversationLoginLink(res, body, services, token) {
|
|
594
|
+
const scope = resolveTargetConversation(body, token);
|
|
595
|
+
if (scope.error) {
|
|
596
|
+
jsonRes(res, 403, { error: scope.error });
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (!services.portalBaseUrl) {
|
|
600
|
+
jsonRes(res, 503, { error: "Portal URL not configured." });
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
if (!services.sandbox) {
|
|
604
|
+
jsonRes(res, 503, { error: "Sandbox config not available." });
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
const sharedName = typeof body.sharedVault === "string" ? body.sharedVault.trim() : "";
|
|
608
|
+
let vaultId;
|
|
609
|
+
if (sharedName) {
|
|
610
|
+
const key = sharedVaultKey(sharedName);
|
|
611
|
+
if (!key) {
|
|
612
|
+
jsonRes(res, 400, { error: "Invalid shared vault name" });
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
vaultId = key;
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
try {
|
|
619
|
+
vaultId = credentialAuthorizationKey(services.sandbox, {
|
|
620
|
+
userId: token.platformUserId,
|
|
621
|
+
address: scope.address,
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
catch (err) {
|
|
625
|
+
jsonRes(res, 500, { error: errorMessage(err) });
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
try {
|
|
630
|
+
const { token: linkToken } = services.linkTokenStore.create(token.platform, token.platformUserId, scope.conversationId, vaultId, "");
|
|
631
|
+
const url = `${services.portalBaseUrl}/link?token=${encodeURIComponent(linkToken)}`;
|
|
632
|
+
jsonRes(res, 200, { ok: true, url, vaultId });
|
|
633
|
+
}
|
|
634
|
+
catch (err) {
|
|
635
|
+
jsonRes(res, 500, { error: errorMessage(err) });
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
function serveGlobalModelUpdate(res, body, services) {
|
|
639
|
+
const provider = typeof body.provider === "string" ? body.provider.trim() : "";
|
|
640
|
+
const model = typeof body.model === "string" ? body.model.trim() : "";
|
|
641
|
+
const thinkingLevel = isThinkingLevel(body.thinkingLevel) ? body.thinkingLevel : undefined;
|
|
642
|
+
if (!provider || !model) {
|
|
643
|
+
jsonRes(res, 400, { error: "Missing provider or model" });
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
respondWithSettingsUpdate(res, () => {
|
|
647
|
+
const result = applyGlobalSettings(services.runtime, {
|
|
648
|
+
provider,
|
|
649
|
+
model,
|
|
650
|
+
thinkingLevel,
|
|
651
|
+
});
|
|
652
|
+
return {
|
|
653
|
+
ok: true,
|
|
654
|
+
staleConversations: result.staleConversations.map((office) => `${office.platform}:${office.conversationId}`),
|
|
655
|
+
};
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
function serveGlobalSlackUpdate(res, body, services) {
|
|
659
|
+
const replyMode = body.replyMode;
|
|
660
|
+
if (replyMode !== "top-level" && replyMode !== "thread") {
|
|
661
|
+
jsonRes(res, 400, { error: "replyMode must be 'top-level' or 'thread'" });
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
respondWithSettingsUpdate(res, () => {
|
|
665
|
+
applyGlobalSettings(services.runtime, { slack: { replyMode } });
|
|
666
|
+
return { ok: true };
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
function serveGlobalSandboxUpdate(res, body, services) {
|
|
670
|
+
const cpus = typeof body.cpus === "string" ? body.cpus.trim() : "";
|
|
671
|
+
const memory = typeof body.memory === "string" ? body.memory.trim() : "";
|
|
672
|
+
const boostCpus = typeof body.boostCpus === "string" ? body.boostCpus.trim() : "";
|
|
673
|
+
const boostMemory = typeof body.boostMemory === "string" ? body.boostMemory.trim() : "";
|
|
674
|
+
const update = {
|
|
675
|
+
...(cpus ? { cpus } : {}),
|
|
676
|
+
...(memory ? { memory } : {}),
|
|
677
|
+
...(boostCpus || boostMemory
|
|
678
|
+
? {
|
|
679
|
+
boost: {
|
|
680
|
+
...(boostCpus ? { cpus: boostCpus } : {}),
|
|
681
|
+
...(boostMemory ? { memory: boostMemory } : {}),
|
|
682
|
+
},
|
|
683
|
+
}
|
|
684
|
+
: {}),
|
|
685
|
+
};
|
|
686
|
+
if (Object.keys(update).length === 0) {
|
|
687
|
+
jsonRes(res, 400, { error: "No valid sandbox fields provided" });
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
respondWithSettingsUpdate(res, () => {
|
|
691
|
+
applyGlobalSettings(services.runtime, { sandbox: update });
|
|
692
|
+
return { ok: true };
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
function respondWithSettingsUpdate(res, update) {
|
|
696
|
+
try {
|
|
697
|
+
jsonRes(res, 200, update());
|
|
698
|
+
}
|
|
699
|
+
catch (err) {
|
|
700
|
+
jsonRes(res, 500, { error: errorMessage(err) });
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
const WORKSPACE_TREE_MAX_DEPTH = 4;
|
|
704
|
+
const WORKSPACE_TREE_MAX_ENTRIES = 800;
|
|
705
|
+
const PREVIEW_FILE_MAX_BYTES = 256 * 1024;
|
|
706
|
+
const WORKSPACE_TOP_DIRS = new Set(["scratch"]);
|
|
707
|
+
function isWorkspacePathAllowed(rel) {
|
|
708
|
+
if (rel === "")
|
|
709
|
+
return true;
|
|
710
|
+
const first = rel.split("/").find(Boolean);
|
|
711
|
+
if (first === undefined)
|
|
712
|
+
return true;
|
|
713
|
+
return WORKSPACE_TOP_DIRS.has(first);
|
|
714
|
+
}
|
|
715
|
+
function resolveConversationFromQuery(url, token) {
|
|
716
|
+
const requested = (url.searchParams.get("conversationId") ?? "").trim();
|
|
717
|
+
const platform = (url.searchParams.get("platform") ?? "").trim();
|
|
718
|
+
return resolveConversationScope(requested, platform, token);
|
|
719
|
+
}
|
|
720
|
+
function safeJoinUnderRoot(rootDir, relative) {
|
|
721
|
+
if (relative.startsWith("/") || relative.includes("\0")) {
|
|
722
|
+
return { absolute: "", error: "Invalid path" };
|
|
723
|
+
}
|
|
724
|
+
if (relative.split(/[\\/]+/).some((part) => part === ".." || part === "")) {
|
|
725
|
+
if (relative !== "")
|
|
726
|
+
return { absolute: "", error: "Invalid path" };
|
|
727
|
+
}
|
|
728
|
+
const target = pathResolve(rootDir, relative);
|
|
729
|
+
const rootAbs = pathResolve(rootDir);
|
|
730
|
+
if (target !== rootAbs && !target.startsWith(rootAbs + pathSep)) {
|
|
731
|
+
return { absolute: "", error: "Path escapes conversation directory" };
|
|
732
|
+
}
|
|
733
|
+
return { absolute: target };
|
|
734
|
+
}
|
|
735
|
+
function buildTree(startDir, relPrefix) {
|
|
736
|
+
let counter = { value: 0 };
|
|
737
|
+
const walk = (dir, rel, depth) => {
|
|
738
|
+
if (counter.value >= WORKSPACE_TREE_MAX_ENTRIES)
|
|
739
|
+
return null;
|
|
740
|
+
let stats;
|
|
741
|
+
try {
|
|
742
|
+
stats = statSync(dir);
|
|
743
|
+
}
|
|
744
|
+
catch {
|
|
745
|
+
return null;
|
|
746
|
+
}
|
|
747
|
+
const name = rel === "" ? "." : basename(rel);
|
|
748
|
+
if (!stats.isDirectory()) {
|
|
749
|
+
counter.value += 1;
|
|
750
|
+
return {
|
|
751
|
+
name,
|
|
752
|
+
path: rel,
|
|
753
|
+
type: "file",
|
|
754
|
+
size: stats.size,
|
|
755
|
+
mtimeMs: stats.mtimeMs,
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
counter.value += 1;
|
|
759
|
+
if (depth >= WORKSPACE_TREE_MAX_DEPTH) {
|
|
760
|
+
return { name, path: rel, type: "dir", truncated: true };
|
|
761
|
+
}
|
|
762
|
+
let entries;
|
|
763
|
+
try {
|
|
764
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
765
|
+
}
|
|
766
|
+
catch {
|
|
767
|
+
return { name, path: rel, type: "dir" };
|
|
768
|
+
}
|
|
769
|
+
const children = [];
|
|
770
|
+
let truncated = false;
|
|
771
|
+
for (const entry of entries.toSorted((a, b) => {
|
|
772
|
+
if (a.isDirectory() !== b.isDirectory())
|
|
773
|
+
return a.isDirectory() ? -1 : 1;
|
|
774
|
+
return a.name.localeCompare(b.name);
|
|
775
|
+
})) {
|
|
776
|
+
const childRel = rel === "" ? entry.name : `${rel}/${entry.name}`;
|
|
777
|
+
if (!isWorkspacePathAllowed(childRel))
|
|
778
|
+
continue;
|
|
779
|
+
if (counter.value >= WORKSPACE_TREE_MAX_ENTRIES) {
|
|
780
|
+
truncated = true;
|
|
781
|
+
break;
|
|
782
|
+
}
|
|
783
|
+
const node = walk(join(dir, entry.name), childRel, depth + 1);
|
|
784
|
+
if (node)
|
|
785
|
+
children.push(node);
|
|
786
|
+
}
|
|
787
|
+
return {
|
|
788
|
+
name,
|
|
789
|
+
path: rel,
|
|
790
|
+
type: "dir",
|
|
791
|
+
children,
|
|
792
|
+
truncated: truncated ? true : undefined,
|
|
793
|
+
};
|
|
794
|
+
};
|
|
795
|
+
const node = walk(startDir, relPrefix, 0);
|
|
796
|
+
return node;
|
|
797
|
+
}
|
|
798
|
+
function serveWorkspaceTree(res, url, services, token) {
|
|
799
|
+
const scope = resolveConversationFromQuery(url, token);
|
|
800
|
+
if (scope.error) {
|
|
801
|
+
jsonRes(res, 403, { error: scope.error });
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
805
|
+
if (!workspace)
|
|
806
|
+
return;
|
|
807
|
+
const convDir = workspace.office(scope.address).dir;
|
|
808
|
+
if (!existsSync(convDir)) {
|
|
809
|
+
jsonRes(res, 200, { conversationId: scope.conversationId, tree: null });
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
const requestedSub = (url.searchParams.get("path") ?? "").trim();
|
|
813
|
+
if (!isWorkspacePathAllowed(requestedSub)) {
|
|
814
|
+
jsonRes(res, 403, { error: "Workspace path is not exposed" });
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
const startSafe = safeJoinUnderRoot(convDir, requestedSub);
|
|
818
|
+
if (startSafe.error) {
|
|
819
|
+
jsonRes(res, 400, { error: startSafe.error });
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
const tree = buildTree(startSafe.absolute, requestedSub);
|
|
823
|
+
jsonRes(res, 200, {
|
|
824
|
+
conversationId: scope.conversationId,
|
|
825
|
+
root: requestedSub || ".",
|
|
826
|
+
tree,
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
const BINARY_PROBE_BYTES = 4096;
|
|
830
|
+
function looksTextual(buf) {
|
|
831
|
+
const limit = Math.min(buf.length, BINARY_PROBE_BYTES);
|
|
832
|
+
for (let i = 0; i < limit; i++) {
|
|
833
|
+
const byte = buf[i];
|
|
834
|
+
if (byte === undefined || byte === 0)
|
|
835
|
+
return false;
|
|
836
|
+
if (byte < 9)
|
|
837
|
+
return false;
|
|
838
|
+
if (byte === 11 || byte === 12)
|
|
839
|
+
return false;
|
|
840
|
+
if (byte > 13 && byte < 32)
|
|
841
|
+
return false;
|
|
842
|
+
}
|
|
843
|
+
return true;
|
|
844
|
+
}
|
|
845
|
+
function servePreviewFile(res, absolutePath, metadata, notFoundMessage) {
|
|
846
|
+
let stats;
|
|
847
|
+
try {
|
|
848
|
+
stats = statSync(absolutePath);
|
|
849
|
+
}
|
|
850
|
+
catch {
|
|
851
|
+
jsonRes(res, 404, { error: notFoundMessage });
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
if (!stats.isFile()) {
|
|
855
|
+
jsonRes(res, 400, { error: "Not a file" });
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
if (stats.size > PREVIEW_FILE_MAX_BYTES) {
|
|
859
|
+
jsonRes(res, 413, {
|
|
860
|
+
error: "File too large to preview",
|
|
861
|
+
size: stats.size,
|
|
862
|
+
limit: PREVIEW_FILE_MAX_BYTES,
|
|
863
|
+
});
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
let buf;
|
|
867
|
+
try {
|
|
868
|
+
buf = readFileSync(absolutePath);
|
|
869
|
+
}
|
|
870
|
+
catch (err) {
|
|
871
|
+
jsonRes(res, 500, { error: errorMessage(err) });
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
if (!looksTextual(buf)) {
|
|
875
|
+
jsonRes(res, 200, {
|
|
876
|
+
...metadata,
|
|
877
|
+
size: stats.size,
|
|
878
|
+
mtimeMs: stats.mtimeMs,
|
|
879
|
+
binary: true,
|
|
880
|
+
content: null,
|
|
881
|
+
});
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
jsonRes(res, 200, {
|
|
885
|
+
...metadata,
|
|
886
|
+
size: stats.size,
|
|
887
|
+
mtimeMs: stats.mtimeMs,
|
|
888
|
+
binary: false,
|
|
889
|
+
content: buf.toString("utf-8"),
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
function serveWorkspaceFile(res, url, services, token) {
|
|
893
|
+
const scope = resolveConversationFromQuery(url, token);
|
|
894
|
+
if (scope.error) {
|
|
895
|
+
jsonRes(res, 403, { error: scope.error });
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
899
|
+
if (!workspace)
|
|
900
|
+
return;
|
|
901
|
+
const requestedPath = (url.searchParams.get("path") ?? "").trim();
|
|
902
|
+
if (!requestedPath) {
|
|
903
|
+
jsonRes(res, 400, { error: "Missing path" });
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
if (!isWorkspacePathAllowed(requestedPath)) {
|
|
907
|
+
jsonRes(res, 403, { error: "Workspace path is not exposed" });
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
const convDir = workspace.office(scope.address).dir;
|
|
911
|
+
const safe = safeJoinUnderRoot(convDir, requestedPath);
|
|
912
|
+
if (safe.error) {
|
|
913
|
+
jsonRes(res, 400, { error: safe.error });
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
servePreviewFile(res, safe.absolute, { path: requestedPath }, "File not found");
|
|
917
|
+
}
|
|
918
|
+
function readSkillMeta(filePath) {
|
|
919
|
+
let text;
|
|
920
|
+
try {
|
|
921
|
+
text = readFileSync(filePath, "utf-8");
|
|
922
|
+
}
|
|
923
|
+
catch {
|
|
924
|
+
return {};
|
|
925
|
+
}
|
|
926
|
+
const { values } = parseFrontmatter(text);
|
|
927
|
+
const out = {};
|
|
928
|
+
for (const [key, value] of Object.entries(values)) {
|
|
929
|
+
const normalized = key.toLowerCase();
|
|
930
|
+
if (normalized === "name")
|
|
931
|
+
out.name = value;
|
|
932
|
+
if (normalized === "description")
|
|
933
|
+
out.description = value;
|
|
934
|
+
}
|
|
935
|
+
return out;
|
|
936
|
+
}
|
|
937
|
+
export function readSkillsFromDir(skillsDir, source) {
|
|
938
|
+
if (!existsSync(skillsDir))
|
|
939
|
+
return [];
|
|
940
|
+
const out = [];
|
|
941
|
+
let entries;
|
|
942
|
+
try {
|
|
943
|
+
entries = readdirSync(skillsDir, { withFileTypes: true });
|
|
944
|
+
}
|
|
945
|
+
catch {
|
|
946
|
+
return [];
|
|
947
|
+
}
|
|
948
|
+
for (const entry of entries) {
|
|
949
|
+
if (!entry.isDirectory() || entry.name.startsWith("."))
|
|
950
|
+
continue;
|
|
951
|
+
const skillMd = join(skillsDir, entry.name, "SKILL.md");
|
|
952
|
+
if (!existsSync(skillMd))
|
|
953
|
+
continue;
|
|
954
|
+
const meta = readSkillMeta(skillMd);
|
|
955
|
+
out.push({
|
|
956
|
+
name: meta.name ?? entry.name,
|
|
957
|
+
description: meta.description ?? "",
|
|
958
|
+
source,
|
|
959
|
+
path: skillMd,
|
|
960
|
+
directory: entry.name,
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
return out.toSorted((a, b) => a.name.localeCompare(b.name));
|
|
964
|
+
}
|
|
965
|
+
function serveMcpServersList(res, url, services, token) {
|
|
966
|
+
const scope = resolveConversationFromQuery(url, token);
|
|
967
|
+
if (scope.error) {
|
|
968
|
+
jsonRes(res, 403, { error: scope.error });
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
972
|
+
if (!workspace)
|
|
973
|
+
return;
|
|
974
|
+
const servers = loadScopeMcpServers(workspace.office(scope.address));
|
|
975
|
+
jsonRes(res, 200, {
|
|
976
|
+
conversationId: scope.conversationId,
|
|
977
|
+
presets: listMcpPresets(),
|
|
978
|
+
global: redactMcpServers(servers.global),
|
|
979
|
+
conversation: redactMcpServers(servers.conversation),
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
function redactMcpServers(map) {
|
|
983
|
+
return Object.fromEntries(Object.entries(map).map(([name, config]) => [
|
|
984
|
+
name,
|
|
985
|
+
{
|
|
986
|
+
command: config.command,
|
|
987
|
+
args: config.args,
|
|
988
|
+
url: config.url !== undefined ? redactMcpUrl(config.url) : undefined,
|
|
989
|
+
disabled: config.disabled,
|
|
990
|
+
envKeys: Object.keys(config.env ?? {}),
|
|
991
|
+
headerKeys: Object.keys(config.headers ?? {}),
|
|
992
|
+
},
|
|
993
|
+
]));
|
|
994
|
+
}
|
|
995
|
+
function mcpStringMap(value) {
|
|
996
|
+
if (typeof value !== "object" || value === null)
|
|
997
|
+
return undefined;
|
|
998
|
+
const out = {};
|
|
999
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1000
|
+
if (typeof v === "string" && k.trim())
|
|
1001
|
+
out[k.trim()] = v;
|
|
1002
|
+
}
|
|
1003
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
1004
|
+
}
|
|
1005
|
+
const MCP_VERIFY_TIMEOUT_MS = 20_000;
|
|
1006
|
+
async function verifyMcpServers(servers) {
|
|
1007
|
+
const names = Object.keys(servers);
|
|
1008
|
+
if (names.length === 0)
|
|
1009
|
+
return [];
|
|
1010
|
+
const loaded = await loadMcpTools(servers, AbortSignal.timeout(MCP_VERIFY_TIMEOUT_MS));
|
|
1011
|
+
try {
|
|
1012
|
+
return names.map((name) => {
|
|
1013
|
+
const failure = loaded.errors.find((e) => e.server === name);
|
|
1014
|
+
if (failure) {
|
|
1015
|
+
const url = servers[name]?.url;
|
|
1016
|
+
const error = url ? failure.error.split(url).join(redactMcpUrl(url)) : failure.error;
|
|
1017
|
+
return { name, error };
|
|
1018
|
+
}
|
|
1019
|
+
const prefix = `mcp__${name}__`;
|
|
1020
|
+
return { name, tools: loaded.tools.filter((t) => t.name.startsWith(prefix)).length };
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
finally {
|
|
1024
|
+
await loaded.dispose();
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
function planMcpMutation(action, body, current) {
|
|
1028
|
+
const next = { ...current };
|
|
1029
|
+
if (action === "import") {
|
|
1030
|
+
const parsed = parseStandardMcpServers(typeof body.json === "string" ? body.json : "");
|
|
1031
|
+
if (!parsed.servers)
|
|
1032
|
+
return { status: 400, error: parsed.error };
|
|
1033
|
+
Object.assign(next, parsed.servers);
|
|
1034
|
+
return { next, touched: parsed.servers };
|
|
1035
|
+
}
|
|
1036
|
+
const preset = action === "install" && typeof body.presetId === "string"
|
|
1037
|
+
? findMcpPreset(body.presetId)
|
|
1038
|
+
: undefined;
|
|
1039
|
+
if (action === "install" && !preset)
|
|
1040
|
+
return { status: 400, error: "unknown MCP preset" };
|
|
1041
|
+
const name = preset?.serverName ?? (typeof body.name === "string" ? body.name.trim() : "");
|
|
1042
|
+
if (!isValidMcpServerName(name)) {
|
|
1043
|
+
return {
|
|
1044
|
+
status: 400,
|
|
1045
|
+
error: "invalid server name (letters, digits, '_' or '-', starting with a letter)",
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
if (action === "install") {
|
|
1049
|
+
try {
|
|
1050
|
+
next[name] = materializeMcpPreset(preset, mcpStringMap(body.credentials) ?? {});
|
|
1051
|
+
}
|
|
1052
|
+
catch (err) {
|
|
1053
|
+
return { status: 400, error: errorMessage(err) };
|
|
1054
|
+
}
|
|
1055
|
+
return { next, touched: { [name]: next[name] } };
|
|
1056
|
+
}
|
|
1057
|
+
if (!(name in next))
|
|
1058
|
+
return { status: 404, error: "Not declared here." };
|
|
1059
|
+
if (action === "remove") {
|
|
1060
|
+
delete next[name];
|
|
1061
|
+
return { next, touched: {} };
|
|
1062
|
+
}
|
|
1063
|
+
const entry = next[name];
|
|
1064
|
+
next[name] = entry.disabled ? { ...entry, disabled: undefined } : { ...entry, disabled: true };
|
|
1065
|
+
return { next, touched: {} };
|
|
1066
|
+
}
|
|
1067
|
+
async function serveMcpServerMutation(res, body, services, token) {
|
|
1068
|
+
const action = body.action;
|
|
1069
|
+
if (action !== "import" &&
|
|
1070
|
+
action !== "install" &&
|
|
1071
|
+
action !== "remove" &&
|
|
1072
|
+
action !== "toggle" &&
|
|
1073
|
+
action !== "test") {
|
|
1074
|
+
jsonRes(res, 400, {
|
|
1075
|
+
error: "action must be 'import', 'install', 'remove', 'toggle', or 'test'",
|
|
1076
|
+
});
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
const mutationScope = body.scope === "global" ? "global" : "conversation";
|
|
1080
|
+
const scope = resolveTargetConversation(body, token);
|
|
1081
|
+
if (scope.error) {
|
|
1082
|
+
jsonRes(res, 403, { error: scope.error });
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
1086
|
+
if (!workspace)
|
|
1087
|
+
return;
|
|
1088
|
+
const office = workspace.office(scope.address);
|
|
1089
|
+
const maps = loadScopeMcpServers(office);
|
|
1090
|
+
const current = mutationScope === "global" ? maps.global : maps.conversation;
|
|
1091
|
+
if (action === "test") {
|
|
1092
|
+
const name = typeof body.name === "string" ? body.name.trim() : "";
|
|
1093
|
+
const entry = current[name];
|
|
1094
|
+
if (!entry) {
|
|
1095
|
+
jsonRes(res, 404, { error: "Not declared here." });
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
const results = await verifyMcpServers({ [name]: { ...entry, disabled: undefined } });
|
|
1099
|
+
jsonRes(res, 200, { ok: true, results });
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
const plan = planMcpMutation(action, body, current);
|
|
1103
|
+
if ("error" in plan) {
|
|
1104
|
+
jsonRes(res, plan.status, { error: plan.error });
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
const result = mutationScope === "global"
|
|
1108
|
+
? applyGlobalSettings(services.runtime, { mcpServers: plan.next })
|
|
1109
|
+
: applyConversationSettings(services.runtime, office, { mcpServers: plan.next });
|
|
1110
|
+
if (!result.ok) {
|
|
1111
|
+
jsonRes(res, 409, { error: "Conversation is busy; try again shortly." });
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
const results = await verifyMcpServers(plan.touched);
|
|
1115
|
+
jsonRes(res, 200, { ok: true, results });
|
|
1116
|
+
}
|
|
1117
|
+
function serveSkillsList(res, url, services, token) {
|
|
1118
|
+
const scope = resolveConversationFromQuery(url, token);
|
|
1119
|
+
if (scope.error) {
|
|
1120
|
+
jsonRes(res, 403, { error: scope.error });
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
1124
|
+
if (!workspace)
|
|
1125
|
+
return;
|
|
1126
|
+
const global = readSkillsFromDir(workspace.skillsDir, "global");
|
|
1127
|
+
const conversation = readSkillsFromDir(workspace.office(scope.address).skillsDir, "conversation");
|
|
1128
|
+
jsonRes(res, 200, {
|
|
1129
|
+
conversationId: scope.conversationId,
|
|
1130
|
+
skills: [...global, ...conversation],
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
function serveSkillFile(res, url, services, token) {
|
|
1134
|
+
const scope = resolveConversationFromQuery(url, token);
|
|
1135
|
+
if (scope.error) {
|
|
1136
|
+
jsonRes(res, 403, { error: scope.error });
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
1140
|
+
if (!workspace)
|
|
1141
|
+
return;
|
|
1142
|
+
const source = (url.searchParams.get("source") ?? "").trim();
|
|
1143
|
+
const directory = (url.searchParams.get("directory") ?? "").trim();
|
|
1144
|
+
if (source !== "global" && source !== "conversation") {
|
|
1145
|
+
jsonRes(res, 400, { error: "Invalid skill source" });
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
if (!directory ||
|
|
1149
|
+
directory.includes("/") ||
|
|
1150
|
+
directory.includes("\\") ||
|
|
1151
|
+
directory.includes("..")) {
|
|
1152
|
+
jsonRes(res, 400, { error: "Invalid skill directory" });
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
const skillsRoot = source === "global" ? workspace.skillsDir : workspace.office(scope.address).skillsDir;
|
|
1156
|
+
const safe = safeJoinUnderRoot(skillsRoot, join(directory, "SKILL.md"));
|
|
1157
|
+
if (safe.error) {
|
|
1158
|
+
jsonRes(res, 400, { error: safe.error });
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
servePreviewFile(res, safe.absolute, { source, directory }, "Skill file not found");
|
|
1162
|
+
}
|
|
1163
|
+
const SKILL_DIRECTORY_PATTERN = /^[a-z0-9-]+$/;
|
|
1164
|
+
function resolveSkillsRoot(workspace, scope, source) {
|
|
1165
|
+
if (source !== "global" && source !== "conversation") {
|
|
1166
|
+
return { error: "Invalid skill source" };
|
|
1167
|
+
}
|
|
1168
|
+
return {
|
|
1169
|
+
root: source === "global" ? workspace.skillsDir : workspace.office(scope.address).skillsDir,
|
|
1170
|
+
source,
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
async function serveSkillMutation(res, body, services, token) {
|
|
1174
|
+
const action = body.action;
|
|
1175
|
+
if (action !== "save" && action !== "delete") {
|
|
1176
|
+
jsonRes(res, 400, { error: "action must be 'save' or 'delete'" });
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
const scope = resolveTargetConversation(body, token);
|
|
1180
|
+
if (scope.error) {
|
|
1181
|
+
jsonRes(res, 403, { error: scope.error });
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
1185
|
+
if (!workspace)
|
|
1186
|
+
return;
|
|
1187
|
+
const resolved = resolveSkillsRoot(workspace, scope, body.source);
|
|
1188
|
+
if ("error" in resolved) {
|
|
1189
|
+
jsonRes(res, 400, { error: resolved.error });
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
const directory = typeof body.directory === "string" ? body.directory.trim() : "";
|
|
1193
|
+
if (!directory || !SKILL_DIRECTORY_PATTERN.test(directory)) {
|
|
1194
|
+
jsonRes(res, 400, {
|
|
1195
|
+
error: "directory must be lowercase a-z, 0-9 and hyphens",
|
|
1196
|
+
});
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
const safe = safeJoinUnderRoot(resolved.root, directory);
|
|
1200
|
+
if (safe.error) {
|
|
1201
|
+
jsonRes(res, 400, { error: safe.error });
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
if (action === "delete") {
|
|
1205
|
+
if (!existsSync(join(safe.absolute, "SKILL.md"))) {
|
|
1206
|
+
jsonRes(res, 404, { error: "Skill not found" });
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
try {
|
|
1210
|
+
rmSync(safe.absolute, { recursive: true, force: true });
|
|
1211
|
+
}
|
|
1212
|
+
catch (err) {
|
|
1213
|
+
jsonRes(res, 500, { error: errorMessage(err) });
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
jsonRes(res, 200, { ok: true });
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
const name = typeof body.name === "string" ? body.name.trim() : directory;
|
|
1220
|
+
const description = typeof body.description === "string" ? body.description.trim() : "";
|
|
1221
|
+
const content = typeof body.content === "string" ? body.content : "";
|
|
1222
|
+
if (!description) {
|
|
1223
|
+
jsonRes(res, 400, { error: "description is required" });
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
const errors = validateSkill(name, description);
|
|
1227
|
+
if (errors.length > 0) {
|
|
1228
|
+
jsonRes(res, 400, { error: errors.join("; ") });
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
try {
|
|
1232
|
+
if (existsSync(safe.absolute) && lstatSync(safe.absolute).isSymbolicLink()) {
|
|
1233
|
+
throw new Error("Refusing to write through a symlinked skill directory");
|
|
1234
|
+
}
|
|
1235
|
+
mkdirSync(safe.absolute, { recursive: true });
|
|
1236
|
+
const frontmatter = ["---", `name: ${name}`, `description: ${description}`, "---", ""].join("\n");
|
|
1237
|
+
atomicWritePrivateFile(join(safe.absolute, "SKILL.md"), frontmatter + content.trim() + "\n");
|
|
1238
|
+
}
|
|
1239
|
+
catch (err) {
|
|
1240
|
+
jsonRes(res, 500, { error: errorMessage(err) });
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
jsonRes(res, 200, { ok: true, name, directory, source: resolved.source });
|
|
1244
|
+
}
|
|
1245
|
+
export async function listOfficeEvents(store) {
|
|
1246
|
+
const entries = await store.list();
|
|
1247
|
+
return entries.map((entry) => {
|
|
1248
|
+
const payload = entry.payload;
|
|
1249
|
+
return {
|
|
1250
|
+
name: entry.filename,
|
|
1251
|
+
officePlatform: store.address.platform,
|
|
1252
|
+
officeConversationId: store.address.conversationId,
|
|
1253
|
+
size: entry.size,
|
|
1254
|
+
mtimeMs: entry.mtimeMs,
|
|
1255
|
+
type: payload?.type ?? null,
|
|
1256
|
+
platform: payload?.platform ?? null,
|
|
1257
|
+
conversationId: payload?.conversationId ?? null,
|
|
1258
|
+
text: payload?.text ?? null,
|
|
1259
|
+
at: payload?.type === "one-shot" ? payload.at : null,
|
|
1260
|
+
schedule: payload?.type === "periodic" ? payload.schedule : null,
|
|
1261
|
+
timezone: payload?.type === "periodic" ? payload.timezone : null,
|
|
1262
|
+
};
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
function requireAdminEventStore(res, services, office) {
|
|
1266
|
+
if (!services.eventStore) {
|
|
1267
|
+
jsonRes(res, 503, { error: "Working directory not available" });
|
|
1268
|
+
return null;
|
|
1269
|
+
}
|
|
1270
|
+
return services.eventStore(office);
|
|
1271
|
+
}
|
|
1272
|
+
async function serveEventsList(res, services) {
|
|
1273
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
1274
|
+
if (!workspace)
|
|
1275
|
+
return;
|
|
1276
|
+
if (!services.eventStore) {
|
|
1277
|
+
jsonRes(res, 503, { error: "Working directory not available" });
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
const events = [];
|
|
1281
|
+
for (const record of listRegisteredOffices(workspace.stateDir)) {
|
|
1282
|
+
events.push(...(await listOfficeEvents(services.eventStore(workspace.office(record)))));
|
|
1283
|
+
}
|
|
1284
|
+
jsonRes(res, 200, { events });
|
|
1285
|
+
}
|
|
1286
|
+
async function serveConversationEventsList(res, url, services, token) {
|
|
1287
|
+
const scope = resolveConversationFromQuery(url, token);
|
|
1288
|
+
if (scope.error) {
|
|
1289
|
+
jsonRes(res, 403, { error: scope.error });
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
1293
|
+
if (!workspace)
|
|
1294
|
+
return;
|
|
1295
|
+
const store = requireAdminEventStore(res, services, workspace.office(scope.address));
|
|
1296
|
+
if (!store)
|
|
1297
|
+
return;
|
|
1298
|
+
jsonRes(res, 200, {
|
|
1299
|
+
conversationId: scope.conversationId,
|
|
1300
|
+
events: await listOfficeEvents(store),
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
async function serveConversationEventDelete(res, body, services, token) {
|
|
1304
|
+
const scope = resolveTargetConversation(body, token);
|
|
1305
|
+
if (scope.error) {
|
|
1306
|
+
jsonRes(res, 403, { error: scope.error });
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
let name;
|
|
1310
|
+
try {
|
|
1311
|
+
name = validateEventFilename(typeof body.name === "string" ? body.name : "");
|
|
1312
|
+
}
|
|
1313
|
+
catch {
|
|
1314
|
+
jsonRes(res, 400, { error: "Invalid name" });
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1317
|
+
const workspace = requireAdminWorkspace(res, services);
|
|
1318
|
+
if (!workspace)
|
|
1319
|
+
return;
|
|
1320
|
+
const store = requireAdminEventStore(res, services, workspace.office(scope.address));
|
|
1321
|
+
if (!store)
|
|
1322
|
+
return;
|
|
1323
|
+
try {
|
|
1324
|
+
await store.delete(name);
|
|
1325
|
+
jsonRes(res, 200, { ok: true });
|
|
1326
|
+
}
|
|
1327
|
+
catch (err) {
|
|
1328
|
+
const message = errorMessage(err);
|
|
1329
|
+
jsonRes(res, /not found/.test(message) ? 404 : 500, { error: message });
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
const esc = escapeHtml;
|
|
1333
|
+
const ICONS = {
|
|
1334
|
+
settings: `<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33h.01a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82v.01a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>`,
|
|
1335
|
+
workspace: `<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z"/></svg>`,
|
|
1336
|
+
skills: `<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5V5a2 2 0 0 1 2-2h11a1 1 0 0 1 1 1v14"/><path d="M6 17h13v4H6.5A2.5 2.5 0 0 1 4 18.5v0A2.5 2.5 0 0 1 6.5 16H20"/></svg>`,
|
|
1337
|
+
mcp: `<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="7" rx="1.5"/><rect x="3" y="13" width="18" height="7" rx="1.5"/><circle cx="7" cy="7.5" r="1" fill="currentColor" stroke="none"/><circle cx="7" cy="16.5" r="1" fill="currentColor" stroke="none"/></svg>`,
|
|
1338
|
+
vault: `<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="10" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>`,
|
|
1339
|
+
events: `<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M3 10h18M8 3v4M16 3v4"/></svg>`,
|
|
1340
|
+
session: `<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>`,
|
|
1341
|
+
overview: `<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="8" height="8" rx="1.5"/><rect x="13" y="3" width="8" height="5" rx="1.5"/><rect x="13" y="11" width="8" height="10" rx="1.5"/><rect x="3" y="14" width="8" height="7" rx="1.5"/></svg>`,
|
|
1342
|
+
usage: `<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21V3"/><path d="M3 21h18"/><path d="M7 17V11M12 17V7M17 17v-4"/></svg>`,
|
|
1343
|
+
};
|
|
1344
|
+
function railLink(id, label, icon, active = false) {
|
|
1345
|
+
const svg = ICONS[icon];
|
|
1346
|
+
return `<button class="rail-link${active ? " active" : ""}" type="button" data-pane="${id}" aria-current="${active ? "page" : "false"}" aria-label="${esc(label)}" title="${esc(label)}">${svg}<span>${esc(label)}</span></button>`;
|
|
1347
|
+
}
|
|
1348
|
+
function settingsPane(id, title, description, actions, body) {
|
|
1349
|
+
return `<section class="pane" id="pane-${id}" data-pane="${id}">
|
|
1350
|
+
<header class="pane-head">
|
|
1351
|
+
<div><h2 class="pane-title">${esc(title)}</h2><p class="pane-desc">${esc(description)}</p></div>
|
|
1352
|
+
<div class="pane-actions">${actions}</div>
|
|
1353
|
+
</header>
|
|
1354
|
+
<div class="pane-body">${body}</div>
|
|
1355
|
+
</section>`;
|
|
1356
|
+
}
|
|
1357
|
+
const adminViewBody = `<div class="settings-shell">
|
|
1358
|
+
<aside class="settings-rail" aria-label="Admin sections">
|
|
1359
|
+
<div class="rail-scope" role="tablist" aria-label="Scope">
|
|
1360
|
+
<button class="rail-scope-btn active" role="tab" aria-selected="true" aria-controls="panel-conversation" data-tab="conversation">This conversation</button>
|
|
1361
|
+
<button class="rail-scope-btn" role="tab" aria-selected="false" aria-controls="panel-global" data-tab="global">Workspace</button>
|
|
1362
|
+
</div>
|
|
1363
|
+
<nav class="rail-nav" id="rail-nav-conversation" data-scope="conversation">
|
|
1364
|
+
${railLink("settings", "Settings", "settings", true)}
|
|
1365
|
+
${railLink("workspace", "Workspace", "workspace")}
|
|
1366
|
+
${railLink("skills", "Skills", "skills")}
|
|
1367
|
+
${railLink("mcp", "MCP servers", "mcp")}
|
|
1368
|
+
${railLink("vault", "Vault", "vault")}
|
|
1369
|
+
${railLink("events", "Events", "events")}
|
|
1370
|
+
${railLink("session", "Session view", "session")}
|
|
1371
|
+
</nav>
|
|
1372
|
+
<nav class="rail-nav" id="rail-nav-global" data-scope="global" hidden>
|
|
1373
|
+
${railLink("g-overview", "All conversations", "overview", true)}
|
|
1374
|
+
${railLink("g-usage", "Token usage", "usage")}
|
|
1375
|
+
${railLink("g-settings", "Defaults", "settings")}
|
|
1376
|
+
${railLink("g-mcp", "MCP servers", "mcp")}
|
|
1377
|
+
${railLink("g-skills", "Skills", "skills")}
|
|
1378
|
+
${railLink("g-events", "Events", "events")}
|
|
1379
|
+
</nav>
|
|
1380
|
+
</aside>
|
|
1381
|
+
|
|
1382
|
+
<div class="settings-panels">
|
|
1383
|
+
<div class="tab-panel active" id="panel-conversation">
|
|
1384
|
+
${settingsPane("settings", "Settings", "Model, thinking level, auto-reply, and workspace visibility for this conversation.", '<button class="refresh-btn" type="button" aria-label="Refresh" title="Refresh" onclick="loadSettings()">↻</button>', '<div id="settings-content"><div class="loading-msg">Loading…</div></div>')}
|
|
1385
|
+
|
|
1386
|
+
${settingsPane("workspace", "Workspace", "Read-only browser for this conversation's files on disk.", '<button class="refresh-btn" type="button" aria-label="Refresh" title="Refresh" onclick="loadWorkspace()">↻</button>', '<div class="workspace-split"><div id="workspace-tree" class="workspace-tree"><div class="loading-msg">Loading…</div></div><div id="workspace-preview" class="workspace-preview"><div class="placeholder-msg">Click a file to preview</div></div></div>')}
|
|
1387
|
+
|
|
1388
|
+
${settingsPane("skills", "Skills", "Instructions the agent can load for specific tasks in this conversation.", '<button class="primary-action-btn" onclick="openSkillDialog(\'conversation\')">+ New skill</button><button class="refresh-btn" type="button" aria-label="Refresh" title="Refresh" onclick="loadSkills()">↻</button>', '<div class="workspace-split"><div id="skills-content" class="workspace-tree"><div class="loading-msg">Loading…</div></div><div id="skills-preview" class="workspace-preview"><div class="placeholder-msg">Click a skill to preview SKILL.md</div></div></div>')}
|
|
1389
|
+
|
|
1390
|
+
${settingsPane("mcp", "MCP servers", "External tools this conversation can call during a run.", '<button class="refresh-btn" type="button" aria-label="Refresh" title="Refresh" onclick="loadMcpServers()">↻</button>', '<div id="mcp-conv-msg" class="status-msg" style="display:none"></div><div id="mcp-conv-content"><div class="loading-msg">Loading…</div></div>')}
|
|
1391
|
+
|
|
1392
|
+
${settingsPane("vault", "Vault", "Credentials scoped to this conversation.", '<button class="primary-action-btn" onclick="openLogin()">Open in new tab ↗</button>', '<div id="vault-link-result" class="link-result" style="display:none"></div><p class="card-desc">Opens the one-time credential form for this conversation\'s vault in a new tab.</p>')}
|
|
1393
|
+
|
|
1394
|
+
${settingsPane("events", "Events", "Scheduled and one-shot events tied to this conversation.", '<button class="refresh-btn" type="button" aria-label="Refresh" title="Refresh" onclick="loadConversationEvents()">↻</button>', '<div id="events-content"><div class="loading-msg">Loading…</div></div>')}
|
|
1395
|
+
|
|
1396
|
+
${settingsPane("session", "Session view", "The full message and tool-call timeline for this conversation.", '<button class="primary-action-btn" onclick="openSessionView()">Open in new tab ↗</button>', '<div id="session-link-result" class="link-result" style="display:none"></div><p class="card-desc">Opens the session timeline for this conversation in a new tab.</p>')}
|
|
1397
|
+
</div>
|
|
1398
|
+
|
|
1399
|
+
<div class="tab-panel" id="panel-global">
|
|
1400
|
+
${settingsPane("g-overview", "All conversations", "Every registered conversation across every platform.", '<button class="refresh-btn" type="button" aria-label="Refresh" title="Refresh" onclick="loadAllConversations()">↻</button>', '<div id="all-conv-content"><div class="loading-msg">Loading…</div></div>')}
|
|
1401
|
+
|
|
1402
|
+
${settingsPane("g-usage", "Token usage", "Spend and volume across every conversation.", '<button class="refresh-btn" type="button" aria-label="Refresh" title="Refresh" onclick="loadTokenUsage()">↻</button>', '<h3 class="card-subtitle">Top 20 sessions</h3><div id="session-usage-content"><div class="loading-msg">Loading…</div></div><h3 class="card-subtitle" style="margin-top:24px">Usage timeline</h3><div class="timeline-controls"><label>Conversation<select id="timeline-conv" onchange="loadUsageTimeline()"></select></label></div><div id="usage-timeline-content"><div class="loading-msg">Loading…</div></div>')}
|
|
1403
|
+
|
|
1404
|
+
${settingsPane("g-settings", "Workspace defaults", "The fallback model, sandbox limits, and Slack behavior every conversation inherits.", '<button class="refresh-btn" type="button" aria-label="Refresh" title="Refresh" onclick="loadGlobalSettings()">↻</button>', '<div id="global-settings-content"><div class="loading-msg">Loading…</div></div>')}
|
|
1405
|
+
|
|
1406
|
+
${settingsPane("g-mcp", "MCP servers", "External tools available to every conversation in this workspace.", '<button class="refresh-btn" type="button" aria-label="Refresh" title="Refresh" onclick="loadMcpServers()">↻</button>', '<div id="mcp-global-msg" class="status-msg" style="display:none"></div><div id="mcp-global-content"><div class="loading-msg">Loading…</div></div>')}
|
|
1407
|
+
|
|
1408
|
+
${settingsPane("g-skills", "Skills", "Shared instructions available to every conversation in this workspace.", '<button class="primary-action-btn" onclick="openSkillDialog(\'global\')">+ New global skill</button><button class="refresh-btn" type="button" aria-label="Refresh" title="Refresh" onclick="loadGlobalSkills()">↻</button>', '<div class="workspace-split"><div id="global-skills-content" class="workspace-tree"><div class="loading-msg">Loading…</div></div><div id="global-skills-preview" class="workspace-preview"><div class="placeholder-msg">Click a skill to preview SKILL.md</div></div></div>')}
|
|
1409
|
+
|
|
1410
|
+
${settingsPane("g-events", "Global events", "Every scheduled event across the whole workspace.", '<button class="refresh-btn" type="button" aria-label="Refresh" title="Refresh" onclick="loadEvents()">↻</button>', '<div id="global-events-content"><div class="loading-msg">Loading…</div></div>')}
|
|
1411
|
+
</div>
|
|
1412
|
+
</div>
|
|
1413
|
+
</div>
|
|
1414
|
+
|
|
1415
|
+
<dialog id="mcp-install-dialog" class="mcp-dialog" aria-labelledby="mcp-dialog-title">
|
|
1416
|
+
<div class="mcp-dialog-head">
|
|
1417
|
+
<div>
|
|
1418
|
+
<p class="eyebrow">Install MCP preset</p>
|
|
1419
|
+
<h2 id="mcp-dialog-title" class="card-title"></h2>
|
|
1420
|
+
</div>
|
|
1421
|
+
<button class="mcp-dialog-close" type="button" aria-label="Close" onclick="closeMcpInstall()">×</button>
|
|
1422
|
+
</div>
|
|
1423
|
+
<div id="mcp-dialog-content"></div>
|
|
1424
|
+
<div id="mcp-dialog-error" class="inline-result err" style="display:none"></div>
|
|
1425
|
+
<div class="mcp-dialog-actions">
|
|
1426
|
+
<button class="mcp-btn" type="button" onclick="closeMcpInstall()">Cancel</button>
|
|
1427
|
+
<button id="mcp-dialog-install" class="primary-action-btn" type="button" onclick="installMcpPreset(this)">Install preset</button>
|
|
1428
|
+
</div>
|
|
1429
|
+
</dialog>
|
|
1430
|
+
|
|
1431
|
+
<dialog id="mcp-custom-dialog" class="mcp-dialog" aria-labelledby="mcp-custom-dialog-title">
|
|
1432
|
+
<div class="mcp-dialog-head">
|
|
1433
|
+
<div>
|
|
1434
|
+
<p class="eyebrow">Add MCP server</p>
|
|
1435
|
+
<h2 id="mcp-custom-dialog-title" class="card-title">新增自訂 server</h2>
|
|
1436
|
+
</div>
|
|
1437
|
+
<button class="mcp-dialog-close" type="button" aria-label="Close" onclick="closeMcpCustomDialog()">×</button>
|
|
1438
|
+
</div>
|
|
1439
|
+
<div id="mcp-custom-dialog-content"></div>
|
|
1440
|
+
<div id="mcp-custom-dialog-error" class="inline-result err" style="display:none"></div>
|
|
1441
|
+
<div class="mcp-dialog-actions">
|
|
1442
|
+
<button class="mcp-btn" type="button" onclick="closeMcpCustomDialog()">Cancel</button>
|
|
1443
|
+
<button id="mcp-custom-dialog-submit" class="primary-action-btn" type="button" onclick="submitMcpCustomDialog(this)">新增並測試連線</button>
|
|
1444
|
+
</div>
|
|
1445
|
+
</dialog>
|
|
1446
|
+
|
|
1447
|
+
<dialog id="skill-dialog" class="mcp-dialog" aria-labelledby="skill-dialog-title">
|
|
1448
|
+
<div class="mcp-dialog-head">
|
|
1449
|
+
<div>
|
|
1450
|
+
<p class="eyebrow" id="skill-dialog-eyebrow">Skill</p>
|
|
1451
|
+
<h2 id="skill-dialog-title" class="card-title">New skill</h2>
|
|
1452
|
+
</div>
|
|
1453
|
+
<button class="mcp-dialog-close" type="button" aria-label="Close" onclick="closeSkillDialog()">×</button>
|
|
1454
|
+
</div>
|
|
1455
|
+
<div class="mcp-field">
|
|
1456
|
+
<span>Directory</span>
|
|
1457
|
+
<input id="skill-dialog-directory" class="form-input mcp-json" placeholder="my-skill" autocomplete="off" />
|
|
1458
|
+
<small class="mcp-secret-note">Lowercase letters, digits, hyphens. Cannot be changed after creation.</small>
|
|
1459
|
+
</div>
|
|
1460
|
+
<div class="mcp-field">
|
|
1461
|
+
<span>Name</span>
|
|
1462
|
+
<input id="skill-dialog-name" class="form-input mcp-json" placeholder="my-skill" autocomplete="off" />
|
|
1463
|
+
</div>
|
|
1464
|
+
<div class="mcp-field">
|
|
1465
|
+
<span>Description</span>
|
|
1466
|
+
<input id="skill-dialog-description" class="form-input mcp-json" placeholder="Use when …" autocomplete="off" />
|
|
1467
|
+
</div>
|
|
1468
|
+
<div class="mcp-field">
|
|
1469
|
+
<span>Instructions (SKILL.md body)</span>
|
|
1470
|
+
<textarea id="skill-dialog-content" class="form-input mcp-json" spellcheck="false" rows="12" placeholder="# My skill Instructions here."></textarea>
|
|
1471
|
+
</div>
|
|
1472
|
+
<div id="skill-dialog-error" class="inline-result err" style="display:none"></div>
|
|
1473
|
+
<div class="mcp-dialog-actions">
|
|
1474
|
+
<button class="mcp-btn" type="button" onclick="closeSkillDialog()">Cancel</button>
|
|
1475
|
+
<button id="skill-dialog-submit" class="primary-action-btn" type="button" onclick="submitSkillDialog(this)">Save</button>
|
|
1476
|
+
</div>
|
|
1477
|
+
</dialog>`;
|
|
1478
|
+
function renderAdminPage(token) {
|
|
1479
|
+
const userLabel = token.platformUserName ?? token.platformUserId;
|
|
1480
|
+
const script = `
|
|
1481
|
+
const adminToken = ${JSON.stringify(token.token)};
|
|
1482
|
+
const defaultConversationKey = ${JSON.stringify(`${token.platform}:${token.conversationId}`)};
|
|
1483
|
+
${adminViewFunctionsScript}
|
|
1484
|
+
${adminViewStartupScript}`;
|
|
1485
|
+
return renderPortalShell({
|
|
1486
|
+
activeView: "admin",
|
|
1487
|
+
pageTitle: "Admin",
|
|
1488
|
+
identity: { primary: token.platform, secondary: userLabel },
|
|
1489
|
+
conversationSwitcher: { currentId: token.conversationId },
|
|
1490
|
+
body: adminViewBody,
|
|
1491
|
+
extraStyles: adminViewStyles,
|
|
1492
|
+
inlineScript: script,
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
function renderAdminErrorPage(message) {
|
|
1496
|
+
return renderPortalShell({
|
|
1497
|
+
activeView: "admin",
|
|
1498
|
+
pageTitle: "Admin",
|
|
1499
|
+
body: `<section class="card" style="text-align:center;padding:40px 32px">
|
|
1500
|
+
<p class="eyebrow">${PRODUCT_NAME} admin</p>
|
|
1501
|
+
<h1 class="page-title" style="margin:12px 0 16px">Access Denied</h1>
|
|
1502
|
+
<div class="err-msg">${esc(message)}</div>
|
|
1503
|
+
</section>`,
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
//# sourceMappingURL=portal.js.map
|