saeeol 1.2.9 → 1.3.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/.turbo/turbo-typecheck.log +1 -0
- package/AGENTS.md +72 -0
- package/BUN_SHELL_MIGRATION_PLAN.md +136 -0
- package/Dockerfile +18 -0
- package/assets/saeeol.ico +0 -0
- package/bin/saeeol.cjs +0 -0
- package/database.db +0 -0
- package/drizzle.config.ts +10 -0
- package/git +0 -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/migration/20260227213759_add_session_workspace_id/migration.sql +2 -0
- package/migration/20260227213759_add_session_workspace_id/snapshot.json +983 -0
- package/migration/20260228203230_blue_harpoon/migration.sql +17 -0
- package/migration/20260228203230_blue_harpoon/snapshot.json +1102 -0
- package/migration/20260303231226_add_workspace_fields/migration.sql +5 -0
- package/migration/20260303231226_add_workspace_fields/snapshot.json +1013 -0
- package/migration/20260309230000_move_org_to_state/migration.sql +3 -0
- package/migration/20260309230000_move_org_to_state/snapshot.json +1156 -0
- package/migration/20260312043431_session_message_cursor/migration.sql +4 -0
- package/migration/20260312043431_session_message_cursor/snapshot.json +1168 -0
- package/migration/20260323234822_events/migration.sql +13 -0
- package/migration/20260323234822_events/snapshot.json +1271 -0
- package/migration/20260410174513_workspace-name/migration.sql +16 -0
- package/migration/20260410174513_workspace-name/snapshot.json +1271 -0
- package/migration/20260413175956_chief_energizer/migration.sql +13 -0
- package/migration/20260413175956_chief_energizer/snapshot.json +1399 -0
- package/migration/20260423070820_add_icon_url_override/migration.sql +2 -0
- package/migration/20260423070820_add_icon_url_override/snapshot.json +1409 -0
- package/migration/20260428004200_add_session_path/migration.sql +1 -0
- package/migration/20260428004200_add_session_path/snapshot.json +1419 -0
- package/npm/bin/saeeol +42 -0
- package/npm/package.json +39 -0
- package/npm/postinstall.js +162 -0
- package/package.json +201 -207
- package/parsers-config.ts +289 -0
- package/script/build.ts +393 -0
- package/script/check-migrations.ts +16 -0
- package/script/fix-node-pty.ts +34 -0
- package/script/generate.ts +23 -0
- package/script/postinstall.mjs +189 -0
- package/script/publish.ts +200 -0
- package/script/run-workspace-server +106 -0
- package/script/schema.ts +63 -0
- package/script/test-runner.ts +420 -0
- package/script/time.ts +6 -0
- package/script/trace-imports.ts +153 -0
- package/script/upgrade-opentui.ts +64 -0
- package/scripts/diff-sdk-types.sh +52 -0
- package/specs/effect/facades.md +221 -0
- package/specs/effect/http-api.md +401 -0
- package/specs/effect/instance-context.md +309 -0
- package/specs/effect/loose-ends.md +34 -0
- package/specs/effect/migration.md +299 -0
- package/specs/effect/routes.md +64 -0
- package/specs/effect/schema.md +399 -0
- package/specs/effect/server-package.md +668 -0
- package/specs/effect/tools.md +90 -0
- package/specs/tui-plugins.md +433 -0
- package/specs/v2/api.ts +67 -0
- package/specs/v2/keymappings.md +10 -0
- package/specs/v2/message-shape.md +136 -0
- package/src/acp/agent-message.ts +1 -1
- package/src/acp/agent-utils.ts +1 -1
- package/src/boxes/ansi.ts +17 -0
- package/src/boxes/atomic-write.ts +35 -0
- package/src/boxes/b64.ts +58 -0
- package/src/boxes/bash-security.ts +129 -0
- package/src/boxes/bom.ts +18 -0
- package/src/boxes/cancel.ts +16 -0
- package/src/boxes/chop.ts +12 -0
- package/src/boxes/clamp.ts +3 -0
- package/src/boxes/compact.ts +9 -0
- package/src/boxes/cost-tracker.ts +116 -0
- package/src/boxes/dataurl.ts +29 -0
- package/src/boxes/delay.ts +27 -0
- package/src/boxes/diff-apply.ts +53 -0
- package/src/boxes/disposable.ts +13 -0
- package/src/boxes/err.ts +34 -0
- package/src/boxes/human.ts +47 -0
- package/src/boxes/iife.ts +9 -0
- package/src/boxes/latch.ts +8 -0
- package/src/boxes/memory.ts +198 -0
- package/src/boxes/net.ts +16 -0
- package/src/boxes/plural.ts +4 -0
- package/src/boxes/puny.ts +21 -0
- package/src/boxes/retry.ts +49 -0
- package/src/boxes/rwlock.ts +41 -0
- package/src/boxes/schedule.ts +71 -0
- package/src/boxes/scope.ts +21 -0
- package/src/boxes/tokens.ts +9 -0
- package/src/boxes/ttl-cache.ts +63 -0
- package/src/boxes/typed-event.ts +51 -0
- package/src/boxes/uid.ts +50 -0
- package/src/boxes/wave6.test.ts +296 -0
- package/src/boxes/wildcard.ts +58 -0
- package/src/bus/global.ts +1 -1
- package/src/cli/cmd/github-run-api.ts +2 -2
- package/src/cli/cmd/run-events.ts +2 -2
- package/src/cli/cmd/tui/component/logo.tsx +1 -1
- package/src/cli/cmd/tui/component/prompt/use-prompt-memos.ts +2 -2
- package/src/cli/cmd/tui/context/app/editor-zed.ts +1 -1
- package/src/cli/cmd/tui/context/app/editor.ts +1 -1
- package/src/cli/cmd/tui/context/app/helper.tsx +1 -0
- package/src/cli/cmd/tui/context/app/theme.tsx +1 -0
- package/src/cli/cmd/tui/util/revert-diff.ts +1 -1
- package/src/overlay/cli/cmd/roll-call-call.ts +1 -1
- package/src/overlay/cost-tracker/format.ts +1 -1
- package/src/overlay/cost-tracker/index.ts +4 -4
- package/src/overlay/cost-tracker/state.ts +2 -2
- package/src/overlay/cost-tracker/types.ts +2 -2
- package/src/overlay/memory/age.ts +1 -1
- package/src/overlay/memory/index.ts +4 -4
- package/src/overlay/memory/paths.ts +2 -2
- package/src/overlay/memory/scan.ts +1 -1
- package/src/overlay/memory/types.ts +2 -2
- package/src/overlay/tool/bash-security.ts +3 -3
- package/src/overlay/util/url.ts +1 -1
- package/src/plugin/codex-auth.ts +1 -1
- package/src/provider/model-cache.ts +2 -2
- package/src/provider/provider-resolve.ts +3 -3
- package/src/provider/transform-message.ts +1 -1
- package/src/server/routes/game.ts +284 -0
- package/src/server/server.ts +2 -0
- package/src/session/core/compaction/compaction-helpers.ts +1 -1
- package/src/session/core/compaction/compaction.ts +1 -1
- package/src/session/core/session-events.ts +50 -8
- package/src/session/core/session.ts +2 -0
- package/src/sessions/ingest-queue.ts +2 -2
- package/src/sessions/remote-ws.ts +1 -1
- package/src/tool/workflow/question.ts +1 -1
- package/src/util/abort.ts +1 -1
- package/src/util/bom.ts +2 -2
- package/src/util/color.ts +1 -1
- package/src/util/data-url.ts +1 -1
- package/src/util/defer.ts +1 -1
- package/src/util/error.ts +2 -2
- package/src/util/filesystem.ts +2 -2
- package/src/util/format.ts +1 -1
- package/src/util/iife.ts +1 -1
- package/src/util/local-context.ts +1 -1
- package/src/util/locale.ts +2 -2
- package/src/util/lock.ts +1 -1
- package/src/util/network.ts +1 -1
- package/src/util/signal.ts +1 -1
- package/src/util/token.ts +1 -1
- package/src/util/wildcard.ts +1 -1
- package/sst-env.d.ts +10 -0
- package/test/AGENTS.md +133 -0
- package/test/account/repo.test.ts +352 -0
- package/test/account/service.test.ts +456 -0
- package/test/acp/agent-interface.test.ts +51 -0
- package/test/acp/event-subscription.test.ts +725 -0
- package/test/agent/agent.test.ts +890 -0
- package/test/auth/auth.test.ts +86 -0
- package/test/bun/registry.test.ts +75 -0
- package/test/bus/bus-effect.test.ts +161 -0
- package/test/bus/bus-integration.test.ts +87 -0
- package/test/bus/bus.test.ts +219 -0
- package/test/cli/account.test.ts +26 -0
- package/test/cli/auto-mode.test.ts +75 -0
- package/test/cli/bin-saeeol.test.ts +8 -0
- package/test/cli/cmd/tui/prompt-part.test.ts +47 -0
- package/test/cli/cmd/tui/prompt-traits.test.ts +38 -0
- package/test/cli/cmd/tui/sync.test.tsx +159 -0
- package/test/cli/error.test.ts +18 -0
- package/test/cli/github-action.test.ts +198 -0
- package/test/cli/github-remote.test.ts +85 -0
- package/test/cli/import.test.ts +97 -0
- package/test/cli/install-artifact.test.ts +72 -0
- package/test/cli/plugin-auth-picker.test.ts +120 -0
- package/test/cli/pr.test.ts +59 -0
- package/test/cli/tui/editor-context-zed.test.ts +356 -0
- package/test/cli/tui/editor-context.test.tsx +228 -0
- package/test/cli/tui/keybind-plugin.test.ts +90 -0
- package/test/cli/tui/markdown.test.ts +161 -0
- package/test/cli/tui/plugin-add.test.ts +111 -0
- package/test/cli/tui/plugin-install.test.ts +87 -0
- package/test/cli/tui/plugin-lifecycle.test.ts +224 -0
- package/test/cli/tui/plugin-loader-entrypoint.test.ts +484 -0
- package/test/cli/tui/plugin-loader-pure.test.ts +71 -0
- package/test/cli/tui/plugin-loader.test.ts +816 -0
- package/test/cli/tui/plugin-toggle.test.ts +157 -0
- package/test/cli/tui/revert-diff.test.ts +35 -0
- package/test/cli/tui/slot-replace.test.tsx +47 -0
- package/test/cli/tui/theme-store.test.ts +54 -0
- package/test/cli/tui/thread.test.ts +28 -0
- package/test/cli/tui/transcript.test.ts +426 -0
- package/test/cli/tui/usage.test.ts +60 -0
- package/test/cli/tui/use-event.test.tsx +175 -0
- package/test/config/agent-color.test.ts +67 -0
- package/test/config/config.test.ts +2544 -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/lsp.test.ts +87 -0
- package/test/config/markdown.test.ts +228 -0
- package/test/config/plugin.test.ts +0 -0
- package/test/config/tui.test.ts +624 -0
- package/test/control-plane/adapters.test.ts +71 -0
- package/test/control-plane/workspace.test.ts +1526 -0
- package/test/effect/app-runtime-logger.test.ts +98 -0
- package/test/effect/config-service.test.ts +65 -0
- package/test/effect/instance-state.test.ts +394 -0
- package/test/effect/run-service.test.ts +89 -0
- package/test/effect/runner.test.ts +523 -0
- package/test/fake/provider.ts +82 -0
- package/test/file/fsmonitor.test.ts +68 -0
- package/test/file/ignore.test.ts +10 -0
- package/test/file/index.test.ts +954 -0
- package/test/file/path-traversal.test.ts +205 -0
- package/test/file/ripgrep.test.ts +226 -0
- package/test/file/watcher.test.ts +249 -0
- package/test/filesystem/filesystem.test.ts +319 -0
- package/test/fixture/db.ts +11 -0
- package/test/fixture/fixture.test.ts +26 -0
- package/test/fixture/fixture.ts +175 -0
- package/test/fixture/flock-worker.ts +72 -0
- package/test/fixture/log-init-worker.ts +62 -0
- package/test/fixture/lsp/fake-lsp-server.js +249 -0
- package/test/fixture/plug-worker.ts +93 -0
- package/test/fixture/plugin-meta-worker.ts +19 -0
- package/test/fixture/skills/agents-sdk/SKILL.md +152 -0
- package/test/fixture/skills/cloudflare/SKILL.md +211 -0
- package/test/fixture/skills/index.json +6 -0
- package/test/fixture/tui-plugin.ts +323 -0
- package/test/fixture/tui-runtime.ts +31 -0
- package/test/format/format.test.ts +272 -0
- package/test/git/git.test.ts +128 -0
- package/test/ide/ide.test.ts +82 -0
- package/test/installation/installation.test.ts +168 -0
- package/test/keybind.test.ts +421 -0
- package/test/lib/effect.ts +53 -0
- package/test/lib/filesystem.ts +10 -0
- package/test/lib/llm-server.ts +778 -0
- package/test/lib/websocket.ts +46 -0
- package/test/lsp/client.test.ts +482 -0
- package/test/lsp/index.test.ts +160 -0
- package/test/lsp/launch.test.ts +22 -0
- package/test/lsp/lifecycle.test.ts +184 -0
- package/test/ltm/ltm.test.ts +230 -0
- package/test/mcp/headers.test.ts +178 -0
- package/test/mcp/lifecycle.test.ts +787 -0
- package/test/mcp/oauth-auto-connect.test.ts +311 -0
- package/test/mcp/oauth-browser.test.ts +276 -0
- package/test/mcp/oauth-callback.test.ts +34 -0
- package/test/memory/abort-leak-webfetch.ts +49 -0
- package/test/memory/abort-leak.test.ts +128 -0
- package/test/patch/patch.test.ts +348 -0
- package/test/permission/arity.test.ts +33 -0
- package/test/permission/next.test.ts +1227 -0
- package/test/permission/next.toConfig.test.ts +110 -0
- package/test/permission-task.test.ts +326 -0
- package/test/plugin/auth-override.test.ts +79 -0
- package/test/plugin/cloudflare.test.ts +68 -0
- package/test/plugin/codex.test.ts +123 -0
- package/test/plugin/github-copilot-models.test.ts +261 -0
- package/test/plugin/install-concurrency.test.ts +140 -0
- package/test/plugin/install.test.ts +570 -0
- package/test/plugin/loader-shared.test.ts +1169 -0
- package/test/plugin/meta.test.ts +137 -0
- package/test/plugin/plugin-contract.test.ts +291 -0
- package/test/plugin/shared.test.ts +88 -0
- package/test/plugin/trigger.test.ts +102 -0
- package/test/plugin/workspace-adapter.test.ts +109 -0
- package/test/preload.ts +77 -0
- package/test/project/instance.test.ts +276 -0
- package/test/project/migrate-global.test.ts +152 -0
- package/test/project/project.test.ts +600 -0
- package/test/project/vcs.test.ts +286 -0
- package/test/project/worktree-remove.test.ts +126 -0
- package/test/project/worktree.test.ts +223 -0
- package/test/provider/amazon-bedrock.test.ts +462 -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 +413 -0
- package/test/provider/local.test.ts +208 -0
- package/test/provider/models.test.ts +261 -0
- package/test/provider/provider-category.test.ts +190 -0
- package/test/provider/provider.test.ts +2758 -0
- package/test/provider/transform.test.ts +3681 -0
- package/test/pty/pty-output-isolation.test.ts +147 -0
- package/test/pty/pty-session.test.ts +102 -0
- package/test/pty/pty-shell.test.ts +104 -0
- package/test/question/question.test.ts +490 -0
- package/test/saeeol/agent-global-config-dirs.test.ts +24 -0
- package/test/saeeol/agent-manager-tool.test.ts +71 -0
- package/test/saeeol/agent-permission-overrides.test.ts +75 -0
- package/test/saeeol/agent-skill-permissions.test.ts +37 -0
- package/test/saeeol/ask-agent-permissions.test.ts +303 -0
- package/test/saeeol/bash-hierarchy.test.ts +64 -0
- package/test/saeeol/bash-permission-metadata.test.ts +66 -0
- package/test/saeeol/bash-security-extended.test.ts +243 -0
- package/test/saeeol/bedrock-claude-empty-content.test.ts +138 -0
- package/test/saeeol/boxes-integration.test.ts +415 -0
- package/test/saeeol/builtin-skills.test.ts +75 -0
- package/test/saeeol/cleanup.ts +28 -0
- package/test/saeeol/cli/dev-setup.test.ts +74 -0
- package/test/saeeol/cli/roll-call.test.ts +161 -0
- package/test/saeeol/cli-run-auto-helper.test.ts +58 -0
- package/test/saeeol/codex-auth-refresh.test.ts +124 -0
- package/test/saeeol/commit-message/generate.test.ts +188 -0
- package/test/saeeol/commit-message/git-context.test.ts +303 -0
- package/test/saeeol/commit-message-windows.test.ts +38 -0
- package/test/saeeol/compaction-payload-recovery.test.ts +406 -0
- package/test/saeeol/compaction-preservation-audit.test.ts +122 -0
- package/test/saeeol/compaction-skip-guard.test.ts +224 -0
- package/test/saeeol/compaction-smart-select.test.ts +100 -0
- package/test/saeeol/config/config.test.ts +166 -0
- package/test/saeeol/config/indexing-default-plugin.test.ts +82 -0
- package/test/saeeol/config/opentelemetry-default.test.ts +29 -0
- package/test/saeeol/config-gitignore.test.ts +70 -0
- package/test/saeeol/config-injector.test.ts +305 -0
- package/test/saeeol/config-resilience.test.ts +234 -0
- package/test/saeeol/config-validation.test.ts +183 -0
- package/test/saeeol/cost-propagation.test.ts +94 -0
- package/test/saeeol/cost-tracker-extended.test.ts +141 -0
- package/test/saeeol/cost-tracker.test.ts +64 -0
- package/test/saeeol/custom-provider-delete.test.ts +149 -0
- package/test/saeeol/diff-full.test.ts +226 -0
- package/test/saeeol/edit-permission-filediff.test.ts +223 -0
- package/test/saeeol/encoding.test.ts +364 -0
- package/test/saeeol/enhance-prompt.test.ts +61 -0
- package/test/saeeol/ensure-plan-dir.test.ts +32 -0
- package/test/saeeol/errors.test.ts +144 -0
- package/test/saeeol/external-directory-boundary.test.ts +96 -0
- package/test/saeeol/gateway-headers.test.ts +88 -0
- package/test/saeeol/help.test.ts +191 -0
- package/test/saeeol/ignore-migrator.test.ts +308 -0
- package/test/saeeol/indexing-auth.test.ts +45 -0
- package/test/saeeol/indexing-feature.test.ts +44 -0
- package/test/saeeol/indexing-label.test.ts +70 -0
- package/test/saeeol/indexing-startup.test.ts +381 -0
- package/test/saeeol/indexing-worktree.test.ts +73 -0
- package/test/saeeol/instruction.test.ts +136 -0
- package/test/saeeol/lancedb-runtime.test.ts +116 -0
- package/test/saeeol/loader-auth.test.ts +168 -0
- package/test/saeeol/local-model.test.ts +621 -0
- package/test/saeeol/logo.test.ts +31 -0
- package/test/saeeol/lsp-typescript-lightweight.test.ts +89 -0
- package/test/saeeol/mcp-branding.test.ts +33 -0
- package/test/saeeol/mcp-docker-rm.test.ts +32 -0
- package/test/saeeol/mcp-migrator.test.ts +736 -0
- package/test/saeeol/mcp-oauth-callback.test.ts +33 -0
- package/test/saeeol/memory-io.test.ts +198 -0
- package/test/saeeol/memory-paths.test.ts +87 -0
- package/test/saeeol/memory-security.test.ts +166 -0
- package/test/saeeol/model-cache-org.test.ts +164 -0
- package/test/saeeol/model-info-panel-utils.test.ts +52 -0
- package/test/saeeol/model-info-panel.types.test.ts +7 -0
- package/test/saeeol/models-401-fallback.test.ts +52 -0
- package/test/saeeol/modes-migrator.test.ts +320 -0
- package/test/saeeol/nvidia-headers.test.ts +74 -0
- package/test/saeeol/patch-jsonc.test.ts +73 -0
- package/test/saeeol/patch.test.ts +172 -0
- package/test/saeeol/paths.test.ts +265 -0
- package/test/saeeol/permission/config-paths.test.ts +174 -0
- package/test/saeeol/permission/env-read.test.ts +149 -0
- package/test/saeeol/permission/external-directory-allow.test.ts +327 -0
- package/test/saeeol/permission/next.always-rules.test.ts +882 -0
- package/test/saeeol/permission/next.reply-http.test.ts +205 -0
- package/test/saeeol/permission/next.reply-routing.test.ts +184 -0
- package/test/saeeol/plan-exit-detection.test.ts +494 -0
- package/test/saeeol/plan-followup.test.ts +1376 -0
- package/test/saeeol/project-config-update.test.ts +120 -0
- package/test/saeeol/project-id.test.ts +455 -0
- package/test/saeeol/provider-cost.test.ts +171 -0
- package/test/saeeol/provider-list-failed-state.test.ts +100 -0
- package/test/saeeol/question-dismiss-all.test.ts +174 -0
- package/test/saeeol/read-directory.test.ts +116 -0
- package/test/saeeol/rules-migrator.test.ts +257 -0
- package/test/saeeol/run-auto.test.ts +176 -0
- package/test/saeeol/run-network.test.ts +224 -0
- package/test/saeeol/semantic-search.test.ts +186 -0
- package/test/saeeol/server/permission-allow-everything.test.ts +125 -0
- package/test/saeeol/session/instruction-substitution.test.ts +72 -0
- package/test/saeeol/session/platform-attribution.test.ts +118 -0
- package/test/saeeol/session/session.test.ts +105 -0
- package/test/saeeol/session-compaction-cap.test.ts +399 -0
- package/test/saeeol/session-compaction-chunks.test.ts +501 -0
- package/test/saeeol/session-compaction-safety.test.ts +481 -0
- package/test/saeeol/session-fork-remap.test.ts +251 -0
- package/test/saeeol/session-import-service.test.ts +114 -0
- package/test/saeeol/session-list.test.ts +47 -0
- package/test/saeeol/session-message-metadata.test.ts +128 -0
- package/test/saeeol/session-overflow.test.ts +78 -0
- package/test/saeeol/session-processor-empty-tool-calls.test.ts +571 -0
- package/test/saeeol/session-processor-network-offline.test.ts +204 -0
- package/test/saeeol/session-processor-retry-limit.test.ts +238 -0
- package/test/saeeol/session-processor-review-telemetry.test.ts +82 -0
- package/test/saeeol/session-prompt-compaction-safety.test.ts +517 -0
- package/test/saeeol/session-prompt-queue.test.ts +815 -0
- package/test/saeeol/sessions/inflight-cache.test.ts +157 -0
- package/test/saeeol/sessions/ingest-queue.test.ts +402 -0
- package/test/saeeol/sessions/remote-protocol.test.ts +258 -0
- package/test/saeeol/sessions/remote-sender.test.ts +1036 -0
- package/test/saeeol/sessions/remote-ws.test.ts +367 -0
- package/test/saeeol/sessions/sessions-enable-remote.test.disable +181 -0
- package/test/saeeol/slot-prop-reactivity.test.ts +142 -0
- package/test/saeeol/snapshot-cache.test.ts +84 -0
- package/test/saeeol/snapshot-freeze-repro.test.ts +100 -0
- package/test/saeeol/snapshot-track-timeout.test.ts +519 -0
- package/test/saeeol/stats-subagent-cost.test.ts +123 -0
- package/test/saeeol/suggestion/auto-dismiss.test.ts +65 -0
- package/test/saeeol/suggestion/suggestion.test.ts +145 -0
- package/test/saeeol/suggestion/tool.test.ts +298 -0
- package/test/saeeol/summary-file-diff.test.ts +28 -0
- package/test/saeeol/system-prompt.test.ts +142 -0
- package/test/saeeol/task-nesting.test.ts +193 -0
- package/test/saeeol/telemetry/feedback.test.ts +8 -0
- package/test/saeeol/todo-view.test.ts +57 -0
- package/test/saeeol/tool-encoding.test.ts +455 -0
- package/test/saeeol/tool-registry-indexing-import-failure.test.ts +49 -0
- package/test/saeeol/tool-registry-indexing.test.ts +236 -0
- package/test/saeeol/tool-registry-semantic-import-failure.test.ts +55 -0
- package/test/saeeol/tool-task-model.test.ts +352 -0
- package/test/saeeol/transform-opus-4.7.test.ts +89 -0
- package/test/saeeol/tui-diff.test.ts +91 -0
- package/test/saeeol/tui-sync.test.ts +80 -0
- package/test/saeeol/util/url.test.ts +141 -0
- package/test/saeeol/workflows-migrator.test.ts +261 -0
- package/test/saeeol/worktree-diff-summary.test.ts +64 -0
- package/test/saeeol/worktree-diff.test.ts +223 -0
- package/test/saeeol/worktree-remove-lock.test.ts +82 -0
- package/test/server/AGENTS.md +15 -0
- package/test/server/contract.test.ts +357 -0
- package/test/server/experimental-session-list.test.ts +157 -0
- package/test/server/global-session-list.test.ts +155 -0
- package/test/server/httpapi-authorization.test.ts +103 -0
- package/test/server/httpapi-bridge.test.ts +440 -0
- package/test/server/httpapi-config.test.ts +67 -0
- package/test/server/httpapi-cors.test.ts +89 -0
- package/test/server/httpapi-event.test.ts +57 -0
- package/test/server/httpapi-experimental.test.ts +219 -0
- package/test/server/httpapi-file.test.ts +79 -0
- package/test/server/httpapi-instance-context.test.ts +237 -0
- package/test/server/httpapi-instance.legacy.test.ts +140 -0
- package/test/server/httpapi-instance.test.ts +83 -0
- package/test/server/httpapi-json-parity.test.ts +263 -0
- package/test/server/httpapi-mcp-oauth.test.ts +76 -0
- package/test/server/httpapi-mcp.test.ts +189 -0
- package/test/server/httpapi-provider.test.ts +153 -0
- package/test/server/httpapi-pty-websocket.test.ts +16 -0
- package/test/server/httpapi-pty.test.ts +175 -0
- package/test/server/httpapi-raw-route-auth.test.ts +89 -0
- package/test/server/httpapi-sdk.test.ts +681 -0
- package/test/server/httpapi-session.test.ts +464 -0
- package/test/server/httpapi-sync.test.ts +130 -0
- package/test/server/httpapi-tui.test.ts +121 -0
- package/test/server/httpapi-workspace-routing.test.ts +471 -0
- package/test/server/httpapi-workspace.test.ts +427 -0
- package/test/server/lib/conformance.ts +88 -0
- package/test/server/lib/stateful.ts +112 -0
- package/test/server/project-init-git.test.ts +113 -0
- package/test/server/proxy-util.test.ts +113 -0
- package/test/server/session-actions.test.ts +49 -0
- package/test/server/session-list.test.ts +238 -0
- package/test/server/session-messages.test.ts +167 -0
- package/test/server/session-select.test.ts +100 -0
- package/test/server/trace-attributes.test.ts +76 -0
- package/test/server/workspace-proxy.test.ts +165 -0
- package/test/server/workspace-routing.test.ts +85 -0
- package/test/session/compaction.test.ts +2420 -0
- package/test/session/instruction.test.ts +247 -0
- package/test/session/llm.test.ts +1273 -0
- package/test/session/message-v2.test.ts +1291 -0
- package/test/session/messages-pagination.test.ts +1173 -0
- package/test/session/network.test.ts +249 -0
- package/test/session/processor-effect.test.ts +847 -0
- package/test/session/prompt.test.ts +2131 -0
- package/test/session/retry.test.ts +340 -0
- package/test/session/revert-compact.test.ts +639 -0
- package/test/session/schema-decoding.test.ts +311 -0
- package/test/session/session-entry-stepper.test.ts +917 -0
- package/test/session/session-schema.test.ts +76 -0
- package/test/session/snapshot-tool-race.test.ts +257 -0
- package/test/session/structured-output-integration.test.ts +265 -0
- package/test/session/structured-output.test.ts +381 -0
- package/test/session/system.test.ts +73 -0
- package/test/share/share-next.test.ts +333 -0
- package/test/shell/shell.test.ts +99 -0
- package/test/skill/discovery.test.ts +116 -0
- package/test/skill/skill.test.ts +393 -0
- package/test/snapshot/snapshot.test.ts +1531 -0
- package/test/storage/db.test.ts +23 -0
- package/test/storage/json-migration.test.ts +832 -0
- package/test/storage/storage.test.ts +293 -0
- package/test/suggestion/suggestion.test.ts +1 -0
- package/test/sync/index.test.ts +256 -0
- package/test/tool/__snapshots__/parameters.test.ts.snap +500 -0
- package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
- package/test/tool/apply_patch.test.ts +614 -0
- package/test/tool/bash.test.ts +1225 -0
- package/test/tool/diagnostics-filter.test.ts +55 -0
- package/test/tool/edit.test.ts +754 -0
- package/test/tool/external-directory.test.ts +169 -0
- package/test/tool/fixtures/large-image.png +0 -0
- package/test/tool/fixtures/models-api.json +65179 -0
- package/test/tool/glob.test.ts +107 -0
- package/test/tool/grep.test.ts +114 -0
- package/test/tool/lsp.test.ts +187 -0
- package/test/tool/parameters.test.ts +243 -0
- package/test/tool/question.test.ts +129 -0
- package/test/tool/read.test.ts +500 -0
- package/test/tool/recall.test.ts +151 -0
- package/test/tool/registry.test.ts +203 -0
- package/test/tool/skill.test.ts +135 -0
- package/test/tool/suggest.test.ts +1 -0
- package/test/tool/task.test.ts +612 -0
- package/test/tool/tool-define.test.ts +99 -0
- package/test/tool/truncation.test.ts +260 -0
- package/test/tool/webfetch.test.ts +103 -0
- package/test/tool/write.test.ts +291 -0
- package/test/util/data-url.test.ts +14 -0
- package/test/util/effect-zod.test.ts +754 -0
- package/test/util/error.test.ts +38 -0
- package/test/util/filesystem.test.ts +656 -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/log.test.ts +86 -0
- package/test/util/module.test.ts +59 -0
- package/test/util/process.test.ts +128 -0
- package/test/util/timeout.test.ts +21 -0
- package/test/util/which.test.ts +100 -0
- package/test/util/wildcard.test.ts +90 -0
- package/test/workspace/workspace-restore.test.ts +296 -0
- package/src/provider/models-snapshot.d.ts +0 -2
- package/src/provider/models-snapshot.js +0 -3
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Custom test runner that executes each test file in its own isolated process.
|
|
3
|
+
// Prevents cross-contamination between test files by ensuring separate PIDs,
|
|
4
|
+
// temp directories, in-memory databases, and environment state.
|
|
5
|
+
|
|
6
|
+
import os from "os"
|
|
7
|
+
import path from "path"
|
|
8
|
+
import fs from "fs/promises"
|
|
9
|
+
|
|
10
|
+
const root = path.resolve(import.meta.dir, "..")
|
|
11
|
+
const argv = process.argv.slice(2)
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Help
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
18
|
+
console.log(
|
|
19
|
+
[
|
|
20
|
+
"",
|
|
21
|
+
"Usage: bun run script/test-runner.ts [options] [patterns...]",
|
|
22
|
+
"",
|
|
23
|
+
"Runs test files in isolated parallel processes to prevent cross-contamination.",
|
|
24
|
+
"",
|
|
25
|
+
"Options:",
|
|
26
|
+
" --ci Enable JUnit XML output to .artifacts/unit/junit.xml",
|
|
27
|
+
" --concurrency <N> Max parallel processes (default: min(4, CPU count))",
|
|
28
|
+
" --timeout <ms> Per-test timeout passed to bun test (default: 60000)",
|
|
29
|
+
" --file-timeout <ms> Per-file process timeout (default: 300000)",
|
|
30
|
+
" --retries <N> Extra attempts for failing files (default: 1)",
|
|
31
|
+
" --bail Stop on first failure",
|
|
32
|
+
" --verbose Show full output for every file",
|
|
33
|
+
" -h, --help Show this help",
|
|
34
|
+
"",
|
|
35
|
+
"Positional:",
|
|
36
|
+
" [patterns...] Filter test files by substring match",
|
|
37
|
+
"",
|
|
38
|
+
].join("\n"),
|
|
39
|
+
)
|
|
40
|
+
process.exit(0)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// CLI parsing
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
function opt(name: string, fallback: number) {
|
|
48
|
+
const i = argv.indexOf(`--${name}`)
|
|
49
|
+
return i >= 0 && i + 1 < argv.length ? Number(argv[i + 1]) || fallback : fallback
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const ci = argv.includes("--ci")
|
|
53
|
+
const bail = argv.includes("--bail")
|
|
54
|
+
const verbose = argv.includes("--verbose")
|
|
55
|
+
// Cap concurrency at 4 even on bigger runners: the bottleneck is shared
|
|
56
|
+
// resources (ports, global filesystem like ~/.local/share/saeeol), not CPU.
|
|
57
|
+
// Eight parallel processes was triggering port/FS races, not going faster.
|
|
58
|
+
const concurrency = opt("concurrency", Math.min(4, os.cpus().length))
|
|
59
|
+
const timeout = opt("timeout", 60000)
|
|
60
|
+
const deadline = opt("file-timeout", 300000)
|
|
61
|
+
const retries = opt("retries", 1)
|
|
62
|
+
|
|
63
|
+
const valued = new Set(["--concurrency", "--timeout", "--file-timeout", "--retries"])
|
|
64
|
+
const patterns = argv.filter((arg, i) => {
|
|
65
|
+
if (arg.startsWith("-")) return false
|
|
66
|
+
if (i > 0 && valued.has(argv[i - 1])) return false
|
|
67
|
+
return true
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Colors
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
const tty = !!process.stdout.isTTY
|
|
75
|
+
const green = (s: string) => (tty ? `\x1b[32m${s}\x1b[0m` : s)
|
|
76
|
+
const red = (s: string) => (tty ? `\x1b[31m${s}\x1b[0m` : s)
|
|
77
|
+
const yellow = (s: string) => (tty ? `\x1b[33m${s}\x1b[0m` : s)
|
|
78
|
+
const dim = (s: string) => (tty ? `\x1b[2m${s}\x1b[0m` : s)
|
|
79
|
+
const bold = (s: string) => (tty ? `\x1b[1m${s}\x1b[0m` : s)
|
|
80
|
+
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// File discovery
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
const glob = new Bun.Glob("**/*.test.{ts,tsx}")
|
|
86
|
+
const all = (await Array.fromAsync(glob.scan({ cwd: path.join(root, "test") }))).sort()
|
|
87
|
+
|
|
88
|
+
export const skipped = new Set([
|
|
89
|
+
// Upstream browser OAuth integration tests bind the fixed callback port and
|
|
90
|
+
// race with other parallel OAuth tests in CI.
|
|
91
|
+
"mcp/oauth-browser.test.ts",
|
|
92
|
+
])
|
|
93
|
+
|
|
94
|
+
const matched =
|
|
95
|
+
patterns.length > 0 ? all.filter((f) => patterns.some((p) => f.includes(p) || path.join("test", f).includes(p))) : all
|
|
96
|
+
const files = patterns.length > 0 ? matched : matched.filter((f) => !skipped.has(f))
|
|
97
|
+
|
|
98
|
+
if (files.length === 0) {
|
|
99
|
+
console.log("No test files found")
|
|
100
|
+
process.exit(0)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Types
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
type Result = {
|
|
108
|
+
file: string
|
|
109
|
+
passed: boolean
|
|
110
|
+
code: number
|
|
111
|
+
stdout: string
|
|
112
|
+
stderr: string
|
|
113
|
+
duration: number
|
|
114
|
+
timedout: boolean
|
|
115
|
+
attempts: number
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// Setup
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
const xmldir = ci ? path.join(os.tmpdir(), `saeeol-junit-${process.pid}`) : ""
|
|
123
|
+
if (ci) await fs.mkdir(xmldir, { recursive: true })
|
|
124
|
+
|
|
125
|
+
const counter = { done: 0 }
|
|
126
|
+
const pad = String(files.length).length
|
|
127
|
+
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// Run a single test file
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
async function run(file: string): Promise<Result> {
|
|
133
|
+
const target = path.join("test", file)
|
|
134
|
+
const cmd = ["bun", "test", target, "--timeout", String(timeout)]
|
|
135
|
+
|
|
136
|
+
if (ci) {
|
|
137
|
+
const name = file.replace(/[/\\]/g, "_") + ".xml"
|
|
138
|
+
cmd.push("--reporter=junit", `--reporter-outfile=${path.join(xmldir, name)}`)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const start = performance.now()
|
|
142
|
+
const killed = { value: false }
|
|
143
|
+
|
|
144
|
+
const proc = Bun.spawn(cmd, {
|
|
145
|
+
cwd: root,
|
|
146
|
+
stdout: "pipe",
|
|
147
|
+
stderr: "pipe",
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
const timer = setTimeout(() => {
|
|
151
|
+
killed.value = true
|
|
152
|
+
proc.kill()
|
|
153
|
+
}, deadline)
|
|
154
|
+
|
|
155
|
+
const [stdout, stderr, code] = await Promise.all([
|
|
156
|
+
new Response(proc.stdout).text(),
|
|
157
|
+
new Response(proc.stderr).text(),
|
|
158
|
+
proc.exited,
|
|
159
|
+
])
|
|
160
|
+
|
|
161
|
+
clearTimeout(timer)
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
file,
|
|
165
|
+
passed: code === 0,
|
|
166
|
+
code,
|
|
167
|
+
stdout,
|
|
168
|
+
stderr,
|
|
169
|
+
duration: performance.now() - start,
|
|
170
|
+
timedout: killed.value,
|
|
171
|
+
attempts: 1,
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// Report a single result
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
function report(result: Result) {
|
|
180
|
+
counter.done++
|
|
181
|
+
const idx = String(counter.done).padStart(pad)
|
|
182
|
+
const secs = (result.duration / 1000).toFixed(1)
|
|
183
|
+
const tries = result.attempts > 1 ? dim(` [attempt ${result.attempts}/${retries + 1}]`) : ""
|
|
184
|
+
|
|
185
|
+
if (result.timedout) {
|
|
186
|
+
console.log(
|
|
187
|
+
`[${idx}/${files.length}] ${red("TIME")} ${result.file} ${dim(`(${secs}s - exceeded ${deadline / 1000}s)`)}${tries}`,
|
|
188
|
+
)
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!result.passed) {
|
|
193
|
+
console.log(`[${idx}/${files.length}] ${red("FAIL")} ${result.file} ${dim(`(${secs}s)`)}${tries}`)
|
|
194
|
+
if (verbose && result.stderr.trim()) console.log(result.stderr)
|
|
195
|
+
if (verbose && result.stdout.trim()) console.log(result.stdout)
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (result.attempts > 1) {
|
|
200
|
+
console.log(`[${idx}/${files.length}] ${yellow("FLAKY")} ${result.file} ${dim(`(${secs}s)`)}${tries}`)
|
|
201
|
+
if (verbose && result.stdout.trim()) console.log(dim(result.stdout))
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
console.log(`[${idx}/${files.length}] ${green("PASS")} ${result.file} ${dim(`(${secs}s)`)}`)
|
|
206
|
+
if (verbose && result.stdout.trim()) console.log(dim(result.stdout))
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
// Parallel execution
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
|
|
213
|
+
console.log(`\nRunning ${bold(String(files.length))} test files with concurrency ${bold(String(concurrency))}\n`)
|
|
214
|
+
|
|
215
|
+
const start = performance.now()
|
|
216
|
+
const results: Result[] = []
|
|
217
|
+
const queue = [...files]
|
|
218
|
+
const stopped = { value: false }
|
|
219
|
+
|
|
220
|
+
const workers = Array.from({ length: Math.min(concurrency, files.length) }, async () => {
|
|
221
|
+
while (queue.length > 0 && !stopped.value) {
|
|
222
|
+
const file = queue.shift()!
|
|
223
|
+
let result = await run(file)
|
|
224
|
+
// Retry failing files up to `retries` extra times. Bugs still fail on every
|
|
225
|
+
// attempt; contention-based flakes (port races, slow FS, slow spawn) recover.
|
|
226
|
+
// Preserve the last attempt's stdout/stderr/duration so a truly broken file
|
|
227
|
+
// still shows a useful diagnostic.
|
|
228
|
+
while (!result.passed && result.attempts <= retries && !stopped.value) {
|
|
229
|
+
const retry = await run(file)
|
|
230
|
+
retry.attempts = result.attempts + 1
|
|
231
|
+
result = retry
|
|
232
|
+
}
|
|
233
|
+
results.push(result)
|
|
234
|
+
report(result)
|
|
235
|
+
if (bail && !result.passed) stopped.value = true
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
await Promise.all(workers)
|
|
240
|
+
|
|
241
|
+
const elapsed = (performance.now() - start) / 1000
|
|
242
|
+
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
// Failure details
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
|
|
247
|
+
const failures = results.filter((r) => !r.passed).sort((a, b) => a.file.localeCompare(b.file))
|
|
248
|
+
|
|
249
|
+
if (failures.length > 0 && !verbose) {
|
|
250
|
+
console.log(`\n${bold(red("--- FAILURES ---"))}\n`)
|
|
251
|
+
for (const f of failures) {
|
|
252
|
+
const tag = f.timedout ? " (TIMED OUT)" : ""
|
|
253
|
+
console.log(`${bold(red(f.file))}${tag}:`)
|
|
254
|
+
const output = (f.stderr || f.stdout).trim()
|
|
255
|
+
if (output)
|
|
256
|
+
console.log(
|
|
257
|
+
output
|
|
258
|
+
.split("\n")
|
|
259
|
+
.map((l) => " " + l)
|
|
260
|
+
.join("\n"),
|
|
261
|
+
)
|
|
262
|
+
console.log()
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
// Summary
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
|
|
270
|
+
const passed = results.filter((r) => r.passed).length
|
|
271
|
+
const flaky = results.filter((r) => r.passed && r.attempts > 1)
|
|
272
|
+
|
|
273
|
+
console.log(
|
|
274
|
+
`\n${bold(String(results.length))} files | ` +
|
|
275
|
+
`${green(passed + " passed")} | ` +
|
|
276
|
+
`${failures.length > 0 ? red(failures.length + " failed") : failures.length + " failed"} | ` +
|
|
277
|
+
`${flaky.length > 0 ? yellow(flaky.length + " flaky") : flaky.length + " flaky"} | ` +
|
|
278
|
+
`${elapsed.toFixed(1)}s\n`,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
if (flaky.length > 0) {
|
|
282
|
+
const sorted = flaky.slice().sort((a, b) => a.file.localeCompare(b.file))
|
|
283
|
+
|
|
284
|
+
console.log(`${bold(yellow("--- FLAKY (passed on retry) ---"))}\n`)
|
|
285
|
+
for (const r of sorted) {
|
|
286
|
+
console.log(` ${yellow(r.file)} ${dim(`(passed on attempt ${r.attempts}/${retries + 1})`)}`)
|
|
287
|
+
}
|
|
288
|
+
console.log()
|
|
289
|
+
|
|
290
|
+
// Surface flakies to the GitHub Actions UI so reviewers don't have to scan
|
|
291
|
+
// the raw log. Annotations show up on the PR; the step summary is visible at
|
|
292
|
+
// the bottom of the job page and in the workflow summary email.
|
|
293
|
+
if (process.env.GITHUB_ACTIONS === "true") {
|
|
294
|
+
for (const r of sorted) {
|
|
295
|
+
const repo = `packages/saeeol/test/${r.file}`
|
|
296
|
+
console.log(`::warning file=${repo},title=Flaky test file::passed on attempt ${r.attempts} of ${retries + 1}`)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const summary = process.env.GITHUB_STEP_SUMMARY
|
|
300
|
+
if (summary) {
|
|
301
|
+
const md = [
|
|
302
|
+
"### ⚠️ Flaky test files (passed on retry)",
|
|
303
|
+
"",
|
|
304
|
+
`${sorted.length} file${sorted.length === 1 ? "" : "s"} needed more than one attempt to pass.`,
|
|
305
|
+
"",
|
|
306
|
+
"| File | Attempts |",
|
|
307
|
+
"|---|---|",
|
|
308
|
+
...sorted.map((r) => `| \`${r.file}\` | ${r.attempts}/${retries + 1} |`),
|
|
309
|
+
"",
|
|
310
|
+
].join("\n")
|
|
311
|
+
await fs.appendFile(summary, md + "\n")
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// ---------------------------------------------------------------------------
|
|
317
|
+
// JUnit XML merge (CI mode)
|
|
318
|
+
// ---------------------------------------------------------------------------
|
|
319
|
+
|
|
320
|
+
if (ci) {
|
|
321
|
+
await merge()
|
|
322
|
+
await fs.rm(xmldir, { recursive: true, force: true }).catch((err) => {
|
|
323
|
+
console.error("cleanup failed:", err)
|
|
324
|
+
})
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
process.exit(failures.length > 0 ? 1 : 0)
|
|
328
|
+
|
|
329
|
+
// ---------------------------------------------------------------------------
|
|
330
|
+
// Helpers
|
|
331
|
+
// ---------------------------------------------------------------------------
|
|
332
|
+
|
|
333
|
+
async function merge() {
|
|
334
|
+
const dir = path.join(root, ".artifacts", "unit")
|
|
335
|
+
await fs.mkdir(dir, { recursive: true })
|
|
336
|
+
|
|
337
|
+
const suites: string[] = []
|
|
338
|
+
const counts = { tests: 0, failures: 0, errors: 0 }
|
|
339
|
+
|
|
340
|
+
for (const file of files) {
|
|
341
|
+
const name = file.replace(/[/\\]/g, "_") + ".xml"
|
|
342
|
+
const fpath = path.join(xmldir, name)
|
|
343
|
+
const found = await Bun.file(fpath).exists()
|
|
344
|
+
|
|
345
|
+
if (found) {
|
|
346
|
+
const content = await Bun.file(fpath).text()
|
|
347
|
+
const extracted = extract(content)
|
|
348
|
+
if (extracted) {
|
|
349
|
+
suites.push(extracted)
|
|
350
|
+
// Counts come from the outer <testsuites ...> root attributes, not from
|
|
351
|
+
// regex-scanning the inner content, so nested <testsuite> blocks (bun
|
|
352
|
+
// emits one per `describe`) don't get double-counted.
|
|
353
|
+
const root = content.match(/<testsuites\b([^>]*)>/)
|
|
354
|
+
if (root) {
|
|
355
|
+
counts.tests += attr(root[1], "tests")
|
|
356
|
+
counts.failures += attr(root[1], "failures")
|
|
357
|
+
counts.errors += attr(root[1], "errors")
|
|
358
|
+
}
|
|
359
|
+
continue
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// No valid XML produced - generate synthetic entry for failed files
|
|
364
|
+
const result = results.find((r) => r.file === file)
|
|
365
|
+
if (!result || result.passed) continue
|
|
366
|
+
|
|
367
|
+
const secs = (result.duration / 1000).toFixed(3)
|
|
368
|
+
const msg = result.timedout
|
|
369
|
+
? `Test file timed out after ${deadline / 1000}s`
|
|
370
|
+
: `Test process exited with code ${result.code}`
|
|
371
|
+
const detail = esc((result.stderr || result.stdout || msg).slice(0, 10000))
|
|
372
|
+
|
|
373
|
+
suites.push(
|
|
374
|
+
` <testsuite name="${esc(file)}" tests="1" failures="1" errors="0" time="${secs}">\n` +
|
|
375
|
+
` <testcase name="${esc(file)}" classname="${esc(file)}" time="${secs}">\n` +
|
|
376
|
+
` <failure message="${esc(msg)}">${detail}</failure>\n` +
|
|
377
|
+
` </testcase>\n` +
|
|
378
|
+
` </testsuite>`,
|
|
379
|
+
)
|
|
380
|
+
counts.tests++
|
|
381
|
+
counts.failures++
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const body = [
|
|
385
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
386
|
+
`<testsuites tests="${counts.tests}" failures="${counts.failures}" errors="${counts.errors}" time="${elapsed.toFixed(3)}">`,
|
|
387
|
+
...suites,
|
|
388
|
+
"</testsuites>",
|
|
389
|
+
"",
|
|
390
|
+
].join("\n")
|
|
391
|
+
|
|
392
|
+
await Bun.write(path.join(dir, "junit.xml"), body)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Grab everything between the outer <testsuites ...> and </testsuites> of a
|
|
396
|
+
// per-file JUnit XML. Preserves nested <testsuite> blocks verbatim — the
|
|
397
|
+
// previous hand-rolled walker matched the first </testsuite> it found, which
|
|
398
|
+
// closed an inner suite and left the outer one dangling in the merged output.
|
|
399
|
+
function extract(content: string): string {
|
|
400
|
+
const open = content.match(/<testsuites\b[^>]*>/)
|
|
401
|
+
if (!open) return ""
|
|
402
|
+
const start = open.index! + open[0].length
|
|
403
|
+
const end = content.lastIndexOf("</testsuites>")
|
|
404
|
+
if (end === -1 || end <= start) return ""
|
|
405
|
+
return content.slice(start, end).trim()
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function attr(attrs: string, name: string): number {
|
|
409
|
+
const m = attrs.match(new RegExp(`\\b${name}="(\\d+)"`))
|
|
410
|
+
return m ? Number(m[1]) : 0
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function esc(s: string): string {
|
|
414
|
+
return s
|
|
415
|
+
.replace(/&/g, "&")
|
|
416
|
+
.replace(/</g, "<")
|
|
417
|
+
.replace(/>/g, ">")
|
|
418
|
+
.replace(/"/g, """)
|
|
419
|
+
.replace(/'/g, "'")
|
|
420
|
+
}
|
package/script/time.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import * as path from "path"
|
|
3
|
+
import * as ts from "typescript"
|
|
4
|
+
|
|
5
|
+
const BASE_DIR = "/home/thdxr/dev/projects/SAEEOL/saeeol/packages/saeeol"
|
|
6
|
+
|
|
7
|
+
// Get entry file from command line arg or use default
|
|
8
|
+
const ENTRY_FILE = process.argv[2] || "src/cli/cmd/tui/plugin/index.ts"
|
|
9
|
+
|
|
10
|
+
const visited = new Set<string>()
|
|
11
|
+
|
|
12
|
+
function resolveImport(importPath: string, fromFile: string): string | null {
|
|
13
|
+
if (importPath.startsWith("@/")) {
|
|
14
|
+
return path.join(BASE_DIR, "src", importPath.slice(2))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (importPath.startsWith("./") || importPath.startsWith("../")) {
|
|
18
|
+
const dir = path.dirname(fromFile)
|
|
19
|
+
return path.resolve(dir, importPath)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isInternalImport(importPath: string): boolean {
|
|
26
|
+
return importPath.startsWith("@/") || importPath.startsWith("./") || importPath.startsWith("../")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function tryExtensions(filePath: string): Promise<string | null> {
|
|
30
|
+
const extensions = [".ts", ".tsx", ".js", ".jsx"]
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const file = Bun.file(filePath)
|
|
34
|
+
const stat = await file.stat()
|
|
35
|
+
|
|
36
|
+
if (stat?.isDirectory()) {
|
|
37
|
+
for (const ext of extensions) {
|
|
38
|
+
const indexPath = path.join(filePath, "index" + ext)
|
|
39
|
+
const indexFile = Bun.file(indexPath)
|
|
40
|
+
if (await indexFile.exists()) return indexPath
|
|
41
|
+
}
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// It's a file
|
|
46
|
+
return filePath
|
|
47
|
+
} catch {
|
|
48
|
+
// Path doesn't exist, try adding extensions
|
|
49
|
+
for (const ext of extensions) {
|
|
50
|
+
const withExt = filePath + ext
|
|
51
|
+
const extFile = Bun.file(withExt)
|
|
52
|
+
if (await extFile.exists()) return withExt
|
|
53
|
+
}
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function extractImports(sourceFile: ts.SourceFile): string[] {
|
|
59
|
+
const imports: string[] = []
|
|
60
|
+
|
|
61
|
+
function visit(node: ts.Node) {
|
|
62
|
+
// import x from "path" or import { x } from "path"
|
|
63
|
+
if (ts.isImportDeclaration(node)) {
|
|
64
|
+
// Skip type-only imports
|
|
65
|
+
if (node.importClause?.isTypeOnly) return
|
|
66
|
+
|
|
67
|
+
const moduleSpec = node.moduleSpecifier
|
|
68
|
+
if (ts.isStringLiteral(moduleSpec)) {
|
|
69
|
+
imports.push(moduleSpec.text)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// export { x } from "path"
|
|
74
|
+
if (ts.isExportDeclaration(node) && node.moduleSpecifier) {
|
|
75
|
+
if (ts.isStringLiteral(node.moduleSpecifier)) {
|
|
76
|
+
imports.push(node.moduleSpecifier.text)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Dynamic import: import("path")
|
|
81
|
+
if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
82
|
+
const arg = node.arguments[0]
|
|
83
|
+
if (arg && ts.isStringLiteral(arg)) {
|
|
84
|
+
imports.push(arg.text)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
ts.forEachChild(node, visit)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
visit(sourceFile)
|
|
92
|
+
return imports
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function traceFile(filePath: string, depth = 0): Promise<void> {
|
|
96
|
+
const normalizedPath = path.relative(BASE_DIR, filePath)
|
|
97
|
+
|
|
98
|
+
if (visited.has(filePath)) {
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Only trace TypeScript/JavaScript files
|
|
103
|
+
if (!filePath.match(/\.(ts|tsx|js|jsx)$/)) {
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
visited.add(filePath)
|
|
108
|
+
console.log("\t".repeat(depth) + normalizedPath)
|
|
109
|
+
|
|
110
|
+
let content: string
|
|
111
|
+
try {
|
|
112
|
+
content = await Bun.file(filePath).text()
|
|
113
|
+
} catch {
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true)
|
|
118
|
+
|
|
119
|
+
const imports = extractImports(sourceFile)
|
|
120
|
+
const internalImports = imports.filter(isInternalImport)
|
|
121
|
+
const externalImports = imports.filter((imp) => !isInternalImport(imp))
|
|
122
|
+
|
|
123
|
+
// Print external imports
|
|
124
|
+
for (const imp of externalImports) {
|
|
125
|
+
console.log("\t".repeat(depth + 1) + `[ext] ${imp}`)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (const imp of internalImports) {
|
|
129
|
+
const resolved = resolveImport(imp, filePath)
|
|
130
|
+
if (!resolved) continue
|
|
131
|
+
|
|
132
|
+
const actualPath = await tryExtensions(resolved)
|
|
133
|
+
if (!actualPath) continue
|
|
134
|
+
|
|
135
|
+
await traceFile(actualPath, depth + 1)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function main() {
|
|
140
|
+
const entryPath = path.join(BASE_DIR, ENTRY_FILE)
|
|
141
|
+
|
|
142
|
+
// Check if file exists
|
|
143
|
+
const file = Bun.file(entryPath)
|
|
144
|
+
if (!(await file.exists())) {
|
|
145
|
+
console.error(`File not found: ${ENTRY_FILE}`)
|
|
146
|
+
console.error(`Resolved to: ${entryPath}`)
|
|
147
|
+
process.exit(1)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
await traceFile(entryPath)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
main().catch(console.error)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
|
|
5
|
+
const raw = process.argv[2]
|
|
6
|
+
if (!raw) {
|
|
7
|
+
console.error("Usage: bun run script/upgrade-opentui.ts <version>")
|
|
8
|
+
process.exit(1)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const ver = raw.replace(/^v/, "")
|
|
12
|
+
const root = path.resolve(import.meta.dir, "../../..")
|
|
13
|
+
const skip = new Set([".git", ".saeeol", ".turbo", "dist", "node_modules"])
|
|
14
|
+
const keys = ["@opentui/core", "@opentui/solid"] as const
|
|
15
|
+
|
|
16
|
+
const files = (await Array.fromAsync(new Bun.Glob("**/package.json").scan({ cwd: root }))).filter(
|
|
17
|
+
(file) => !file.split("/").some((part) => skip.has(part)),
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
const set = (cur: string) => {
|
|
21
|
+
if (cur.startsWith(">=")) return `>=${ver}`
|
|
22
|
+
if (cur.startsWith("^")) return `^${ver}`
|
|
23
|
+
if (cur.startsWith("~")) return `~${ver}`
|
|
24
|
+
return ver
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const edit = (obj: unknown) => {
|
|
28
|
+
if (!obj || typeof obj !== "object") return false
|
|
29
|
+
const map = obj as Record<string, unknown>
|
|
30
|
+
return keys
|
|
31
|
+
.map((key) => {
|
|
32
|
+
const cur = map[key]
|
|
33
|
+
if (typeof cur !== "string") return false
|
|
34
|
+
const next = set(cur)
|
|
35
|
+
if (next === cur) return false
|
|
36
|
+
map[key] = next
|
|
37
|
+
return true
|
|
38
|
+
})
|
|
39
|
+
.some(Boolean)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const out = (
|
|
43
|
+
await Promise.all(
|
|
44
|
+
files.map(async (rel) => {
|
|
45
|
+
const file = path.join(root, rel)
|
|
46
|
+
const txt = await Bun.file(file).text()
|
|
47
|
+
const json = JSON.parse(txt)
|
|
48
|
+
const hit = [json.dependencies, json.devDependencies, json.peerDependencies].map(edit).some(Boolean)
|
|
49
|
+
if (!hit) return null
|
|
50
|
+
await Bun.write(file, `${JSON.stringify(json, null, 2)}\n`)
|
|
51
|
+
return rel
|
|
52
|
+
}),
|
|
53
|
+
)
|
|
54
|
+
).filter((item): item is string => item !== null)
|
|
55
|
+
|
|
56
|
+
if (out.length === 0) {
|
|
57
|
+
console.log("No opentui deps found")
|
|
58
|
+
process.exit(0)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
console.log(`Updated opentui to ${ver} in:`)
|
|
62
|
+
for (const file of out) {
|
|
63
|
+
console.log(`- ${file}`)
|
|
64
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Compare SDK types generated from Hono vs HttpApi specs.
|
|
3
|
+
# Sorts types alphabetically so only meaningful body differences show.
|
|
4
|
+
#
|
|
5
|
+
# Usage: ./scripts/diff-sdk-types.sh # full diff
|
|
6
|
+
# ./scripts/diff-sdk-types.sh --stat # summary only
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
10
|
+
SDK="$(cd "$DIR/../sdk/js" && pwd)"
|
|
11
|
+
|
|
12
|
+
normalize() {
|
|
13
|
+
python3 -c "
|
|
14
|
+
import re, sys
|
|
15
|
+
content = open(sys.argv[1]).read()
|
|
16
|
+
blocks = re.split(r'(?=^export (?:type|function|const) )', content, flags=re.MULTILINE)
|
|
17
|
+
header, body = blocks[0], blocks[1:]
|
|
18
|
+
body.sort(key=lambda b: m.group(1) if (m := re.match(r'export \w+ (\w+)', b)) else '')
|
|
19
|
+
sys.stdout.write(header + ''.join(body))
|
|
20
|
+
" "$1"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
echo "Generating Hono SDK..." >&2
|
|
24
|
+
(cd "$SDK" && bun run script/build.ts >/dev/null 2>&1)
|
|
25
|
+
normalize "$SDK/src/v2/gen/types.gen.ts" > /tmp/sdk-types-hono.ts
|
|
26
|
+
git -C "$SDK" checkout -- src/ 2>/dev/null
|
|
27
|
+
|
|
28
|
+
echo "Generating HttpApi SDK..." >&2
|
|
29
|
+
(cd "$SDK" && OPENCODE_SDK_OPENAPI=httpapi bun run script/build.ts >/dev/null 2>&1)
|
|
30
|
+
normalize "$SDK/src/v2/gen/types.gen.ts" > /tmp/sdk-types-httpapi.ts
|
|
31
|
+
git -C "$SDK" checkout -- src/ 2>/dev/null
|
|
32
|
+
|
|
33
|
+
echo "" >&2
|
|
34
|
+
if [[ "${1:-}" == "--stat" ]]; then
|
|
35
|
+
diff_output=$(diff /tmp/sdk-types-hono.ts /tmp/sdk-types-httpapi.ts || true)
|
|
36
|
+
honly=$(printf "%s\n" "$diff_output" | grep -c '^< export type' || true)
|
|
37
|
+
aonly=$(printf "%s\n" "$diff_output" | grep -c '^> export type' || true)
|
|
38
|
+
total=$(printf "%s\n" "$diff_output" | wc -l | tr -d ' ')
|
|
39
|
+
echo "Hono-only: $honly types HttpApi-only: $aonly types Diff lines: $total"
|
|
40
|
+
echo ""
|
|
41
|
+
if [[ $honly -gt 0 ]]; then
|
|
42
|
+
echo "=== Hono-only types ==="
|
|
43
|
+
printf "%s\n" "$diff_output" | grep '^< export type' | sed 's/< export type //' | sed 's/[ =].*//' | sed 's/^/ /'
|
|
44
|
+
echo ""
|
|
45
|
+
fi
|
|
46
|
+
if [[ $aonly -gt 0 ]]; then
|
|
47
|
+
echo "=== HttpApi-only types ==="
|
|
48
|
+
printf "%s\n" "$diff_output" | grep '^> export type' | sed 's/> export type //' | sed 's/[ =].*//' | sed 's/^/ /'
|
|
49
|
+
fi
|
|
50
|
+
else
|
|
51
|
+
diff /tmp/sdk-types-hono.ts /tmp/sdk-types-httpapi.ts || true
|
|
52
|
+
fi
|