innocode 1.0.0
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/AGENTS.md +27 -0
- package/Dockerfile +18 -0
- package/README.md +15 -0
- package/bin/innocode +84 -0
- package/bin/opencode +84 -0
- package/bunfig.toml +5 -0
- package/package.json +126 -0
- package/parsers-config.ts +253 -0
- package/script/build.ts +198 -0
- package/script/postinstall.mjs +125 -0
- package/script/publish.ts +186 -0
- package/script/schema.ts +47 -0
- package/script/seed-e2e.ts +50 -0
- package/src/acp/README.md +164 -0
- package/src/acp/agent.ts +1676 -0
- package/src/acp/session.ts +117 -0
- package/src/acp/types.ts +23 -0
- package/src/agent/agent.ts +338 -0
- package/src/agent/generate.txt +75 -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 +70 -0
- package/src/bun/index.ts +137 -0
- package/src/bun/registry.ts +48 -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/cli/bootstrap.ts +17 -0
- package/src/cli/cmd/acp.ts +70 -0
- package/src/cli/cmd/agent.ts +257 -0
- package/src/cli/cmd/auth.ts +400 -0
- package/src/cli/cmd/cmd.ts +7 -0
- package/src/cli/cmd/debug/agent.ts +167 -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 +1540 -0
- package/src/cli/cmd/import.ts +147 -0
- package/src/cli/cmd/mcp.ts +765 -0
- package/src/cli/cmd/models.ts +77 -0
- package/src/cli/cmd/pr.ts +113 -0
- package/src/cli/cmd/run.ts +598 -0
- package/src/cli/cmd/serve.ts +20 -0
- package/src/cli/cmd/session.ts +135 -0
- package/src/cli/cmd/stats.ts +426 -0
- package/src/cli/cmd/tui/app.tsx +812 -0
- package/src/cli/cmd/tui/attach.ts +60 -0
- package/src/cli/cmd/tui/component/border.tsx +21 -0
- package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-command.tsx +148 -0
- package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog-model.tsx +165 -0
- package/src/cli/cmd/tui/component/dialog-provider.tsx +243 -0
- package/src/cli/cmd/tui/component/dialog-session-list.tsx +108 -0
- package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-skill.tsx +36 -0
- package/src/cli/cmd/tui/component/dialog-stash.tsx +87 -0
- package/src/cli/cmd/tui/component/dialog-status.tsx +167 -0
- package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
- package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
- package/src/cli/cmd/tui/component/logo.tsx +85 -0
- package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +666 -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 +1153 -0
- package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
- package/src/cli/cmd/tui/component/spinner.tsx +24 -0
- package/src/cli/cmd/tui/component/textarea-keybindings.ts +73 -0
- package/src/cli/cmd/tui/component/tips.tsx +153 -0
- package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
- package/src/cli/cmd/tui/context/args.tsx +15 -0
- package/src/cli/cmd/tui/context/directory.ts +13 -0
- package/src/cli/cmd/tui/context/exit.tsx +54 -0
- package/src/cli/cmd/tui/context/helper.tsx +25 -0
- package/src/cli/cmd/tui/context/keybind.tsx +100 -0
- package/src/cli/cmd/tui/context/kv.tsx +52 -0
- package/src/cli/cmd/tui/context/local.tsx +409 -0
- package/src/cli/cmd/tui/context/prompt.tsx +18 -0
- package/src/cli/cmd/tui/context/route.tsx +46 -0
- package/src/cli/cmd/tui/context/sdk.tsx +101 -0
- package/src/cli/cmd/tui/context/sync.tsx +470 -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/carbonfox.json +248 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
- package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
- package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
- package/src/cli/cmd/tui/context/theme/dracula.json +219 -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/github.json +233 -0
- package/src/cli/cmd/tui/context/theme/gruvbox.json +242 -0
- package/src/cli/cmd/tui/context/theme/innocode.json +245 -0
- package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
- package/src/cli/cmd/tui/context/theme/lucent-orng.json +237 -0
- package/src/cli/cmd/tui/context/theme/material.json +235 -0
- package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
- package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
- package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
- package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
- package/src/cli/cmd/tui/context/theme/nord.json +223 -0
- package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
- package/src/cli/cmd/tui/context/theme/orng.json +249 -0
- package/src/cli/cmd/tui/context/theme/osaka-jade.json +93 -0
- package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
- package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
- package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
- package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
- package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -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/zenburn.json +223 -0
- package/src/cli/cmd/tui/context/theme.tsx +1154 -0
- package/src/cli/cmd/tui/event.ts +48 -0
- package/src/cli/cmd/tui/routes/home.tsx +145 -0
- package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +64 -0
- package/src/cli/cmd/tui/routes/session/dialog-message.tsx +109 -0
- package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
- package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
- package/src/cli/cmd/tui/routes/session/footer.tsx +91 -0
- package/src/cli/cmd/tui/routes/session/header.tsx +135 -0
- package/src/cli/cmd/tui/routes/session/index.tsx +2139 -0
- package/src/cli/cmd/tui/routes/session/permission.tsx +508 -0
- package/src/cli/cmd/tui/routes/session/question.tsx +466 -0
- package/src/cli/cmd/tui/routes/session/sidebar.tsx +313 -0
- package/src/cli/cmd/tui/thread.ts +188 -0
- package/src/cli/cmd/tui/ui/dialog-alert.tsx +59 -0
- package/src/cli/cmd/tui/ui/dialog-confirm.tsx +85 -0
- package/src/cli/cmd/tui/ui/dialog-export-options.tsx +207 -0
- package/src/cli/cmd/tui/ui/dialog-help.tsx +40 -0
- package/src/cli/cmd/tui/ui/dialog-prompt.tsx +80 -0
- package/src/cli/cmd/tui/ui/dialog-select.tsx +401 -0
- package/src/cli/cmd/tui/ui/dialog.tsx +167 -0
- package/src/cli/cmd/tui/ui/link.tsx +28 -0
- package/src/cli/cmd/tui/ui/spinner.ts +368 -0
- package/src/cli/cmd/tui/ui/toast.tsx +100 -0
- package/src/cli/cmd/tui/util/clipboard.ts +159 -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 +129 -0
- package/src/cli/cmd/tui/worker.ts +152 -0
- package/src/cli/cmd/uninstall.ts +363 -0
- package/src/cli/cmd/upgrade.ts +73 -0
- package/src/cli/cmd/web.ts +81 -0
- package/src/cli/error.ts +57 -0
- package/src/cli/logo.ts +6 -0
- package/src/cli/network.ts +60 -0
- package/src/cli/ui.ts +113 -0
- package/src/cli/upgrade.ts +25 -0
- package/src/command/index.ts +150 -0
- package/src/command/template/initialize.txt +10 -0
- package/src/command/template/review.txt +101 -0
- package/src/config/config.ts +1517 -0
- package/src/config/markdown.ts +98 -0
- package/src/env/index.ts +28 -0
- package/src/file/ignore.ts +83 -0
- package/src/file/index.ts +583 -0
- package/src/file/ripgrep.ts +375 -0
- package/src/file/time.ts +69 -0
- package/src/file/watcher.ts +127 -0
- package/src/flag/flag.ts +148 -0
- package/src/format/formatter.ts +366 -0
- package/src/format/index.ts +137 -0
- package/src/global/index.ts +80 -0
- package/src/id/id.ts +83 -0
- package/src/ide/index.ts +76 -0
- package/src/index.ts +160 -0
- package/src/installation/index.ts +268 -0
- package/src/lsp/client.ts +252 -0
- package/src/lsp/index.ts +485 -0
- package/src/lsp/language.ts +119 -0
- package/src/lsp/server.ts +2046 -0
- package/src/mcp/auth.ts +132 -0
- package/src/mcp/index.ts +937 -0
- package/src/mcp/oauth-callback.ts +200 -0
- package/src/mcp/oauth-provider.ts +154 -0
- package/src/patch/index.ts +680 -0
- package/src/permission/arity.ts +163 -0
- package/src/permission/index.ts +210 -0
- package/src/permission/next.ts +280 -0
- package/src/plugin/codex.ts +624 -0
- package/src/plugin/copilot.ts +327 -0
- package/src/plugin/index.ts +138 -0
- package/src/project/bootstrap.ts +35 -0
- package/src/project/instance.ts +114 -0
- package/src/project/project.ts +371 -0
- package/src/project/state.ts +70 -0
- package/src/project/vcs.ts +76 -0
- package/src/provider/auth.ts +147 -0
- package/src/provider/error.ts +189 -0
- package/src/provider/models.ts +133 -0
- package/src/provider/provider.ts +1370 -0
- package/src/provider/sdk/copilot/README.md +5 -0
- package/src/provider/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts +164 -0
- package/src/provider/sdk/copilot/chat/get-response-metadata.ts +15 -0
- package/src/provider/sdk/copilot/chat/map-openai-compatible-finish-reason.ts +17 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-api-types.ts +64 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-chat-language-model.ts +780 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-chat-options.ts +28 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-metadata-extractor.ts +44 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-prepare-tools.ts +87 -0
- package/src/provider/sdk/copilot/copilot-provider.ts +100 -0
- package/src/provider/sdk/copilot/index.ts +2 -0
- package/src/provider/sdk/copilot/openai-compatible-error.ts +27 -0
- package/src/provider/sdk/copilot/responses/convert-to-openai-responses-input.ts +303 -0
- package/src/provider/sdk/copilot/responses/map-openai-responses-finish-reason.ts +22 -0
- package/src/provider/sdk/copilot/responses/openai-config.ts +18 -0
- package/src/provider/sdk/copilot/responses/openai-error.ts +22 -0
- package/src/provider/sdk/copilot/responses/openai-responses-api-types.ts +207 -0
- package/src/provider/sdk/copilot/responses/openai-responses-language-model.ts +1732 -0
- package/src/provider/sdk/copilot/responses/openai-responses-prepare-tools.ts +177 -0
- package/src/provider/sdk/copilot/responses/openai-responses-settings.ts +1 -0
- package/src/provider/sdk/copilot/responses/tool/code-interpreter.ts +88 -0
- package/src/provider/sdk/copilot/responses/tool/file-search.ts +128 -0
- package/src/provider/sdk/copilot/responses/tool/image-generation.ts +115 -0
- package/src/provider/sdk/copilot/responses/tool/local-shell.ts +65 -0
- package/src/provider/sdk/copilot/responses/tool/web-search-preview.ts +104 -0
- package/src/provider/sdk/copilot/responses/tool/web-search.ts +103 -0
- package/src/provider/transform.ts +806 -0
- package/src/pty/index.ts +286 -0
- package/src/question/index.ts +171 -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 +60 -0
- package/src/server/routes/config.ts +92 -0
- package/src/server/routes/experimental.ts +208 -0
- package/src/server/routes/file.ts +197 -0
- package/src/server/routes/global.ts +183 -0
- package/src/server/routes/mcp.ts +225 -0
- package/src/server/routes/permission.ts +68 -0
- package/src/server/routes/project.ts +82 -0
- package/src/server/routes/provider.ts +179 -0
- package/src/server/routes/pty.ts +176 -0
- package/src/server/routes/question.ts +98 -0
- package/src/server/routes/session.ts +939 -0
- package/src/server/routes/tui.ts +379 -0
- package/src/server/server.ts +621 -0
- package/src/session/compaction.ts +261 -0
- package/src/session/index.ts +543 -0
- package/src/session/instruction.ts +197 -0
- package/src/session/llm.ts +283 -0
- package/src/session/message-v2.ts +841 -0
- package/src/session/message.ts +189 -0
- package/src/session/processor.ts +410 -0
- package/src/session/prompt/anthropic-20250930.txt +166 -0
- package/src/session/prompt/anthropic.txt +105 -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 +26 -0
- package/src/session/prompt/qwen.txt +109 -0
- package/src/session/prompt/trinity.txt +97 -0
- package/src/session/prompt.ts +1964 -0
- package/src/session/retry.ts +101 -0
- package/src/session/revert.ts +121 -0
- package/src/session/status.ts +76 -0
- package/src/session/summary.ts +203 -0
- package/src/session/system.ts +54 -0
- package/src/session/todo.ts +37 -0
- package/src/share/share-next.ts +200 -0
- package/src/share/share.ts +92 -0
- package/src/shell/shell.ts +67 -0
- package/src/skill/discovery.ts +97 -0
- package/src/skill/index.ts +1 -0
- package/src/skill/skill.ts +188 -0
- package/src/snapshot/index.ts +255 -0
- package/src/storage/storage.ts +227 -0
- package/src/tool/apply_patch.ts +281 -0
- package/src/tool/apply_patch.txt +33 -0
- package/src/tool/bash.ts +269 -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/edit.ts +655 -0
- package/src/tool/edit.txt +10 -0
- package/src/tool/external-directory.ts +32 -0
- package/src/tool/glob.ts +80 -0
- package/src/tool/glob.txt +6 -0
- package/src/tool/grep.ts +150 -0
- package/src/tool/grep.txt +8 -0
- package/src/tool/invalid.ts +17 -0
- package/src/tool/ls.ts +121 -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/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/read.ts +261 -0
- package/src/tool/read.txt +14 -0
- package/src/tool/registry.ts +160 -0
- package/src/tool/skill.ts +123 -0
- package/src/tool/task.ts +165 -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 +89 -0
- package/src/tool/truncation.ts +106 -0
- package/src/tool/webfetch.ts +186 -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 +85 -0
- package/src/tool/write.txt +8 -0
- package/src/util/abort.ts +35 -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/eventloop.ts +20 -0
- package/src/util/filesystem.ts +93 -0
- package/src/util/fn.ts +11 -0
- package/src/util/format.ts +20 -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/proxied.ts +3 -0
- package/src/util/queue.ts +32 -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/worktree/index.ts +612 -0
- package/sst-env.d.ts +9 -0
- package/test/acp/agent-interface.test.ts +51 -0
- package/test/acp/event-subscription.test.ts +436 -0
- package/test/agent/agent.test.ts +675 -0
- package/test/bun.test.ts +53 -0
- package/test/cli/github-action.test.ts +161 -0
- package/test/cli/github-remote.test.ts +80 -0
- package/test/cli/import.test.ts +38 -0
- package/test/cli/tui/transcript.test.ts +322 -0
- package/test/config/agent-color.test.ts +71 -0
- package/test/config/config.test.ts +1802 -0
- package/test/config/fixtures/empty-frontmatter.md +4 -0
- package/test/config/fixtures/frontmatter.md +28 -0
- package/test/config/fixtures/markdown-header.md +11 -0
- package/test/config/fixtures/no-frontmatter.md +1 -0
- package/test/config/fixtures/weird-model-id.md +13 -0
- package/test/config/markdown.test.ts +228 -0
- package/test/file/ignore.test.ts +10 -0
- package/test/file/path-traversal.test.ts +198 -0
- package/test/file/ripgrep.test.ts +39 -0
- package/test/fixture/fixture.ts +45 -0
- package/test/fixture/lsp/fake-lsp-server.js +77 -0
- package/test/ide/ide.test.ts +82 -0
- package/test/keybind.test.ts +421 -0
- package/test/lsp/client.test.ts +95 -0
- package/test/mcp/headers.test.ts +153 -0
- package/test/mcp/oauth-browser.test.ts +249 -0
- package/test/memory/abort-leak.test.ts +136 -0
- package/test/patch/patch.test.ts +348 -0
- package/test/permission/arity.test.ts +33 -0
- package/test/permission/next.test.ts +690 -0
- package/test/permission-task.test.ts +319 -0
- package/test/plugin/auth-override.test.ts +44 -0
- package/test/plugin/codex.test.ts +123 -0
- package/test/preload.ts +63 -0
- package/test/project/project.test.ts +120 -0
- package/test/provider/amazon-bedrock.test.ts +445 -0
- package/test/provider/copilot/convert-to-copilot-messages.test.ts +523 -0
- package/test/provider/copilot/copilot-chat-model.test.ts +592 -0
- package/test/provider/gitlab-duo.test.ts +262 -0
- package/test/provider/provider.test.ts +2129 -0
- package/test/provider/transform.test.ts +1928 -0
- package/test/question/question.test.ts +300 -0
- package/test/scheduler.test.ts +73 -0
- package/test/server/session-list.test.ts +39 -0
- package/test/server/session-select.test.ts +78 -0
- package/test/session/compaction.test.ts +423 -0
- package/test/session/instruction.test.ts +170 -0
- package/test/session/llm.test.ts +667 -0
- package/test/session/message-v2.test.ts +924 -0
- package/test/session/prompt-missing-file.test.ts +53 -0
- package/test/session/prompt-special-chars.test.ts +56 -0
- package/test/session/prompt-variant.test.ts +68 -0
- package/test/session/retry.test.ts +188 -0
- package/test/session/revert-compact.test.ts +285 -0
- package/test/session/session.test.ts +71 -0
- package/test/session/structured-output-integration.test.ts +233 -0
- package/test/session/structured-output.test.ts +385 -0
- package/test/skill/discovery.test.ts +60 -0
- package/test/skill/skill.test.ts +388 -0
- package/test/snapshot/snapshot.test.ts +1040 -0
- package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
- package/test/tool/apply_patch.test.ts +559 -0
- package/test/tool/bash.test.ts +399 -0
- package/test/tool/external-directory.test.ts +127 -0
- package/test/tool/fixtures/large-image.png +0 -0
- package/test/tool/fixtures/models-api.json +38413 -0
- package/test/tool/grep.test.ts +110 -0
- package/test/tool/question.test.ts +107 -0
- package/test/tool/read.test.ts +421 -0
- package/test/tool/registry.test.ts +122 -0
- package/test/tool/skill.test.ts +112 -0
- package/test/tool/truncation.test.ts +159 -0
- package/test/util/filesystem.test.ts +39 -0
- package/test/util/format.test.ts +59 -0
- package/test/util/iife.test.ts +36 -0
- package/test/util/lazy.test.ts +50 -0
- package/test/util/lock.test.ts +72 -0
- package/test/util/timeout.test.ts +21 -0
- package/test/util/wildcard.test.ts +75 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import fs from "fs/promises"
|
|
2
|
+
import { xdgData, xdgCache, xdgConfig, xdgState } from "xdg-basedir"
|
|
3
|
+
import path from "path"
|
|
4
|
+
import os from "os"
|
|
5
|
+
|
|
6
|
+
const app = "innocode"
|
|
7
|
+
const legacyApp = "opencode"
|
|
8
|
+
|
|
9
|
+
const data = path.join(xdgData!, app)
|
|
10
|
+
const cache = path.join(xdgCache!, app)
|
|
11
|
+
const config = path.join(xdgConfig!, app)
|
|
12
|
+
const state = path.join(xdgState!, app)
|
|
13
|
+
|
|
14
|
+
const legacyData = path.join(xdgData!, legacyApp)
|
|
15
|
+
const legacyConfig = path.join(xdgConfig!, legacyApp)
|
|
16
|
+
const legacyState = path.join(xdgState!, legacyApp)
|
|
17
|
+
|
|
18
|
+
export namespace Global {
|
|
19
|
+
export const Path = {
|
|
20
|
+
// Allow override via OPENCODE_TEST_HOME for test isolation
|
|
21
|
+
get home() {
|
|
22
|
+
return process.env.OPENCODE_TEST_HOME || os.homedir()
|
|
23
|
+
},
|
|
24
|
+
data,
|
|
25
|
+
bin: path.join(data, "bin"),
|
|
26
|
+
log: path.join(data, "log"),
|
|
27
|
+
cache,
|
|
28
|
+
config,
|
|
29
|
+
state,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function pathExists(target: string) {
|
|
34
|
+
return fs
|
|
35
|
+
.access(target)
|
|
36
|
+
.then(() => true)
|
|
37
|
+
.catch(() => false)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function migrateLegacyDir(source: string, dest: string) {
|
|
41
|
+
const sourceExists = await pathExists(source)
|
|
42
|
+
const destExists = await pathExists(dest)
|
|
43
|
+
if (!sourceExists || destExists) return
|
|
44
|
+
await fs.cp(source, dest, { recursive: true, errorOnExist: false })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
await Promise.all([
|
|
48
|
+
migrateLegacyDir(legacyData, data),
|
|
49
|
+
migrateLegacyDir(legacyConfig, config),
|
|
50
|
+
migrateLegacyDir(legacyState, state),
|
|
51
|
+
])
|
|
52
|
+
|
|
53
|
+
await Promise.all([
|
|
54
|
+
fs.mkdir(Global.Path.data, { recursive: true }),
|
|
55
|
+
fs.mkdir(Global.Path.config, { recursive: true }),
|
|
56
|
+
fs.mkdir(Global.Path.state, { recursive: true }),
|
|
57
|
+
fs.mkdir(Global.Path.log, { recursive: true }),
|
|
58
|
+
fs.mkdir(Global.Path.bin, { recursive: true }),
|
|
59
|
+
])
|
|
60
|
+
|
|
61
|
+
const CACHE_VERSION = "21"
|
|
62
|
+
|
|
63
|
+
const version = await Bun.file(path.join(Global.Path.cache, "version"))
|
|
64
|
+
.text()
|
|
65
|
+
.catch(() => "0")
|
|
66
|
+
|
|
67
|
+
if (version !== CACHE_VERSION) {
|
|
68
|
+
try {
|
|
69
|
+
const contents = await fs.readdir(Global.Path.cache)
|
|
70
|
+
await Promise.all(
|
|
71
|
+
contents.map((item) =>
|
|
72
|
+
fs.rm(path.join(Global.Path.cache, item), {
|
|
73
|
+
recursive: true,
|
|
74
|
+
force: true,
|
|
75
|
+
}),
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
} catch (e) {}
|
|
79
|
+
await Bun.file(path.join(Global.Path.cache, "version")).write(CACHE_VERSION)
|
|
80
|
+
}
|
package/src/id/id.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import z from "zod"
|
|
2
|
+
import { randomBytes } from "crypto"
|
|
3
|
+
|
|
4
|
+
export namespace Identifier {
|
|
5
|
+
const prefixes = {
|
|
6
|
+
session: "ses",
|
|
7
|
+
message: "msg",
|
|
8
|
+
permission: "per",
|
|
9
|
+
question: "que",
|
|
10
|
+
user: "usr",
|
|
11
|
+
part: "prt",
|
|
12
|
+
pty: "pty",
|
|
13
|
+
tool: "tool",
|
|
14
|
+
} as const
|
|
15
|
+
|
|
16
|
+
export function schema(prefix: keyof typeof prefixes) {
|
|
17
|
+
return z.string().startsWith(prefixes[prefix])
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const LENGTH = 26
|
|
21
|
+
|
|
22
|
+
// State for monotonic ID generation
|
|
23
|
+
let lastTimestamp = 0
|
|
24
|
+
let counter = 0
|
|
25
|
+
|
|
26
|
+
export function ascending(prefix: keyof typeof prefixes, given?: string) {
|
|
27
|
+
return generateID(prefix, false, given)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function descending(prefix: keyof typeof prefixes, given?: string) {
|
|
31
|
+
return generateID(prefix, true, given)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function generateID(prefix: keyof typeof prefixes, descending: boolean, given?: string): string {
|
|
35
|
+
if (!given) {
|
|
36
|
+
return create(prefix, descending)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!given.startsWith(prefixes[prefix])) {
|
|
40
|
+
throw new Error(`ID ${given} does not start with ${prefixes[prefix]}`)
|
|
41
|
+
}
|
|
42
|
+
return given
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function randomBase62(length: number): string {
|
|
46
|
+
const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
47
|
+
let result = ""
|
|
48
|
+
const bytes = randomBytes(length)
|
|
49
|
+
for (let i = 0; i < length; i++) {
|
|
50
|
+
result += chars[bytes[i] % 62]
|
|
51
|
+
}
|
|
52
|
+
return result
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function create(prefix: keyof typeof prefixes, descending: boolean, timestamp?: number): string {
|
|
56
|
+
const currentTimestamp = timestamp ?? Date.now()
|
|
57
|
+
|
|
58
|
+
if (currentTimestamp !== lastTimestamp) {
|
|
59
|
+
lastTimestamp = currentTimestamp
|
|
60
|
+
counter = 0
|
|
61
|
+
}
|
|
62
|
+
counter++
|
|
63
|
+
|
|
64
|
+
let now = BigInt(currentTimestamp) * BigInt(0x1000) + BigInt(counter)
|
|
65
|
+
|
|
66
|
+
now = descending ? ~now : now
|
|
67
|
+
|
|
68
|
+
const timeBytes = Buffer.alloc(6)
|
|
69
|
+
for (let i = 0; i < 6; i++) {
|
|
70
|
+
timeBytes[i] = Number((now >> BigInt(40 - 8 * i)) & BigInt(0xff))
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return prefixes[prefix] + "_" + timeBytes.toString("hex") + randomBase62(LENGTH - 12)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Extract timestamp from an ascending ID. Does not work with descending IDs. */
|
|
77
|
+
export function timestamp(id: string): number {
|
|
78
|
+
const prefix = id.split("_")[0]
|
|
79
|
+
const hex = id.slice(prefix.length + 1, prefix.length + 13)
|
|
80
|
+
const encoded = BigInt("0x" + hex)
|
|
81
|
+
return Number(encoded / BigInt(0x1000))
|
|
82
|
+
}
|
|
83
|
+
}
|
package/src/ide/index.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { BusEvent } from "@/bus/bus-event"
|
|
2
|
+
import { Bus } from "@/bus"
|
|
3
|
+
import { spawn } from "bun"
|
|
4
|
+
import z from "zod"
|
|
5
|
+
import { NamedError } from "@opencode-ai/util/error"
|
|
6
|
+
import { Log } from "../util/log"
|
|
7
|
+
|
|
8
|
+
const SUPPORTED_IDES = [
|
|
9
|
+
{ name: "Windsurf" as const, cmd: "windsurf" },
|
|
10
|
+
{ name: "Visual Studio Code - Insiders" as const, cmd: "code-insiders" },
|
|
11
|
+
{ name: "Visual Studio Code" as const, cmd: "code" },
|
|
12
|
+
{ name: "Cursor" as const, cmd: "cursor" },
|
|
13
|
+
{ name: "VSCodium" as const, cmd: "codium" },
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
export namespace Ide {
|
|
17
|
+
const log = Log.create({ service: "ide" })
|
|
18
|
+
|
|
19
|
+
export const Event = {
|
|
20
|
+
Installed: BusEvent.define(
|
|
21
|
+
"ide.installed",
|
|
22
|
+
z.object({
|
|
23
|
+
ide: z.string(),
|
|
24
|
+
}),
|
|
25
|
+
),
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const AlreadyInstalledError = NamedError.create("AlreadyInstalledError", z.object({}))
|
|
29
|
+
|
|
30
|
+
export const InstallFailedError = NamedError.create(
|
|
31
|
+
"InstallFailedError",
|
|
32
|
+
z.object({
|
|
33
|
+
stderr: z.string(),
|
|
34
|
+
}),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
export function ide() {
|
|
38
|
+
if (process.env["TERM_PROGRAM"] === "vscode") {
|
|
39
|
+
const v = process.env["GIT_ASKPASS"]
|
|
40
|
+
for (const ide of SUPPORTED_IDES) {
|
|
41
|
+
if (v?.includes(ide.name)) return ide.name
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return "unknown"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function alreadyInstalled() {
|
|
48
|
+
return process.env["OPENCODE_CALLER"] === "vscode" || process.env["OPENCODE_CALLER"] === "vscode-insiders"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function install(ide: (typeof SUPPORTED_IDES)[number]["name"]) {
|
|
52
|
+
const cmd = SUPPORTED_IDES.find((i) => i.name === ide)?.cmd
|
|
53
|
+
if (!cmd) throw new Error(`Unknown IDE: ${ide}`)
|
|
54
|
+
|
|
55
|
+
const p = spawn([cmd, "--install-extension", "sst-dev.opencode"], {
|
|
56
|
+
stdout: "pipe",
|
|
57
|
+
stderr: "pipe",
|
|
58
|
+
})
|
|
59
|
+
await p.exited
|
|
60
|
+
const stdout = await new Response(p.stdout).text()
|
|
61
|
+
const stderr = await new Response(p.stderr).text()
|
|
62
|
+
|
|
63
|
+
log.info("installed", {
|
|
64
|
+
ide,
|
|
65
|
+
stdout,
|
|
66
|
+
stderr,
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
if (p.exitCode !== 0) {
|
|
70
|
+
throw new InstallFailedError({ stderr })
|
|
71
|
+
}
|
|
72
|
+
if (stdout.includes("already installed")) {
|
|
73
|
+
throw new AlreadyInstalledError({})
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import yargs from "yargs"
|
|
2
|
+
import { hideBin } from "yargs/helpers"
|
|
3
|
+
import { RunCommand } from "./cli/cmd/run"
|
|
4
|
+
import { GenerateCommand } from "./cli/cmd/generate"
|
|
5
|
+
import { Log } from "./util/log"
|
|
6
|
+
import { AuthCommand } from "./cli/cmd/auth"
|
|
7
|
+
import { AgentCommand } from "./cli/cmd/agent"
|
|
8
|
+
import { UpgradeCommand } from "./cli/cmd/upgrade"
|
|
9
|
+
import { UninstallCommand } from "./cli/cmd/uninstall"
|
|
10
|
+
import { ModelsCommand } from "./cli/cmd/models"
|
|
11
|
+
import { UI } from "./cli/ui"
|
|
12
|
+
import { Installation } from "./installation"
|
|
13
|
+
import { NamedError } from "@opencode-ai/util/error"
|
|
14
|
+
import { FormatError } from "./cli/error"
|
|
15
|
+
import { ServeCommand } from "./cli/cmd/serve"
|
|
16
|
+
import { DebugCommand } from "./cli/cmd/debug"
|
|
17
|
+
import { StatsCommand } from "./cli/cmd/stats"
|
|
18
|
+
import { McpCommand } from "./cli/cmd/mcp"
|
|
19
|
+
import { GithubCommand } from "./cli/cmd/github"
|
|
20
|
+
import { ExportCommand } from "./cli/cmd/export"
|
|
21
|
+
import { ImportCommand } from "./cli/cmd/import"
|
|
22
|
+
import { AttachCommand } from "./cli/cmd/tui/attach"
|
|
23
|
+
import { TuiThreadCommand } from "./cli/cmd/tui/thread"
|
|
24
|
+
import { AcpCommand } from "./cli/cmd/acp"
|
|
25
|
+
import { EOL } from "os"
|
|
26
|
+
import { WebCommand } from "./cli/cmd/web"
|
|
27
|
+
import { PrCommand } from "./cli/cmd/pr"
|
|
28
|
+
import { SessionCommand } from "./cli/cmd/session"
|
|
29
|
+
|
|
30
|
+
process.on("unhandledRejection", (e) => {
|
|
31
|
+
Log.Default.error("rejection", {
|
|
32
|
+
e: e instanceof Error ? e.message : e,
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
process.on("uncaughtException", (e) => {
|
|
37
|
+
Log.Default.error("exception", {
|
|
38
|
+
e: e instanceof Error ? e.message : e,
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const cli = yargs(hideBin(process.argv))
|
|
43
|
+
.parserConfiguration({ "populate--": true })
|
|
44
|
+
.scriptName("innocode")
|
|
45
|
+
.wrap(100)
|
|
46
|
+
.help("help", "show help")
|
|
47
|
+
.alias("help", "h")
|
|
48
|
+
.version("version", "show version number", Installation.VERSION)
|
|
49
|
+
.alias("version", "v")
|
|
50
|
+
.option("print-logs", {
|
|
51
|
+
describe: "print logs to stderr",
|
|
52
|
+
type: "boolean",
|
|
53
|
+
})
|
|
54
|
+
.option("log-level", {
|
|
55
|
+
describe: "log level",
|
|
56
|
+
type: "string",
|
|
57
|
+
choices: ["DEBUG", "INFO", "WARN", "ERROR"],
|
|
58
|
+
})
|
|
59
|
+
.middleware(async (opts) => {
|
|
60
|
+
await Log.init({
|
|
61
|
+
print: process.argv.includes("--print-logs"),
|
|
62
|
+
dev: Installation.isLocal(),
|
|
63
|
+
level: (() => {
|
|
64
|
+
if (opts.logLevel) return opts.logLevel as Log.Level
|
|
65
|
+
if (Installation.isLocal()) return "DEBUG"
|
|
66
|
+
return "INFO"
|
|
67
|
+
})(),
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
process.env.AGENT = "1"
|
|
71
|
+
process.env.OPENCODE = "1"
|
|
72
|
+
process.env.INNOCODE = "1"
|
|
73
|
+
|
|
74
|
+
Log.Default.info("innocode", {
|
|
75
|
+
version: Installation.VERSION,
|
|
76
|
+
args: process.argv.slice(2),
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
.usage("\n" + UI.logo())
|
|
80
|
+
.completion("completion", "generate shell completion script")
|
|
81
|
+
.command(AcpCommand)
|
|
82
|
+
.command(McpCommand)
|
|
83
|
+
.command(TuiThreadCommand)
|
|
84
|
+
.command(AttachCommand)
|
|
85
|
+
.command(RunCommand)
|
|
86
|
+
.command(GenerateCommand)
|
|
87
|
+
.command(DebugCommand)
|
|
88
|
+
.command(AuthCommand)
|
|
89
|
+
.command(AgentCommand)
|
|
90
|
+
.command(UpgradeCommand)
|
|
91
|
+
.command(UninstallCommand)
|
|
92
|
+
.command(ServeCommand)
|
|
93
|
+
.command(WebCommand)
|
|
94
|
+
.command(ModelsCommand)
|
|
95
|
+
.command(StatsCommand)
|
|
96
|
+
.command(ExportCommand)
|
|
97
|
+
.command(ImportCommand)
|
|
98
|
+
.command(GithubCommand)
|
|
99
|
+
.command(PrCommand)
|
|
100
|
+
.command(SessionCommand)
|
|
101
|
+
.fail((msg, err) => {
|
|
102
|
+
if (
|
|
103
|
+
msg?.startsWith("Unknown argument") ||
|
|
104
|
+
msg?.startsWith("Not enough non-option arguments") ||
|
|
105
|
+
msg?.startsWith("Invalid values:")
|
|
106
|
+
) {
|
|
107
|
+
if (err) throw err
|
|
108
|
+
cli.showHelp("log")
|
|
109
|
+
}
|
|
110
|
+
if (err) throw err
|
|
111
|
+
process.exit(1)
|
|
112
|
+
})
|
|
113
|
+
.strict()
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
await cli.parse()
|
|
117
|
+
} catch (e) {
|
|
118
|
+
let data: Record<string, any> = {}
|
|
119
|
+
if (e instanceof NamedError) {
|
|
120
|
+
const obj = e.toObject()
|
|
121
|
+
Object.assign(data, {
|
|
122
|
+
...obj.data,
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (e instanceof Error) {
|
|
127
|
+
Object.assign(data, {
|
|
128
|
+
name: e.name,
|
|
129
|
+
message: e.message,
|
|
130
|
+
cause: e.cause?.toString(),
|
|
131
|
+
stack: e.stack,
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (e instanceof ResolveMessage) {
|
|
136
|
+
Object.assign(data, {
|
|
137
|
+
name: e.name,
|
|
138
|
+
message: e.message,
|
|
139
|
+
code: e.code,
|
|
140
|
+
specifier: e.specifier,
|
|
141
|
+
referrer: e.referrer,
|
|
142
|
+
position: e.position,
|
|
143
|
+
importKind: e.importKind,
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
Log.Default.error("fatal", data)
|
|
147
|
+
const formatted = FormatError(e)
|
|
148
|
+
if (formatted) UI.error(formatted)
|
|
149
|
+
if (formatted === undefined) {
|
|
150
|
+
UI.error("Unexpected error, check log file at " + Log.file() + " for more details" + EOL)
|
|
151
|
+
console.error(e instanceof Error ? e.message : String(e))
|
|
152
|
+
}
|
|
153
|
+
process.exitCode = 1
|
|
154
|
+
} finally {
|
|
155
|
+
// Some subprocesses don't react properly to SIGTERM and similar signals.
|
|
156
|
+
// Most notably, some docker-container-based MCP servers don't handle such signals unless
|
|
157
|
+
// run using `docker run --init`.
|
|
158
|
+
// Explicitly exit to avoid any hanging subprocesses.
|
|
159
|
+
process.exit()
|
|
160
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { BusEvent } from "@/bus/bus-event"
|
|
2
|
+
import path from "path"
|
|
3
|
+
import { $ } from "bun"
|
|
4
|
+
import z from "zod"
|
|
5
|
+
import { NamedError } from "@opencode-ai/util/error"
|
|
6
|
+
import { Log } from "../util/log"
|
|
7
|
+
import { iife } from "@/util/iife"
|
|
8
|
+
import { Flag } from "../flag/flag"
|
|
9
|
+
|
|
10
|
+
declare global {
|
|
11
|
+
const OPENCODE_VERSION: string
|
|
12
|
+
const OPENCODE_CHANNEL: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export namespace Installation {
|
|
16
|
+
const log = Log.create({ service: "installation" })
|
|
17
|
+
|
|
18
|
+
export type Method = Awaited<ReturnType<typeof method>>
|
|
19
|
+
|
|
20
|
+
export const Event = {
|
|
21
|
+
Updated: BusEvent.define(
|
|
22
|
+
"installation.updated",
|
|
23
|
+
z.object({
|
|
24
|
+
version: z.string(),
|
|
25
|
+
}),
|
|
26
|
+
),
|
|
27
|
+
UpdateAvailable: BusEvent.define(
|
|
28
|
+
"installation.update-available",
|
|
29
|
+
z.object({
|
|
30
|
+
version: z.string(),
|
|
31
|
+
}),
|
|
32
|
+
),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const Info = z
|
|
36
|
+
.object({
|
|
37
|
+
version: z.string(),
|
|
38
|
+
latest: z.string(),
|
|
39
|
+
})
|
|
40
|
+
.meta({
|
|
41
|
+
ref: "InstallationInfo",
|
|
42
|
+
})
|
|
43
|
+
export type Info = z.infer<typeof Info>
|
|
44
|
+
|
|
45
|
+
export async function info() {
|
|
46
|
+
return {
|
|
47
|
+
version: VERSION,
|
|
48
|
+
latest: await latest(),
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function isPreview() {
|
|
53
|
+
return CHANNEL !== "latest"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function isLocal() {
|
|
57
|
+
return CHANNEL === "local"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function method() {
|
|
61
|
+
if (process.execPath.includes(path.join(".innocode", "bin"))) return "curl"
|
|
62
|
+
if (process.execPath.includes(path.join(".opencode", "bin"))) return "curl"
|
|
63
|
+
if (process.execPath.includes(path.join(".local", "bin"))) return "curl"
|
|
64
|
+
const exec = process.execPath.toLowerCase()
|
|
65
|
+
|
|
66
|
+
const checks = [
|
|
67
|
+
{
|
|
68
|
+
name: "npm" as const,
|
|
69
|
+
command: () => $`npm list -g --depth=0`.throws(false).quiet().text(),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "yarn" as const,
|
|
73
|
+
command: () => $`yarn global list`.throws(false).quiet().text(),
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "pnpm" as const,
|
|
77
|
+
command: () => $`pnpm list -g --depth=0`.throws(false).quiet().text(),
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "bun" as const,
|
|
81
|
+
command: () => $`bun pm ls -g`.throws(false).quiet().text(),
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "brew" as const,
|
|
85
|
+
command: () => $`brew list --formula`.throws(false).quiet().text(),
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "scoop" as const,
|
|
89
|
+
command: () => $`scoop list`.throws(false).quiet().text(),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "choco" as const,
|
|
93
|
+
command: () => $`choco list --limit-output`.throws(false).quiet().text(),
|
|
94
|
+
},
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
checks.sort((a, b) => {
|
|
98
|
+
const aMatches = exec.includes(a.name)
|
|
99
|
+
const bMatches = exec.includes(b.name)
|
|
100
|
+
if (aMatches && !bMatches) return -1
|
|
101
|
+
if (!aMatches && bMatches) return 1
|
|
102
|
+
return 0
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
for (const check of checks) {
|
|
106
|
+
const output = await check.command()
|
|
107
|
+
const installedNames =
|
|
108
|
+
check.name === "brew" || check.name === "choco" || check.name === "scoop"
|
|
109
|
+
? ["innocode", "opencode"]
|
|
110
|
+
: ["innocode", "opencode-ai"]
|
|
111
|
+
if (installedNames.some((name) => output.includes(name))) {
|
|
112
|
+
return check.name
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return "unknown"
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const UpgradeFailedError = NamedError.create(
|
|
120
|
+
"UpgradeFailedError",
|
|
121
|
+
z.object({
|
|
122
|
+
stderr: z.string(),
|
|
123
|
+
}),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
async function getBrewFormula() {
|
|
127
|
+
const tapFormula = await $`brew list --formula inno-ki/tap/innocode`.throws(false).quiet().text()
|
|
128
|
+
if (tapFormula.includes("innocode")) return "inno-ki/tap/innocode"
|
|
129
|
+
const coreFormula = await $`brew list --formula innocode`.throws(false).quiet().text()
|
|
130
|
+
if (coreFormula.includes("innocode")) return "innocode"
|
|
131
|
+
const legacyTap = await $`brew list --formula anomalyco/tap/opencode`.throws(false).quiet().text()
|
|
132
|
+
if (legacyTap.includes("opencode")) return "anomalyco/tap/opencode"
|
|
133
|
+
const legacyCore = await $`brew list --formula opencode`.throws(false).quiet().text()
|
|
134
|
+
if (legacyCore.includes("opencode")) return "opencode"
|
|
135
|
+
return "innocode"
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function upgrade(method: Method, target: string) {
|
|
139
|
+
let cmd
|
|
140
|
+
switch (method) {
|
|
141
|
+
case "curl":
|
|
142
|
+
cmd = $`curl -fsSL https://innocode.io/install | bash`.env({
|
|
143
|
+
...process.env,
|
|
144
|
+
VERSION: target,
|
|
145
|
+
})
|
|
146
|
+
break
|
|
147
|
+
case "npm":
|
|
148
|
+
cmd = $`npm install -g innocode@${target}`
|
|
149
|
+
break
|
|
150
|
+
case "pnpm":
|
|
151
|
+
cmd = $`pnpm install -g innocode@${target}`
|
|
152
|
+
break
|
|
153
|
+
case "bun":
|
|
154
|
+
cmd = $`bun install -g innocode@${target}`
|
|
155
|
+
break
|
|
156
|
+
case "brew": {
|
|
157
|
+
const formula = await getBrewFormula()
|
|
158
|
+
if (formula.includes("/")) {
|
|
159
|
+
cmd =
|
|
160
|
+
$`brew tap inno-ki/tap && cd "$(brew --repo inno-ki/tap)" && git pull --ff-only && brew upgrade ${formula}`.env(
|
|
161
|
+
{
|
|
162
|
+
HOMEBREW_NO_AUTO_UPDATE: "1",
|
|
163
|
+
...process.env,
|
|
164
|
+
},
|
|
165
|
+
)
|
|
166
|
+
break
|
|
167
|
+
}
|
|
168
|
+
cmd = $`brew upgrade ${formula}`.env({
|
|
169
|
+
HOMEBREW_NO_AUTO_UPDATE: "1",
|
|
170
|
+
...process.env,
|
|
171
|
+
})
|
|
172
|
+
break
|
|
173
|
+
}
|
|
174
|
+
case "choco":
|
|
175
|
+
cmd = $`echo Y | choco upgrade innocode --version=${target}`
|
|
176
|
+
break
|
|
177
|
+
case "scoop":
|
|
178
|
+
cmd = $`scoop install innocode@${target}`
|
|
179
|
+
break
|
|
180
|
+
default:
|
|
181
|
+
throw new Error(`Unknown method: ${method}`)
|
|
182
|
+
}
|
|
183
|
+
const result = await cmd.quiet().throws(false)
|
|
184
|
+
if (result.exitCode !== 0) {
|
|
185
|
+
const stderr = method === "choco" ? "not running from an elevated command shell" : result.stderr.toString("utf8")
|
|
186
|
+
throw new UpgradeFailedError({
|
|
187
|
+
stderr: stderr,
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
log.info("upgraded", {
|
|
191
|
+
method,
|
|
192
|
+
target,
|
|
193
|
+
stdout: result.stdout.toString(),
|
|
194
|
+
stderr: result.stderr.toString(),
|
|
195
|
+
})
|
|
196
|
+
await $`${process.execPath} --version`.nothrow().quiet().text()
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export const VERSION = typeof OPENCODE_VERSION === "string" ? OPENCODE_VERSION : "local"
|
|
200
|
+
export const CHANNEL = typeof OPENCODE_CHANNEL === "string" ? OPENCODE_CHANNEL : "local"
|
|
201
|
+
export const USER_AGENT = `innocode/${CHANNEL}/${VERSION}/${Flag.INNOCODE_CLIENT}`
|
|
202
|
+
|
|
203
|
+
export async function latest(installMethod?: Method) {
|
|
204
|
+
const detectedMethod = installMethod || (await method())
|
|
205
|
+
|
|
206
|
+
if (detectedMethod === "brew") {
|
|
207
|
+
const formula = await getBrewFormula()
|
|
208
|
+
if (formula.includes("/")) {
|
|
209
|
+
const infoJson = await $`brew info --json=v2 ${formula}`.quiet().text()
|
|
210
|
+
const info = JSON.parse(infoJson)
|
|
211
|
+
const version = info.formulae?.[0]?.versions?.stable
|
|
212
|
+
if (!version) throw new Error(`Could not detect version for tap formula: ${formula}`)
|
|
213
|
+
return version
|
|
214
|
+
}
|
|
215
|
+
return fetch("https://formulae.brew.sh/api/formula/innocode.json")
|
|
216
|
+
.then((res) => {
|
|
217
|
+
if (!res.ok) throw new Error(res.statusText)
|
|
218
|
+
return res.json()
|
|
219
|
+
})
|
|
220
|
+
.then((data: any) => data.versions.stable)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (detectedMethod === "npm" || detectedMethod === "bun" || detectedMethod === "pnpm") {
|
|
224
|
+
const registry = await iife(async () => {
|
|
225
|
+
const r = (await $`npm config get registry`.quiet().nothrow().text()).trim()
|
|
226
|
+
const reg = r || "https://registry.npmjs.org"
|
|
227
|
+
return reg.endsWith("/") ? reg.slice(0, -1) : reg
|
|
228
|
+
})
|
|
229
|
+
const channel = CHANNEL
|
|
230
|
+
return fetch(`${registry}/innocode/${channel}`)
|
|
231
|
+
.then((res) => {
|
|
232
|
+
if (!res.ok) throw new Error(res.statusText)
|
|
233
|
+
return res.json()
|
|
234
|
+
})
|
|
235
|
+
.then((data: any) => data.version)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (detectedMethod === "choco") {
|
|
239
|
+
return fetch(
|
|
240
|
+
"https://community.chocolatey.org/api/v2/Packages?$filter=Id%20eq%20%27innocode%27%20and%20IsLatestVersion&$select=Version",
|
|
241
|
+
{ headers: { Accept: "application/json;odata=verbose" } },
|
|
242
|
+
)
|
|
243
|
+
.then((res) => {
|
|
244
|
+
if (!res.ok) throw new Error(res.statusText)
|
|
245
|
+
return res.json()
|
|
246
|
+
})
|
|
247
|
+
.then((data: any) => data.d.results[0].Version)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (detectedMethod === "scoop") {
|
|
251
|
+
return fetch("https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/innocode.json", {
|
|
252
|
+
headers: { Accept: "application/json" },
|
|
253
|
+
})
|
|
254
|
+
.then((res) => {
|
|
255
|
+
if (!res.ok) throw new Error(res.statusText)
|
|
256
|
+
return res.json()
|
|
257
|
+
})
|
|
258
|
+
.then((data: any) => data.version)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return fetch("https://api.github.com/repos/Inno-ki/innocode/releases/latest")
|
|
262
|
+
.then((res) => {
|
|
263
|
+
if (!res.ok) throw new Error(res.statusText)
|
|
264
|
+
return res.json()
|
|
265
|
+
})
|
|
266
|
+
.then((data: any) => data.tag_name.replace(/^v/, ""))
|
|
267
|
+
}
|
|
268
|
+
}
|