@neurocode-ai/core 1.18.8
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/bin/neurocode +199 -0
- package/bunfig.toml +2 -0
- package/drizzle.config.ts +10 -0
- package/package.json +131 -0
- package/schema.json +2071 -0
- package/script/fix-node-pty.ts +28 -0
- package/script/migration.ts +196 -0
- package/src/account/sql.ts +39 -0
- package/src/account.ts +101 -0
- package/src/agent.ts +111 -0
- package/src/aisdk.ts +235 -0
- package/src/background-job.ts +365 -0
- package/src/catalog.ts +301 -0
- package/src/command.ts +64 -0
- package/src/config/agent.ts +25 -0
- package/src/config/attachments.ts +15 -0
- package/src/config/command.ts +12 -0
- package/src/config/compaction.ts +15 -0
- package/src/config/experimental.ts +18 -0
- package/src/config/formatter.ts +12 -0
- package/src/config/lsp.ts +18 -0
- package/src/config/markdown.ts +36 -0
- package/src/config/mcp.ts +48 -0
- package/src/config/plugin/agent.ts +179 -0
- package/src/config/plugin/command.ts +83 -0
- package/src/config/plugin/external.ts +91 -0
- package/src/config/plugin/provider.ts +113 -0
- package/src/config/plugin/reference.ts +70 -0
- package/src/config/plugin/skill.ts +50 -0
- package/src/config/plugin.ts +13 -0
- package/src/config/provider.ts +71 -0
- package/src/config/reference.ts +22 -0
- package/src/config/tool-output.ts +9 -0
- package/src/config/watcher.ts +7 -0
- package/src/config.ts +227 -0
- package/src/control-plane/move-session.ts +148 -0
- package/src/control-plane/workspace.sql.ts +20 -0
- package/src/credential/sql.ts +14 -0
- package/src/credential.ts +138 -0
- package/src/cross-spawn-spawner.ts +507 -0
- package/src/data-migration.sql.ts +6 -0
- package/src/database/database.ts +57 -0
- package/src/database/migration/20260127222353_familiar_lady_ursula.ts +107 -0
- package/src/database/migration/20260211171708_add_project_commands.ts +11 -0
- package/src/database/migration/20260213144116_wakeful_the_professor.ts +23 -0
- package/src/database/migration/20260225215848_workspace.ts +19 -0
- package/src/database/migration/20260227213759_add_session_workspace_id.ts +12 -0
- package/src/database/migration/20260228203230_blue_harpoon.ts +30 -0
- package/src/database/migration/20260303231226_add_workspace_fields.ts +15 -0
- package/src/database/migration/20260309230000_move_org_to_state.ts +15 -0
- package/src/database/migration/20260312043431_session_message_cursor.ts +16 -0
- package/src/database/migration/20260323234822_events.ts +26 -0
- package/src/database/migration/20260410174513_workspace-name.ts +29 -0
- package/src/database/migration/20260413175956_chief_energizer.ts +24 -0
- package/src/database/migration/20260423070820_add_icon_url_override.ts +14 -0
- package/src/database/migration/20260427172553_slow_nightmare.ts +30 -0
- package/src/database/migration/20260428004200_add_session_path.ts +11 -0
- package/src/database/migration/20260501142318_next_venus.ts +12 -0
- package/src/database/migration/20260504145000_add_sync_owner.ts +11 -0
- package/src/database/migration/20260507164347_add_workspace_time.ts +11 -0
- package/src/database/migration/20260510033149_session_usage.ts +56 -0
- package/src/database/migration/20260511000411_data_migration_state.ts +16 -0
- package/src/database/migration/20260511173437_session-metadata.ts +16 -0
- package/src/database/migration/20260601010001_normalize_storage_paths.ts +22 -0
- package/src/database/migration/20260601202201_amazing_prowler.ts +11 -0
- package/src/database/migration/20260602002951_lowly_union_jack.ts +24 -0
- package/src/database/migration/20260602182828_add_project_directories.ts +20 -0
- package/src/database/migration/20260603001617_session_message_projection_indexes.ts +19 -0
- package/src/database/migration/20260603040000_session_message_projection_order.ts +19 -0
- package/src/database/migration/20260603141458_session_input_inbox.ts +25 -0
- package/src/database/migration/20260603160727_jittery_ezekiel_stane.ts +20 -0
- package/src/database/migration/20260604172448_event_sourced_session_input.ts +47 -0
- package/src/database/migration/20260605003541_add_session_context_snapshot.ts +21 -0
- package/src/database/migration/20260605042240_add_context_epoch_agent.ts +11 -0
- package/src/database/migration/20260611035744_credential.ts +25 -0
- package/src/database/migration/20260611192811_lush_chimera.ts +25 -0
- package/src/database/migration/20260612174303_project_dir_strategy.ts +29 -0
- package/src/database/migration/20260622142730_simplify_session_context_epoch.ts +13 -0
- package/src/database/migration/20260622170816_reset_v2_session_state.ts +17 -0
- package/src/database/migration/20260622202450_simplify_session_input.ts +17 -0
- package/src/database/migration.gen.ts +44 -0
- package/src/database/migration.ts +81 -0
- package/src/database/path.ts +91 -0
- package/src/database/schema.gen.ts +274 -0
- package/src/database/schema.sql.ts +10 -0
- package/src/database/sqlite.bun.ts +183 -0
- package/src/database/sqlite.node.ts +178 -0
- package/src/database/sqlite.ts +8 -0
- package/src/effect/app-node-builder.ts +23 -0
- package/src/effect/app-node-platform.ts +18 -0
- package/src/effect/app-node.ts +14 -0
- package/src/effect/dfdf +1 -0
- package/src/effect/keyed-mutex.ts +45 -0
- package/src/effect/layer-node.ts +333 -0
- package/src/effect/memo-map.ts +3 -0
- package/src/effect/runtime.ts +21 -0
- package/src/effect/service-use.ts +43 -0
- package/src/event/sql.ts +25 -0
- package/src/event.ts +638 -0
- package/src/file-mutation.ts +207 -0
- package/src/file.ts +6 -0
- package/src/filesystem/fff.bun.ts +140 -0
- package/src/filesystem/fff.node.ts +138 -0
- package/src/filesystem/ignore.ts +67 -0
- package/src/filesystem/protected.ts +53 -0
- package/src/filesystem/search.ts +239 -0
- package/src/filesystem/watcher.ts +140 -0
- package/src/filesystem.ts +118 -0
- package/src/flag/flag.ts +78 -0
- package/src/fs-util.ts +274 -0
- package/src/git.ts +987 -0
- package/src/github-copilot/README.md +5 -0
- package/src/github-copilot/chat/convert-to-openai-compatible-chat-messages.ts +170 -0
- package/src/github-copilot/chat/get-response-metadata.ts +15 -0
- package/src/github-copilot/chat/map-openai-compatible-finish-reason.ts +19 -0
- package/src/github-copilot/chat/openai-compatible-api-types.ts +64 -0
- package/src/github-copilot/chat/openai-compatible-chat-language-model.ts +815 -0
- package/src/github-copilot/chat/openai-compatible-chat-options.ts +28 -0
- package/src/github-copilot/chat/openai-compatible-metadata-extractor.ts +44 -0
- package/src/github-copilot/chat/openai-compatible-prepare-tools.ts +83 -0
- package/src/github-copilot/copilot-provider.ts +100 -0
- package/src/github-copilot/openai-compatible-error.ts +27 -0
- package/src/github-copilot/responses/convert-to-openai-responses-input.ts +335 -0
- package/src/github-copilot/responses/map-openai-responses-finish-reason.ts +22 -0
- package/src/github-copilot/responses/openai-config.ts +18 -0
- package/src/github-copilot/responses/openai-error.ts +22 -0
- package/src/github-copilot/responses/openai-responses-api-types.ts +214 -0
- package/src/github-copilot/responses/openai-responses-language-model.ts +1770 -0
- package/src/github-copilot/responses/openai-responses-prepare-tools.ts +173 -0
- package/src/github-copilot/responses/openai-responses-settings.ts +1 -0
- package/src/github-copilot/responses/tool/code-interpreter.ts +87 -0
- package/src/github-copilot/responses/tool/file-search.ts +127 -0
- package/src/github-copilot/responses/tool/image-generation.ts +114 -0
- package/src/github-copilot/responses/tool/local-shell.ts +64 -0
- package/src/github-copilot/responses/tool/web-search-preview.ts +103 -0
- package/src/github-copilot/responses/tool/web-search.ts +102 -0
- package/src/global.ts +87 -0
- package/src/id/id.ts +47 -0
- package/src/image/photon.ts +94 -0
- package/src/image.ts +81 -0
- package/src/installation/version.ts +8 -0
- package/src/instruction-context.ts +101 -0
- package/src/integration/connection.ts +12 -0
- package/src/integration.ts +520 -0
- package/src/location-mutation.ts +162 -0
- package/src/location-service-map.ts +18 -0
- package/src/location-services.ts +115 -0
- package/src/location.ts +39 -0
- package/src/markdown.d.ts +4 -0
- package/src/model.ts +41 -0
- package/src/models-dev.ts +260 -0
- package/src/npm-config.ts +40 -0
- package/src/npm.ts +269 -0
- package/src/oauth/page.ts +276 -0
- package/src/observability/logging.ts +71 -0
- package/src/observability/otlp.ts +79 -0
- package/src/observability/shared.ts +1 -0
- package/src/observability.ts +24 -0
- package/src/patch.ts +197 -0
- package/src/permission/saved.ts +79 -0
- package/src/permission/sql.ts +20 -0
- package/src/permission.ts +310 -0
- package/src/plugin/agent.ts +206 -0
- package/src/plugin/command/initialize.txt +65 -0
- package/src/plugin/command/review.txt +100 -0
- package/src/plugin/command.ts +25 -0
- package/src/plugin/host.ts +219 -0
- package/src/plugin/internal.ts +153 -0
- package/src/plugin/layer-map.example.ts +94 -0
- package/src/plugin/models-dev.ts +183 -0
- package/src/plugin/promise.ts +93 -0
- package/src/plugin/provider/alibaba.ts +15 -0
- package/src/plugin/provider/amazon-bedrock.ts +126 -0
- package/src/plugin/provider/anthropic.ts +27 -0
- package/src/plugin/provider/azure.ts +82 -0
- package/src/plugin/provider/cerebras.ts +26 -0
- package/src/plugin/provider/cloudflare-ai-gateway.ts +81 -0
- package/src/plugin/provider/cloudflare-workers-ai.ts +81 -0
- package/src/plugin/provider/cohere.ts +15 -0
- package/src/plugin/provider/deepinfra.ts +15 -0
- package/src/plugin/provider/dynamic.ts +31 -0
- package/src/plugin/provider/gateway.ts +15 -0
- package/src/plugin/provider/github-copilot.ts +52 -0
- package/src/plugin/provider/gitlab.ts +65 -0
- package/src/plugin/provider/google-vertex.ts +173 -0
- package/src/plugin/provider/google.ts +15 -0
- package/src/plugin/provider/groq.ts +15 -0
- package/src/plugin/provider/kilo.ts +21 -0
- package/src/plugin/provider/llmgateway.ts +26 -0
- package/src/plugin/provider/mistral.ts +15 -0
- package/src/plugin/provider/nvidia.ts +22 -0
- package/src/plugin/provider/openai-compatible.ts +17 -0
- package/src/plugin/provider/openai.ts +292 -0
- package/src/plugin/provider/opencode.ts +311 -0
- package/src/plugin/provider/openrouter.ts +36 -0
- package/src/plugin/provider/perplexity.ts +15 -0
- package/src/plugin/provider/sap-ai-core.ts +46 -0
- package/src/plugin/provider/snowflake-cortex.ts +89 -0
- package/src/plugin/provider/togetherai.ts +15 -0
- package/src/plugin/provider/venice.ts +15 -0
- package/src/plugin/provider/vercel.ts +27 -0
- package/src/plugin/provider/xai.ts +22 -0
- package/src/plugin/provider/zenmux.ts +21 -0
- package/src/plugin/provider.ts +71 -0
- package/src/plugin/skill/customize-neurocode.md +452 -0
- package/src/plugin/skill.ts +31 -0
- package/src/plugin/variant.ts +39 -0
- package/src/plugin.ts +167 -0
- package/src/policy.ts +49 -0
- package/src/process.ts +261 -0
- package/src/project/copy-strategies.ts +35 -0
- package/src/project/copy.ts +292 -0
- package/src/project/directories.ts +158 -0
- package/src/project/schema.ts +16 -0
- package/src/project/sql.ts +35 -0
- package/src/project.ts +136 -0
- package/src/provider.ts +25 -0
- package/src/pty/protocol.ts +37 -0
- package/src/pty/pty.bun.ts +26 -0
- package/src/pty/pty.node.ts +29 -0
- package/src/pty/pty.ts +25 -0
- package/src/pty/schema.ts +1 -0
- package/src/pty/ticket.ts +56 -0
- package/src/pty.ts +318 -0
- package/src/public-event-manifest.ts +7 -0
- package/src/question.ts +153 -0
- package/src/reference/guidance.ts +69 -0
- package/src/reference.ts +125 -0
- package/src/repository-cache.ts +259 -0
- package/src/repository.ts +214 -0
- package/src/ripgrep/binary.ts +132 -0
- package/src/ripgrep.ts +281 -0
- package/src/schema.ts +79 -0
- package/src/session/compaction.ts +241 -0
- package/src/session/context-epoch.ts +174 -0
- package/src/session/error.ts +24 -0
- package/src/session/event.ts +2 -0
- package/src/session/execution/local.ts +46 -0
- package/src/session/execution.ts +34 -0
- package/src/session/history.ts +101 -0
- package/src/session/info.ts +50 -0
- package/src/session/input.ts +288 -0
- package/src/session/message-updater.ts +397 -0
- package/src/session/message.ts +2 -0
- package/src/session/projector.ts +458 -0
- package/src/session/prompt.ts +1 -0
- package/src/session/revert.ts +121 -0
- package/src/session/run-coordinator.ts +104 -0
- package/src/session/runner/index.ts +28 -0
- package/src/session/runner/llm.ts +432 -0
- package/src/session/runner/max-steps.ts +16 -0
- package/src/session/runner/model.ts +218 -0
- package/src/session/runner/publish-llm-event.ts +423 -0
- package/src/session/runner/to-llm-message.ts +171 -0
- package/src/session/schema.ts +9 -0
- package/src/session/sql.ts +176 -0
- package/src/session/store.ts +63 -0
- package/src/session/todo.ts +78 -0
- package/src/session.ts +486 -0
- package/src/share/sql.ts +13 -0
- package/src/shell.ts +226 -0
- package/src/skill/discovery.ts +213 -0
- package/src/skill/guidance.ts +76 -0
- package/src/skill.ts +132 -0
- package/src/snapshot.ts +266 -0
- package/src/state.ts +128 -0
- package/src/system-context/builtins.ts +50 -0
- package/src/system-context/index.ts +320 -0
- package/src/system-context/registry.ts +49 -0
- package/src/tool/AGENTS.md +59 -0
- package/src/tool/application-tools.ts +57 -0
- package/src/tool/apply-patch.ts +219 -0
- package/src/tool/bash.ts +207 -0
- package/src/tool/builtins.ts +48 -0
- package/src/tool/edit.ts +223 -0
- package/src/tool/glob.ts +105 -0
- package/src/tool/grep.ts +137 -0
- package/src/tool/http-body.ts +30 -0
- package/src/tool/question.ts +94 -0
- package/src/tool/read-filesystem.ts +366 -0
- package/src/tool/read.ts +117 -0
- package/src/tool/registry.ts +147 -0
- package/src/tool/skill.ts +109 -0
- package/src/tool/todowrite.ts +62 -0
- package/src/tool/tool.ts +162 -0
- package/src/tool/tools.ts +13 -0
- package/src/tool/webfetch.ts +218 -0
- package/src/tool/websearch.ts +260 -0
- package/src/tool/write.ts +101 -0
- package/src/tool-output-store.ts +211 -0
- package/src/util/array.ts +10 -0
- package/src/util/binary.ts +41 -0
- package/src/util/effect-flock.ts +284 -0
- package/src/util/encode.ts +51 -0
- package/src/util/error.ts +70 -0
- package/src/util/flock.ts +358 -0
- package/src/util/glob.ts +34 -0
- package/src/util/hash.ts +11 -0
- package/src/util/identifier.ts +1 -0
- package/src/util/iife.ts +3 -0
- package/src/util/lazy.ts +11 -0
- package/src/util/module.ts +10 -0
- package/src/util/path.ts +37 -0
- package/src/util/retry.ts +42 -0
- package/src/util/slug.ts +74 -0
- package/src/util/token.ts +5 -0
- package/src/util/which.ts +14 -0
- package/src/util/wildcard.ts +14 -0
- package/src/v1/config/agent.ts +89 -0
- package/src/v1/config/attachment.ts +25 -0
- package/src/v1/config/command.ts +13 -0
- package/src/v1/config/config.ts +192 -0
- package/src/v1/config/console-state.ts +16 -0
- package/src/v1/config/error.ts +39 -0
- package/src/v1/config/formatter.ts +13 -0
- package/src/v1/config/layout.ts +6 -0
- package/src/v1/config/lsp.ts +80 -0
- package/src/v1/config/mcp.ts +63 -0
- package/src/v1/config/migrate.ts +254 -0
- package/src/v1/config/permission.ts +50 -0
- package/src/v1/config/plugin.ts +9 -0
- package/src/v1/config/provider-options.ts +227 -0
- package/src/v1/config/provider.ts +121 -0
- package/src/v1/config/server.ts +19 -0
- package/src/v1/config/skills.ts +13 -0
- package/src/v1/permission.ts +33 -0
- package/src/v1/session.ts +68 -0
- package/src/v2-schema.ts +3 -0
- package/src/workspace.ts +6 -0
- package/sst-env.d.ts +10 -0
- package/test/agent.test.ts +131 -0
- package/test/application-tools.test.ts +287 -0
- package/test/background-job.test.ts +106 -0
- package/test/catalog.test.ts +353 -0
- package/test/command.test.ts +56 -0
- package/test/config/agent.test.ts +349 -0
- package/test/config/command.test.ts +83 -0
- package/test/config/config.test.ts +798 -0
- package/test/config/fixtures/plugin/directory-plugin.ts +13 -0
- package/test/config/plugin.test.ts +248 -0
- package/test/config/provider-options.test.ts +224 -0
- package/test/config/provider.test.ts +269 -0
- package/test/config/skill.test.ts +80 -0
- package/test/credential.test.ts +36 -0
- package/test/database-migration.test.ts +644 -0
- package/test/effect/cross-spawn-spawner.test.ts +426 -0
- package/test/effect/keyed-mutex.test.ts +73 -0
- package/test/effect/layer-node/layer-node-types.test.ts +143 -0
- package/test/effect/layer-node/layer-node.test.ts +261 -0
- package/test/effect/layer-node/node-build.test.ts +125 -0
- package/test/effect/observability.test.ts +109 -0
- package/test/event.test.ts +1124 -0
- package/test/file-mutation.test.ts +368 -0
- package/test/filesystem/filesystem.test.ts +387 -0
- package/test/filesystem/ignore.test.ts +10 -0
- package/test/filesystem/search.test.ts +44 -0
- package/test/filesystem/watcher.test.ts +266 -0
- package/test/fixture/effect-flock-worker.ts +60 -0
- package/test/fixture/flock-worker.ts +72 -0
- package/test/fixture/git.ts +49 -0
- package/test/fixture/location.ts +26 -0
- package/test/fixture/tmpdir.ts +25 -0
- package/test/fixtures/recordings/session-runner/openai-chat-streams-text.json +27 -0
- package/test/git.test.ts +164 -0
- package/test/github-copilot/convert-to-copilot-messages.test.ts +523 -0
- package/test/github-copilot/copilot-chat-model.test.ts +592 -0
- package/test/github-copilot/openai-responses-language-model.test.ts +206 -0
- package/test/global.test.ts +16 -0
- package/test/instruction-context.test.ts +323 -0
- package/test/integration.test.ts +349 -0
- package/test/legacy-event-schema.test.ts +16 -0
- package/test/lib/effect.ts +53 -0
- package/test/lib/tool.ts +20 -0
- package/test/location-filesystem.test.ts +69 -0
- package/test/location-layer.test.ts +226 -0
- package/test/location-mutation.test.ts +178 -0
- package/test/location.test.ts +42 -0
- package/test/model.test.ts +23 -0
- package/test/models.test.ts +290 -0
- package/test/move-session.test.ts +235 -0
- package/test/npm-config.test.ts +51 -0
- package/test/npm.test.ts +84 -0
- package/test/oauth-page.test.ts +15 -0
- package/test/patch.test.ts +68 -0
- package/test/permission.test.ts +315 -0
- package/test/plugin/command.test.ts +47 -0
- package/test/plugin/fixture.ts +52 -0
- package/test/plugin/fixtures/config-effect-plugin.ts +15 -0
- package/test/plugin/fixtures/config-promise-plugin.ts +13 -0
- package/test/plugin/fixtures/invalid-plugin.ts +1 -0
- package/test/plugin/fixtures/models-dev.json +14 -0
- package/test/plugin/fixtures/provider-factory.ts +9 -0
- package/test/plugin/host.ts +303 -0
- package/test/plugin/models-dev.test.ts +170 -0
- package/test/plugin/promise.test.ts +67 -0
- package/test/plugin/provider-alibaba.test.ts +92 -0
- package/test/plugin/provider-amazon-bedrock.test.ts +622 -0
- package/test/plugin/provider-anthropic.test.ts +94 -0
- package/test/plugin/provider-azure-cognitive-services.test.ts +199 -0
- package/test/plugin/provider-azure.test.ts +293 -0
- package/test/plugin/provider-cerebras.test.ts +136 -0
- package/test/plugin/provider-cloudflare-ai-gateway.test.ts +426 -0
- package/test/plugin/provider-cloudflare-workers-ai.test.ts +279 -0
- package/test/plugin/provider-cohere.test.ts +123 -0
- package/test/plugin/provider-deepinfra.test.ts +159 -0
- package/test/plugin/provider-dynamic.test.ts +184 -0
- package/test/plugin/provider-gateway.test.ts +115 -0
- package/test/plugin/provider-github-copilot.test.ts +278 -0
- package/test/plugin/provider-gitlab.test.ts +308 -0
- package/test/plugin/provider-google-vertex-anthropic.test.ts +264 -0
- package/test/plugin/provider-google-vertex.test.ts +387 -0
- package/test/plugin/provider-google.test.ts +80 -0
- package/test/plugin/provider-groq.test.ts +121 -0
- package/test/plugin/provider-kilo.test.ts +99 -0
- package/test/plugin/provider-llmgateway.test.ts +80 -0
- package/test/plugin/provider-mistral.test.ts +129 -0
- package/test/plugin/provider-nvidia.test.ts +97 -0
- package/test/plugin/provider-openai-compatible.test.ts +107 -0
- package/test/plugin/provider-openai.test.ts +176 -0
- package/test/plugin/provider-opencode.test.ts +423 -0
- package/test/plugin/provider-openrouter.test.ts +113 -0
- package/test/plugin/provider-perplexity.test.ts +122 -0
- package/test/plugin/provider-sap-ai-core.test.ts +159 -0
- package/test/plugin/provider-snowflake-cortex.test.ts +261 -0
- package/test/plugin/provider-togetherai.test.ts +129 -0
- package/test/plugin/provider-venice.test.ts +115 -0
- package/test/plugin/provider-vercel.test.ts +83 -0
- package/test/plugin/provider-xai.test.ts +125 -0
- package/test/plugin/provider-zenmux.test.ts +110 -0
- package/test/plugin/skill.test.ts +25 -0
- package/test/plugin/variant.test.ts +67 -0
- package/test/plugin.test.ts +71 -0
- package/test/policy.test.ts +85 -0
- package/test/preload.ts +5 -0
- package/test/process/process.test.ts +368 -0
- package/test/project-copy.test.ts +400 -0
- package/test/project-directories.test.ts +61 -0
- package/test/project.test.ts +221 -0
- package/test/provider-mistral.test.ts +282 -0
- package/test/provider-xai-responses.test.ts +32 -0
- package/test/pty/info-schema.test.ts +32 -0
- package/test/pty/protocol.test.ts +27 -0
- package/test/pty/pty-session.test.ts +240 -0
- package/test/pty/ticket.test.ts +62 -0
- package/test/question.test.ts +114 -0
- package/test/reference-guidance.test.ts +76 -0
- package/test/reference.test.ts +78 -0
- package/test/repository-cache.test.ts +153 -0
- package/test/repository.test.ts +71 -0
- package/test/ripgrep.test.ts +65 -0
- package/test/session-compaction.test.ts +27 -0
- package/test/session-create.test.ts +425 -0
- package/test/session-history.test.ts +165 -0
- package/test/session-projector.test.ts +533 -0
- package/test/session-prompt.test.ts +584 -0
- package/test/session-run-coordinator.test.ts +418 -0
- package/test/session-runner-message.test.ts +501 -0
- package/test/session-runner-model.test.ts +347 -0
- package/test/session-runner-recorded.test.ts +193 -0
- package/test/session-runner-tool-events.test.ts +136 -0
- package/test/session-runner-tool-registry.test.ts +452 -0
- package/test/session-runner.test.ts +3365 -0
- package/test/session-todo.test.ts +94 -0
- package/test/session-tool-progress.test.ts +158 -0
- package/test/shared-schema.test.ts +206 -0
- package/test/shell.test.ts +108 -0
- package/test/skill/guidance.test.ts +144 -0
- package/test/skill-discovery.test.ts +165 -0
- package/test/skill.test.ts +125 -0
- package/test/snapshot.test.ts +178 -0
- package/test/state.test.ts +115 -0
- package/test/system-context/builtins.test.ts +129 -0
- package/test/system-context/index.test.ts +307 -0
- package/test/system-context/registry.test.ts +114 -0
- package/test/tool-apply-patch.test.ts +436 -0
- package/test/tool-bash.test.ts +437 -0
- package/test/tool-edit.test.ts +434 -0
- package/test/tool-output-store.test.ts +247 -0
- package/test/tool-question.test.ts +162 -0
- package/test/tool-read-filesystem.test.ts +118 -0
- package/test/tool-read.test.ts +674 -0
- package/test/tool-skill.test.ts +149 -0
- package/test/tool-todowrite.test.ts +125 -0
- package/test/tool-webfetch.test.ts +281 -0
- package/test/tool-websearch.test.ts +316 -0
- package/test/tool-write.test.ts +305 -0
- package/test/util/effect-flock.test.ts +392 -0
- package/test/util/flock.test.ts +428 -0
- package/test/util/which.test.ts +100 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
export * as ProjectDirectories from "./directories"
|
|
2
|
+
|
|
3
|
+
import { and, asc, desc, eq, isNotNull, isNull, ne, or } from "drizzle-orm"
|
|
4
|
+
import { Context, Effect, Layer, Schema } from "effect"
|
|
5
|
+
import { Database } from "../database/database"
|
|
6
|
+
import { makeGlobalNode } from "../effect/app-node"
|
|
7
|
+
import { AbsolutePath, optional } from "../schema"
|
|
8
|
+
import { ProjectSchema } from "./schema"
|
|
9
|
+
import { ProjectDirectoryTable } from "./sql"
|
|
10
|
+
import type { EffectDrizzleSqlite } from "@neurocode-ai/effect-drizzle-sqlite"
|
|
11
|
+
|
|
12
|
+
export interface Directory {
|
|
13
|
+
readonly directory: AbsolutePath
|
|
14
|
+
readonly strategy?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const CreateInput = Schema.Struct({
|
|
18
|
+
projectID: ProjectSchema.ID,
|
|
19
|
+
directory: AbsolutePath,
|
|
20
|
+
strategy: Schema.optional(Schema.String),
|
|
21
|
+
behavior: Schema.Literals(["ignore", "replace"]).pipe(Schema.optional),
|
|
22
|
+
})
|
|
23
|
+
export type CreateInput = typeof CreateInput.Type
|
|
24
|
+
|
|
25
|
+
export const RemoveInput = Schema.Struct({
|
|
26
|
+
projectID: ProjectSchema.ID,
|
|
27
|
+
directory: AbsolutePath,
|
|
28
|
+
})
|
|
29
|
+
export type RemoveInput = typeof RemoveInput.Type
|
|
30
|
+
|
|
31
|
+
type DatabaseClient = EffectDrizzleSqlite.EffectSQLiteDatabase
|
|
32
|
+
export type Transaction = Parameters<Parameters<DatabaseClient["transaction"]>[0]>[0]
|
|
33
|
+
|
|
34
|
+
export const ListInput = Schema.Struct({
|
|
35
|
+
projectID: ProjectSchema.ID,
|
|
36
|
+
}).annotate({ identifier: "Project.DirectoriesInput" })
|
|
37
|
+
export type ListInput = typeof ListInput.Type
|
|
38
|
+
|
|
39
|
+
export const ListOutput = Schema.Array(
|
|
40
|
+
Schema.Struct({
|
|
41
|
+
directory: AbsolutePath,
|
|
42
|
+
strategy: optional(Schema.String),
|
|
43
|
+
}),
|
|
44
|
+
).annotate({ identifier: "Project.Directories" })
|
|
45
|
+
export type ListOutput = typeof ListOutput.Type
|
|
46
|
+
|
|
47
|
+
export interface Interface {
|
|
48
|
+
readonly list: (projectID: ProjectSchema.ID) => Effect.Effect<ReadonlyArray<Directory>>
|
|
49
|
+
readonly get: (input: {
|
|
50
|
+
projectID: ProjectSchema.ID
|
|
51
|
+
directory: AbsolutePath
|
|
52
|
+
}) => Effect.Effect<Directory | undefined>
|
|
53
|
+
readonly contains: (input: { projectID: ProjectSchema.ID; directory: AbsolutePath }) => Effect.Effect<boolean>
|
|
54
|
+
readonly create: (input: CreateInput, tx?: Transaction) => Effect.Effect<boolean>
|
|
55
|
+
readonly remove: (input: RemoveInput, tx?: Transaction) => Effect.Effect<boolean>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class Service extends Context.Service<Service, Interface>()("@neurocode/ProjectDirectories") {}
|
|
59
|
+
|
|
60
|
+
const layer = Layer.effect(
|
|
61
|
+
Service,
|
|
62
|
+
Effect.gen(function* () {
|
|
63
|
+
const db = (yield* Database.Service).db
|
|
64
|
+
|
|
65
|
+
const create = Effect.fn("ProjectDirectories.create")(function* (input: CreateInput, tx?: Transaction) {
|
|
66
|
+
const insert = (tx ?? db)
|
|
67
|
+
.insert(ProjectDirectoryTable)
|
|
68
|
+
.values({ project_id: input.projectID, directory: input.directory, strategy: input.strategy })
|
|
69
|
+
const query =
|
|
70
|
+
input.behavior === "replace"
|
|
71
|
+
? insert.onConflictDoUpdate({
|
|
72
|
+
target: [ProjectDirectoryTable.project_id, ProjectDirectoryTable.directory],
|
|
73
|
+
set: { strategy: input.strategy ?? null },
|
|
74
|
+
setWhere: input.strategy
|
|
75
|
+
? or(isNull(ProjectDirectoryTable.strategy), ne(ProjectDirectoryTable.strategy, input.strategy))
|
|
76
|
+
: isNotNull(ProjectDirectoryTable.strategy),
|
|
77
|
+
})
|
|
78
|
+
: insert.onConflictDoNothing()
|
|
79
|
+
return (
|
|
80
|
+
(yield* query.returning({ directory: ProjectDirectoryTable.directory }).get().pipe(Effect.orDie)) !== undefined
|
|
81
|
+
)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const remove = Effect.fn("ProjectDirectories.remove")(function* (input: RemoveInput, tx?: Transaction) {
|
|
85
|
+
return (
|
|
86
|
+
(yield* (tx ?? db)
|
|
87
|
+
.delete(ProjectDirectoryTable)
|
|
88
|
+
.where(
|
|
89
|
+
and(
|
|
90
|
+
eq(ProjectDirectoryTable.project_id, input.projectID),
|
|
91
|
+
eq(ProjectDirectoryTable.directory, input.directory),
|
|
92
|
+
),
|
|
93
|
+
)
|
|
94
|
+
.returning({ directory: ProjectDirectoryTable.directory })
|
|
95
|
+
.get()
|
|
96
|
+
.pipe(Effect.orDie)) !== undefined
|
|
97
|
+
)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
const list = Effect.fn("ProjectDirectories.list")(function* (projectID: ProjectSchema.ID) {
|
|
101
|
+
const rows = yield* db
|
|
102
|
+
.select({ directory: ProjectDirectoryTable.directory, strategy: ProjectDirectoryTable.strategy })
|
|
103
|
+
.from(ProjectDirectoryTable)
|
|
104
|
+
.where(eq(ProjectDirectoryTable.project_id, projectID))
|
|
105
|
+
.orderBy(desc(ProjectDirectoryTable.time_created), asc(ProjectDirectoryTable.directory))
|
|
106
|
+
.all()
|
|
107
|
+
.pipe(Effect.orDie)
|
|
108
|
+
return rows.map((row) => ({ directory: row.directory, strategy: row.strategy ?? undefined }))
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
const contains = Effect.fn("ProjectDirectories.contains")(function* (input: {
|
|
112
|
+
projectID: ProjectSchema.ID
|
|
113
|
+
directory: AbsolutePath
|
|
114
|
+
}) {
|
|
115
|
+
return (
|
|
116
|
+
(yield* db
|
|
117
|
+
.select({ directory: ProjectDirectoryTable.directory })
|
|
118
|
+
.from(ProjectDirectoryTable)
|
|
119
|
+
.where(
|
|
120
|
+
and(
|
|
121
|
+
eq(ProjectDirectoryTable.project_id, input.projectID),
|
|
122
|
+
eq(ProjectDirectoryTable.directory, input.directory),
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
.get()
|
|
126
|
+
.pipe(Effect.orDie)) !== undefined
|
|
127
|
+
)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
const get = Effect.fn("ProjectDirectories.get")(function* (input: {
|
|
131
|
+
projectID: ProjectSchema.ID
|
|
132
|
+
directory: AbsolutePath
|
|
133
|
+
}) {
|
|
134
|
+
const row = yield* db
|
|
135
|
+
.select({ directory: ProjectDirectoryTable.directory, strategy: ProjectDirectoryTable.strategy })
|
|
136
|
+
.from(ProjectDirectoryTable)
|
|
137
|
+
.where(
|
|
138
|
+
and(
|
|
139
|
+
eq(ProjectDirectoryTable.project_id, input.projectID),
|
|
140
|
+
eq(ProjectDirectoryTable.directory, input.directory),
|
|
141
|
+
),
|
|
142
|
+
)
|
|
143
|
+
.get()
|
|
144
|
+
.pipe(Effect.orDie)
|
|
145
|
+
return row ? { directory: row.directory, strategy: row.strategy ?? undefined } : undefined
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
return Service.of({
|
|
149
|
+
list,
|
|
150
|
+
get,
|
|
151
|
+
contains,
|
|
152
|
+
create,
|
|
153
|
+
remove,
|
|
154
|
+
})
|
|
155
|
+
}),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
export const node = makeGlobalNode({ service: Service, layer: layer, deps: [Database.node] })
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export * as ProjectSchema from "./schema"
|
|
2
|
+
|
|
3
|
+
import { Schema } from "effect"
|
|
4
|
+
import { Project } from "@neurocode-ai/schema/project"
|
|
5
|
+
import { AbsolutePath } from "../schema"
|
|
6
|
+
|
|
7
|
+
export const ID = Project.ID
|
|
8
|
+
export type ID = typeof ID.Type
|
|
9
|
+
|
|
10
|
+
export const Vcs = Schema.Union([
|
|
11
|
+
Schema.Struct({
|
|
12
|
+
type: Schema.Literal("git"),
|
|
13
|
+
store: AbsolutePath,
|
|
14
|
+
}),
|
|
15
|
+
])
|
|
16
|
+
export type Vcs = typeof Vcs.Type
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { sqliteTable, text, integer, primaryKey } from "drizzle-orm/sqlite-core"
|
|
2
|
+
import * as DatabasePath from "../database/path"
|
|
3
|
+
import { Timestamps } from "../database/schema.sql"
|
|
4
|
+
import { ProjectSchema } from "./schema"
|
|
5
|
+
|
|
6
|
+
export const ProjectTable = sqliteTable("project", {
|
|
7
|
+
id: text().$type<ProjectSchema.ID>().primaryKey(),
|
|
8
|
+
worktree: DatabasePath.absoluteColumn().notNull(),
|
|
9
|
+
vcs: text(),
|
|
10
|
+
name: text(),
|
|
11
|
+
icon_url: text(),
|
|
12
|
+
icon_url_override: text(),
|
|
13
|
+
icon_color: text(),
|
|
14
|
+
...Timestamps,
|
|
15
|
+
time_initialized: integer(),
|
|
16
|
+
sandboxes: DatabasePath.absoluteArrayColumn().notNull(),
|
|
17
|
+
commands: text({ mode: "json" }).$type<{ start?: string }>(),
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
export const ProjectDirectoryTable = sqliteTable(
|
|
21
|
+
"project_directory",
|
|
22
|
+
{
|
|
23
|
+
project_id: text()
|
|
24
|
+
.$type<ProjectSchema.ID>()
|
|
25
|
+
.notNull()
|
|
26
|
+
.references(() => ProjectTable.id, { onDelete: "cascade" }),
|
|
27
|
+
directory: DatabasePath.absoluteColumn().notNull(),
|
|
28
|
+
type: text().$type<"main" | "root" | "git_worktree">(),
|
|
29
|
+
strategy: text(),
|
|
30
|
+
time_created: integer()
|
|
31
|
+
.notNull()
|
|
32
|
+
.$default(() => Date.now()),
|
|
33
|
+
},
|
|
34
|
+
(table) => [primaryKey({ columns: [table.project_id, table.directory] })],
|
|
35
|
+
)
|
package/src/project.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
export * as ProjectV2 from "./project"
|
|
2
|
+
export * as Project from "./project"
|
|
3
|
+
|
|
4
|
+
import { Context, Effect, Layer, Schema } from "effect"
|
|
5
|
+
import path from "path"
|
|
6
|
+
import { AbsolutePath } from "./schema"
|
|
7
|
+
import { FSUtil } from "./fs-util"
|
|
8
|
+
import { Git } from "./git"
|
|
9
|
+
import { makeGlobalNode } from "./effect/app-node"
|
|
10
|
+
import { Hash } from "./util/hash"
|
|
11
|
+
import { ProjectDirectories } from "./project/directories"
|
|
12
|
+
import { ProjectSchema } from "./project/schema"
|
|
13
|
+
|
|
14
|
+
export const ID = ProjectSchema.ID
|
|
15
|
+
export type ID = ProjectSchema.ID
|
|
16
|
+
|
|
17
|
+
export const Vcs = ProjectSchema.Vcs
|
|
18
|
+
export type Vcs = ProjectSchema.Vcs
|
|
19
|
+
|
|
20
|
+
export class Info extends Schema.Class<Info>("Project.Info")({
|
|
21
|
+
id: ID,
|
|
22
|
+
}) {}
|
|
23
|
+
|
|
24
|
+
export const DirectoriesInput = ProjectDirectories.ListInput
|
|
25
|
+
export type DirectoriesInput = typeof DirectoriesInput.Type
|
|
26
|
+
|
|
27
|
+
export const Directories = ProjectDirectories.ListOutput
|
|
28
|
+
export type Directories = typeof Directories.Type
|
|
29
|
+
|
|
30
|
+
export interface Resolved {
|
|
31
|
+
readonly previous?: ID
|
|
32
|
+
readonly id: ID
|
|
33
|
+
readonly directory: AbsolutePath
|
|
34
|
+
readonly vcs?: Vcs
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface Interface {
|
|
38
|
+
readonly directories: (input: DirectoriesInput) => Effect.Effect<Directories>
|
|
39
|
+
readonly resolve: (input: AbsolutePath) => Effect.Effect<Resolved>
|
|
40
|
+
/**
|
|
41
|
+
* Temporary bridge method for writing the resolved project ID to the repo-local cache.
|
|
42
|
+
*
|
|
43
|
+
* This exists while the old neurocode project service and this core project
|
|
44
|
+
* service work together: core resolves the ID, while the old service still owns
|
|
45
|
+
* database migration and persistence. The old service should call this after it
|
|
46
|
+
* finishes migrating from `resolve().previous` to `resolve().id`; once project
|
|
47
|
+
* persistence moves into core, this separate bridge method can go away.
|
|
48
|
+
*/
|
|
49
|
+
readonly commit: (input: { store: AbsolutePath; id: ID }) => Effect.Effect<void>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class Service extends Context.Service<Service, Interface>()("@neurocode/ProjectV2") {}
|
|
53
|
+
|
|
54
|
+
const layer = Layer.effect(
|
|
55
|
+
Service,
|
|
56
|
+
Effect.gen(function* () {
|
|
57
|
+
const fs = yield* FSUtil.Service
|
|
58
|
+
const git = yield* Git.Service
|
|
59
|
+
const projectDirectories = yield* ProjectDirectories.Service
|
|
60
|
+
|
|
61
|
+
const directories = Effect.fn("Project.directories")(function* (input: DirectoriesInput) {
|
|
62
|
+
return yield* projectDirectories.list(input.projectID)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const cached = Effect.fnUntraced(function* (dir: string) {
|
|
66
|
+
return yield* fs.readFileString(path.join(dir, "neurocode")).pipe(
|
|
67
|
+
Effect.map((value) => value.trim()),
|
|
68
|
+
Effect.map((value) => (value ? ID.make(value) : undefined)),
|
|
69
|
+
Effect.catch(() => Effect.succeed(undefined)),
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const remote = Effect.fnUntraced(function* (repo: Git.Repository) {
|
|
74
|
+
const origin = yield* git.remote.get(repo)
|
|
75
|
+
if (!origin) return undefined
|
|
76
|
+
const normalized = url(origin)
|
|
77
|
+
if (!normalized) return undefined
|
|
78
|
+
return ID.make(Hash.fast(`git-remote:${normalized}`))
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
function url(input: string) {
|
|
82
|
+
const value = input.trim()
|
|
83
|
+
if (!value) return undefined
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const parsed = new URL(value)
|
|
87
|
+
if (parsed.protocol === "file:") return undefined
|
|
88
|
+
return parts(parsed.hostname, parsed.pathname)
|
|
89
|
+
} catch {
|
|
90
|
+
const scp = value.match(/^([^@/:]+@)?([^/:]+):(.+)$/)
|
|
91
|
+
if (scp) return parts(scp[2], scp[3])
|
|
92
|
+
return undefined
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function parts(host: string, name: string) {
|
|
97
|
+
const pathname = name
|
|
98
|
+
.replace(/^\/+/, "")
|
|
99
|
+
.replace(/\.git\/?$/, "")
|
|
100
|
+
.replace(/\/+$/, "")
|
|
101
|
+
if (!host || !pathname) return undefined
|
|
102
|
+
return `${host.toLowerCase()}/${pathname}`
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const root = Effect.fnUntraced(function* (repo: Git.Repository) {
|
|
106
|
+
const root = (yield* git.history.rootCommits(repo))[0]
|
|
107
|
+
return root ? ID.make(root) : undefined
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const resolve = Effect.fn("Project.resolve")(function* (input: AbsolutePath) {
|
|
111
|
+
const repo = yield* git.repo.discover(input)
|
|
112
|
+
if (!repo) return { id: ID.global, directory: AbsolutePath.make(path.parse(input).root), vcs: undefined }
|
|
113
|
+
|
|
114
|
+
const previous = yield* cached(repo.commonDirectory)
|
|
115
|
+
const id = (yield* remote(repo)) ?? previous ?? (yield* root(repo))
|
|
116
|
+
return {
|
|
117
|
+
previous,
|
|
118
|
+
id: id ?? ID.global,
|
|
119
|
+
directory: repo.worktree,
|
|
120
|
+
vcs: { type: "git" as const, store: repo.commonDirectory },
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
const commit = Effect.fn("Project.commit")(function* (input: { store: AbsolutePath; id: ID }) {
|
|
125
|
+
yield* fs.writeFileString(path.join(input.store, "neurocode"), input.id).pipe(Effect.ignore)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
return Service.of({ directories, resolve, commit })
|
|
129
|
+
}),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
export const node = makeGlobalNode({
|
|
133
|
+
service: Service,
|
|
134
|
+
layer: layer,
|
|
135
|
+
deps: [FSUtil.node, Git.node, ProjectDirectories.node],
|
|
136
|
+
})
|
package/src/provider.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export * as ProviderV2 from "./provider"
|
|
2
|
+
|
|
3
|
+
import { Types } from "effect"
|
|
4
|
+
import { Provider } from "@neurocode-ai/schema/provider"
|
|
5
|
+
|
|
6
|
+
export const ID = Provider.ID
|
|
7
|
+
export type ID = typeof ID.Type
|
|
8
|
+
|
|
9
|
+
export const AISDK = Provider.AISDK
|
|
10
|
+
|
|
11
|
+
export const Native = Provider.Native
|
|
12
|
+
|
|
13
|
+
export const Api = Provider.Api
|
|
14
|
+
export type Api = Provider.Api
|
|
15
|
+
export type MutableApi<T extends Api = Api> = T extends Api
|
|
16
|
+
? Omit<Types.DeepMutable<T>, "settings"> & (undefined extends T["settings"] ? { settings?: any } : { settings: any })
|
|
17
|
+
: never
|
|
18
|
+
|
|
19
|
+
export const Request = Provider.Request
|
|
20
|
+
export type Request = Provider.Request
|
|
21
|
+
|
|
22
|
+
export const Info = Provider.Info
|
|
23
|
+
export type Info = Provider.Info
|
|
24
|
+
|
|
25
|
+
export type MutableInfo = Omit<Types.DeepMutable<Info>, "api"> & { api: MutableApi }
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export * as PtyProtocol from "./protocol"
|
|
2
|
+
|
|
3
|
+
// Wire protocol for PTY websocket transports. The PTY domain service is transport-free; server
|
|
4
|
+
// routes adapt Pty.attach to websockets with these helpers so every surface speaks one protocol.
|
|
5
|
+
//
|
|
6
|
+
// Outbound frames are raw UTF-8 terminal chunks. One control frame — a 0x00 byte followed by
|
|
7
|
+
// UTF-8 JSON — carries the absolute output cursor after replay so clients can resume later.
|
|
8
|
+
|
|
9
|
+
const encoder = new TextEncoder()
|
|
10
|
+
const decoder = new TextDecoder("utf-8", { fatal: true })
|
|
11
|
+
|
|
12
|
+
// Replay can be megabytes; send it in bounded frames.
|
|
13
|
+
export const REPLAY_CHUNK = 64 * 1024
|
|
14
|
+
|
|
15
|
+
export function metaFrame(cursor: number) {
|
|
16
|
+
const bytes = encoder.encode(JSON.stringify({ cursor }))
|
|
17
|
+
const out = new Uint8Array(bytes.length + 1)
|
|
18
|
+
out[0] = 0
|
|
19
|
+
out.set(bytes, 1)
|
|
20
|
+
return out
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function chunks(data: string) {
|
|
24
|
+
const out: string[] = []
|
|
25
|
+
for (let i = 0; i < data.length; i += REPLAY_CHUNK) out.push(data.slice(i, i + REPLAY_CHUNK))
|
|
26
|
+
return out
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Inbound client frames are UTF-8 text or binary; invalid UTF-8 input is dropped.
|
|
30
|
+
export function decodeInput(message: string | Uint8Array | ArrayBuffer) {
|
|
31
|
+
if (typeof message === "string") return message
|
|
32
|
+
try {
|
|
33
|
+
return decoder.decode(message instanceof ArrayBuffer ? new Uint8Array(message) : message)
|
|
34
|
+
} catch {
|
|
35
|
+
return undefined
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { spawn as create } from "bun-pty"
|
|
2
|
+
import type { Opts, Proc } from "./pty"
|
|
3
|
+
|
|
4
|
+
export type { Disp, Exit, Opts, Proc } from "./pty"
|
|
5
|
+
|
|
6
|
+
export function spawn(file: string, args: string[], opts: Opts): Proc {
|
|
7
|
+
const pty = create(file, args, opts)
|
|
8
|
+
return {
|
|
9
|
+
pid: pty.pid,
|
|
10
|
+
onData(listener) {
|
|
11
|
+
return pty.onData(listener)
|
|
12
|
+
},
|
|
13
|
+
onExit(listener) {
|
|
14
|
+
return pty.onExit(listener)
|
|
15
|
+
},
|
|
16
|
+
write(data) {
|
|
17
|
+
pty.write(data)
|
|
18
|
+
},
|
|
19
|
+
resize(cols, rows) {
|
|
20
|
+
pty.resize(cols, rows)
|
|
21
|
+
},
|
|
22
|
+
kill(signal) {
|
|
23
|
+
pty.kill(signal)
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as pty from "@lydell/node-pty"
|
|
2
|
+
import type { Opts, Proc } from "./pty"
|
|
3
|
+
|
|
4
|
+
export type { Disp, Exit, Opts, Proc } from "./pty"
|
|
5
|
+
|
|
6
|
+
export function spawn(file: string, args: string[], opts: Opts): Proc {
|
|
7
|
+
const proc = pty.spawn(file, args, {
|
|
8
|
+
...opts,
|
|
9
|
+
...(process.platform === "win32" ? { useConptyDll: true } : {}),
|
|
10
|
+
})
|
|
11
|
+
return {
|
|
12
|
+
pid: proc.pid,
|
|
13
|
+
onData(listener) {
|
|
14
|
+
return proc.onData(listener)
|
|
15
|
+
},
|
|
16
|
+
onExit(listener) {
|
|
17
|
+
return proc.onExit(listener)
|
|
18
|
+
},
|
|
19
|
+
write(data) {
|
|
20
|
+
proc.write(data)
|
|
21
|
+
},
|
|
22
|
+
resize(cols, rows) {
|
|
23
|
+
proc.resize(cols, rows)
|
|
24
|
+
},
|
|
25
|
+
kill(signal) {
|
|
26
|
+
proc.kill(signal)
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/pty/pty.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type Disp = {
|
|
2
|
+
dispose(): void
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export type Exit = {
|
|
6
|
+
exitCode: number
|
|
7
|
+
signal?: number | string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type Opts = {
|
|
11
|
+
name: string
|
|
12
|
+
cols?: number
|
|
13
|
+
rows?: number
|
|
14
|
+
cwd?: string
|
|
15
|
+
env?: Record<string, string>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type Proc = {
|
|
19
|
+
pid: number
|
|
20
|
+
onData(listener: (data: string) => void): Disp
|
|
21
|
+
onExit(listener: (event: Exit) => void): Disp
|
|
22
|
+
write(data: string): void
|
|
23
|
+
resize(cols: number, rows: number): void
|
|
24
|
+
kill(signal?: string): void
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ID as PtyID } from "@neurocode-ai/schema/pty"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export * as PtyTicket from "./ticket"
|
|
2
|
+
|
|
3
|
+
import { WorkspaceV2 } from "../workspace"
|
|
4
|
+
import { PtyTicket } from "@neurocode-ai/schema/pty-ticket"
|
|
5
|
+
import { PtyID } from "./schema"
|
|
6
|
+
import { Cache, Context, Duration, Effect, Layer } from "effect"
|
|
7
|
+
import { makeGlobalNode } from "../effect/app-node"
|
|
8
|
+
|
|
9
|
+
const DEFAULT_TTL = Duration.seconds(60)
|
|
10
|
+
const CAPACITY = 10_000
|
|
11
|
+
|
|
12
|
+
export const ConnectToken = PtyTicket.ConnectToken
|
|
13
|
+
|
|
14
|
+
export type Scope = {
|
|
15
|
+
readonly ptyID: PtyID
|
|
16
|
+
readonly directory?: string
|
|
17
|
+
readonly workspaceID?: WorkspaceV2.ID
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Interface {
|
|
21
|
+
issue(input: Scope): Effect.Effect<typeof ConnectToken.Type>
|
|
22
|
+
consume(input: Scope & { readonly ticket: string }): Effect.Effect<boolean>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class Service extends Context.Service<Service, Interface>()("@neurocode/PtyTicket") {}
|
|
26
|
+
|
|
27
|
+
function matches(record: Scope, input: Scope) {
|
|
28
|
+
return (
|
|
29
|
+
record.ptyID === input.ptyID && record.directory === input.directory && record.workspaceID === input.workspaceID
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Tickets are inserted via Cache.set and removed atomically via invalidateWhen. The lookup is
|
|
34
|
+
// never invoked; it dies if it ever is, which would signal a misuse of the Service interface.
|
|
35
|
+
const noLookup = () => Effect.die("PtyTicket cache must be used via set/invalidateWhen, never get")
|
|
36
|
+
|
|
37
|
+
// Visible for tests so the TTL can be shortened. Production uses `layer` with the default TTL.
|
|
38
|
+
export const make = (ttl: Duration.Input = DEFAULT_TTL) =>
|
|
39
|
+
Effect.gen(function* () {
|
|
40
|
+
const cache = yield* Cache.make<string, Scope>({ capacity: CAPACITY, lookup: noLookup, timeToLive: ttl })
|
|
41
|
+
const expiresIn = Math.max(1, Math.round(Duration.toSeconds(Duration.fromInputUnsafe(ttl))))
|
|
42
|
+
return Service.of({
|
|
43
|
+
issue: Effect.fn("PtyTicket.issue")(function* (input) {
|
|
44
|
+
const ticket = crypto.randomUUID()
|
|
45
|
+
yield* Cache.set(cache, ticket, input)
|
|
46
|
+
return { ticket, expires_in: expiresIn }
|
|
47
|
+
}),
|
|
48
|
+
consume: Effect.fn("PtyTicket.consume")(function* (input) {
|
|
49
|
+
return yield* Cache.invalidateWhen(cache, input.ticket, (stored) => matches(stored, input))
|
|
50
|
+
}),
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const layer = Layer.effect(Service, make())
|
|
55
|
+
|
|
56
|
+
export const node = makeGlobalNode({ service: Service, layer: layer, deps: [] })
|