nikcli 0.0.6
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/.turbo/turbo-typecheck.log +1 -0
- package/AGENTS.md +27 -0
- package/Dockerfile +18 -0
- package/README.md +15 -0
- package/bin/nikcli +84 -0
- package/config.json +13 -0
- package/docs/tailscale-mobile/01-tailscale-setup.md +94 -0
- package/docs/tailscale-mobile/02-host-setup.md +115 -0
- package/docs/tailscale-mobile/03-phone-and-serve.md +134 -0
- package/docs/tailscale-mobile/README.md +59 -0
- package/examples/README.md +54 -0
- package/package.json +147 -0
- package/parsers-config.ts +253 -0
- package/script/build.ts +179 -0
- package/script/postinstall.mjs +125 -0
- package/script/publish-registries.ts +187 -0
- package/script/publish.ts +100 -0
- package/script/schema.ts +47 -0
- package/script/seed-e2e.ts +50 -0
- package/sequential-prancing-forest.md +373 -0
- package/src/acp/README.md +164 -0
- package/src/acp/agent.ts +1303 -0
- package/src/acp/session.ts +105 -0
- package/src/acp/types.ts +22 -0
- package/src/agent/agent.ts +528 -0
- package/src/agent/generate.txt +32 -0
- package/src/agent/prompt/compaction.txt +14 -0
- package/src/agent/prompt/explore.txt +18 -0
- package/src/agent/prompt/summary.txt +11 -0
- package/src/agent/prompt/title.txt +44 -0
- package/src/auth/index.ts +73 -0
- package/src/bun/index.ts +119 -0
- package/src/bun/registry.ts +54 -0
- package/src/bus/bus-event.ts +43 -0
- package/src/bus/global.ts +10 -0
- package/src/bus/index.ts +105 -0
- package/src/chatbot/handlers.ts +150 -0
- package/src/chatbot/index.ts +132 -0
- package/src/cli/bootstrap.ts +17 -0
- package/src/cli/cmd/acp.ts +69 -0
- package/src/cli/cmd/ads.ts +377 -0
- package/src/cli/cmd/agent.ts +259 -0
- package/src/cli/cmd/auth.ts +400 -0
- package/src/cli/cmd/chatbot.ts +420 -0
- package/src/cli/cmd/cmd.ts +7 -0
- package/src/cli/cmd/companion.ts +81 -0
- package/src/cli/cmd/connectors.ts +593 -0
- package/src/cli/cmd/debug/agent.ts +166 -0
- package/src/cli/cmd/debug/config.ts +16 -0
- package/src/cli/cmd/debug/file.ts +97 -0
- package/src/cli/cmd/debug/index.ts +48 -0
- package/src/cli/cmd/debug/lsp.ts +52 -0
- package/src/cli/cmd/debug/ripgrep.ts +87 -0
- package/src/cli/cmd/debug/scrap.ts +16 -0
- package/src/cli/cmd/debug/skill.ts +16 -0
- package/src/cli/cmd/debug/snapshot.ts +52 -0
- package/src/cli/cmd/export.ts +88 -0
- package/src/cli/cmd/generate.ts +38 -0
- package/src/cli/cmd/github.ts +412 -0
- package/src/cli/cmd/image-model.ts +128 -0
- package/src/cli/cmd/import.ts +201 -0
- package/src/cli/cmd/lovable.ts +128 -0
- package/src/cli/cmd/mcp.ts +738 -0
- package/src/cli/cmd/mobile.ts +223 -0
- package/src/cli/cmd/models.ts +77 -0
- package/src/cli/cmd/plug.ts +231 -0
- package/src/cli/cmd/pr.ts +104 -0
- package/src/cli/cmd/rag-model.ts +167 -0
- package/src/cli/cmd/remote.ts +416 -0
- package/src/cli/cmd/run.ts +589 -0
- package/src/cli/cmd/serve.ts +51 -0
- package/src/cli/cmd/session.ts +133 -0
- package/src/cli/cmd/speak-model.ts +204 -0
- package/src/cli/cmd/stats.ts +402 -0
- package/src/cli/cmd/tui/app.tsx +841 -0
- package/src/cli/cmd/tui/attach.ts +31 -0
- package/src/cli/cmd/tui/component/border.tsx +75 -0
- package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-command.tsx +172 -0
- package/src/cli/cmd/tui/component/dialog-config.tsx +291 -0
- package/src/cli/cmd/tui/component/dialog-connectors.tsx +440 -0
- package/src/cli/cmd/tui/component/dialog-image-model.tsx +97 -0
- package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog-model.tsx +234 -0
- package/src/cli/cmd/tui/component/dialog-provider.tsx +260 -0
- package/src/cli/cmd/tui/component/dialog-rag-model.tsx +217 -0
- package/src/cli/cmd/tui/component/dialog-remote.tsx +489 -0
- package/src/cli/cmd/tui/component/dialog-session-list.tsx +170 -0
- package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-settings/index.tsx +59 -0
- package/src/cli/cmd/tui/component/dialog-settings/prompt.tsx +40 -0
- package/src/cli/cmd/tui/component/dialog-settings/sidebar.tsx +39 -0
- package/src/cli/cmd/tui/component/dialog-settings/spinner.tsx +62 -0
- package/src/cli/cmd/tui/component/dialog-settings/ui.tsx +58 -0
- package/src/cli/cmd/tui/component/dialog-skills.tsx +117 -0
- package/src/cli/cmd/tui/component/dialog-speak-model.tsx +304 -0
- package/src/cli/cmd/tui/component/dialog-stash.tsx +87 -0
- package/src/cli/cmd/tui/component/dialog-status.tsx +165 -0
- package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
- package/src/cli/cmd/tui/component/dialog-theme-create.tsx +717 -0
- package/src/cli/cmd/tui/component/dialog-theme-list.tsx +52 -0
- package/src/cli/cmd/tui/component/dialog-workspace-list.tsx +350 -0
- package/src/cli/cmd/tui/component/error-component.tsx +91 -0
- package/src/cli/cmd/tui/component/logo.tsx +103 -0
- package/src/cli/cmd/tui/component/plugin-route-missing.tsx +14 -0
- package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +669 -0
- package/src/cli/cmd/tui/component/prompt/frecency.tsx +89 -0
- package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
- package/src/cli/cmd/tui/component/prompt/index.tsx +2165 -0
- package/src/cli/cmd/tui/component/prompt/stash.tsx +63 -0
- package/src/cli/cmd/tui/component/spinner.tsx +24 -0
- package/src/cli/cmd/tui/component/startup-loading.tsx +63 -0
- package/src/cli/cmd/tui/component/table/markdown-table.tsx +267 -0
- package/src/cli/cmd/tui/component/table-db/db/connections.ts +75 -0
- package/src/cli/cmd/tui/component/table-db/db/db-connection.ts +223 -0
- package/src/cli/cmd/tui/component/table-db/db/db-preview.ts +202 -0
- package/src/cli/cmd/tui/component/table-db/db/factory.ts +77 -0
- package/src/cli/cmd/tui/component/table-db/db/index.ts +9 -0
- package/src/cli/cmd/tui/component/table-db/db/mysql-connection.ts +330 -0
- package/src/cli/cmd/tui/component/table-db/db/postgres-connection.ts +338 -0
- package/src/cli/cmd/tui/component/table-db/db/sqlite-connection.ts +302 -0
- package/src/cli/cmd/tui/component/table-db/db/types.ts +108 -0
- package/src/cli/cmd/tui/component/table-db/table/dbedit-hooks.ts +74 -0
- package/src/cli/cmd/tui/component/table-db/table/index.ts +15 -0
- package/src/cli/cmd/tui/component/table-db/table/table-events.ts +54 -0
- package/src/cli/cmd/tui/component/table-db/table/table-formatters.ts +191 -0
- package/src/cli/cmd/tui/component/table-db/table/table-hooks.ts +105 -0
- package/src/cli/cmd/tui/component/table-db/table/table-keyboard-handler.ts +255 -0
- package/src/cli/cmd/tui/component/table-db/table/table-layout-engine.ts +208 -0
- package/src/cli/cmd/tui/component/table-db/table/table-renderable.ts +486 -0
- package/src/cli/cmd/tui/component/table-db/table/table-selection-manager.ts +136 -0
- package/src/cli/cmd/tui/component/table-db/table/table-state.ts +198 -0
- package/src/cli/cmd/tui/component/table-db/table/types.ts +69 -0
- package/src/cli/cmd/tui/component/table-db/ui/db-visualizer.tsx +71 -0
- package/src/cli/cmd/tui/component/table-db/ui/index.ts +2 -0
- package/src/cli/cmd/tui/component/table-db/ui/table-renderer.ts +607 -0
- package/src/cli/cmd/tui/component/textarea-keybindings.ts +73 -0
- package/src/cli/cmd/tui/component/tips.tsx +195 -0
- package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
- package/src/cli/cmd/tui/context/args.tsx +14 -0
- package/src/cli/cmd/tui/context/directory.ts +13 -0
- package/src/cli/cmd/tui/context/exit.tsx +24 -0
- package/src/cli/cmd/tui/context/helper.tsx +25 -0
- package/src/cli/cmd/tui/context/keybind.tsx +102 -0
- package/src/cli/cmd/tui/context/kv.tsx +52 -0
- package/src/cli/cmd/tui/context/local.tsx +458 -0
- package/src/cli/cmd/tui/context/plugin-keybinds.ts +41 -0
- package/src/cli/cmd/tui/context/prompt.tsx +18 -0
- package/src/cli/cmd/tui/context/route.tsx +54 -0
- package/src/cli/cmd/tui/context/sdk.tsx +128 -0
- package/src/cli/cmd/tui/context/server.tsx +8 -0
- package/src/cli/cmd/tui/context/sync.tsx +510 -0
- package/src/cli/cmd/tui/context/theme/abyss.json +233 -0
- package/src/cli/cmd/tui/context/theme/apple.json +235 -0
- package/src/cli/cmd/tui/context/theme/arctic.json +232 -0
- package/src/cli/cmd/tui/context/theme/aura.json +69 -0
- package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
- package/src/cli/cmd/tui/context/theme/ayuai.json +229 -0
- package/src/cli/cmd/tui/context/theme/blood.json +229 -0
- package/src/cli/cmd/tui/context/theme/carbonfox.json +248 -0
- package/src/cli/cmd/tui/context/theme/catmoe.json +235 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-latte.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin.json +259 -0
- package/src/cli/cmd/tui/context/theme/charcoal.json +230 -0
- package/src/cli/cmd/tui/context/theme/chromatic.json +235 -0
- package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
- package/src/cli/cmd/tui/context/theme/cosmic.json +234 -0
- package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
- package/src/cli/cmd/tui/context/theme/cyber.json +235 -0
- package/src/cli/cmd/tui/context/theme/dawnfox.json +229 -0
- package/src/cli/cmd/tui/context/theme/dimension.json +235 -0
- package/src/cli/cmd/tui/context/theme/dracula-official.json +222 -0
- package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
- package/src/cli/cmd/tui/context/theme/dream.json +235 -0
- package/src/cli/cmd/tui/context/theme/duo.json +235 -0
- package/src/cli/cmd/tui/context/theme/dusk.json +235 -0
- package/src/cli/cmd/tui/context/theme/ebony.json +232 -0
- package/src/cli/cmd/tui/context/theme/equilibrium.json +232 -0
- package/src/cli/cmd/tui/context/theme/ethereal.json +235 -0
- package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
- package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
- package/src/cli/cmd/tui/context/theme/fusion.json +235 -0
- package/src/cli/cmd/tui/context/theme/ghost.json +235 -0
- package/src/cli/cmd/tui/context/theme/github-dark.json +229 -0
- package/src/cli/cmd/tui/context/theme/github-dimmed.json +231 -0
- package/src/cli/cmd/tui/context/theme/github-light.json +229 -0
- package/src/cli/cmd/tui/context/theme/github.json +233 -0
- package/src/cli/cmd/tui/context/theme/glass.json +235 -0
- package/src/cli/cmd/tui/context/theme/gold.json +235 -0
- package/src/cli/cmd/tui/context/theme/gone.json +234 -0
- package/src/cli/cmd/tui/context/theme/greyscale.json +229 -0
- package/src/cli/cmd/tui/context/theme/gruvbox.json +242 -0
- package/src/cli/cmd/tui/context/theme/hacker.json +229 -0
- package/src/cli/cmd/tui/context/theme/holo.json +235 -0
- package/src/cli/cmd/tui/context/theme/ink.json +235 -0
- package/src/cli/cmd/tui/context/theme/jet.json +233 -0
- package/src/cli/cmd/tui/context/theme/kanagawa.json +227 -0
- package/src/cli/cmd/tui/context/theme/lavender.json +236 -0
- package/src/cli/cmd/tui/context/theme/lightph.json +235 -0
- package/src/cli/cmd/tui/context/theme/lucent-orng.json +237 -0
- package/src/cli/cmd/tui/context/theme/material-ocean.json +230 -0
- package/src/cli/cmd/tui/context/theme/material.json +235 -0
- package/src/cli/cmd/tui/context/theme/matrix.json +227 -0
- package/src/cli/cmd/tui/context/theme/mercury.json +245 -0
- package/src/cli/cmd/tui/context/theme/midnight.json +235 -0
- package/src/cli/cmd/tui/context/theme/modern.json +235 -0
- package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
- package/src/cli/cmd/tui/context/theme/muted.json +229 -0
- package/src/cli/cmd/tui/context/theme/neon.json +229 -0
- package/src/cli/cmd/tui/context/theme/neonfusion.json +235 -0
- package/src/cli/cmd/tui/context/theme/neutral.json +235 -0
- package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
- package/src/cli/cmd/tui/context/theme/nikcli.json +245 -0
- package/src/cli/cmd/tui/context/theme/nord.json +223 -0
- package/src/cli/cmd/tui/context/theme/nordic.json +235 -0
- package/src/cli/cmd/tui/context/theme/nova.json +235 -0
- package/src/cli/cmd/tui/context/theme/obsidian.json +234 -0
- package/src/cli/cmd/tui/context/theme/one-dark.json +231 -0
- package/src/cli/cmd/tui/context/theme/one-pro.json +229 -0
- package/src/cli/cmd/tui/context/theme/onyx.json +233 -0
- package/src/cli/cmd/tui/context/theme/orng.json +249 -0
- package/src/cli/cmd/tui/context/theme/osaka-jade.json +240 -0
- package/src/cli/cmd/tui/context/theme/oxocarbon.json +229 -0
- package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
- package/src/cli/cmd/tui/context/theme/poimandres.json +230 -0
- package/src/cli/cmd/tui/context/theme/prism.json +235 -0
- package/src/cli/cmd/tui/context/theme/radiant.json +235 -0
- package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
- package/src/cli/cmd/tui/context/theme/shadow.json +235 -0
- package/src/cli/cmd/tui/context/theme/silicon.json +235 -0
- package/src/cli/cmd/tui/context/theme/slate.json +233 -0
- package/src/cli/cmd/tui/context/theme/soft.json +235 -0
- package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
- package/src/cli/cmd/tui/context/theme/spectrum.json +235 -0
- package/src/cli/cmd/tui/context/theme/starlight.json +233 -0
- package/src/cli/cmd/tui/context/theme/sunrise.json +235 -0
- package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
- package/src/cli/cmd/tui/context/theme/tech.json +235 -0
- package/src/cli/cmd/tui/context/theme/tokyonight-storm.json +245 -0
- package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
- package/src/cli/cmd/tui/context/theme/vapor.json +235 -0
- package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
- package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
- package/src/cli/cmd/tui/context/theme/vivid.json +232 -0
- package/src/cli/cmd/tui/context/theme/void.json +235 -0
- package/src/cli/cmd/tui/context/theme/vscode.json +235 -0
- package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
- package/src/cli/cmd/tui/context/theme/zinc.json +236 -0
- package/src/cli/cmd/tui/context/theme.tsx +1303 -0
- package/src/cli/cmd/tui/event.ts +48 -0
- package/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx +152 -0
- package/src/cli/cmd/tui/feature-plugins/home/tips.tsx +50 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/context.tsx +63 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/files.tsx +62 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/footer.tsx +93 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/lsp.tsx +66 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/mcp.tsx +96 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/todo.tsx +48 -0
- package/src/cli/cmd/tui/feature-plugins/system/plugins.tsx +288 -0
- package/src/cli/cmd/tui/plugin/api.tsx +407 -0
- package/src/cli/cmd/tui/plugin/index.ts +3 -0
- package/src/cli/cmd/tui/plugin/internal.ts +25 -0
- package/src/cli/cmd/tui/plugin/runtime.ts +1048 -0
- package/src/cli/cmd/tui/plugin/slots.tsx +61 -0
- package/src/cli/cmd/tui/routes/home.tsx +153 -0
- package/src/cli/cmd/tui/routes/session/dbedit.tsx +474 -0
- package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +65 -0
- package/src/cli/cmd/tui/routes/session/dialog-message.tsx +110 -0
- package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +105 -0
- package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
- package/src/cli/cmd/tui/routes/session/footer.tsx +75 -0
- package/src/cli/cmd/tui/routes/session/header.tsx +177 -0
- package/src/cli/cmd/tui/routes/session/index.tsx +2280 -0
- package/src/cli/cmd/tui/routes/session/permission.tsx +540 -0
- package/src/cli/cmd/tui/routes/session/question.tsx +435 -0
- package/src/cli/cmd/tui/routes/session/sidebar.tsx +313 -0
- package/src/cli/cmd/tui/thread.ts +174 -0
- package/src/cli/cmd/tui/ui/dialog-alert.tsx +57 -0
- package/src/cli/cmd/tui/ui/dialog-confirm.tsx +83 -0
- package/src/cli/cmd/tui/ui/dialog-export-options.tsx +204 -0
- package/src/cli/cmd/tui/ui/dialog-help.tsx +38 -0
- package/src/cli/cmd/tui/ui/dialog-prompt.tsx +102 -0
- package/src/cli/cmd/tui/ui/dialog-select.tsx +389 -0
- package/src/cli/cmd/tui/ui/dialog.tsx +180 -0
- package/src/cli/cmd/tui/ui/link.tsx +34 -0
- package/src/cli/cmd/tui/ui/spinner.ts +368 -0
- package/src/cli/cmd/tui/ui/toast.tsx +138 -0
- package/src/cli/cmd/tui/util/clipboard.ts +154 -0
- package/src/cli/cmd/tui/util/editor.ts +32 -0
- package/src/cli/cmd/tui/util/signal.ts +7 -0
- package/src/cli/cmd/tui/util/terminal.ts +114 -0
- package/src/cli/cmd/tui/util/transcript.ts +98 -0
- package/src/cli/cmd/tui/win32.ts +110 -0
- package/src/cli/cmd/tui/worker.ts +156 -0
- package/src/cli/cmd/uninstall.ts +357 -0
- package/src/cli/cmd/upgrade.ts +72 -0
- package/src/cli/cmd/web.ts +87 -0
- package/src/cli/cmd/workspace-serve.ts +16 -0
- package/src/cli/error.ts +57 -0
- package/src/cli/network.ts +55 -0
- package/src/cli/remote/index.ts +36 -0
- package/src/cli/remote/notifications.ts +104 -0
- package/src/cli/remote/qr-renderer.ts +86 -0
- package/src/cli/remote/remote-service.ts +757 -0
- package/src/cli/remote/session-manager.ts +284 -0
- package/src/cli/remote/subagent-hooks.ts +151 -0
- package/src/cli/remote/types.ts +121 -0
- package/src/cli/ui.ts +96 -0
- package/src/cli/upgrade.ts +25 -0
- package/src/command/index.ts +174 -0
- package/src/command/template/initialize.txt +10 -0
- package/src/command/template/review.txt +99 -0
- package/src/config/config.ts +1760 -0
- package/src/config/markdown.ts +88 -0
- package/src/config/migrate-tui-config.ts +155 -0
- package/src/config/paths.ts +174 -0
- package/src/config/tui-schema.ts +36 -0
- package/src/config/tui.ts +209 -0
- package/src/connectors/api/base.ts +75 -0
- package/src/connectors/api/figma.ts +103 -0
- package/src/connectors/api/github.ts +247 -0
- package/src/connectors/api/lovable.ts +126 -0
- package/src/connectors/api/slack.ts +137 -0
- package/src/connectors/auth.ts +68 -0
- package/src/connectors/cache.ts +119 -0
- package/src/connectors/credentials.ts +81 -0
- package/src/connectors/index.ts +202 -0
- package/src/connectors/registry.ts +358 -0
- package/src/docs/context.ts +120 -0
- package/src/docs/library.ts +189 -0
- package/src/env/index.ts +26 -0
- package/src/file/ignore.ts +83 -0
- package/src/file/index.ts +411 -0
- package/src/file/ripgrep.ts +402 -0
- package/src/file/time.ts +65 -0
- package/src/file/watcher.ts +127 -0
- package/src/flag/flag.ts +128 -0
- package/src/format/formatter.ts +356 -0
- package/src/format/index.ts +137 -0
- package/src/global/index.ts +57 -0
- package/src/id/id.ts +83 -0
- package/src/ide/index.ts +76 -0
- package/src/index.ts +184 -0
- package/src/installation/index.ts +246 -0
- package/src/lsp/client.ts +250 -0
- package/src/lsp/index.ts +483 -0
- package/src/lsp/language.ts +119 -0
- package/src/lsp/server.ts +2046 -0
- package/src/mcp/auth.ts +121 -0
- package/src/mcp/index.ts +860 -0
- package/src/mcp/oauth-callback.ts +198 -0
- package/src/mcp/oauth-provider.ts +148 -0
- package/src/mobile/auth.ts +97 -0
- package/src/mobile/github-repo.ts +185 -0
- package/src/patch/index.ts +631 -0
- package/src/permission/arity.ts +150 -0
- package/src/permission/dbedit.ts +236 -0
- package/src/permission/index.ts +210 -0
- package/src/permission/next.ts +287 -0
- package/src/plugin/codex.ts +493 -0
- package/src/plugin/copilot.ts +261 -0
- package/src/plugin/index.ts +714 -0
- package/src/plugin/install.ts +379 -0
- package/src/plugin/meta.ts +165 -0
- package/src/plugin/shared.ts +188 -0
- package/src/project/bootstrap.ts +35 -0
- package/src/project/instance.ts +84 -0
- package/src/project/project.ts +373 -0
- package/src/project/state.ts +66 -0
- package/src/project/vcs.ts +76 -0
- package/src/prompt/stash-store.ts +93 -0
- package/src/provider/auth.ts +147 -0
- package/src/provider/models-macro.ts +22 -0
- package/src/provider/models.ts +216 -0
- package/src/provider/provider.ts +1483 -0
- package/src/provider/sdk/openai-compatible/src/README.md +5 -0
- package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
- package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
- package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
- package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +22 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1732 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
- package/src/provider/transform.ts +828 -0
- package/src/pty/index.ts +241 -0
- package/src/question/index.ts +171 -0
- package/src/rag/chunk.ts +43 -0
- package/src/rag/embed.ts +179 -0
- package/src/rag/index.ts +376 -0
- package/src/rag/storage.ts +76 -0
- package/src/scheduler/index.ts +61 -0
- package/src/server/error.ts +36 -0
- package/src/server/event.ts +7 -0
- package/src/server/mdns.ts +59 -0
- package/src/server/routes/chatbot.ts +205 -0
- package/src/server/routes/companion.ts +729 -0
- package/src/server/routes/config.ts +92 -0
- package/src/server/routes/connectors.ts +121 -0
- package/src/server/routes/dbedit.ts +76 -0
- package/src/server/routes/experimental.ts +210 -0
- package/src/server/routes/file.ts +197 -0
- package/src/server/routes/global.ts +135 -0
- package/src/server/routes/mcp.ts +225 -0
- package/src/server/routes/mobile.ts +2044 -0
- package/src/server/routes/permission.ts +68 -0
- package/src/server/routes/project.ts +82 -0
- package/src/server/routes/provider.ts +235 -0
- package/src/server/routes/pty.ts +169 -0
- package/src/server/routes/question.ts +98 -0
- package/src/server/routes/session.ts +968 -0
- package/src/server/routes/tui.ts +379 -0
- package/src/server/routes/workspace.ts +104 -0
- package/src/server/server.ts +761 -0
- package/src/server/ssh.ts +207 -0
- package/src/session/auth.ts +402 -0
- package/src/session/compaction.ts +253 -0
- package/src/session/generate.ts +38 -0
- package/src/session/index.ts +598 -0
- package/src/session/llm.ts +273 -0
- package/src/session/message-v2.ts +836 -0
- package/src/session/message.ts +189 -0
- package/src/session/processor.ts +408 -0
- package/src/session/prompt/anthropic-20250930.txt +165 -0
- package/src/session/prompt/anthropic.txt +105 -0
- package/src/session/prompt/anthropic_spoof.txt +1 -0
- package/src/session/prompt/beast.txt +147 -0
- package/src/session/prompt/build-switch.txt +5 -0
- package/src/session/prompt/codex_header.txt +79 -0
- package/src/session/prompt/copilot-gpt-5.txt +143 -0
- package/src/session/prompt/gemini.txt +155 -0
- package/src/session/prompt/max-steps.txt +16 -0
- package/src/session/prompt/plan-reminder-anthropic.txt +67 -0
- package/src/session/prompt/plan.txt +25 -0
- package/src/session/prompt/qwen.txt +108 -0
- package/src/session/prompt.ts +1942 -0
- package/src/session/retry.ts +90 -0
- package/src/session/revert.ts +120 -0
- package/src/session/stats.ts +404 -0
- package/src/session/status.ts +84 -0
- package/src/session/summary.ts +184 -0
- package/src/session/system.ts +195 -0
- package/src/session/toast.tsx +105 -0
- package/src/session/todo.ts +258 -0
- package/src/session/uninstall.ts +357 -0
- package/src/share/share-next.ts +421 -0
- package/src/share/share.ts +92 -0
- package/src/shell/shell.ts +65 -0
- package/src/skill/index.ts +1 -0
- package/src/skill/skill.ts +232 -0
- package/src/snapshot/index.ts +297 -0
- package/src/storage/storage.ts +227 -0
- package/src/tool/apply_patch.ts +288 -0
- package/src/tool/apply_patch.txt +33 -0
- package/src/tool/bash.ts +252 -0
- package/src/tool/bash.txt +115 -0
- package/src/tool/batch.ts +175 -0
- package/src/tool/batch.txt +24 -0
- package/src/tool/codesearch.ts +132 -0
- package/src/tool/codesearch.txt +12 -0
- package/src/tool/context_collect.ts +152 -0
- package/src/tool/context_collect.txt +9 -0
- package/src/tool/context_diagnostics.ts +81 -0
- package/src/tool/context_diagnostics.txt +5 -0
- package/src/tool/context_related.ts +117 -0
- package/src/tool/context_related.txt +5 -0
- package/src/tool/context_search.ts +108 -0
- package/src/tool/context_search.txt +8 -0
- package/src/tool/db-diff.ts +434 -0
- package/src/tool/db-table.txt +15 -0
- package/src/tool/docs_add.ts +50 -0
- package/src/tool/docs_add.txt +5 -0
- package/src/tool/docs_context.ts +56 -0
- package/src/tool/docs_context.txt +4 -0
- package/src/tool/docs_gap_report.ts +79 -0
- package/src/tool/docs_gap_report.txt +7 -0
- package/src/tool/docs_load.ts +41 -0
- package/src/tool/docs_load.txt +4 -0
- package/src/tool/docs_request.ts +129 -0
- package/src/tool/docs_request.txt +7 -0
- package/src/tool/docs_search.ts +51 -0
- package/src/tool/docs_search.txt +6 -0
- package/src/tool/docs_unload.ts +38 -0
- package/src/tool/docs_unload.txt +5 -0
- package/src/tool/edit.ts +614 -0
- package/src/tool/edit.txt +10 -0
- package/src/tool/external-directory.ts +32 -0
- package/src/tool/generate_image.ts +174 -0
- package/src/tool/generate_image.txt +12 -0
- package/src/tool/glob.ts +79 -0
- package/src/tool/glob.txt +6 -0
- package/src/tool/grep.ts +153 -0
- package/src/tool/grep.txt +8 -0
- package/src/tool/invalid.ts +17 -0
- package/src/tool/ls.ts +116 -0
- package/src/tool/ls.txt +1 -0
- package/src/tool/lsp.ts +96 -0
- package/src/tool/lsp.txt +19 -0
- package/src/tool/memory_search.ts +141 -0
- package/src/tool/memory_search.txt +8 -0
- package/src/tool/multiedit.ts +46 -0
- package/src/tool/multiedit.txt +41 -0
- package/src/tool/plan-enter.txt +14 -0
- package/src/tool/plan-exit.txt +13 -0
- package/src/tool/plan.ts +130 -0
- package/src/tool/question.ts +33 -0
- package/src/tool/question.txt +10 -0
- package/src/tool/rag_index.ts +77 -0
- package/src/tool/rag_index.txt +10 -0
- package/src/tool/rag_reset.ts +26 -0
- package/src/tool/rag_reset.txt +4 -0
- package/src/tool/rag_search.ts +62 -0
- package/src/tool/rag_search.txt +6 -0
- package/src/tool/rag_status.ts +45 -0
- package/src/tool/rag_status.txt +4 -0
- package/src/tool/read.ts +203 -0
- package/src/tool/read.txt +12 -0
- package/src/tool/registry.ts +214 -0
- package/src/tool/skill.ts +169 -0
- package/src/tool/skill.txt +3 -0
- package/src/tool/smart_docs.ts +74 -0
- package/src/tool/smart_docs.txt +7 -0
- package/src/tool/speak/elevenlabs.ts +201 -0
- package/src/tool/speak/openrouter.ts +240 -0
- package/src/tool/speak/provider.ts +83 -0
- package/src/tool/speak.ts +440 -0
- package/src/tool/task.ts +194 -0
- package/src/tool/task.txt +60 -0
- package/src/tool/todo.ts +53 -0
- package/src/tool/todoread.txt +14 -0
- package/src/tool/todowrite.txt +167 -0
- package/src/tool/tool.ts +87 -0
- package/src/tool/tree.ts +218 -0
- package/src/tool/tree.txt +8 -0
- package/src/tool/truncation.ts +106 -0
- package/src/tool/use-connector.ts +47 -0
- package/src/tool/voice.ts +188 -0
- package/src/tool/webfetch.ts +205 -0
- package/src/tool/webfetch.txt +13 -0
- package/src/tool/websearch.ts +150 -0
- package/src/tool/websearch.txt +14 -0
- package/src/tool/write.ts +80 -0
- package/src/tool/write.txt +8 -0
- package/src/util/archive.ts +16 -0
- package/src/util/color.ts +19 -0
- package/src/util/context.ts +25 -0
- package/src/util/defer.ts +12 -0
- package/src/util/error.ts +77 -0
- package/src/util/eventloop.ts +20 -0
- package/src/util/filesystem.ts +125 -0
- package/src/util/flock.ts +329 -0
- package/src/util/fn.ts +11 -0
- package/src/util/format.ts +20 -0
- package/src/util/hash.ts +7 -0
- package/src/util/iife.ts +3 -0
- package/src/util/keybind.ts +103 -0
- package/src/util/lazy.ts +18 -0
- package/src/util/locale.ts +81 -0
- package/src/util/lock.ts +98 -0
- package/src/util/log.ts +180 -0
- package/src/util/network.ts +9 -0
- package/src/util/process.ts +15 -0
- package/src/util/queue.ts +32 -0
- package/src/util/record.ts +3 -0
- package/src/util/rpc.ts +66 -0
- package/src/util/scrap.ts +10 -0
- package/src/util/signal.ts +12 -0
- package/src/util/timeout.ts +14 -0
- package/src/util/token.ts +7 -0
- package/src/util/wildcard.ts +56 -0
- package/src/workspace/adaptors/index.ts +271 -0
- package/src/workspace/adaptors/types.ts +14 -0
- package/src/workspace/adaptors/worktree.ts +31 -0
- package/src/workspace/config.ts +19 -0
- package/src/workspace/index.ts +223 -0
- package/src/workspace/session-proxy-middleware.ts +97 -0
- package/src/workspace/sse.ts +66 -0
- package/src/workspace/workspace-context.ts +23 -0
- package/src/workspace/workspace-server/routes.ts +33 -0
- package/src/workspace/workspace-server/server.ts +47 -0
- package/src/worktree/index.ts +487 -0
- package/sst-env.d.ts +10 -0
- package/test/benchmark.test.ts +121 -0
- package/test/build-optimizations.test.ts +124 -0
- package/test/id-benchmark.test.ts +132 -0
- package/test/optimizations.test.ts +302 -0
- package/test/preload.ts +1 -0
- package/test/solidjs-benchmark.test.ts +262 -0
- package/test/solidjs-optimizations.test.ts +259 -0
- package/test/tui-benchmark.test.ts +230 -0
- package/test/wildcard-benchmark.test.ts +180 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
import type { Hooks, PluginInput, Plugin as PluginInstance } from "@nikcli-ai/plugin"
|
|
2
|
+
import { createNikcliClient } from "@nikcli-ai/sdk"
|
|
3
|
+
import os from "os"
|
|
4
|
+
import path from "path"
|
|
5
|
+
import { fileURLToPath } from "url"
|
|
6
|
+
import { NamedError } from "@nikcli-ai/util/error"
|
|
7
|
+
import { retry } from "@nikcli-ai/util/retry"
|
|
8
|
+
import { Bus } from "../bus"
|
|
9
|
+
import { BunProc } from "../bun"
|
|
10
|
+
import { Config } from "../config/config"
|
|
11
|
+
import { resolveCredential } from "../connectors/credentials"
|
|
12
|
+
import { Flag } from "../flag/flag"
|
|
13
|
+
import { Instance } from "../project/instance"
|
|
14
|
+
import { Server } from "../server/server"
|
|
15
|
+
import { Session } from "../session"
|
|
16
|
+
import { Log } from "../util/log"
|
|
17
|
+
import { AsyncQueue } from "../util/queue"
|
|
18
|
+
import { withTimeout } from "../util/timeout"
|
|
19
|
+
import { CodexAuthPlugin } from "./codex"
|
|
20
|
+
import { CopilotAuthPlugin } from "./copilot"
|
|
21
|
+
import { readV1Plugin, readPluginId, resolvePluginId, pluginSource } from "./shared"
|
|
22
|
+
import type { PluginModule } from "@nikcli-ai/plugin"
|
|
23
|
+
|
|
24
|
+
type NotifyChannel = "macos" | "slack" | "discord"
|
|
25
|
+
type NotifyPriority = "low" | "normal" | "high" | "critical"
|
|
26
|
+
type NotifyEventKey = "sessionIdle" | "sessionError" | "permissionAsked" | "questionAsked"
|
|
27
|
+
|
|
28
|
+
type NotifyJob = {
|
|
29
|
+
title: string
|
|
30
|
+
body: string
|
|
31
|
+
priority: NotifyPriority
|
|
32
|
+
source: string
|
|
33
|
+
sessionID?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type MacIcon = {
|
|
37
|
+
value: string
|
|
38
|
+
remote: boolean
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type PermissionAsked = {
|
|
42
|
+
sessionID: string
|
|
43
|
+
permission: string
|
|
44
|
+
patterns?: string[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type QuestionAsked = {
|
|
48
|
+
sessionID: string
|
|
49
|
+
questions?: Array<{ header: string; question: string }>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type SessionErrorEvent = {
|
|
53
|
+
sessionID?: string
|
|
54
|
+
error?: { name?: string; data?: { message?: string } }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type SessionStatusEvent = {
|
|
58
|
+
sessionID: string
|
|
59
|
+
status: { type: "idle" | "busy" | "retry"; attempt?: number; message?: string; next?: number }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type NotifyConfig = NonNullable<NonNullable<Config.Info["notifications"]>["notify"]>
|
|
63
|
+
type RateState = { window: number; count: number }
|
|
64
|
+
type BreakerState = { fails: number; openUntil: number }
|
|
65
|
+
type ConnectorEntry = NonNullable<Config.Info["connectors"]>[string]
|
|
66
|
+
|
|
67
|
+
const notifyLog = Log.create({ service: "notify" })
|
|
68
|
+
|
|
69
|
+
const DEFAULT_RATE_WINDOW_MS = 60_000
|
|
70
|
+
const DEFAULT_RATE_MAX = 20
|
|
71
|
+
const DEFAULT_RETRY_ATTEMPTS = 3
|
|
72
|
+
const DEFAULT_RETRY_DELAY = 500
|
|
73
|
+
const DEFAULT_RETRY_FACTOR = 2
|
|
74
|
+
const DEFAULT_RETRY_MAX_DELAY = 10_000
|
|
75
|
+
const DEFAULT_TIMEOUT_MS = 10_000
|
|
76
|
+
const DEFAULT_BREAKER_FAILURES = 3
|
|
77
|
+
const DEFAULT_BREAKER_COOLDOWN_MS = 120_000
|
|
78
|
+
const DEFAULT_IDLE_MIN_MS = 30_000
|
|
79
|
+
const DEFAULT_QUIET_SUPPRESS: NotifyChannel[] = ["macos"]
|
|
80
|
+
|
|
81
|
+
const notifyState = Instance.state(() => ({
|
|
82
|
+
queue: new AsyncQueue<NotifyJob>(),
|
|
83
|
+
started: false,
|
|
84
|
+
rate: new Map<NotifyChannel, RateState>(),
|
|
85
|
+
breaker: new Map<NotifyChannel, BreakerState>(),
|
|
86
|
+
busy: new Map<string, number>(),
|
|
87
|
+
config: undefined as Config.Info | undefined,
|
|
88
|
+
}))
|
|
89
|
+
|
|
90
|
+
function isConnector(entry: ConnectorEntry | undefined): entry is Config.Connector {
|
|
91
|
+
return typeof entry === "object" && entry !== null && "type" in entry
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function flag(value: boolean | undefined, fallback: boolean) {
|
|
95
|
+
if (value === undefined) return fallback
|
|
96
|
+
return value
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function settings(config: Config.Info | undefined): NotifyConfig {
|
|
100
|
+
return (config?.notifications?.notify ?? {}) as NotifyConfig
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function eventOn(config: NotifyConfig | undefined, key: NotifyEventKey, fallback: boolean) {
|
|
104
|
+
const events = config?.events
|
|
105
|
+
if (!events) return fallback
|
|
106
|
+
const value = events[key]
|
|
107
|
+
if (value === undefined) return fallback
|
|
108
|
+
return value
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function rateConfig(config: NotifyConfig | undefined) {
|
|
112
|
+
const input = config?.rateLimit
|
|
113
|
+
const windowMs = input?.windowMs ?? DEFAULT_RATE_WINDOW_MS
|
|
114
|
+
const maxPerWindow = input?.maxPerWindow ?? DEFAULT_RATE_MAX
|
|
115
|
+
return {
|
|
116
|
+
windowMs: Math.max(1000, windowMs),
|
|
117
|
+
maxPerWindow: Math.max(1, maxPerWindow),
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function retryConfig(config: NotifyConfig | undefined) {
|
|
122
|
+
const input = config?.retry
|
|
123
|
+
return {
|
|
124
|
+
attempts: input?.attempts ?? DEFAULT_RETRY_ATTEMPTS,
|
|
125
|
+
delay: input?.delay ?? DEFAULT_RETRY_DELAY,
|
|
126
|
+
factor: input?.factor ?? DEFAULT_RETRY_FACTOR,
|
|
127
|
+
maxDelay: input?.maxDelay ?? DEFAULT_RETRY_MAX_DELAY,
|
|
128
|
+
timeoutMs: input?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function breakerConfig(config: NotifyConfig | undefined) {
|
|
133
|
+
const input = config?.breaker
|
|
134
|
+
const failures = input?.failures ?? DEFAULT_BREAKER_FAILURES
|
|
135
|
+
const cooldownMs = input?.cooldownMs ?? DEFAULT_BREAKER_COOLDOWN_MS
|
|
136
|
+
return {
|
|
137
|
+
failures: Math.max(1, failures),
|
|
138
|
+
cooldownMs: Math.max(1000, cooldownMs),
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function idleMin(config: NotifyConfig | undefined) {
|
|
143
|
+
const value = config?.idleMinMs
|
|
144
|
+
if (value && value > 0) return value
|
|
145
|
+
return DEFAULT_IDLE_MIN_MS
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function preview(items: string[], max: number) {
|
|
149
|
+
if (items.length === 0) return ""
|
|
150
|
+
if (items.length <= max) return items.join(", ")
|
|
151
|
+
const head = items.slice(0, max).join(", ")
|
|
152
|
+
return `${head} +${items.length - max} more`
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function formatMessage(title: string, body: string) {
|
|
156
|
+
if (!body) return title
|
|
157
|
+
return `${title}\n${body}`
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function slackText(title: string, body: string) {
|
|
161
|
+
if (!body) return `*${title}*`
|
|
162
|
+
return `*${title}*\n${body}`
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function span(ms: number) {
|
|
166
|
+
const total = Math.max(1, Math.round(ms / 1000))
|
|
167
|
+
const mins = Math.floor(total / 60)
|
|
168
|
+
const secs = total % 60
|
|
169
|
+
if (mins === 0) return `${secs}s`
|
|
170
|
+
if (secs === 0) return `${mins}m`
|
|
171
|
+
return `${mins}m ${secs}s`
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function errorText(error: { name?: string; data?: { message?: string } } | undefined) {
|
|
175
|
+
if (!error) return "Unknown error"
|
|
176
|
+
const data = typeof error === "object" ? error.data : undefined
|
|
177
|
+
if (data && typeof data.message === "string" && data.message.length > 0) return data.message
|
|
178
|
+
const message =
|
|
179
|
+
typeof (error as { message?: string }).message === "string" ? (error as { message?: string }).message : ""
|
|
180
|
+
if (message) return message
|
|
181
|
+
if (typeof error.name === "string" && error.name.length > 0) return error.name
|
|
182
|
+
return "Unknown error"
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function clock(value: string | undefined) {
|
|
186
|
+
if (!value) return undefined
|
|
187
|
+
const parts = value.split(":")
|
|
188
|
+
if (parts.length !== 2) return undefined
|
|
189
|
+
const hour = Number(parts[0])
|
|
190
|
+
const minute = Number(parts[1])
|
|
191
|
+
if (!Number.isFinite(hour) || !Number.isFinite(minute)) return undefined
|
|
192
|
+
if (hour < 0 || hour > 23) return undefined
|
|
193
|
+
if (minute < 0 || minute > 59) return undefined
|
|
194
|
+
return hour * 60 + minute
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function quiet(config: NotifyConfig | undefined, channel: NotifyChannel, now: Date) {
|
|
198
|
+
const hours = config?.quietHours
|
|
199
|
+
if (!hours) return false
|
|
200
|
+
if (hours.enabled === false) return false
|
|
201
|
+
const start = clock(hours.start)
|
|
202
|
+
const end = clock(hours.end)
|
|
203
|
+
if (start === undefined || end === undefined) return false
|
|
204
|
+
const minutes = now.getHours() * 60 + now.getMinutes()
|
|
205
|
+
const active = start < end ? minutes >= start && minutes < end : minutes >= start || minutes < end
|
|
206
|
+
if (!active) return false
|
|
207
|
+
const suppress = hours.suppress ?? DEFAULT_QUIET_SUPPRESS
|
|
208
|
+
return suppress.includes(channel)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function slackReady(config: NotifyConfig | undefined) {
|
|
212
|
+
const slack = config?.slack
|
|
213
|
+
if (!slack) return false
|
|
214
|
+
if (slack.enabled === false) return false
|
|
215
|
+
if (!slack.connector) return false
|
|
216
|
+
const channel = slack.channel ?? Flag.NIKCLI_SLACK_CHANNEL
|
|
217
|
+
if (!channel) return false
|
|
218
|
+
return true
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function discordReady(config: NotifyConfig | undefined) {
|
|
222
|
+
const discord = config?.discord
|
|
223
|
+
if (!discord) return false
|
|
224
|
+
if (discord.enabled === false) return false
|
|
225
|
+
const webhook = discord.webhook ?? Flag.NIKCLI_DISCORD_WEBHOOK_URL
|
|
226
|
+
if (!webhook) return false
|
|
227
|
+
return true
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function available(config: NotifyConfig | undefined) {
|
|
231
|
+
const list: NotifyChannel[] = []
|
|
232
|
+
if (config?.macos === true && process.platform === "darwin") list.push("macos")
|
|
233
|
+
if (slackReady(config)) list.push("slack")
|
|
234
|
+
if (discordReady(config)) list.push("discord")
|
|
235
|
+
return list
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function iconPath(value: string) {
|
|
239
|
+
if (!value) return undefined
|
|
240
|
+
if (value === "~") return os.homedir()
|
|
241
|
+
if (value.startsWith("~/")) return path.join(os.homedir(), value.slice(2))
|
|
242
|
+
if (value.startsWith("file://")) return fileURLToPath(value)
|
|
243
|
+
if (path.isAbsolute(value)) return value
|
|
244
|
+
return undefined
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function iconMac(config: Config.Info | undefined): MacIcon | undefined {
|
|
248
|
+
const icon = config?.notifications?.icon
|
|
249
|
+
if (!icon) return undefined
|
|
250
|
+
const value = icon.url
|
|
251
|
+
if (!value) return undefined
|
|
252
|
+
if (value.startsWith("http://") || value.startsWith("https://")) {
|
|
253
|
+
return { value, remote: true }
|
|
254
|
+
}
|
|
255
|
+
const local = iconPath(value)
|
|
256
|
+
if (!local) return undefined
|
|
257
|
+
return { value: local, remote: false }
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function route(priority: NotifyPriority, list: NotifyChannel[]) {
|
|
261
|
+
if (list.length === 0) return []
|
|
262
|
+
const preferred: NotifyChannel[] =
|
|
263
|
+
priority === "low" || priority === "normal" ? ["macos"] : ["macos", "slack", "discord"]
|
|
264
|
+
const chosen = preferred.filter((item) => list.includes(item))
|
|
265
|
+
if (chosen.length > 0) return chosen
|
|
266
|
+
return list
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function rateAllow(
|
|
270
|
+
data: ReturnType<typeof notifyState>,
|
|
271
|
+
config: NotifyConfig | undefined,
|
|
272
|
+
channel: NotifyChannel,
|
|
273
|
+
priority: NotifyPriority,
|
|
274
|
+
now: number,
|
|
275
|
+
) {
|
|
276
|
+
if (priority === "high" || priority === "critical") return true
|
|
277
|
+
const limit = rateConfig(config)
|
|
278
|
+
const entry = data.rate.get(channel)
|
|
279
|
+
if (!entry) {
|
|
280
|
+
data.rate.set(channel, { window: now, count: 1 })
|
|
281
|
+
return true
|
|
282
|
+
}
|
|
283
|
+
if (now - entry.window >= limit.windowMs) {
|
|
284
|
+
entry.window = now
|
|
285
|
+
entry.count = 1
|
|
286
|
+
return true
|
|
287
|
+
}
|
|
288
|
+
if (entry.count >= limit.maxPerWindow) return false
|
|
289
|
+
entry.count += 1
|
|
290
|
+
return true
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function breakerOpen(data: ReturnType<typeof notifyState>, channel: NotifyChannel, now: number) {
|
|
294
|
+
const entry = data.breaker.get(channel)
|
|
295
|
+
if (!entry) return false
|
|
296
|
+
if (entry.openUntil === 0) return false
|
|
297
|
+
if (entry.openUntil <= now) {
|
|
298
|
+
data.breaker.delete(channel)
|
|
299
|
+
return false
|
|
300
|
+
}
|
|
301
|
+
return true
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function breakerReset(data: ReturnType<typeof notifyState>, channel: NotifyChannel) {
|
|
305
|
+
data.breaker.delete(channel)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function breakerFail(
|
|
309
|
+
data: ReturnType<typeof notifyState>,
|
|
310
|
+
config: NotifyConfig | undefined,
|
|
311
|
+
channel: NotifyChannel,
|
|
312
|
+
now: number,
|
|
313
|
+
) {
|
|
314
|
+
const limit = breakerConfig(config)
|
|
315
|
+
const current = data.breaker.get(channel) ?? { fails: 0, openUntil: 0 }
|
|
316
|
+
const fails = current.fails + 1
|
|
317
|
+
const openUntil = fails >= limit.failures ? now + limit.cooldownMs : current.openUntil
|
|
318
|
+
data.breaker.set(channel, { fails, openUntil })
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function startQueue(data: ReturnType<typeof notifyState>) {
|
|
322
|
+
if (data.started) return
|
|
323
|
+
data.started = true
|
|
324
|
+
void runQueue(data)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function enqueue(data: ReturnType<typeof notifyState>, job: NotifyJob) {
|
|
328
|
+
data.queue.push(job)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async function loadConfig(data: ReturnType<typeof notifyState>) {
|
|
332
|
+
if (data.config) return data.config
|
|
333
|
+
const config = await Config.get()
|
|
334
|
+
data.config = config
|
|
335
|
+
return config
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
async function runQueue(data: ReturnType<typeof notifyState>) {
|
|
339
|
+
for await (const job of data.queue) {
|
|
340
|
+
await processJob(data, job).catch((error) => {
|
|
341
|
+
const text = error instanceof Error ? error.message : String(error)
|
|
342
|
+
notifyLog.error("notification processing failed", { error: text, source: job.source })
|
|
343
|
+
})
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async function processJob(data: ReturnType<typeof notifyState>, job: NotifyJob) {
|
|
348
|
+
const config = await loadConfig(data)
|
|
349
|
+
const cfg = settings(config)
|
|
350
|
+
if (!flag(cfg.enabled, true)) return
|
|
351
|
+
const list = available(cfg)
|
|
352
|
+
if (list.length === 0) return
|
|
353
|
+
const targets = route(job.priority, list)
|
|
354
|
+
if (targets.length === 0) return
|
|
355
|
+
const now = new Date()
|
|
356
|
+
const stamp = now.getTime()
|
|
357
|
+
for (const channel of targets) {
|
|
358
|
+
if (quiet(cfg, channel, now)) continue
|
|
359
|
+
if (!rateAllow(data, cfg, channel, job.priority, stamp)) {
|
|
360
|
+
notifyLog.debug("notification rate limited", { channel, source: job.source })
|
|
361
|
+
continue
|
|
362
|
+
}
|
|
363
|
+
if (breakerOpen(data, channel, stamp)) {
|
|
364
|
+
notifyLog.warn("notification circuit open", { channel, source: job.source })
|
|
365
|
+
continue
|
|
366
|
+
}
|
|
367
|
+
await deliver(config, cfg, channel, job)
|
|
368
|
+
.then((sent) => {
|
|
369
|
+
if (!sent) return false
|
|
370
|
+
breakerReset(data, channel)
|
|
371
|
+
return true
|
|
372
|
+
})
|
|
373
|
+
.catch((error) => {
|
|
374
|
+
breakerFail(data, cfg, channel, stamp)
|
|
375
|
+
const text = error instanceof Error ? error.message : String(error)
|
|
376
|
+
notifyLog.error("notification delivery failed", { channel, source: job.source, error: text })
|
|
377
|
+
return false
|
|
378
|
+
})
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async function deliver(config: Config.Info, cfg: NotifyConfig, channel: NotifyChannel, job: NotifyJob) {
|
|
383
|
+
if (channel === "macos") return macos(config, job.title, job.body)
|
|
384
|
+
if (channel === "slack") return slack(config, cfg, job)
|
|
385
|
+
if (channel === "discord") return discord(config, cfg, job)
|
|
386
|
+
return false
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async function macos(config: Config.Info, title: string, body: string) {
|
|
390
|
+
if (process.platform !== "darwin") return false
|
|
391
|
+
const icon = iconMac(config)
|
|
392
|
+
const message = body.length ? body : " "
|
|
393
|
+
const notifier = icon && !icon.remote ? Bun.which("terminal-notifier") : null
|
|
394
|
+
if (icon && notifier && !icon.remote) {
|
|
395
|
+
const result = await Bun.$`${notifier} -title ${title} -message ${message} -contentImage ${icon.value}`
|
|
396
|
+
.nothrow()
|
|
397
|
+
.quiet()
|
|
398
|
+
if (result.exitCode === 0) return true
|
|
399
|
+
}
|
|
400
|
+
if (!icon) {
|
|
401
|
+
const safeTitle = title.replace(/"/g, '\\"')
|
|
402
|
+
const safeBody = body.replace(/"/g, '\\"')
|
|
403
|
+
await Bun.$`osascript -e 'display notification "${safeBody}" with title "${safeTitle}"'`.nothrow().quiet()
|
|
404
|
+
return true
|
|
405
|
+
}
|
|
406
|
+
const script = `
|
|
407
|
+
ObjC.import('Cocoa')
|
|
408
|
+
const title = ${JSON.stringify(title)}
|
|
409
|
+
const body = ${JSON.stringify(body)}
|
|
410
|
+
const image = ${JSON.stringify(icon.value)}
|
|
411
|
+
const remote = ${JSON.stringify(icon.remote)}
|
|
412
|
+
const notification = $.NSUserNotification.alloc.init
|
|
413
|
+
notification.title = title
|
|
414
|
+
notification.informativeText = body
|
|
415
|
+
const url = remote ? $.NSURL.URLWithString(image) : $.NSURL.fileURLWithPath(image)
|
|
416
|
+
const img = $.NSImage.alloc.initWithContentsOfURL(url)
|
|
417
|
+
if (!img) throw new Error('icon load failed')
|
|
418
|
+
notification.contentImage = img
|
|
419
|
+
const center = $.NSUserNotificationCenter.defaultUserNotificationCenter
|
|
420
|
+
center.deliverNotification(notification)
|
|
421
|
+
$.NSThread.sleepForTimeInterval(0.2)
|
|
422
|
+
if (!notification.presented) throw new Error('notification not presented')
|
|
423
|
+
`
|
|
424
|
+
const result = await Bun.$`osascript -l JavaScript -e ${script}`.nothrow().quiet()
|
|
425
|
+
if (result.exitCode === 0) return true
|
|
426
|
+
const safeTitle = title.replace(/"/g, '\\"')
|
|
427
|
+
const safeBody = body.replace(/"/g, '\\"')
|
|
428
|
+
await Bun.$`osascript -e 'display notification "${safeBody}" with title "${safeTitle}"'`.nothrow().quiet()
|
|
429
|
+
return true
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async function slack(config: Config.Info, cfg: NotifyConfig, job: NotifyJob) {
|
|
433
|
+
const slack = cfg.slack
|
|
434
|
+
if (!slack) return false
|
|
435
|
+
if (slack.enabled === false) return false
|
|
436
|
+
if (!slack.connector) return false
|
|
437
|
+
const channel = slack.channel ?? Flag.NIKCLI_SLACK_CHANNEL
|
|
438
|
+
if (!channel) return false
|
|
439
|
+
const connectors = config.connectors ?? {}
|
|
440
|
+
const entry = connectors[slack.connector]
|
|
441
|
+
if (!isConnector(entry)) return false
|
|
442
|
+
if (entry.enabled === false) return false
|
|
443
|
+
if (entry.type !== "slack") return false
|
|
444
|
+
const credential = await resolveCredential(slack.connector, entry)
|
|
445
|
+
if (!credential) return false
|
|
446
|
+
const api = await import("../connectors/api/slack")
|
|
447
|
+
const retryCfg = retryConfig(cfg)
|
|
448
|
+
const text = slackText(job.title, job.body)
|
|
449
|
+
const action = () => withTimeout(api.SlackApi.sendMessage(credential, channel, text), retryCfg.timeoutMs)
|
|
450
|
+
await retry(action, {
|
|
451
|
+
attempts: retryCfg.attempts,
|
|
452
|
+
delay: retryCfg.delay,
|
|
453
|
+
factor: retryCfg.factor,
|
|
454
|
+
maxDelay: retryCfg.maxDelay,
|
|
455
|
+
})
|
|
456
|
+
return true
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
async function discord(config: Config.Info, cfg: NotifyConfig, job: NotifyJob) {
|
|
460
|
+
const discord = cfg.discord
|
|
461
|
+
if (!discord) return false
|
|
462
|
+
if (discord.enabled === false) return false
|
|
463
|
+
const webhook = discord.webhook ?? Flag.NIKCLI_DISCORD_WEBHOOK_URL
|
|
464
|
+
if (!webhook) return false
|
|
465
|
+
const retryCfg = retryConfig(cfg)
|
|
466
|
+
const message = formatMessage(job.title, job.body)
|
|
467
|
+
const action = async () => {
|
|
468
|
+
const response = await fetch(webhook, {
|
|
469
|
+
method: "POST",
|
|
470
|
+
headers: { "Content-Type": "application/json" },
|
|
471
|
+
body: JSON.stringify({ content: message }),
|
|
472
|
+
})
|
|
473
|
+
if (response.ok) return
|
|
474
|
+
const text = await response.text()
|
|
475
|
+
const detail = text ? `Discord webhook failed: ${text}` : `Discord webhook failed: ${response.status}`
|
|
476
|
+
throw new Error(detail)
|
|
477
|
+
}
|
|
478
|
+
await retry(() => withTimeout(action(), retryCfg.timeoutMs), {
|
|
479
|
+
attempts: retryCfg.attempts,
|
|
480
|
+
delay: retryCfg.delay,
|
|
481
|
+
factor: retryCfg.factor,
|
|
482
|
+
maxDelay: retryCfg.maxDelay,
|
|
483
|
+
})
|
|
484
|
+
return true
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
async function handleEvent(data: ReturnType<typeof notifyState>, event: { type: string; properties: unknown }) {
|
|
488
|
+
// Session status events are frequent and can arrive back-to-back (busy -> idle).
|
|
489
|
+
// Track busy timing before any async work so we don't miss idle notifications.
|
|
490
|
+
if (event.type === "session.status") {
|
|
491
|
+
const info = event.properties as SessionStatusEvent
|
|
492
|
+
const status = info.status
|
|
493
|
+
if (status.type === "busy" || status.type === "retry") {
|
|
494
|
+
if (!data.busy.has(info.sessionID)) data.busy.set(info.sessionID, Date.now())
|
|
495
|
+
return
|
|
496
|
+
}
|
|
497
|
+
if (status.type !== "idle") return
|
|
498
|
+
|
|
499
|
+
const start = data.busy.get(info.sessionID)
|
|
500
|
+
data.busy.delete(info.sessionID)
|
|
501
|
+
if (!start) return
|
|
502
|
+
|
|
503
|
+
const config = await loadConfig(data)
|
|
504
|
+
const cfg = settings(config)
|
|
505
|
+
if (!flag(cfg.enabled, true)) return
|
|
506
|
+
if (!eventOn(cfg, "sessionIdle", true)) return
|
|
507
|
+
const elapsed = Date.now() - start
|
|
508
|
+
if (elapsed < idleMin(cfg)) return
|
|
509
|
+
const infoSession = await Session.get(info.sessionID).catch(() => undefined)
|
|
510
|
+
const title =
|
|
511
|
+
infoSession && !Session.isDefaultTitle(infoSession.title)
|
|
512
|
+
? `Session ready: ${infoSession.title}`
|
|
513
|
+
: "Session ready"
|
|
514
|
+
enqueue(data, {
|
|
515
|
+
title,
|
|
516
|
+
body: `Completed in ${span(elapsed)}`,
|
|
517
|
+
priority: "normal",
|
|
518
|
+
source: event.type,
|
|
519
|
+
sessionID: info.sessionID,
|
|
520
|
+
})
|
|
521
|
+
return
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const config = await loadConfig(data)
|
|
525
|
+
const cfg = settings(config)
|
|
526
|
+
if (!flag(cfg.enabled, true)) return
|
|
527
|
+
if (event.type === "permission.asked") {
|
|
528
|
+
if (!eventOn(cfg, "permissionAsked", true)) return
|
|
529
|
+
const info = event.properties as PermissionAsked
|
|
530
|
+
const list = preview(info.patterns ?? [], 3)
|
|
531
|
+
const body = list ? `Permission: ${info.permission}\nPatterns: ${list}` : `Permission: ${info.permission}`
|
|
532
|
+
enqueue(data, {
|
|
533
|
+
title: "Permission required",
|
|
534
|
+
body,
|
|
535
|
+
priority: "high",
|
|
536
|
+
source: event.type,
|
|
537
|
+
sessionID: info.sessionID,
|
|
538
|
+
})
|
|
539
|
+
return
|
|
540
|
+
}
|
|
541
|
+
if (event.type === "question.asked") {
|
|
542
|
+
if (!eventOn(cfg, "questionAsked", true)) return
|
|
543
|
+
const info = event.properties as QuestionAsked
|
|
544
|
+
const first = info.questions?.[0]
|
|
545
|
+
const count = info.questions?.length ?? 0
|
|
546
|
+
const extra = count > 1 ? ` (+${count - 1} more)` : ""
|
|
547
|
+
const body = first ? `${first.header}: ${first.question}${extra}` : "Input required"
|
|
548
|
+
enqueue(data, {
|
|
549
|
+
title: "Input required",
|
|
550
|
+
body,
|
|
551
|
+
priority: "high",
|
|
552
|
+
source: event.type,
|
|
553
|
+
sessionID: info.sessionID,
|
|
554
|
+
})
|
|
555
|
+
return
|
|
556
|
+
}
|
|
557
|
+
if (event.type === "session.error") {
|
|
558
|
+
if (!eventOn(cfg, "sessionError", true)) return
|
|
559
|
+
const info = event.properties as SessionErrorEvent
|
|
560
|
+
const body = errorText(info.error)
|
|
561
|
+
enqueue(data, {
|
|
562
|
+
title: "Session error",
|
|
563
|
+
body,
|
|
564
|
+
priority: "critical",
|
|
565
|
+
source: event.type,
|
|
566
|
+
sessionID: info.sessionID,
|
|
567
|
+
})
|
|
568
|
+
return
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
export async function NotifyPlugin(_input: PluginInput): Promise<Hooks> {
|
|
573
|
+
const data = notifyState()
|
|
574
|
+
startQueue(data)
|
|
575
|
+
return {
|
|
576
|
+
async config(cfg) {
|
|
577
|
+
data.config = cfg as Config.Info
|
|
578
|
+
},
|
|
579
|
+
async event(input) {
|
|
580
|
+
await handleEvent(data, input.event)
|
|
581
|
+
},
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
export namespace Plugin {
|
|
586
|
+
const log = Log.create({ service: "plugin" })
|
|
587
|
+
|
|
588
|
+
const BUILTIN = ["@gitlab/nikcli-gitlab-auth@1.3.2"]
|
|
589
|
+
|
|
590
|
+
// Built-in plugins that are directly imported (not installed from npm)
|
|
591
|
+
const INTERNAL_PLUGINS: PluginInstance[] = [CodexAuthPlugin, CopilotAuthPlugin, NotifyPlugin]
|
|
592
|
+
|
|
593
|
+
const state = Instance.state(async () => {
|
|
594
|
+
const client = createNikcliClient({
|
|
595
|
+
baseUrl: "http://localhost:4096",
|
|
596
|
+
// @ts-ignore - fetch type incompatibility
|
|
597
|
+
fetch: async (...args) => Server.App().fetch(...args),
|
|
598
|
+
})
|
|
599
|
+
const config = await Config.get()
|
|
600
|
+
const hooks: Hooks[] = []
|
|
601
|
+
const input: PluginInput = {
|
|
602
|
+
client,
|
|
603
|
+
project: Instance.project,
|
|
604
|
+
worktree: Instance.worktree,
|
|
605
|
+
directory: Instance.directory,
|
|
606
|
+
serverUrl: Server.url(),
|
|
607
|
+
$: Bun.$,
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
for (const plugin of INTERNAL_PLUGINS) {
|
|
611
|
+
log.info("loading internal plugin", { name: plugin.name })
|
|
612
|
+
const init = await plugin(input)
|
|
613
|
+
hooks.push(init)
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const plugins = [...(config.plugin ?? [])]
|
|
617
|
+
if (!Flag.NIKCLI_DISABLE_DEFAULT_PLUGINS) {
|
|
618
|
+
plugins.push(...BUILTIN)
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
for (let plugin of plugins) {
|
|
622
|
+
// ignore old codex plugin since it is supported first party now
|
|
623
|
+
if (plugin.includes("nikcli-openai-codex-auth") || plugin.includes("nikcli-copilot-auth")) continue
|
|
624
|
+
const spec = plugin
|
|
625
|
+
log.info("loading plugin", { path: plugin })
|
|
626
|
+
if (!plugin.startsWith("file://")) {
|
|
627
|
+
const lastAtIndex = plugin.lastIndexOf("@")
|
|
628
|
+
const pkg = lastAtIndex > 0 ? plugin.substring(0, lastAtIndex) : plugin
|
|
629
|
+
const version = lastAtIndex > 0 ? plugin.substring(lastAtIndex + 1) : "latest"
|
|
630
|
+
const builtin = BUILTIN.some((x) => x.startsWith(pkg + "@"))
|
|
631
|
+
plugin = await BunProc.install(pkg, version).catch((err) => {
|
|
632
|
+
if (!builtin) throw err
|
|
633
|
+
|
|
634
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
635
|
+
log.error("failed to install builtin plugin", {
|
|
636
|
+
pkg,
|
|
637
|
+
version,
|
|
638
|
+
error: message,
|
|
639
|
+
})
|
|
640
|
+
Bus.publish(Session.Event.Error, {
|
|
641
|
+
error: new NamedError.Unknown({
|
|
642
|
+
message: `Failed to install built-in plugin ${pkg}@${version}: ${message}`,
|
|
643
|
+
}).toObject(),
|
|
644
|
+
})
|
|
645
|
+
|
|
646
|
+
return ""
|
|
647
|
+
})
|
|
648
|
+
if (!plugin) continue
|
|
649
|
+
}
|
|
650
|
+
const mod = await import(plugin)
|
|
651
|
+
const v1 = readV1Plugin(mod, spec, "server", "detect")
|
|
652
|
+
if (v1) {
|
|
653
|
+
const source = pluginSource(spec)
|
|
654
|
+
const id = readPluginId(v1.id, spec)
|
|
655
|
+
await resolvePluginId(source, spec, plugin, id)
|
|
656
|
+
hooks.push(await (v1 as PluginModule).server!(input, Config.pluginOptions(spec)))
|
|
657
|
+
} else {
|
|
658
|
+
// Prevent duplicate initialization when plugins export the same function
|
|
659
|
+
// as both a named export and default export (e.g., `export const X` and `export default X`).
|
|
660
|
+
// Object.entries(mod) would return both entries pointing to the same function reference.
|
|
661
|
+
const seen = new Set<PluginInstance>()
|
|
662
|
+
for (const [_name, fn] of Object.entries<PluginInstance>(mod)) {
|
|
663
|
+
if (seen.has(fn)) continue
|
|
664
|
+
seen.add(fn)
|
|
665
|
+
const init = await fn(input)
|
|
666
|
+
hooks.push(init)
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
return {
|
|
672
|
+
hooks,
|
|
673
|
+
input,
|
|
674
|
+
}
|
|
675
|
+
})
|
|
676
|
+
|
|
677
|
+
export async function trigger<
|
|
678
|
+
Name extends Exclude<keyof Required<Hooks>, "auth" | "event" | "tool">,
|
|
679
|
+
Input = Parameters<Required<Hooks>[Name]>[0],
|
|
680
|
+
Output = Parameters<Required<Hooks>[Name]>[1],
|
|
681
|
+
>(name: Name, input: Input, output: Output): Promise<Output> {
|
|
682
|
+
if (!name) return output
|
|
683
|
+
for (const hook of await state().then((x) => x.hooks)) {
|
|
684
|
+
const fn = hook[name]
|
|
685
|
+
if (!fn) continue
|
|
686
|
+
// @ts-expect-error if you feel adventurous, please fix the typing, make sure to bump the try-counter if you
|
|
687
|
+
// give up.
|
|
688
|
+
// try-counter: 2
|
|
689
|
+
await fn(input, output)
|
|
690
|
+
}
|
|
691
|
+
return output
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
export async function list() {
|
|
695
|
+
return state().then((x) => x.hooks)
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
export async function init() {
|
|
699
|
+
const hooks = await state().then((x) => x.hooks)
|
|
700
|
+
const config = await Config.get()
|
|
701
|
+
for (const hook of hooks) {
|
|
702
|
+
// @ts-expect-error this is because we haven't moved plugin to sdk v2
|
|
703
|
+
await hook.config?.(config)
|
|
704
|
+
}
|
|
705
|
+
Bus.subscribeAll(async (input) => {
|
|
706
|
+
const hooks = await state().then((x) => x.hooks)
|
|
707
|
+
for (const hook of hooks) {
|
|
708
|
+
hook["event"]?.({
|
|
709
|
+
event: input,
|
|
710
|
+
})
|
|
711
|
+
}
|
|
712
|
+
})
|
|
713
|
+
}
|
|
714
|
+
}
|