@stigmer/cli 3.35.0 → 3.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/auth/auth0.d.ts.map +1 -1
- package/auth/auth0.js.map +1 -1
- package/auth/callback-server.d.ts.map +1 -1
- package/auth/callback-server.js.map +1 -1
- package/auth/index.d.ts.map +1 -1
- package/auth/index.js.map +1 -1
- package/auth/login.d.ts.map +1 -1
- package/auth/login.js.map +1 -1
- package/auth/pages.d.ts.map +1 -1
- package/auth/pages.js.map +1 -1
- package/auth/pkce.d.ts.map +1 -1
- package/auth/pkce.js.map +1 -1
- package/auth/token.d.ts.map +1 -1
- package/auth/token.js.map +1 -1
- package/backend.d.ts.map +1 -1
- package/backend.js.map +1 -1
- package/cli/stigmer.d.ts.map +1 -1
- package/cli/stigmer.js.map +1 -1
- package/client/client.d.ts.map +1 -1
- package/client/client.js.map +1 -1
- package/client/index.d.ts.map +1 -1
- package/client/index.js.map +1 -1
- package/commands/agent-exec-flags.d.ts.map +1 -1
- package/commands/agent-exec-flags.js.map +1 -1
- package/commands/apikey/duration.d.ts.map +1 -1
- package/commands/apikey/duration.js.map +1 -1
- package/commands/apikey/index.d.ts.map +1 -1
- package/commands/apikey/index.js.map +1 -1
- package/commands/apply.d.ts.map +1 -1
- package/commands/apply.js.map +1 -1
- package/commands/auth/index.d.ts.map +1 -1
- package/commands/auth/index.js.map +1 -1
- package/commands/auth/login.d.ts.map +1 -1
- package/commands/auth/login.js.map +1 -1
- package/commands/auth/whoami.d.ts.map +1 -1
- package/commands/auth/whoami.js.map +1 -1
- package/commands/bootstrap.d.ts.map +1 -1
- package/commands/bootstrap.js.map +1 -1
- package/commands/completion.d.ts.map +1 -1
- package/commands/completion.js.map +1 -1
- package/commands/config/backend.d.ts.map +1 -1
- package/commands/config/backend.js.map +1 -1
- package/commands/config/context.d.ts.map +1 -1
- package/commands/config/context.js.map +1 -1
- package/commands/config/index.d.ts.map +1 -1
- package/commands/config/index.js.map +1 -1
- package/commands/config/kv.d.ts.map +1 -1
- package/commands/config/kv.js.map +1 -1
- package/commands/connect.d.ts.map +1 -1
- package/commands/connect.js.map +1 -1
- package/commands/delete.d.ts.map +1 -1
- package/commands/delete.js.map +1 -1
- package/commands/diff.d.ts.map +1 -1
- package/commands/diff.js.map +1 -1
- package/commands/down.d.ts.map +1 -1
- package/commands/down.js.map +1 -1
- package/commands/download.d.ts.map +1 -1
- package/commands/download.js.map +1 -1
- package/commands/execution/approve.d.ts.map +1 -1
- package/commands/execution/approve.js.map +1 -1
- package/commands/execution/control.d.ts.map +1 -1
- package/commands/execution/control.js.map +1 -1
- package/commands/execution/index.d.ts.map +1 -1
- package/commands/execution/index.js.map +1 -1
- package/commands/execution/logs.d.ts.map +1 -1
- package/commands/execution/logs.js.map +1 -1
- package/commands/execution/trace.d.ts.map +1 -1
- package/commands/execution/trace.js.map +1 -1
- package/commands/get.d.ts.map +1 -1
- package/commands/get.js.map +1 -1
- package/commands/install.d.ts.map +1 -1
- package/commands/install.js.map +1 -1
- package/commands/internal-daemon.d.ts.map +1 -1
- package/commands/internal-daemon.js.map +1 -1
- package/commands/list.d.ts.map +1 -1
- package/commands/list.js.map +1 -1
- package/commands/logs.d.ts.map +1 -1
- package/commands/logs.js.map +1 -1
- package/commands/marketplace.d.ts.map +1 -1
- package/commands/marketplace.js.map +1 -1
- package/commands/mcp-server.d.ts.map +1 -1
- package/commands/mcp-server.js.map +1 -1
- package/commands/push.d.ts.map +1 -1
- package/commands/push.js.map +1 -1
- package/commands/reset.d.ts.map +1 -1
- package/commands/reset.js.map +1 -1
- package/commands/resume.d.ts.map +1 -1
- package/commands/resume.js.map +1 -1
- package/commands/run.d.ts.map +1 -1
- package/commands/run.js.map +1 -1
- package/commands/schedule.d.ts.map +1 -1
- package/commands/schedule.js.map +1 -1
- package/commands/search.d.ts.map +1 -1
- package/commands/search.js.map +1 -1
- package/commands/setup.d.ts.map +1 -1
- package/commands/setup.js.map +1 -1
- package/commands/share.d.ts.map +1 -1
- package/commands/share.js.map +1 -1
- package/commands/shared.d.ts.map +1 -1
- package/commands/shared.js.map +1 -1
- package/commands/status.d.ts.map +1 -1
- package/commands/status.js.map +1 -1
- package/commands/tag.d.ts.map +1 -1
- package/commands/tag.js.map +1 -1
- package/commands/up.d.ts.map +1 -1
- package/commands/up.js.map +1 -1
- package/commands/usage.d.ts.map +1 -1
- package/commands/usage.js.map +1 -1
- package/commands/validate.d.ts.map +1 -1
- package/commands/validate.js.map +1 -1
- package/commands/version.d.ts.map +1 -1
- package/commands/version.js.map +1 -1
- package/config/config.d.ts.map +1 -1
- package/config/config.js.map +1 -1
- package/config/index.d.ts.map +1 -1
- package/config/index.js.map +1 -1
- package/config/keys.d.ts.map +1 -1
- package/config/keys.js.map +1 -1
- package/config/paths.d.ts.map +1 -1
- package/config/paths.js.map +1 -1
- package/config/resolve.d.ts.map +1 -1
- package/config/resolve.js.map +1 -1
- package/errors/classify.d.ts.map +1 -1
- package/errors/classify.js.map +1 -1
- package/errors/cli-exit-error.d.ts.map +1 -1
- package/errors/cli-exit-error.js.map +1 -1
- package/errors/exit-codes.d.ts.map +1 -1
- package/errors/exit-codes.js.map +1 -1
- package/errors/handle.d.ts.map +1 -1
- package/errors/handle.js.map +1 -1
- package/errors/index.d.ts.map +1 -1
- package/errors/index.js.map +1 -1
- package/errors/usage-error.d.ts.map +1 -1
- package/errors/usage-error.js.map +1 -1
- package/index.d.ts.map +1 -1
- package/index.js.map +1 -1
- package/local/artifact.d.ts.map +1 -1
- package/local/artifact.js.map +1 -1
- package/local/bootstrap.d.ts.map +1 -1
- package/local/bootstrap.js.map +1 -1
- package/local/constants.d.ts.map +1 -1
- package/local/constants.js.map +1 -1
- package/local/daemon/components.d.ts.map +1 -1
- package/local/daemon/components.js.map +1 -1
- package/local/daemon/env.d.ts.map +1 -1
- package/local/daemon/env.js.map +1 -1
- package/local/daemon/host.d.ts.map +1 -1
- package/local/daemon/host.js.map +1 -1
- package/local/daemon/index.d.ts.map +1 -1
- package/local/daemon/index.js.map +1 -1
- package/local/daemon/launch.d.ts.map +1 -1
- package/local/daemon/launch.js.map +1 -1
- package/local/daemon/process.d.ts.map +1 -1
- package/local/daemon/process.js.map +1 -1
- package/local/daemon/supervisor.d.ts.map +1 -1
- package/local/daemon/supervisor.js.map +1 -1
- package/local/daemon/types.d.ts.map +1 -1
- package/local/daemon/types.js.map +1 -1
- package/local/llm-config.d.ts.map +1 -1
- package/local/llm-config.js.map +1 -1
- package/local/logs/tail.d.ts.map +1 -1
- package/local/logs/tail.js.map +1 -1
- package/local/logs/view.d.ts.map +1 -1
- package/local/logs/view.js.map +1 -1
- package/local/net/tcp.d.ts.map +1 -1
- package/local/net/tcp.js.map +1 -1
- package/local/operator-config.d.ts.map +1 -1
- package/local/operator-config.js.map +1 -1
- package/local/paths.d.ts.map +1 -1
- package/local/paths.js.map +1 -1
- package/local/reset.d.ts.map +1 -1
- package/local/reset.js.map +1 -1
- package/local/runtime/index.d.ts.map +1 -1
- package/local/runtime/index.js.map +1 -1
- package/local/runtime/node.d.ts.map +1 -1
- package/local/runtime/node.js.map +1 -1
- package/local/runtime/runner.d.ts.map +1 -1
- package/local/runtime/runner.js.map +1 -1
- package/local/runtime/runtimes-install.d.ts.map +1 -1
- package/local/runtime/runtimes-install.js.map +1 -1
- package/local/runtime/server.d.ts.map +1 -1
- package/local/runtime/server.js.map +1 -1
- package/local/runtime/which.d.ts.map +1 -1
- package/local/runtime/which.js.map +1 -1
- package/local/setup/prompt.d.ts.map +1 -1
- package/local/setup/prompt.js.map +1 -1
- package/local/setup/wizard.d.ts.map +1 -1
- package/local/setup/wizard.js.map +1 -1
- package/local/state/health-state.d.ts.map +1 -1
- package/local/state/health-state.js.map +1 -1
- package/local/state/index.d.ts.map +1 -1
- package/local/state/index.js.map +1 -1
- package/local/state/lock.d.ts.map +1 -1
- package/local/state/lock.js.map +1 -1
- package/local/state/log-rotation.d.ts.map +1 -1
- package/local/state/log-rotation.js.map +1 -1
- package/local/state/pidfile.d.ts.map +1 -1
- package/local/state/pidfile.js.map +1 -1
- package/local/state/proc.d.ts.map +1 -1
- package/local/state/proc.js.map +1 -1
- package/local/state/startup-config.d.ts.map +1 -1
- package/local/state/startup-config.js.map +1 -1
- package/local/status.d.ts.map +1 -1
- package/local/status.js.map +1 -1
- package/local/temporal/download.d.ts.map +1 -1
- package/local/temporal/download.js.map +1 -1
- package/local/temporal/index.d.ts.map +1 -1
- package/local/temporal/index.js.map +1 -1
- package/local/temporal/inspect.d.ts.map +1 -1
- package/local/temporal/inspect.js.map +1 -1
- package/local/temporal/manager.d.ts.map +1 -1
- package/local/temporal/manager.js.map +1 -1
- package/local/temporal/supervisor.d.ts.map +1 -1
- package/local/temporal/supervisor.js.map +1 -1
- package/local/webconsole/index.d.ts.map +1 -1
- package/local/webconsole/index.js.map +1 -1
- package/logger.d.ts.map +1 -1
- package/logger.js.map +1 -1
- package/marketplace/config.d.ts.map +1 -1
- package/marketplace/config.js.map +1 -1
- package/marketplace/github.d.ts.map +1 -1
- package/marketplace/github.js.map +1 -1
- package/marketplace/index.d.ts.map +1 -1
- package/marketplace/index.js.map +1 -1
- package/marketplace/install.d.ts.map +1 -1
- package/marketplace/install.js.map +1 -1
- package/marketplace/materialize.d.ts.map +1 -1
- package/marketplace/materialize.js.map +1 -1
- package/marketplace/official.d.ts.map +1 -1
- package/marketplace/official.js.map +1 -1
- package/marketplace/read.d.ts.map +1 -1
- package/marketplace/read.js.map +1 -1
- package/marketplace/ref.d.ts.map +1 -1
- package/marketplace/ref.js.map +1 -1
- package/marketplace/render.d.ts.map +1 -1
- package/marketplace/render.js.map +1 -1
- package/output/command-result.d.ts.map +1 -1
- package/output/command-result.js.map +1 -1
- package/output/confirm.d.ts.map +1 -1
- package/output/confirm.js.map +1 -1
- package/output/format.d.ts.map +1 -1
- package/output/format.js.map +1 -1
- package/output/index.d.ts.map +1 -1
- package/output/index.js.map +1 -1
- package/output/ndjson.d.ts.map +1 -1
- package/output/ndjson.js.map +1 -1
- package/output/proto.d.ts.map +1 -1
- package/output/proto.js.map +1 -1
- package/output/style.d.ts.map +1 -1
- package/output/style.js.map +1 -1
- package/output/table.d.ts.map +1 -1
- package/output/table.js.map +1 -1
- package/package.json +7 -7
- package/program.d.ts.map +1 -1
- package/program.js.map +1 -1
- package/registry/aliases.d.ts.map +1 -1
- package/registry/aliases.js.map +1 -1
- package/registry/index.d.ts.map +1 -1
- package/registry/index.js.map +1 -1
- package/registry/metadata.d.ts.map +1 -1
- package/registry/metadata.js.map +1 -1
- package/registry/registry.d.ts.map +1 -1
- package/registry/registry.js.map +1 -1
- package/registry/verb-support.d.ts.map +1 -1
- package/registry/verb-support.js.map +1 -1
- package/registry/verbs.d.ts.map +1 -1
- package/registry/verbs.js.map +1 -1
- package/resources/apply/apply.d.ts.map +1 -1
- package/resources/apply/apply.js.map +1 -1
- package/resources/apply/discovery.d.ts.map +1 -1
- package/resources/apply/discovery.js.map +1 -1
- package/resources/apply/handlers.d.ts.map +1 -1
- package/resources/apply/handlers.js.map +1 -1
- package/resources/connect/connect.d.ts.map +1 -1
- package/resources/connect/connect.js.map +1 -1
- package/resources/connect/discover.d.ts.map +1 -1
- package/resources/connect/discover.js.map +1 -1
- package/resources/connect/display.d.ts.map +1 -1
- package/resources/connect/display.js.map +1 -1
- package/resources/connect/oauth.d.ts.map +1 -1
- package/resources/connect/oauth.js.map +1 -1
- package/resources/cursor-pages.d.ts.map +1 -1
- package/resources/cursor-pages.js.map +1 -1
- package/resources/delete.d.ts.map +1 -1
- package/resources/delete.js.map +1 -1
- package/resources/diff.d.ts.map +1 -1
- package/resources/diff.js.map +1 -1
- package/resources/documents.d.ts.map +1 -1
- package/resources/documents.js.map +1 -1
- package/resources/download.d.ts.map +1 -1
- package/resources/download.js.map +1 -1
- package/resources/execution-approve.d.ts.map +1 -1
- package/resources/execution-approve.js.map +1 -1
- package/resources/execution-control.d.ts.map +1 -1
- package/resources/execution-control.js.map +1 -1
- package/resources/execution-format.d.ts.map +1 -1
- package/resources/execution-format.js.map +1 -1
- package/resources/execution-logs.d.ts.map +1 -1
- package/resources/execution-logs.js.map +1 -1
- package/resources/execution-trace.d.ts.map +1 -1
- package/resources/execution-trace.js.map +1 -1
- package/resources/execution.d.ts.map +1 -1
- package/resources/execution.js.map +1 -1
- package/resources/get-bindings.d.ts.map +1 -1
- package/resources/get-bindings.js.map +1 -1
- package/resources/get.d.ts.map +1 -1
- package/resources/get.js.map +1 -1
- package/resources/git.d.ts.map +1 -1
- package/resources/git.js.map +1 -1
- package/resources/ignore/index.d.ts.map +1 -1
- package/resources/ignore/index.js.map +1 -1
- package/resources/ignore/matcher.d.ts.map +1 -1
- package/resources/ignore/matcher.js.map +1 -1
- package/resources/list.d.ts.map +1 -1
- package/resources/list.js.map +1 -1
- package/resources/mcp/placeholder-resolver.d.ts.map +1 -1
- package/resources/mcp/placeholder-resolver.js.map +1 -1
- package/resources/mcp/runtime-env.d.ts.map +1 -1
- package/resources/mcp/runtime-env.js.map +1 -1
- package/resources/picker/ink.d.ts.map +1 -1
- package/resources/picker/ink.js.map +1 -1
- package/resources/picker/tty.d.ts.map +1 -1
- package/resources/picker/tty.js.map +1 -1
- package/resources/plugin.d.ts.map +1 -1
- package/resources/plugin.js.map +1 -1
- package/resources/reference.d.ts.map +1 -1
- package/resources/reference.js.map +1 -1
- package/resources/render.d.ts.map +1 -1
- package/resources/render.js.map +1 -1
- package/resources/run/agent-exec.d.ts.map +1 -1
- package/resources/run/agent-exec.js.map +1 -1
- package/resources/run/attachments.d.ts.map +1 -1
- package/resources/run/attachments.js.map +1 -1
- package/resources/run/create.d.ts.map +1 -1
- package/resources/run/create.js.map +1 -1
- package/resources/run/env.d.ts.map +1 -1
- package/resources/run/env.js.map +1 -1
- package/resources/run/epilogue.d.ts.map +1 -1
- package/resources/run/epilogue.js.map +1 -1
- package/resources/run/header.d.ts.map +1 -1
- package/resources/run/header.js.map +1 -1
- package/resources/run/prepare.d.ts.map +1 -1
- package/resources/run/prepare.js.map +1 -1
- package/resources/run/resolve.d.ts.map +1 -1
- package/resources/run/resolve.js.map +1 -1
- package/resources/run/resume.d.ts.map +1 -1
- package/resources/run/resume.js.map +1 -1
- package/resources/run/stream.d.ts.map +1 -1
- package/resources/run/stream.js.map +1 -1
- package/resources/run/workflow-stream.d.ts.map +1 -1
- package/resources/run/workflow-stream.js.map +1 -1
- package/resources/run/workspace.d.ts.map +1 -1
- package/resources/run/workspace.js.map +1 -1
- package/resources/schedule.d.ts.map +1 -1
- package/resources/schedule.js.map +1 -1
- package/resources/search.d.ts.map +1 -1
- package/resources/search.js.map +1 -1
- package/resources/session.d.ts.map +1 -1
- package/resources/session.js.map +1 -1
- package/resources/share.d.ts.map +1 -1
- package/resources/share.js.map +1 -1
- package/resources/skill.d.ts.map +1 -1
- package/resources/skill.js.map +1 -1
- package/resources/stream/approval-detect.d.ts.map +1 -1
- package/resources/stream/approval-detect.js.map +1 -1
- package/resources/stream/convert.d.ts.map +1 -1
- package/resources/stream/convert.js.map +1 -1
- package/resources/stream/diff.d.ts.map +1 -1
- package/resources/stream/diff.js.map +1 -1
- package/resources/stream/events.d.ts.map +1 -1
- package/resources/stream/events.js.map +1 -1
- package/resources/stream/headless.d.ts.map +1 -1
- package/resources/stream/headless.js.map +1 -1
- package/resources/stream/ink.d.ts.map +1 -1
- package/resources/stream/ink.js.map +1 -1
- package/resources/stream/render-ndjson.d.ts.map +1 -1
- package/resources/stream/render-ndjson.js.map +1 -1
- package/resources/stream/render-plaintext.d.ts.map +1 -1
- package/resources/stream/render-plaintext.js.map +1 -1
- package/resources/stream/snapshot.d.ts.map +1 -1
- package/resources/stream/snapshot.js.map +1 -1
- package/resources/stream/subagent.d.ts.map +1 -1
- package/resources/stream/subagent.js.map +1 -1
- package/resources/stream/submit.d.ts.map +1 -1
- package/resources/stream/submit.js.map +1 -1
- package/resources/stream/todo.d.ts.map +1 -1
- package/resources/stream/todo.js.map +1 -1
- package/resources/stream/tool-state.d.ts.map +1 -1
- package/resources/stream/tool-state.js.map +1 -1
- package/resources/stream/tty.d.ts.map +1 -1
- package/resources/stream/tty.js.map +1 -1
- package/resources/stream/workflow-event-view.d.ts.map +1 -1
- package/resources/stream/workflow-event-view.js.map +1 -1
- package/resources/stream/workflow-render-ndjson.d.ts.map +1 -1
- package/resources/stream/workflow-render-ndjson.js.map +1 -1
- package/resources/stream/workflow-render-plaintext.d.ts.map +1 -1
- package/resources/stream/workflow-render-plaintext.js.map +1 -1
- package/resources/tag.d.ts.map +1 -1
- package/resources/tag.js.map +1 -1
- package/resources/task-configs.d.ts.map +1 -1
- package/resources/task-configs.js.map +1 -1
- package/resources/usage.d.ts.map +1 -1
- package/resources/usage.js.map +1 -1
- package/resources/validate.d.ts.map +1 -1
- package/resources/validate.js.map +1 -1
- package/resources/version.d.ts.map +1 -1
- package/resources/version.js.map +1 -1
- package/runtime.d.ts.map +1 -1
- package/runtime.js.map +1 -1
- package/version.d.ts.map +1 -1
- package/version.js.map +1 -1
- package/src/auth/pkce.test.ts +0 -37
- package/src/auth/token.test.ts +0 -151
- package/src/client/client.test.ts +0 -142
- package/src/commands/apikey/duration.test.ts +0 -23
- package/src/commands/auth/login.test.ts +0 -52
- package/src/commands/auth/whoami.test.ts +0 -96
- package/src/commands/bootstrap.test.ts +0 -178
- package/src/commands/config/backend.test.ts +0 -122
- package/src/commands/config/context.test.ts +0 -171
- package/src/commands/connect.test.ts +0 -113
- package/src/commands/install.test.ts +0 -307
- package/src/commands/marketplace.test.ts +0 -205
- package/src/commands/mcp-server.test.ts +0 -65
- package/src/commands/validate.test.ts +0 -168
- package/src/config/config.test.ts +0 -132
- package/src/config/keys.test.ts +0 -61
- package/src/config/resolve.test.ts +0 -242
- package/src/errors/clierr.test.ts +0 -172
- package/src/local/artifact.test.ts +0 -54
- package/src/local/bootstrap.test.ts +0 -166
- package/src/local/daemon/components.test.ts +0 -148
- package/src/local/daemon/daemon.integration.test.ts +0 -216
- package/src/local/daemon/env.test.ts +0 -108
- package/src/local/daemon/launch.test.ts +0 -136
- package/src/local/daemon/supervisor.test.ts +0 -260
- package/src/local/llm-config.test.ts +0 -66
- package/src/local/logs/tail.test.ts +0 -70
- package/src/local/logs/view.test.ts +0 -46
- package/src/local/operator-config.test.ts +0 -72
- package/src/local/reset.test.ts +0 -53
- package/src/local/runtime/runtime.test.ts +0 -240
- package/src/local/runtime/runtimes-install.test.ts +0 -58
- package/src/local/runtime/server.test.ts +0 -253
- package/src/local/setup/wizard.test.ts +0 -94
- package/src/local/state/state.test.ts +0 -210
- package/src/local/status.test.ts +0 -108
- package/src/local/temporal/download.test.ts +0 -248
- package/src/local/temporal/inspect.test.ts +0 -83
- package/src/local/temporal/manager.test.ts +0 -64
- package/src/local/temporal/supervisor.test.ts +0 -77
- package/src/marketplace/__fixtures__/cursor-marketplace.ts +0 -69
- package/src/marketplace/config.test.ts +0 -254
- package/src/marketplace/github.test.ts +0 -394
- package/src/marketplace/install.test.ts +0 -261
- package/src/marketplace/official.test.ts +0 -107
- package/src/marketplace/read.test.ts +0 -93
- package/src/marketplace/ref.test.ts +0 -179
- package/src/output/command-result.test.ts +0 -73
- package/src/output/confirm.test.ts +0 -43
- package/src/output/format.test.ts +0 -65
- package/src/output/proto.test.ts +0 -72
- package/src/output/table.test.ts +0 -37
- package/src/program.test.ts +0 -62
- package/src/registry/aliases.test.ts +0 -142
- package/src/registry/registry.test.ts +0 -355
- package/src/resources/apply/apply.integration.test.ts +0 -353
- package/src/resources/apply/apply.test.ts +0 -134
- package/src/resources/apply/discovery.integration.test.ts +0 -99
- package/src/resources/connect/__fixtures__/stdio-crash.mjs +0 -6
- package/src/resources/connect/__fixtures__/stdio-server.mjs +0 -48
- package/src/resources/connect/connect.integration.test.ts +0 -383
- package/src/resources/connect/discover.test.ts +0 -84
- package/src/resources/connect/oauth.test.ts +0 -158
- package/src/resources/cursor-pages.test.ts +0 -55
- package/src/resources/diff.integration.test.ts +0 -103
- package/src/resources/diff.test.ts +0 -34
- package/src/resources/documents.test.ts +0 -67
- package/src/resources/execution-approve.test.ts +0 -50
- package/src/resources/execution-format.test.ts +0 -48
- package/src/resources/execution.integration.test.ts +0 -285
- package/src/resources/execution.test.ts +0 -138
- package/src/resources/ignore/ignore.test.ts +0 -62
- package/src/resources/mcp/placeholder-resolver.test.ts +0 -71
- package/src/resources/mcp/runtime-env.test.ts +0 -102
- package/src/resources/mutate-verbs.integration.test.ts +0 -455
- package/src/resources/picker/tty.test.ts +0 -37
- package/src/resources/plugin.test.ts +0 -379
- package/src/resources/push-download.integration.test.ts +0 -175
- package/src/resources/read-completion.integration.test.ts +0 -164
- package/src/resources/read-verbs.integration.test.ts +0 -603
- package/src/resources/reference.test.ts +0 -107
- package/src/resources/run/agent-exec.test.ts +0 -179
- package/src/resources/run/attachments.test.ts +0 -135
- package/src/resources/run/create.test.ts +0 -336
- package/src/resources/run/env.test.ts +0 -131
- package/src/resources/run/header.test.ts +0 -79
- package/src/resources/run/prepare.test.ts +0 -428
- package/src/resources/run/workflow-stream.integration.test.ts +0 -157
- package/src/resources/run/workspace.test.ts +0 -93
- package/src/resources/share.test.ts +0 -523
- package/src/resources/skill.test.ts +0 -364
- package/src/resources/stream/diff.test.ts +0 -338
- package/src/resources/stream/headless.test.ts +0 -119
- package/src/resources/stream/lazy-import.test.ts +0 -63
- package/src/resources/stream/render-ndjson.test.ts +0 -126
- package/src/resources/stream/render-plaintext.test.ts +0 -127
- package/src/resources/stream/snapshot.test.ts +0 -111
- package/src/resources/stream/submit.test.ts +0 -77
- package/src/resources/stream/workflow-event-view.test.ts +0 -116
- package/src/resources/stream/workflow-render.test.ts +0 -81
- package/src/resources/task-configs.test.ts +0 -98
- package/src/resources/usage.test.ts +0 -94
- package/src/resources/validate.test.ts +0 -79
|
@@ -1,394 +0,0 @@
|
|
|
1
|
-
// Pins the GitHub source over zipballs built in-test: a good tree lands with
|
|
2
|
-
// its top directory stripped; every cap refuses naming itself, from the
|
|
3
|
-
// central directory before anything inflates; an escaping path, a second
|
|
4
|
-
// top-level directory and a lying declared size refuse; 404 and a network
|
|
5
|
-
// failure carry their own exit codes. The peek reads one raw file, the first
|
|
6
|
-
// of the four locations that exists, as declared; a repository with none is
|
|
7
|
-
// not a marketplace, a refused file travels with its sentences, and a
|
|
8
|
-
// network failure carries its exit code.
|
|
9
|
-
|
|
10
|
-
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
11
|
-
import { tmpdir } from "node:os";
|
|
12
|
-
import { join } from "node:path";
|
|
13
|
-
import { zipSync } from "fflate";
|
|
14
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
15
|
-
import { ExitCode } from "../errors/index.js";
|
|
16
|
-
import {
|
|
17
|
-
GITHUB_ZIPBALL_LIMITS,
|
|
18
|
-
fetchGitHubTree,
|
|
19
|
-
peekGitHubMarketplace,
|
|
20
|
-
rawFileUrl,
|
|
21
|
-
zipballUrl,
|
|
22
|
-
} from "./github.js";
|
|
23
|
-
|
|
24
|
-
let dest: string;
|
|
25
|
-
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
dest = mkdtempSync(join(tmpdir(), "stigmer-github-tree-"));
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
afterEach(() => {
|
|
31
|
-
rmSync(dest, { recursive: true, force: true });
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const encoder = new TextEncoder();
|
|
35
|
-
|
|
36
|
-
function zipball(files: Record<string, string | Uint8Array>): Uint8Array {
|
|
37
|
-
const tree: Record<string, Uint8Array> = {};
|
|
38
|
-
for (const [path, content] of Object.entries(files)) {
|
|
39
|
-
tree[path] =
|
|
40
|
-
typeof content === "string" ? encoder.encode(content) : content;
|
|
41
|
-
}
|
|
42
|
-
return zipSync(tree, { level: 6 });
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function serving(
|
|
46
|
-
bytes: Uint8Array,
|
|
47
|
-
init: { status?: number; contentLength?: number } = {},
|
|
48
|
-
): typeof globalThis.fetch {
|
|
49
|
-
return async () => {
|
|
50
|
-
const headers = new Headers();
|
|
51
|
-
if (init.contentLength !== undefined)
|
|
52
|
-
headers.set("content-length", String(init.contentLength));
|
|
53
|
-
const body = bytes.buffer.slice(
|
|
54
|
-
bytes.byteOffset,
|
|
55
|
-
bytes.byteOffset + bytes.byteLength,
|
|
56
|
-
) as ArrayBuffer;
|
|
57
|
-
return new Response(
|
|
58
|
-
init.status === undefined || init.status === 200 ? body : "nope",
|
|
59
|
-
{
|
|
60
|
-
status: init.status ?? 200,
|
|
61
|
-
headers,
|
|
62
|
-
},
|
|
63
|
-
);
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* The same archive with one entry's central-directory record rewritten: a
|
|
69
|
-
* declaration that lies (`uncompressedSize`) or a checksum that does not
|
|
70
|
-
* match (`crc32`). The payload is untouched, so only a reader that budgets
|
|
71
|
-
* from the central directory and verifies the CRC can refuse it.
|
|
72
|
-
*/
|
|
73
|
-
function declaring(
|
|
74
|
-
bytes: Uint8Array,
|
|
75
|
-
entryName: string,
|
|
76
|
-
patch: { uncompressedSize?: number; crc32?: number },
|
|
77
|
-
): Uint8Array {
|
|
78
|
-
const patched = new Uint8Array(bytes);
|
|
79
|
-
const view = new DataView(
|
|
80
|
-
patched.buffer,
|
|
81
|
-
patched.byteOffset,
|
|
82
|
-
patched.byteLength,
|
|
83
|
-
);
|
|
84
|
-
const name = encoder.encode(entryName);
|
|
85
|
-
for (let offset = 0; offset + 46 <= patched.length; offset += 1) {
|
|
86
|
-
if (view.getUint32(offset, true) !== 0x02014b50) continue;
|
|
87
|
-
const nameLength = view.getUint16(offset + 28, true);
|
|
88
|
-
const recorded = patched.subarray(offset + 46, offset + 46 + nameLength);
|
|
89
|
-
if (
|
|
90
|
-
recorded.length === name.length &&
|
|
91
|
-
recorded.every((byte, i) => byte === name[i])
|
|
92
|
-
) {
|
|
93
|
-
if (patch.crc32 !== undefined)
|
|
94
|
-
view.setUint32(offset + 16, patch.crc32, true);
|
|
95
|
-
if (patch.uncompressedSize !== undefined)
|
|
96
|
-
view.setUint32(offset + 24, patch.uncompressedSize, true);
|
|
97
|
-
return patched;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
throw new Error(`no central directory record for ${entryName}`);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
async function refusal(
|
|
104
|
-
promise: Promise<unknown>,
|
|
105
|
-
): Promise<{ message: string; exitCode: number }> {
|
|
106
|
-
try {
|
|
107
|
-
await promise;
|
|
108
|
-
} catch (error) {
|
|
109
|
-
const e = error as { message: string; exitCode: number };
|
|
110
|
-
return { message: e.message, exitCode: e.exitCode };
|
|
111
|
-
}
|
|
112
|
-
throw new Error("expected a refusal");
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
describe("zipballUrl", () => {
|
|
116
|
-
it("names codeload's HEAD by default and the ref when given", () => {
|
|
117
|
-
expect(zipballUrl({ repo: "cursor/plugins" })).toBe(
|
|
118
|
-
"https://codeload.github.com/cursor/plugins/zip/HEAD",
|
|
119
|
-
);
|
|
120
|
-
expect(zipballUrl({ repo: "cursor/plugins", ref: "v1" })).toBe(
|
|
121
|
-
"https://codeload.github.com/cursor/plugins/zip/v1",
|
|
122
|
-
);
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
describe("fetchGitHubTree", () => {
|
|
127
|
-
it("extracts the tree with the zipball's top directory stripped", async () => {
|
|
128
|
-
const bytes = zipball({
|
|
129
|
-
"plugins-HEAD/.cursor-plugin/marketplace.json": JSON.stringify({
|
|
130
|
-
name: "cursor-plugins",
|
|
131
|
-
plugins: [],
|
|
132
|
-
}),
|
|
133
|
-
"plugins-HEAD/thermos/.cursor-plugin/plugin.json": JSON.stringify({
|
|
134
|
-
name: "thermos",
|
|
135
|
-
}),
|
|
136
|
-
"plugins-HEAD/thermos/skills/x/SKILL.md":
|
|
137
|
-
"---\nname: x\ndescription: d\n---\nbody\n",
|
|
138
|
-
});
|
|
139
|
-
await fetchGitHubTree(
|
|
140
|
-
{ repo: "cursor/plugins" },
|
|
141
|
-
{ destDir: dest, fetchImpl: serving(bytes) },
|
|
142
|
-
);
|
|
143
|
-
expect(existsSync(join(dest, "plugins-HEAD"))).toBe(false);
|
|
144
|
-
expect(
|
|
145
|
-
JSON.parse(
|
|
146
|
-
readFileSync(join(dest, ".cursor-plugin", "marketplace.json"), "utf8"),
|
|
147
|
-
).name,
|
|
148
|
-
).toBe("cursor-plugins");
|
|
149
|
-
expect(
|
|
150
|
-
readFileSync(join(dest, "thermos", "skills", "x", "SKILL.md"), "utf8"),
|
|
151
|
-
).toContain("body");
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it("refuses an archive over the download cap from content-length, before reading the body", async () => {
|
|
155
|
-
const bytes = zipball({ "r-HEAD/marketplace.json": "{}" });
|
|
156
|
-
const outcome = await refusal(
|
|
157
|
-
fetchGitHubTree(
|
|
158
|
-
{ repo: "a/b" },
|
|
159
|
-
{
|
|
160
|
-
destDir: dest,
|
|
161
|
-
fetchImpl: serving(bytes, {
|
|
162
|
-
contentLength: GITHUB_ZIPBALL_LIMITS.downloadBytes + 1,
|
|
163
|
-
}),
|
|
164
|
-
},
|
|
165
|
-
),
|
|
166
|
-
);
|
|
167
|
-
expect(outcome.message).toMatch(/over the 64\.0 MiB download cap/);
|
|
168
|
-
expect(outcome.exitCode).toBe(ExitCode.Usage);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it("refuses more entries than the cap from the central directory", async () => {
|
|
172
|
-
const files: Record<string, string> = {};
|
|
173
|
-
for (let i = 0; i <= GITHUB_ZIPBALL_LIMITS.entries; i += 1)
|
|
174
|
-
files[`r-HEAD/f${i}`] = "";
|
|
175
|
-
const outcome = await refusal(
|
|
176
|
-
fetchGitHubTree(
|
|
177
|
-
{ repo: "a/b" },
|
|
178
|
-
{ destDir: dest, fetchImpl: serving(zipball(files)) },
|
|
179
|
-
),
|
|
180
|
-
);
|
|
181
|
-
expect(outcome.message).toMatch(/entries, over the 20000 cap/);
|
|
182
|
-
expect(existsSync(join(dest, "f0"))).toBe(false);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it("refuses a declared inflated size over the cap from the central directory, before inflating", async () => {
|
|
186
|
-
const bytes = declaring(
|
|
187
|
-
zipball({ "r-HEAD/marketplace.json": "{}", "r-HEAD/big.bin": "tiny" }),
|
|
188
|
-
"r-HEAD/big.bin",
|
|
189
|
-
{
|
|
190
|
-
uncompressedSize: GITHUB_ZIPBALL_LIMITS.inflatedBytes + 1,
|
|
191
|
-
},
|
|
192
|
-
);
|
|
193
|
-
const outcome = await refusal(
|
|
194
|
-
fetchGitHubTree(
|
|
195
|
-
{ repo: "a/b" },
|
|
196
|
-
{ destDir: dest, fetchImpl: serving(bytes) },
|
|
197
|
-
),
|
|
198
|
-
);
|
|
199
|
-
expect(outcome.message).toMatch(
|
|
200
|
-
/inflates to 256\.0 MiB, over the 256\.0 MiB cap/,
|
|
201
|
-
);
|
|
202
|
-
expect(existsSync(join(dest, "marketplace.json"))).toBe(false);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it("refuses an entry whose declared size undercounts what it inflates to", async () => {
|
|
206
|
-
const bytes = declaring(
|
|
207
|
-
zipball({
|
|
208
|
-
"r-HEAD/marketplace.json": "{}",
|
|
209
|
-
"r-HEAD/liar.txt": "twelve bytes, and then some more text",
|
|
210
|
-
}),
|
|
211
|
-
"r-HEAD/liar.txt",
|
|
212
|
-
{ uncompressedSize: 3 },
|
|
213
|
-
);
|
|
214
|
-
const outcome = await refusal(
|
|
215
|
-
fetchGitHubTree(
|
|
216
|
-
{ repo: "a/b" },
|
|
217
|
-
{ destDir: dest, fetchImpl: serving(bytes) },
|
|
218
|
-
),
|
|
219
|
-
);
|
|
220
|
-
expect(outcome.message).toMatch(
|
|
221
|
-
/entry 'liar\.txt' inflated past its declared size/,
|
|
222
|
-
);
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it("refuses an entry whose bytes do not match its checksum", async () => {
|
|
226
|
-
const bytes = declaring(
|
|
227
|
-
zipball({
|
|
228
|
-
"r-HEAD/marketplace.json": "{}",
|
|
229
|
-
"r-HEAD/tampered.txt": "some content here",
|
|
230
|
-
}),
|
|
231
|
-
"r-HEAD/tampered.txt",
|
|
232
|
-
{ crc32: 0xdeadbeef },
|
|
233
|
-
);
|
|
234
|
-
const outcome = await refusal(
|
|
235
|
-
fetchGitHubTree(
|
|
236
|
-
{ repo: "a/b" },
|
|
237
|
-
{ destDir: dest, fetchImpl: serving(bytes) },
|
|
238
|
-
),
|
|
239
|
-
);
|
|
240
|
-
expect(outcome.message).toMatch(
|
|
241
|
-
/entry 'tampered\.txt' failed its checksum/,
|
|
242
|
-
);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it("refuses an entry that would escape the destination", async () => {
|
|
246
|
-
const outcome = await refusal(
|
|
247
|
-
fetchGitHubTree(
|
|
248
|
-
{ repo: "a/b" },
|
|
249
|
-
{
|
|
250
|
-
destDir: dest,
|
|
251
|
-
fetchImpl: serving(
|
|
252
|
-
zipball({ "r-HEAD/marketplace.json": "{}", "r-HEAD/../evil": "x" }),
|
|
253
|
-
),
|
|
254
|
-
},
|
|
255
|
-
),
|
|
256
|
-
);
|
|
257
|
-
expect(outcome.message).toMatch(/would escape the destination directory/);
|
|
258
|
-
expect(existsSync(join(dest, "..", "evil"))).toBe(false);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it("refuses an archive with more than one top-level directory", async () => {
|
|
262
|
-
const outcome = await refusal(
|
|
263
|
-
fetchGitHubTree(
|
|
264
|
-
{ repo: "a/b" },
|
|
265
|
-
{
|
|
266
|
-
destDir: dest,
|
|
267
|
-
fetchImpl: serving(
|
|
268
|
-
zipball({
|
|
269
|
-
"one/marketplace.json": "{}",
|
|
270
|
-
"two/marketplace.json": "{}",
|
|
271
|
-
}),
|
|
272
|
-
),
|
|
273
|
-
},
|
|
274
|
-
),
|
|
275
|
-
);
|
|
276
|
-
expect(outcome.message).toMatch(
|
|
277
|
-
/more than one top-level directory \('one' and 'two'\)/,
|
|
278
|
-
);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it("refuses bytes that are not a zip", async () => {
|
|
282
|
-
const outcome = await refusal(
|
|
283
|
-
fetchGitHubTree(
|
|
284
|
-
{ repo: "a/b" },
|
|
285
|
-
{
|
|
286
|
-
destDir: dest,
|
|
287
|
-
fetchImpl: serving(encoder.encode("<html>not a zip</html>")),
|
|
288
|
-
},
|
|
289
|
-
),
|
|
290
|
-
);
|
|
291
|
-
expect(outcome.message).toMatch(/not a readable zip/);
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
it("maps 404 to NotFound and a network failure to Connection, each with its own sentence", async () => {
|
|
295
|
-
const missing = await refusal(
|
|
296
|
-
fetchGitHubTree(
|
|
297
|
-
{ repo: "a/b", ref: "v9" },
|
|
298
|
-
{
|
|
299
|
-
destDir: dest,
|
|
300
|
-
fetchImpl: serving(new Uint8Array(), { status: 404 }),
|
|
301
|
-
},
|
|
302
|
-
),
|
|
303
|
-
);
|
|
304
|
-
expect(missing.message).toBe(
|
|
305
|
-
"GitHub has no public repository 'a/b' at ref 'v9'",
|
|
306
|
-
);
|
|
307
|
-
expect(missing.exitCode).toBe(ExitCode.NotFound);
|
|
308
|
-
|
|
309
|
-
const offline = await refusal(
|
|
310
|
-
fetchGitHubTree(
|
|
311
|
-
{ repo: "a/b" },
|
|
312
|
-
{
|
|
313
|
-
destDir: dest,
|
|
314
|
-
fetchImpl: async () => {
|
|
315
|
-
throw new Error("ENOTFOUND");
|
|
316
|
-
},
|
|
317
|
-
},
|
|
318
|
-
),
|
|
319
|
-
);
|
|
320
|
-
expect(offline.message).toBe("could not reach GitHub for a/b");
|
|
321
|
-
expect(offline.exitCode).toBe(ExitCode.Connection);
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
/** A fetch answering raw.githubusercontent.com from a path map, 404 elsewhere. */
|
|
326
|
-
function rawServing(
|
|
327
|
-
files: Record<string, string>,
|
|
328
|
-
): typeof globalThis.fetch {
|
|
329
|
-
return async (input) => {
|
|
330
|
-
const url = String(input);
|
|
331
|
-
const prefix = "https://raw.githubusercontent.com/a/b/HEAD/";
|
|
332
|
-
const path = url.startsWith(prefix) ? url.slice(prefix.length) : undefined;
|
|
333
|
-
const content = path === undefined ? undefined : files[path];
|
|
334
|
-
return content === undefined
|
|
335
|
-
? new Response("Not Found", { status: 404 })
|
|
336
|
-
: new Response(content, { status: 200 });
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
describe("rawFileUrl", () => {
|
|
341
|
-
it("names raw.githubusercontent.com at HEAD by default and the ref when given", () => {
|
|
342
|
-
expect(rawFileUrl({ repo: "a/b" }, ".cursor-plugin/marketplace.json")).toBe(
|
|
343
|
-
"https://raw.githubusercontent.com/a/b/HEAD/.cursor-plugin/marketplace.json",
|
|
344
|
-
);
|
|
345
|
-
expect(rawFileUrl({ repo: "a/b", ref: "v1" }, "marketplace.json")).toBe(
|
|
346
|
-
"https://raw.githubusercontent.com/a/b/v1/marketplace.json",
|
|
347
|
-
);
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
describe("peekGitHubMarketplace", () => {
|
|
352
|
-
it("reads the first location present in the library's precedence, as declared, tree unverified", async () => {
|
|
353
|
-
const peek = await peekGitHubMarketplace(
|
|
354
|
-
{ repo: "a/b" },
|
|
355
|
-
rawServing({
|
|
356
|
-
".cursor-plugin/marketplace.json": JSON.stringify({
|
|
357
|
-
name: "acme",
|
|
358
|
-
plugins: [{ name: "thermos", source: "thermos" }],
|
|
359
|
-
}),
|
|
360
|
-
}),
|
|
361
|
-
);
|
|
362
|
-
expect(peek.ok).toBe(true);
|
|
363
|
-
if (!peek.ok) return;
|
|
364
|
-
expect(peek.peeked.location).toBe(".cursor-plugin/marketplace.json");
|
|
365
|
-
expect(peek.peeked.marketplace.plugins.map((p) => p.name)).toEqual([
|
|
366
|
-
"thermos",
|
|
367
|
-
]);
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
it("is not-a-marketplace when no location exists, and refused with the library's sentences when the file is broken", async () => {
|
|
371
|
-
expect(await peekGitHubMarketplace({ repo: "a/b" }, rawServing({}))).toEqual({
|
|
372
|
-
ok: false,
|
|
373
|
-
kind: "not-a-marketplace",
|
|
374
|
-
});
|
|
375
|
-
const refused = await peekGitHubMarketplace(
|
|
376
|
-
{ repo: "a/b" },
|
|
377
|
-
rawServing({ "marketplace.json": JSON.stringify({ plugins: "no" }) }),
|
|
378
|
-
);
|
|
379
|
-
expect(refused.ok).toBe(false);
|
|
380
|
-
if (refused.ok || refused.kind !== "refused") throw new Error("expected refused");
|
|
381
|
-
expect(refused.location).toBe("marketplace.json");
|
|
382
|
-
expect(refused.errors.length).toBeGreaterThan(0);
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
it("carries the connection exit code when GitHub cannot be reached", async () => {
|
|
386
|
-
const offline = await peekGitHubMarketplace({ repo: "a/b" }, async () => {
|
|
387
|
-
throw new Error("ENOTFOUND");
|
|
388
|
-
}).catch((e: unknown) => e);
|
|
389
|
-
expect(offline).toMatchObject({
|
|
390
|
-
message: "could not reach GitHub for a/b",
|
|
391
|
-
exitCode: ExitCode.Connection,
|
|
392
|
-
});
|
|
393
|
-
});
|
|
394
|
-
});
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
// Pins how a ref becomes a prepared push: an offered entry prepares through
|
|
2
|
-
// the same walker `push plugin` uses (one digest for one tree); a version
|
|
3
|
-
// pin is an assertion; a bare name is searched across the known sources and
|
|
4
|
-
// must land in exactly one; a GitHub source is asked through its marketplace
|
|
5
|
-
// file alone and its zipball fetched only when the file declares the name (a
|
|
6
|
-
// repository with no file declares nothing; a declared entry the tree does
|
|
7
|
-
// not hold is not a holder); every temp tree a search opened is disposed,
|
|
8
|
-
// whatever the outcome.
|
|
9
|
-
|
|
10
|
-
import { existsSync, mkdtempSync, readdirSync, rmSync } from "node:fs";
|
|
11
|
-
import { tmpdir } from "node:os";
|
|
12
|
-
import { join } from "node:path";
|
|
13
|
-
import { zipSync } from "fflate";
|
|
14
|
-
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
15
|
-
import { preparePluginPush } from "../resources/plugin.js";
|
|
16
|
-
import { writeCursorMarketplace } from "./__fixtures__/cursor-marketplace.js";
|
|
17
|
-
import { type MarketplaceListing, OFFICIAL_MARKETPLACE } from "./config.js";
|
|
18
|
-
import { assertVersion, locateEntry, prepareEntry } from "./install.js";
|
|
19
|
-
import { readMarketplaceTree } from "./read.js";
|
|
20
|
-
import { parseInstallRef } from "./ref.js";
|
|
21
|
-
|
|
22
|
-
let root: string;
|
|
23
|
-
|
|
24
|
-
beforeAll(() => {
|
|
25
|
-
root = mkdtempSync(join(tmpdir(), "stigmer-marketplace-install-"));
|
|
26
|
-
writeCursorMarketplace(root);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
afterAll(() => {
|
|
30
|
-
rmSync(root, { recursive: true, force: true });
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe("prepareEntry", () => {
|
|
34
|
-
it("prepares an offered entry exactly as push plugin would prepare its directory", async () => {
|
|
35
|
-
const tree = readMarketplaceTree(root);
|
|
36
|
-
const viaMarketplace = await prepareEntry(tree, "codeforge");
|
|
37
|
-
const viaFolder = await preparePluginPush(join(root, "third_party", "codeforge"));
|
|
38
|
-
expect(viaMarketplace.plugin.name).toBe("codeforge");
|
|
39
|
-
expect(viaMarketplace.digest).toBe(viaFolder.digest);
|
|
40
|
-
expect(
|
|
41
|
-
Buffer.from(viaMarketplace.archive).equals(
|
|
42
|
-
Buffer.from(viaFolder.archive),
|
|
43
|
-
),
|
|
44
|
-
).toBe(true);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("refuses an entry the marketplace does not offer, quoting the drop sentence when there is one", async () => {
|
|
48
|
-
const tree = readMarketplaceTree(root);
|
|
49
|
-
await expect(prepareEntry(tree, "ghost")).rejects.toThrow(
|
|
50
|
-
/does not offer a plugin named 'ghost'\n\n.*ghost.*\n\nRun 'stigmer marketplace show acme-plugins' to see the 2 it offers/s,
|
|
51
|
-
);
|
|
52
|
-
await expect(prepareEntry(tree, "nope")).rejects.toThrow(
|
|
53
|
-
/does not offer a plugin named 'nope'\n\nRun 'stigmer marketplace show/,
|
|
54
|
-
);
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
describe("assertVersion", () => {
|
|
59
|
-
it("passes without a pin or with the offered version, refuses another naming the offered one", async () => {
|
|
60
|
-
const prepared = await prepareEntry(readMarketplaceTree(root), "warmer");
|
|
61
|
-
expect(() => assertVersion(prepared, undefined, "warmer")).not.toThrow();
|
|
62
|
-
expect(() =>
|
|
63
|
-
assertVersion(prepared, "1.0.0", "warmer@1.0.0"),
|
|
64
|
-
).not.toThrow();
|
|
65
|
-
expect(() =>
|
|
66
|
-
assertVersion(prepared, "9.9.9", "acme-plugins/warmer@9.9.9"),
|
|
67
|
-
).toThrow(
|
|
68
|
-
/pins version 9\.9\.9, but the marketplace offers 'warmer' at 1\.0\.0\n\nInstall it as 'acme-plugins\/warmer@1\.0\.0'/,
|
|
69
|
-
);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
describe("locateEntry", () => {
|
|
74
|
-
const encoder = new TextEncoder();
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* A GitHub source served from memory: raw.githubusercontent.com answers a
|
|
78
|
-
* single file from the map (404 when absent), codeload answers the tree
|
|
79
|
-
* zipped under one top directory. `requests` records every URL asked.
|
|
80
|
-
*/
|
|
81
|
-
function servingZipOf(
|
|
82
|
-
files: Record<string, string>,
|
|
83
|
-
top = "r-HEAD",
|
|
84
|
-
requests: string[] = [],
|
|
85
|
-
): typeof globalThis.fetch {
|
|
86
|
-
const tree: Record<string, Uint8Array> = {};
|
|
87
|
-
for (const [path, content] of Object.entries(files))
|
|
88
|
-
tree[`${top}/${path}`] = encoder.encode(content);
|
|
89
|
-
const bytes = zipSync(tree);
|
|
90
|
-
return async (input) => {
|
|
91
|
-
const url = String(input);
|
|
92
|
-
requests.push(url);
|
|
93
|
-
const raw = url.match(/^https:\/\/raw\.githubusercontent\.com\/[^/]+\/[^/]+\/[^/]+\/(.+)$/);
|
|
94
|
-
if (raw !== null) {
|
|
95
|
-
const content = files[raw[1] ?? ""];
|
|
96
|
-
return content === undefined
|
|
97
|
-
? new Response("Not Found", { status: 404 })
|
|
98
|
-
: new Response(content, { status: 200 });
|
|
99
|
-
}
|
|
100
|
-
return new Response(bytes, { status: 200 });
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const remoteFiles = {
|
|
105
|
-
"marketplace.json": JSON.stringify({
|
|
106
|
-
name: "remote",
|
|
107
|
-
plugins: [{ name: "warmer", source: "./warmer" }],
|
|
108
|
-
}),
|
|
109
|
-
"warmer/plugin.json": JSON.stringify({
|
|
110
|
-
name: "warmer",
|
|
111
|
-
version: "3.0.0",
|
|
112
|
-
}),
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
function listing(...extra: MarketplaceListing["known"]): MarketplaceListing {
|
|
116
|
-
return { known: [OFFICIAL_MARKETPLACE, ...extra], unreadable: [] };
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
it("opens the marketplace a prefixed ref names and refuses an unknown prefix", async () => {
|
|
120
|
-
const local = {
|
|
121
|
-
name: "acme-plugins",
|
|
122
|
-
source: { type: "local", path: root } as const,
|
|
123
|
-
};
|
|
124
|
-
const located = await locateEntry(
|
|
125
|
-
parseInstallRef("acme-plugins/warmer"),
|
|
126
|
-
listing(local),
|
|
127
|
-
);
|
|
128
|
-
try {
|
|
129
|
-
expect(located.marketplace.name).toBe("acme-plugins");
|
|
130
|
-
expect(located.tree.root).toBe(root);
|
|
131
|
-
} finally {
|
|
132
|
-
located.open.dispose();
|
|
133
|
-
}
|
|
134
|
-
await expect(
|
|
135
|
-
locateEntry(parseInstallRef("nowhere/warmer"), listing(local)),
|
|
136
|
-
).rejects.toThrow(/no marketplace named 'nowhere' is configured/);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("searches a bare name across every marketplace and lands in the one holder, disposing the rest", async () => {
|
|
140
|
-
const local = {
|
|
141
|
-
name: "acme-plugins",
|
|
142
|
-
source: { type: "local", path: root } as const,
|
|
143
|
-
};
|
|
144
|
-
const located = await locateEntry(
|
|
145
|
-
parseInstallRef("codeforge"),
|
|
146
|
-
listing(local),
|
|
147
|
-
);
|
|
148
|
-
try {
|
|
149
|
-
expect(located.marketplace.name).toBe("acme-plugins");
|
|
150
|
-
} finally {
|
|
151
|
-
located.open.dispose();
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it("refuses a bare name nobody offers, naming what was searched and what could not be read", async () => {
|
|
156
|
-
const local = {
|
|
157
|
-
name: "acme-plugins",
|
|
158
|
-
source: { type: "local", path: root } as const,
|
|
159
|
-
};
|
|
160
|
-
await expect(
|
|
161
|
-
locateEntry(parseInstallRef("nope"), {
|
|
162
|
-
known: [OFFICIAL_MARKETPLACE, local],
|
|
163
|
-
unreadable: [{ name: "broken", reason: "x" }],
|
|
164
|
-
}),
|
|
165
|
-
).rejects.toThrow(
|
|
166
|
-
/no configured marketplace offers a plugin named 'nope'\n\nSearched: stigmer, acme-plugins\. Not searched.*broken/,
|
|
167
|
-
);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it("refuses a bare name two marketplaces offer, naming each qualified ref, and leaves no temp tree behind", async () => {
|
|
171
|
-
const local = {
|
|
172
|
-
name: "acme-plugins",
|
|
173
|
-
source: { type: "local", path: root } as const,
|
|
174
|
-
};
|
|
175
|
-
const remote = {
|
|
176
|
-
name: "remote",
|
|
177
|
-
source: { type: "github", repo: "a/b" } as const,
|
|
178
|
-
};
|
|
179
|
-
const before = countTempTrees();
|
|
180
|
-
await expect(
|
|
181
|
-
locateEntry(parseInstallRef("warmer"), listing(local, remote), {
|
|
182
|
-
fetchImpl: servingZipOf(remoteFiles),
|
|
183
|
-
}),
|
|
184
|
-
).rejects.toThrow(
|
|
185
|
-
/offered by more than one marketplace: acme-plugins, remote\n\nName the one you mean: acme-plugins\/warmer or remote\/warmer/,
|
|
186
|
-
);
|
|
187
|
-
expect(countTempTrees()).toBe(before);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it("asks a GitHub source through its marketplace file alone and downloads the zipball only for a holder", async () => {
|
|
191
|
-
const local = {
|
|
192
|
-
name: "acme-plugins",
|
|
193
|
-
source: { type: "local", path: root } as const,
|
|
194
|
-
};
|
|
195
|
-
const remote = {
|
|
196
|
-
name: "remote",
|
|
197
|
-
source: { type: "github", repo: "a/b" } as const,
|
|
198
|
-
};
|
|
199
|
-
const requests: string[] = [];
|
|
200
|
-
// `codeforge` is offered by the local fixture alone: the remote is peeked, not downloaded.
|
|
201
|
-
const located = await locateEntry(parseInstallRef("codeforge"), listing(local, remote), {
|
|
202
|
-
fetchImpl: servingZipOf(remoteFiles, "r-HEAD", requests),
|
|
203
|
-
});
|
|
204
|
-
located.open.dispose();
|
|
205
|
-
expect(requests.some((url) => url.startsWith("https://raw.githubusercontent.com/a/b/"))).toBe(true);
|
|
206
|
-
expect(requests.some((url) => url.startsWith("https://codeload.github.com/"))).toBe(false);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it("treats a GitHub repository with no marketplace file as declaring nothing, and a declared entry the tree lacks as not held", async () => {
|
|
210
|
-
const empty = {
|
|
211
|
-
name: "empty",
|
|
212
|
-
source: { type: "github", repo: "a/b" } as const,
|
|
213
|
-
};
|
|
214
|
-
await expect(
|
|
215
|
-
locateEntry(parseInstallRef("nope"), listing(empty), {
|
|
216
|
-
fetchImpl: servingZipOf({}),
|
|
217
|
-
}),
|
|
218
|
-
).rejects.toThrow(/no configured marketplace offers a plugin named 'nope'\n\nSearched: stigmer, empty\./);
|
|
219
|
-
|
|
220
|
-
const phantom = {
|
|
221
|
-
name: "phantom",
|
|
222
|
-
source: { type: "github", repo: "a/b" } as const,
|
|
223
|
-
};
|
|
224
|
-
const before = countTempTrees();
|
|
225
|
-
await expect(
|
|
226
|
-
locateEntry(parseInstallRef("ghost"), listing(phantom), {
|
|
227
|
-
fetchImpl: servingZipOf({
|
|
228
|
-
"marketplace.json": JSON.stringify({
|
|
229
|
-
name: "phantom",
|
|
230
|
-
plugins: [{ name: "ghost", source: "./ghost" }],
|
|
231
|
-
}),
|
|
232
|
-
}),
|
|
233
|
-
}),
|
|
234
|
-
).rejects.toThrow(/no configured marketplace offers a plugin named 'ghost'/);
|
|
235
|
-
expect(countTempTrees()).toBe(before);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
it("disposes a fetched tree when the located entry is released", async () => {
|
|
239
|
-
const remote = {
|
|
240
|
-
name: "remote",
|
|
241
|
-
source: { type: "github", repo: "a/b" } as const,
|
|
242
|
-
};
|
|
243
|
-
const located = await locateEntry(
|
|
244
|
-
parseInstallRef("remote/warmer"),
|
|
245
|
-
listing(remote),
|
|
246
|
-
{
|
|
247
|
-
fetchImpl: servingZipOf(remoteFiles),
|
|
248
|
-
},
|
|
249
|
-
);
|
|
250
|
-
expect(existsSync(located.tree.root)).toBe(true);
|
|
251
|
-
located.open.dispose();
|
|
252
|
-
expect(existsSync(located.tree.root)).toBe(false);
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
/** The temp trees `openMarketplace` creates (`stigmer-marketplace-` plus mkdtemp's suffix and nothing else). */
|
|
257
|
-
function countTempTrees(): number {
|
|
258
|
-
return readdirSync(tmpdir()).filter((name) =>
|
|
259
|
-
/^stigmer-marketplace-[A-Za-z0-9]+$/.test(name),
|
|
260
|
-
).length;
|
|
261
|
-
}
|