@stonerzju/opencode 1.2.16-offline.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +10 -0
- package/BUN_SHELL_MIGRATION_PLAN.md +136 -0
- package/Dockerfile +18 -0
- package/README.md +15 -0
- package/bin/opencode +179 -0
- package/bunfig.toml +7 -0
- package/drizzle.config.ts +10 -0
- package/migration/20260127222353_familiar_lady_ursula/migration.sql +90 -0
- package/migration/20260127222353_familiar_lady_ursula/snapshot.json +796 -0
- package/migration/20260211171708_add_project_commands/migration.sql +1 -0
- package/migration/20260211171708_add_project_commands/snapshot.json +806 -0
- package/migration/20260213144116_wakeful_the_professor/migration.sql +11 -0
- package/migration/20260213144116_wakeful_the_professor/snapshot.json +897 -0
- package/migration/20260225215848_workspace/migration.sql +7 -0
- package/migration/20260225215848_workspace/snapshot.json +959 -0
- package/package.json +140 -0
- package/package.json.bak +140 -0
- package/parsers-config.ts +254 -0
- package/script/build.ts +224 -0
- package/script/check-migrations.ts +16 -0
- package/script/postinstall.mjs +131 -0
- package/script/publish.ts +181 -0
- package/script/schema.ts +63 -0
- package/script/seed-e2e.ts +50 -0
- package/src/acp/README.md +174 -0
- package/src/acp/agent.ts +1741 -0
- package/src/acp/session.ts +116 -0
- package/src/acp/types.ts +23 -0
- package/src/agent/agent.ts +339 -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 +68 -0
- package/src/bun/index.ts +131 -0
- package/src/bun/registry.ts +50 -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 +449 -0
- package/src/cli/cmd/cmd.ts +7 -0
- package/src/cli/cmd/db.ts +118 -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 +1631 -0
- package/src/cli/cmd/import.ts +170 -0
- package/src/cli/cmd/mcp.ts +754 -0
- package/src/cli/cmd/models.ts +77 -0
- package/src/cli/cmd/pr.ts +112 -0
- package/src/cli/cmd/run.ts +625 -0
- package/src/cli/cmd/serve.ts +31 -0
- package/src/cli/cmd/session.ts +156 -0
- package/src/cli/cmd/stats.ts +410 -0
- package/src/cli/cmd/tui/app.tsx +845 -0
- package/src/cli/cmd/tui/attach.ts +88 -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 +147 -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 +259 -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 +667 -0
- package/src/cli/cmd/tui/component/prompt/frecency.tsx +90 -0
- package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
- package/src/cli/cmd/tui/component/prompt/index.tsx +1155 -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 +152 -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 +53 -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 +406 -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 +488 -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/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 +1152 -0
- package/src/cli/cmd/tui/context/tui-config.tsx +9 -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 +2219 -0
- package/src/cli/cmd/tui/routes/session/permission.tsx +685 -0
- package/src/cli/cmd/tui/routes/session/question.tsx +466 -0
- package/src/cli/cmd/tui/routes/session/sidebar.tsx +321 -0
- package/src/cli/cmd/tui/thread.ts +199 -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 +182 -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 +164 -0
- package/src/cli/cmd/tui/util/editor.ts +33 -0
- package/src/cli/cmd/tui/util/selection.ts +25 -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 +157 -0
- package/src/cli/cmd/uninstall.ts +356 -0
- package/src/cli/cmd/upgrade.ts +73 -0
- package/src/cli/cmd/web.ts +81 -0
- package/src/cli/cmd/workspace-serve.ts +16 -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 +116 -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 +1408 -0
- package/src/config/markdown.ts +99 -0
- package/src/config/migrate-tui-config.ts +155 -0
- package/src/config/paths.ts +174 -0
- package/src/config/tui-schema.ts +34 -0
- package/src/config/tui.ts +118 -0
- package/src/control/control.sql.ts +22 -0
- package/src/control/index.ts +67 -0
- package/src/control-plane/adaptors/index.ts +10 -0
- package/src/control-plane/adaptors/types.ts +7 -0
- package/src/control-plane/adaptors/worktree.ts +26 -0
- package/src/control-plane/config.ts +10 -0
- package/src/control-plane/session-proxy-middleware.ts +46 -0
- package/src/control-plane/sse.ts +66 -0
- package/src/control-plane/workspace-server/routes.ts +33 -0
- package/src/control-plane/workspace-server/server.ts +24 -0
- package/src/control-plane/workspace.sql.ts +12 -0
- package/src/control-plane/workspace.ts +160 -0
- package/src/env/index.ts +28 -0
- package/src/file/ignore.ts +82 -0
- package/src/file/index.ts +646 -0
- package/src/file/ripgrep.ts +372 -0
- package/src/file/time.ts +71 -0
- package/src/file/watcher.ts +128 -0
- package/src/flag/flag.ts +109 -0
- package/src/format/formatter.ts +395 -0
- package/src/format/index.ts +140 -0
- package/src/global/index.ts +54 -0
- package/src/id/id.ts +84 -0
- package/src/ide/index.ts +76 -0
- package/src/index.ts +210 -0
- package/src/installation/index.ts +266 -0
- package/src/lsp/client.ts +251 -0
- package/src/lsp/index.ts +485 -0
- package/src/lsp/language.ts +120 -0
- package/src/lsp/server.ts +2142 -0
- package/src/mcp/auth.ts +130 -0
- package/src/mcp/index.ts +937 -0
- package/src/mcp/oauth-callback.ts +200 -0
- package/src/mcp/oauth-provider.ts +176 -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 +286 -0
- package/src/plugin/codex.ts +624 -0
- package/src/plugin/copilot.ts +327 -0
- package/src/plugin/index.ts +143 -0
- package/src/project/bootstrap.ts +33 -0
- package/src/project/instance.ts +114 -0
- package/src/project/project.sql.ts +15 -0
- package/src/project/project.ts +441 -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 +146 -0
- package/src/provider/provider.ts +1338 -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 +955 -0
- package/src/pty/index.ts +324 -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 +270 -0
- package/src/server/routes/file.ts +197 -0
- package/src/server/routes/global.ts +185 -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 +165 -0
- package/src/server/routes/pty.ts +200 -0
- package/src/server/routes/question.ts +98 -0
- package/src/server/routes/session.ts +974 -0
- package/src/server/routes/tui.ts +379 -0
- package/src/server/routes/workspace.ts +104 -0
- package/src/server/server.ts +623 -0
- package/src/session/compaction.ts +261 -0
- package/src/session/index.ts +877 -0
- package/src/session/instruction.ts +192 -0
- package/src/session/llm.ts +279 -0
- package/src/session/message-v2.ts +899 -0
- package/src/session/message.ts +189 -0
- package/src/session/processor.ts +421 -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 +1959 -0
- package/src/session/retry.ts +101 -0
- package/src/session/revert.ts +138 -0
- package/src/session/session.sql.ts +88 -0
- package/src/session/status.ts +76 -0
- package/src/session/summary.ts +161 -0
- package/src/session/system.ts +54 -0
- package/src/session/todo.ts +56 -0
- package/src/share/share-next.ts +210 -0
- package/src/share/share.sql.ts +13 -0
- package/src/shell/shell.ts +68 -0
- package/src/skill/discovery.ts +98 -0
- package/src/skill/index.ts +1 -0
- package/src/skill/skill.ts +189 -0
- package/src/snapshot/index.ts +297 -0
- package/src/sql.d.ts +4 -0
- package/src/storage/db.ts +155 -0
- package/src/storage/json-migration.ts +425 -0
- package/src/storage/schema.sql.ts +10 -0
- package/src/storage/schema.ts +5 -0
- package/src/storage/storage.ts +220 -0
- package/src/tool/apply_patch.ts +281 -0
- package/src/tool/apply_patch.txt +33 -0
- package/src/tool/bash.ts +274 -0
- package/src/tool/bash.txt +115 -0
- package/src/tool/batch.ts +181 -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 +654 -0
- package/src/tool/edit.txt +10 -0
- package/src/tool/external-directory.ts +32 -0
- package/src/tool/glob.ts +78 -0
- package/src/tool/glob.txt +6 -0
- package/src/tool/grep.ts +156 -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 +97 -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 +131 -0
- package/src/tool/question.ts +33 -0
- package/src/tool/question.txt +10 -0
- package/src/tool/read.ts +293 -0
- package/src/tool/read.txt +14 -0
- package/src/tool/registry.ts +173 -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 +107 -0
- package/src/tool/webfetch.ts +206 -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 +84 -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 +189 -0
- package/src/util/fn.ts +11 -0
- package/src/util/format.ts +20 -0
- package/src/util/git.ts +35 -0
- package/src/util/glob.ts +34 -0
- package/src/util/iife.ts +3 -0
- package/src/util/keybind.ts +103 -0
- package/src/util/lazy.ts +23 -0
- package/src/util/locale.ts +81 -0
- package/src/util/lock.ts +98 -0
- package/src/util/log.ts +182 -0
- package/src/util/process.ts +126 -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 +59 -0
- package/src/worktree/index.ts +643 -0
- package/sst-env.d.ts +10 -0
- package/test/AGENTS.md +81 -0
- package/test/acp/agent-interface.test.ts +51 -0
- package/test/acp/event-subscription.test.ts +683 -0
- package/test/agent/agent.test.ts +689 -0
- package/test/bun.test.ts +53 -0
- package/test/cli/github-action.test.ts +197 -0
- package/test/cli/github-remote.test.ts +80 -0
- package/test/cli/import.test.ts +38 -0
- package/test/cli/plugin-auth-picker.test.ts +120 -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 +1886 -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/config/tui.test.ts +510 -0
- package/test/control-plane/session-proxy-middleware.test.ts +147 -0
- package/test/control-plane/sse.test.ts +56 -0
- package/test/control-plane/workspace-server-sse.test.ts +65 -0
- package/test/control-plane/workspace-sync.test.ts +97 -0
- package/test/file/ignore.test.ts +10 -0
- package/test/file/index.test.ts +394 -0
- package/test/file/path-traversal.test.ts +198 -0
- package/test/file/ripgrep.test.ts +39 -0
- package/test/file/time.test.ts +361 -0
- package/test/fixture/db.ts +11 -0
- package/test/fixture/fixture.ts +45 -0
- package/test/fixture/lsp/fake-lsp-server.js +77 -0
- package/test/fixture/skills/agents-sdk/SKILL.md +152 -0
- package/test/fixture/skills/agents-sdk/references/callable.md +92 -0
- package/test/fixture/skills/cloudflare/SKILL.md +211 -0
- package/test/fixture/skills/index.json +6 -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 +689 -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 +80 -0
- package/test/project/project.test.ts +348 -0
- package/test/project/worktree-remove.test.ts +65 -0
- package/test/provider/amazon-bedrock.test.ts +446 -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 +2220 -0
- package/test/provider/transform.test.ts +2353 -0
- package/test/pty/pty-output-isolation.test.ts +140 -0
- package/test/question/question.test.ts +300 -0
- package/test/scheduler.test.ts +73 -0
- package/test/server/global-session-list.test.ts +89 -0
- package/test/server/session-list.test.ts +90 -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.test.ts +211 -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 +110 -0
- package/test/skill/skill.test.ts +388 -0
- package/test/snapshot/snapshot.test.ts +1180 -0
- package/test/storage/json-migration.test.ts +846 -0
- package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
- package/test/tool/apply_patch.test.ts +566 -0
- package/test/tool/bash.test.ts +402 -0
- package/test/tool/edit.test.ts +496 -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 +504 -0
- package/test/tool/registry.test.ts +122 -0
- package/test/tool/skill.test.ts +112 -0
- package/test/tool/truncation.test.ts +160 -0
- package/test/tool/webfetch.test.ts +100 -0
- package/test/tool/write.test.ts +348 -0
- package/test/util/filesystem.test.ts +443 -0
- package/test/util/format.test.ts +59 -0
- package/test/util/glob.test.ts +164 -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/process.test.ts +59 -0
- package/test/util/timeout.test.ts +21 -0
- package/test/util/wildcard.test.ts +90 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import { Instance } from "../../src/project/instance"
|
|
3
|
+
import { Pty } from "../../src/pty"
|
|
4
|
+
import { tmpdir } from "../fixture/fixture"
|
|
5
|
+
|
|
6
|
+
describe("pty", () => {
|
|
7
|
+
test("does not leak output when websocket objects are reused", async () => {
|
|
8
|
+
await using dir = await tmpdir({ git: true })
|
|
9
|
+
|
|
10
|
+
await Instance.provide({
|
|
11
|
+
directory: dir.path,
|
|
12
|
+
fn: async () => {
|
|
13
|
+
const a = await Pty.create({ command: "cat", title: "a" })
|
|
14
|
+
const b = await Pty.create({ command: "cat", title: "b" })
|
|
15
|
+
try {
|
|
16
|
+
const outA: string[] = []
|
|
17
|
+
const outB: string[] = []
|
|
18
|
+
|
|
19
|
+
const ws = {
|
|
20
|
+
readyState: 1,
|
|
21
|
+
data: { events: { connection: "a" } },
|
|
22
|
+
send: (data: unknown) => {
|
|
23
|
+
outA.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8"))
|
|
24
|
+
},
|
|
25
|
+
close: () => {
|
|
26
|
+
// no-op (simulate abrupt drop)
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Connect "a" first with ws.
|
|
31
|
+
Pty.connect(a.id, ws as any)
|
|
32
|
+
|
|
33
|
+
// Now "reuse" the same ws object for another connection.
|
|
34
|
+
ws.data = { events: { connection: "b" } }
|
|
35
|
+
ws.send = (data: unknown) => {
|
|
36
|
+
outB.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8"))
|
|
37
|
+
}
|
|
38
|
+
Pty.connect(b.id, ws as any)
|
|
39
|
+
|
|
40
|
+
// Clear connect metadata writes.
|
|
41
|
+
outA.length = 0
|
|
42
|
+
outB.length = 0
|
|
43
|
+
|
|
44
|
+
// Output from a must never show up in b.
|
|
45
|
+
Pty.write(a.id, "AAA\n")
|
|
46
|
+
await Bun.sleep(100)
|
|
47
|
+
|
|
48
|
+
expect(outB.join("")).not.toContain("AAA")
|
|
49
|
+
} finally {
|
|
50
|
+
await Pty.remove(a.id)
|
|
51
|
+
await Pty.remove(b.id)
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test("does not leak output when Bun recycles websocket objects before re-connect", async () => {
|
|
58
|
+
await using dir = await tmpdir({ git: true })
|
|
59
|
+
|
|
60
|
+
await Instance.provide({
|
|
61
|
+
directory: dir.path,
|
|
62
|
+
fn: async () => {
|
|
63
|
+
const a = await Pty.create({ command: "cat", title: "a" })
|
|
64
|
+
try {
|
|
65
|
+
const outA: string[] = []
|
|
66
|
+
const outB: string[] = []
|
|
67
|
+
|
|
68
|
+
const ws = {
|
|
69
|
+
readyState: 1,
|
|
70
|
+
data: { events: { connection: "a" } },
|
|
71
|
+
send: (data: unknown) => {
|
|
72
|
+
outA.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8"))
|
|
73
|
+
},
|
|
74
|
+
close: () => {
|
|
75
|
+
// no-op (simulate abrupt drop)
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Connect "a" first.
|
|
80
|
+
Pty.connect(a.id, ws as any)
|
|
81
|
+
outA.length = 0
|
|
82
|
+
|
|
83
|
+
// Simulate Bun reusing the same websocket object for another
|
|
84
|
+
// connection before the next onOpen calls Pty.connect.
|
|
85
|
+
ws.data = { events: { connection: "b" } }
|
|
86
|
+
ws.send = (data: unknown) => {
|
|
87
|
+
outB.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8"))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
Pty.write(a.id, "AAA\n")
|
|
91
|
+
await Bun.sleep(100)
|
|
92
|
+
|
|
93
|
+
expect(outB.join("")).not.toContain("AAA")
|
|
94
|
+
} finally {
|
|
95
|
+
await Pty.remove(a.id)
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test("treats in-place socket data mutation as the same connection", async () => {
|
|
102
|
+
await using dir = await tmpdir({ git: true })
|
|
103
|
+
|
|
104
|
+
await Instance.provide({
|
|
105
|
+
directory: dir.path,
|
|
106
|
+
fn: async () => {
|
|
107
|
+
const a = await Pty.create({ command: "cat", title: "a" })
|
|
108
|
+
try {
|
|
109
|
+
const out: string[] = []
|
|
110
|
+
|
|
111
|
+
const ctx = { connId: 1 }
|
|
112
|
+
const ws = {
|
|
113
|
+
readyState: 1,
|
|
114
|
+
data: ctx,
|
|
115
|
+
send: (data: unknown) => {
|
|
116
|
+
out.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8"))
|
|
117
|
+
},
|
|
118
|
+
close: () => {
|
|
119
|
+
// no-op
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
Pty.connect(a.id, ws as any)
|
|
124
|
+
out.length = 0
|
|
125
|
+
|
|
126
|
+
// Mutating fields on ws.data should not look like a new
|
|
127
|
+
// connection lifecycle when the object identity stays stable.
|
|
128
|
+
ctx.connId = 2
|
|
129
|
+
|
|
130
|
+
Pty.write(a.id, "AAA\n")
|
|
131
|
+
await Bun.sleep(100)
|
|
132
|
+
|
|
133
|
+
expect(out.join("")).toContain("AAA")
|
|
134
|
+
} finally {
|
|
135
|
+
await Pty.remove(a.id)
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
})
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { test, expect } from "bun:test"
|
|
2
|
+
import { Question } from "../../src/question"
|
|
3
|
+
import { Instance } from "../../src/project/instance"
|
|
4
|
+
import { tmpdir } from "../fixture/fixture"
|
|
5
|
+
|
|
6
|
+
test("ask - returns pending promise", async () => {
|
|
7
|
+
await using tmp = await tmpdir({ git: true })
|
|
8
|
+
await Instance.provide({
|
|
9
|
+
directory: tmp.path,
|
|
10
|
+
fn: async () => {
|
|
11
|
+
const promise = Question.ask({
|
|
12
|
+
sessionID: "ses_test",
|
|
13
|
+
questions: [
|
|
14
|
+
{
|
|
15
|
+
question: "What would you like to do?",
|
|
16
|
+
header: "Action",
|
|
17
|
+
options: [
|
|
18
|
+
{ label: "Option 1", description: "First option" },
|
|
19
|
+
{ label: "Option 2", description: "Second option" },
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
})
|
|
24
|
+
expect(promise).toBeInstanceOf(Promise)
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test("ask - adds to pending list", async () => {
|
|
30
|
+
await using tmp = await tmpdir({ git: true })
|
|
31
|
+
await Instance.provide({
|
|
32
|
+
directory: tmp.path,
|
|
33
|
+
fn: async () => {
|
|
34
|
+
const questions = [
|
|
35
|
+
{
|
|
36
|
+
question: "What would you like to do?",
|
|
37
|
+
header: "Action",
|
|
38
|
+
options: [
|
|
39
|
+
{ label: "Option 1", description: "First option" },
|
|
40
|
+
{ label: "Option 2", description: "Second option" },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
Question.ask({
|
|
46
|
+
sessionID: "ses_test",
|
|
47
|
+
questions,
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const pending = await Question.list()
|
|
51
|
+
expect(pending.length).toBe(1)
|
|
52
|
+
expect(pending[0].questions).toEqual(questions)
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
// reply tests
|
|
58
|
+
|
|
59
|
+
test("reply - resolves the pending ask with answers", async () => {
|
|
60
|
+
await using tmp = await tmpdir({ git: true })
|
|
61
|
+
await Instance.provide({
|
|
62
|
+
directory: tmp.path,
|
|
63
|
+
fn: async () => {
|
|
64
|
+
const questions = [
|
|
65
|
+
{
|
|
66
|
+
question: "What would you like to do?",
|
|
67
|
+
header: "Action",
|
|
68
|
+
options: [
|
|
69
|
+
{ label: "Option 1", description: "First option" },
|
|
70
|
+
{ label: "Option 2", description: "Second option" },
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
const askPromise = Question.ask({
|
|
76
|
+
sessionID: "ses_test",
|
|
77
|
+
questions,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const pending = await Question.list()
|
|
81
|
+
const requestID = pending[0].id
|
|
82
|
+
|
|
83
|
+
await Question.reply({
|
|
84
|
+
requestID,
|
|
85
|
+
answers: [["Option 1"]],
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const answers = await askPromise
|
|
89
|
+
expect(answers).toEqual([["Option 1"]])
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test("reply - removes from pending list", async () => {
|
|
95
|
+
await using tmp = await tmpdir({ git: true })
|
|
96
|
+
await Instance.provide({
|
|
97
|
+
directory: tmp.path,
|
|
98
|
+
fn: async () => {
|
|
99
|
+
Question.ask({
|
|
100
|
+
sessionID: "ses_test",
|
|
101
|
+
questions: [
|
|
102
|
+
{
|
|
103
|
+
question: "What would you like to do?",
|
|
104
|
+
header: "Action",
|
|
105
|
+
options: [
|
|
106
|
+
{ label: "Option 1", description: "First option" },
|
|
107
|
+
{ label: "Option 2", description: "Second option" },
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const pending = await Question.list()
|
|
114
|
+
expect(pending.length).toBe(1)
|
|
115
|
+
|
|
116
|
+
await Question.reply({
|
|
117
|
+
requestID: pending[0].id,
|
|
118
|
+
answers: [["Option 1"]],
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
const pendingAfter = await Question.list()
|
|
122
|
+
expect(pendingAfter.length).toBe(0)
|
|
123
|
+
},
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
test("reply - does nothing for unknown requestID", async () => {
|
|
128
|
+
await using tmp = await tmpdir({ git: true })
|
|
129
|
+
await Instance.provide({
|
|
130
|
+
directory: tmp.path,
|
|
131
|
+
fn: async () => {
|
|
132
|
+
await Question.reply({
|
|
133
|
+
requestID: "que_unknown",
|
|
134
|
+
answers: [["Option 1"]],
|
|
135
|
+
})
|
|
136
|
+
// Should not throw
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
// reject tests
|
|
142
|
+
|
|
143
|
+
test("reject - throws RejectedError", async () => {
|
|
144
|
+
await using tmp = await tmpdir({ git: true })
|
|
145
|
+
await Instance.provide({
|
|
146
|
+
directory: tmp.path,
|
|
147
|
+
fn: async () => {
|
|
148
|
+
const askPromise = Question.ask({
|
|
149
|
+
sessionID: "ses_test",
|
|
150
|
+
questions: [
|
|
151
|
+
{
|
|
152
|
+
question: "What would you like to do?",
|
|
153
|
+
header: "Action",
|
|
154
|
+
options: [
|
|
155
|
+
{ label: "Option 1", description: "First option" },
|
|
156
|
+
{ label: "Option 2", description: "Second option" },
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
const pending = await Question.list()
|
|
163
|
+
await Question.reject(pending[0].id)
|
|
164
|
+
|
|
165
|
+
await expect(askPromise).rejects.toBeInstanceOf(Question.RejectedError)
|
|
166
|
+
},
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
test("reject - removes from pending list", async () => {
|
|
171
|
+
await using tmp = await tmpdir({ git: true })
|
|
172
|
+
await Instance.provide({
|
|
173
|
+
directory: tmp.path,
|
|
174
|
+
fn: async () => {
|
|
175
|
+
const askPromise = Question.ask({
|
|
176
|
+
sessionID: "ses_test",
|
|
177
|
+
questions: [
|
|
178
|
+
{
|
|
179
|
+
question: "What would you like to do?",
|
|
180
|
+
header: "Action",
|
|
181
|
+
options: [
|
|
182
|
+
{ label: "Option 1", description: "First option" },
|
|
183
|
+
{ label: "Option 2", description: "Second option" },
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
const pending = await Question.list()
|
|
190
|
+
expect(pending.length).toBe(1)
|
|
191
|
+
|
|
192
|
+
await Question.reject(pending[0].id)
|
|
193
|
+
askPromise.catch(() => {}) // Ignore rejection
|
|
194
|
+
|
|
195
|
+
const pendingAfter = await Question.list()
|
|
196
|
+
expect(pendingAfter.length).toBe(0)
|
|
197
|
+
},
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test("reject - does nothing for unknown requestID", async () => {
|
|
202
|
+
await using tmp = await tmpdir({ git: true })
|
|
203
|
+
await Instance.provide({
|
|
204
|
+
directory: tmp.path,
|
|
205
|
+
fn: async () => {
|
|
206
|
+
await Question.reject("que_unknown")
|
|
207
|
+
// Should not throw
|
|
208
|
+
},
|
|
209
|
+
})
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
// multiple questions tests
|
|
213
|
+
|
|
214
|
+
test("ask - handles multiple questions", async () => {
|
|
215
|
+
await using tmp = await tmpdir({ git: true })
|
|
216
|
+
await Instance.provide({
|
|
217
|
+
directory: tmp.path,
|
|
218
|
+
fn: async () => {
|
|
219
|
+
const questions = [
|
|
220
|
+
{
|
|
221
|
+
question: "What would you like to do?",
|
|
222
|
+
header: "Action",
|
|
223
|
+
options: [
|
|
224
|
+
{ label: "Build", description: "Build the project" },
|
|
225
|
+
{ label: "Test", description: "Run tests" },
|
|
226
|
+
],
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
question: "Which environment?",
|
|
230
|
+
header: "Env",
|
|
231
|
+
options: [
|
|
232
|
+
{ label: "Dev", description: "Development" },
|
|
233
|
+
{ label: "Prod", description: "Production" },
|
|
234
|
+
],
|
|
235
|
+
},
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
const askPromise = Question.ask({
|
|
239
|
+
sessionID: "ses_test",
|
|
240
|
+
questions,
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
const pending = await Question.list()
|
|
244
|
+
|
|
245
|
+
await Question.reply({
|
|
246
|
+
requestID: pending[0].id,
|
|
247
|
+
answers: [["Build"], ["Dev"]],
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
const answers = await askPromise
|
|
251
|
+
expect(answers).toEqual([["Build"], ["Dev"]])
|
|
252
|
+
},
|
|
253
|
+
})
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
// list tests
|
|
257
|
+
|
|
258
|
+
test("list - returns all pending requests", async () => {
|
|
259
|
+
await using tmp = await tmpdir({ git: true })
|
|
260
|
+
await Instance.provide({
|
|
261
|
+
directory: tmp.path,
|
|
262
|
+
fn: async () => {
|
|
263
|
+
Question.ask({
|
|
264
|
+
sessionID: "ses_test1",
|
|
265
|
+
questions: [
|
|
266
|
+
{
|
|
267
|
+
question: "Question 1?",
|
|
268
|
+
header: "Q1",
|
|
269
|
+
options: [{ label: "A", description: "A" }],
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
Question.ask({
|
|
275
|
+
sessionID: "ses_test2",
|
|
276
|
+
questions: [
|
|
277
|
+
{
|
|
278
|
+
question: "Question 2?",
|
|
279
|
+
header: "Q2",
|
|
280
|
+
options: [{ label: "B", description: "B" }],
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
const pending = await Question.list()
|
|
286
|
+
expect(pending.length).toBe(2)
|
|
287
|
+
},
|
|
288
|
+
})
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
test("list - returns empty when no pending", async () => {
|
|
292
|
+
await using tmp = await tmpdir({ git: true })
|
|
293
|
+
await Instance.provide({
|
|
294
|
+
directory: tmp.path,
|
|
295
|
+
fn: async () => {
|
|
296
|
+
const pending = await Question.list()
|
|
297
|
+
expect(pending.length).toBe(0)
|
|
298
|
+
},
|
|
299
|
+
})
|
|
300
|
+
})
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import { Scheduler } from "../src/scheduler"
|
|
3
|
+
import { Instance } from "../src/project/instance"
|
|
4
|
+
import { tmpdir } from "./fixture/fixture"
|
|
5
|
+
|
|
6
|
+
describe("Scheduler.register", () => {
|
|
7
|
+
const hour = 60 * 60 * 1000
|
|
8
|
+
|
|
9
|
+
test("defaults to instance scope per directory", async () => {
|
|
10
|
+
await using one = await tmpdir({ git: true })
|
|
11
|
+
await using two = await tmpdir({ git: true })
|
|
12
|
+
const runs = { count: 0 }
|
|
13
|
+
const id = "scheduler.instance." + Math.random().toString(36).slice(2)
|
|
14
|
+
const task = {
|
|
15
|
+
id,
|
|
16
|
+
interval: hour,
|
|
17
|
+
run: async () => {
|
|
18
|
+
runs.count += 1
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
await Instance.provide({
|
|
23
|
+
directory: one.path,
|
|
24
|
+
fn: async () => {
|
|
25
|
+
Scheduler.register(task)
|
|
26
|
+
await Instance.dispose()
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
expect(runs.count).toBe(1)
|
|
30
|
+
|
|
31
|
+
await Instance.provide({
|
|
32
|
+
directory: two.path,
|
|
33
|
+
fn: async () => {
|
|
34
|
+
Scheduler.register(task)
|
|
35
|
+
await Instance.dispose()
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
expect(runs.count).toBe(2)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test("global scope runs once across instances", async () => {
|
|
42
|
+
await using one = await tmpdir({ git: true })
|
|
43
|
+
await using two = await tmpdir({ git: true })
|
|
44
|
+
const runs = { count: 0 }
|
|
45
|
+
const id = "scheduler.global." + Math.random().toString(36).slice(2)
|
|
46
|
+
const task = {
|
|
47
|
+
id,
|
|
48
|
+
interval: hour,
|
|
49
|
+
run: async () => {
|
|
50
|
+
runs.count += 1
|
|
51
|
+
},
|
|
52
|
+
scope: "global" as const,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
await Instance.provide({
|
|
56
|
+
directory: one.path,
|
|
57
|
+
fn: async () => {
|
|
58
|
+
Scheduler.register(task)
|
|
59
|
+
await Instance.dispose()
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
expect(runs.count).toBe(1)
|
|
63
|
+
|
|
64
|
+
await Instance.provide({
|
|
65
|
+
directory: two.path,
|
|
66
|
+
fn: async () => {
|
|
67
|
+
Scheduler.register(task)
|
|
68
|
+
await Instance.dispose()
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
expect(runs.count).toBe(1)
|
|
72
|
+
})
|
|
73
|
+
})
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import { Instance } from "../../src/project/instance"
|
|
3
|
+
import { Project } from "../../src/project/project"
|
|
4
|
+
import { Session } from "../../src/session"
|
|
5
|
+
import { Log } from "../../src/util/log"
|
|
6
|
+
import { tmpdir } from "../fixture/fixture"
|
|
7
|
+
|
|
8
|
+
Log.init({ print: false })
|
|
9
|
+
|
|
10
|
+
describe("Session.listGlobal", () => {
|
|
11
|
+
test("lists sessions across projects with project metadata", async () => {
|
|
12
|
+
await using first = await tmpdir({ git: true })
|
|
13
|
+
await using second = await tmpdir({ git: true })
|
|
14
|
+
|
|
15
|
+
const firstSession = await Instance.provide({
|
|
16
|
+
directory: first.path,
|
|
17
|
+
fn: async () => Session.create({ title: "first-session" }),
|
|
18
|
+
})
|
|
19
|
+
const secondSession = await Instance.provide({
|
|
20
|
+
directory: second.path,
|
|
21
|
+
fn: async () => Session.create({ title: "second-session" }),
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const sessions = [...Session.listGlobal({ limit: 200 })]
|
|
25
|
+
const ids = sessions.map((session) => session.id)
|
|
26
|
+
|
|
27
|
+
expect(ids).toContain(firstSession.id)
|
|
28
|
+
expect(ids).toContain(secondSession.id)
|
|
29
|
+
|
|
30
|
+
const firstProject = Project.get(firstSession.projectID)
|
|
31
|
+
const secondProject = Project.get(secondSession.projectID)
|
|
32
|
+
|
|
33
|
+
const firstItem = sessions.find((session) => session.id === firstSession.id)
|
|
34
|
+
const secondItem = sessions.find((session) => session.id === secondSession.id)
|
|
35
|
+
|
|
36
|
+
expect(firstItem?.project?.id).toBe(firstProject?.id)
|
|
37
|
+
expect(firstItem?.project?.worktree).toBe(firstProject?.worktree)
|
|
38
|
+
expect(secondItem?.project?.id).toBe(secondProject?.id)
|
|
39
|
+
expect(secondItem?.project?.worktree).toBe(secondProject?.worktree)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test("excludes archived sessions by default", async () => {
|
|
43
|
+
await using tmp = await tmpdir({ git: true })
|
|
44
|
+
|
|
45
|
+
const archived = await Instance.provide({
|
|
46
|
+
directory: tmp.path,
|
|
47
|
+
fn: async () => Session.create({ title: "archived-session" }),
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
await Instance.provide({
|
|
51
|
+
directory: tmp.path,
|
|
52
|
+
fn: async () => Session.setArchived({ sessionID: archived.id, time: Date.now() }),
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const sessions = [...Session.listGlobal({ limit: 200 })]
|
|
56
|
+
const ids = sessions.map((session) => session.id)
|
|
57
|
+
|
|
58
|
+
expect(ids).not.toContain(archived.id)
|
|
59
|
+
|
|
60
|
+
const allSessions = [...Session.listGlobal({ limit: 200, archived: true })]
|
|
61
|
+
const allIds = allSessions.map((session) => session.id)
|
|
62
|
+
|
|
63
|
+
expect(allIds).toContain(archived.id)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test("supports cursor pagination", async () => {
|
|
67
|
+
await using tmp = await tmpdir({ git: true })
|
|
68
|
+
|
|
69
|
+
const first = await Instance.provide({
|
|
70
|
+
directory: tmp.path,
|
|
71
|
+
fn: async () => Session.create({ title: "page-one" }),
|
|
72
|
+
})
|
|
73
|
+
await new Promise((resolve) => setTimeout(resolve, 5))
|
|
74
|
+
const second = await Instance.provide({
|
|
75
|
+
directory: tmp.path,
|
|
76
|
+
fn: async () => Session.create({ title: "page-two" }),
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const page = [...Session.listGlobal({ directory: tmp.path, limit: 1 })]
|
|
80
|
+
expect(page.length).toBe(1)
|
|
81
|
+
expect(page[0].id).toBe(second.id)
|
|
82
|
+
|
|
83
|
+
const next = [...Session.listGlobal({ directory: tmp.path, limit: 10, cursor: page[0].time.updated })]
|
|
84
|
+
const ids = next.map((session) => session.id)
|
|
85
|
+
|
|
86
|
+
expect(ids).toContain(first.id)
|
|
87
|
+
expect(ids).not.toContain(second.id)
|
|
88
|
+
})
|
|
89
|
+
})
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import path from "path"
|
|
3
|
+
import { Instance } from "../../src/project/instance"
|
|
4
|
+
import { Session } from "../../src/session"
|
|
5
|
+
import { Log } from "../../src/util/log"
|
|
6
|
+
|
|
7
|
+
const projectRoot = path.join(__dirname, "../..")
|
|
8
|
+
Log.init({ print: false })
|
|
9
|
+
|
|
10
|
+
describe("Session.list", () => {
|
|
11
|
+
test("filters by directory", async () => {
|
|
12
|
+
await Instance.provide({
|
|
13
|
+
directory: projectRoot,
|
|
14
|
+
fn: async () => {
|
|
15
|
+
const first = await Session.create({})
|
|
16
|
+
|
|
17
|
+
const otherDir = path.join(projectRoot, "..", "__session_list_other")
|
|
18
|
+
const second = await Instance.provide({
|
|
19
|
+
directory: otherDir,
|
|
20
|
+
fn: async () => Session.create({}),
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const sessions = [...Session.list({ directory: projectRoot })]
|
|
24
|
+
const ids = sessions.map((s) => s.id)
|
|
25
|
+
|
|
26
|
+
expect(ids).toContain(first.id)
|
|
27
|
+
expect(ids).not.toContain(second.id)
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test("filters root sessions", async () => {
|
|
33
|
+
await Instance.provide({
|
|
34
|
+
directory: projectRoot,
|
|
35
|
+
fn: async () => {
|
|
36
|
+
const root = await Session.create({ title: "root-session" })
|
|
37
|
+
const child = await Session.create({ title: "child-session", parentID: root.id })
|
|
38
|
+
|
|
39
|
+
const sessions = [...Session.list({ roots: true })]
|
|
40
|
+
const ids = sessions.map((s) => s.id)
|
|
41
|
+
|
|
42
|
+
expect(ids).toContain(root.id)
|
|
43
|
+
expect(ids).not.toContain(child.id)
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test("filters by start time", async () => {
|
|
49
|
+
await Instance.provide({
|
|
50
|
+
directory: projectRoot,
|
|
51
|
+
fn: async () => {
|
|
52
|
+
const session = await Session.create({ title: "new-session" })
|
|
53
|
+
const futureStart = Date.now() + 86400000
|
|
54
|
+
|
|
55
|
+
const sessions = [...Session.list({ start: futureStart })]
|
|
56
|
+
expect(sessions.length).toBe(0)
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test("filters by search term", async () => {
|
|
62
|
+
await Instance.provide({
|
|
63
|
+
directory: projectRoot,
|
|
64
|
+
fn: async () => {
|
|
65
|
+
await Session.create({ title: "unique-search-term-abc" })
|
|
66
|
+
await Session.create({ title: "other-session-xyz" })
|
|
67
|
+
|
|
68
|
+
const sessions = [...Session.list({ search: "unique-search" })]
|
|
69
|
+
const titles = sessions.map((s) => s.title)
|
|
70
|
+
|
|
71
|
+
expect(titles).toContain("unique-search-term-abc")
|
|
72
|
+
expect(titles).not.toContain("other-session-xyz")
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test("respects limit parameter", async () => {
|
|
78
|
+
await Instance.provide({
|
|
79
|
+
directory: projectRoot,
|
|
80
|
+
fn: async () => {
|
|
81
|
+
await Session.create({ title: "session-1" })
|
|
82
|
+
await Session.create({ title: "session-2" })
|
|
83
|
+
await Session.create({ title: "session-3" })
|
|
84
|
+
|
|
85
|
+
const sessions = [...Session.list({ limit: 2 })]
|
|
86
|
+
expect(sessions.length).toBe(2)
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
})
|