runwork 0.15.0 → 0.15.2
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/dist/index.js +19272 -116
- package/package.json +4 -4
- package/dist/__tests__/curl-parser.test.d.ts +0 -1
- package/dist/__tests__/curl-parser.test.js +0 -250
- package/dist/__tests__/install-scripts.test.d.ts +0 -1
- package/dist/__tests__/install-scripts.test.js +0 -280
- package/dist/__tests__/types-manager.test.d.ts +0 -1
- package/dist/__tests__/types-manager.test.js +0 -89
- package/dist/agents/__tests__/chatgpt-registry.test.d.ts +0 -1
- package/dist/agents/__tests__/chatgpt-registry.test.js +0 -61
- package/dist/agents/__tests__/claude-code-managed-block.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-code-managed-block.test.js +0 -97
- package/dist/agents/__tests__/claude-code-plugin.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-code-plugin.test.js +0 -47
- package/dist/agents/__tests__/claude-code-stats.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-code-stats.test.js +0 -390
- package/dist/agents/__tests__/claude-desktop-memory.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-memory.test.js +0 -154
- package/dist/agents/__tests__/claude-desktop-plugin-tree.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-plugin-tree.test.js +0 -161
- package/dist/agents/__tests__/claude-desktop-plugin.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-plugin.test.js +0 -117
- package/dist/agents/__tests__/claude-desktop-rpm.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-rpm.test.js +0 -126
- package/dist/agents/__tests__/claude-desktop-stats.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-stats.test.js +0 -280
- package/dist/agents/__tests__/codex-instructions.test.d.ts +0 -1
- package/dist/agents/__tests__/codex-instructions.test.js +0 -57
- package/dist/agents/__tests__/codex-minimum-permissions.test.d.ts +0 -1
- package/dist/agents/__tests__/codex-minimum-permissions.test.js +0 -82
- package/dist/agents/__tests__/codex-stats.test.d.ts +0 -1
- package/dist/agents/__tests__/codex-stats.test.js +0 -370
- package/dist/agents/__tests__/cursor-merge.test.d.ts +0 -1
- package/dist/agents/__tests__/cursor-merge.test.js +0 -66
- package/dist/agents/__tests__/cursor-stats.test.d.ts +0 -1
- package/dist/agents/__tests__/cursor-stats.test.js +0 -183
- package/dist/agents/__tests__/defaults-merge.test.d.ts +0 -1
- package/dist/agents/__tests__/defaults-merge.test.js +0 -268
- package/dist/agents/__tests__/detection.test.d.ts +0 -1
- package/dist/agents/__tests__/detection.test.js +0 -89
- package/dist/agents/__tests__/graceful-degradation.test.d.ts +0 -1
- package/dist/agents/__tests__/graceful-degradation.test.js +0 -129
- package/dist/agents/__tests__/instruction-hint.test.d.ts +0 -1
- package/dist/agents/__tests__/instruction-hint.test.js +0 -69
- package/dist/agents/__tests__/intro-skill.test.d.ts +0 -1
- package/dist/agents/__tests__/intro-skill.test.js +0 -193
- package/dist/agents/__tests__/schema-validation.test.d.ts +0 -1
- package/dist/agents/__tests__/schema-validation.test.js +0 -212
- package/dist/agents/__tests__/skill-slug.test.d.ts +0 -1
- package/dist/agents/__tests__/skill-slug.test.js +0 -77
- package/dist/agents/claude-code.d.ts +0 -53
- package/dist/agents/claude-code.js +0 -754
- package/dist/agents/claude-desktop-plugin-tree.d.ts +0 -59
- package/dist/agents/claude-desktop-plugin-tree.js +0 -143
- package/dist/agents/claude-desktop.d.ts +0 -47
- package/dist/agents/claude-desktop.js +0 -684
- package/dist/agents/cline.d.ts +0 -15
- package/dist/agents/cline.js +0 -130
- package/dist/agents/codex.d.ts +0 -44
- package/dist/agents/codex.js +0 -453
- package/dist/agents/conversation-skills.d.ts +0 -18
- package/dist/agents/conversation-skills.js +0 -229
- package/dist/agents/cursor.d.ts +0 -48
- package/dist/agents/cursor.js +0 -324
- package/dist/agents/default-config.d.ts +0 -30
- package/dist/agents/default-config.js +0 -67
- package/dist/agents/defaults-merge.d.ts +0 -72
- package/dist/agents/defaults-merge.js +0 -131
- package/dist/agents/detect.d.ts +0 -4
- package/dist/agents/detect.js +0 -67
- package/dist/agents/detection.d.ts +0 -18
- package/dist/agents/detection.js +0 -94
- package/dist/agents/gemini.d.ts +0 -15
- package/dist/agents/gemini.js +0 -122
- package/dist/agents/generic-adapter.d.ts +0 -25
- package/dist/agents/generic-adapter.js +0 -151
- package/dist/agents/intro-skill.d.ts +0 -45
- package/dist/agents/intro-skill.js +0 -447
- package/dist/agents/registry-data.d.ts +0 -192
- package/dist/agents/registry-data.js +0 -669
- package/dist/agents/registry.d.ts +0 -13
- package/dist/agents/registry.js +0 -36
- package/dist/agents/runtime-detection.d.ts +0 -82
- package/dist/agents/runtime-detection.js +0 -271
- package/dist/agents/session-start-hook.d.ts +0 -37
- package/dist/agents/session-start-hook.js +0 -159
- package/dist/agents/types.d.ts +0 -171
- package/dist/agents/types.js +0 -75
- package/dist/agents/utils/instruction-hint.d.ts +0 -21
- package/dist/agents/utils/instruction-hint.js +0 -108
- package/dist/agents/utils/json-config.d.ts +0 -19
- package/dist/agents/utils/json-config.js +0 -69
- package/dist/agents/utils/toml-config.d.ts +0 -8
- package/dist/agents/utils/toml-config.js +0 -27
- package/dist/agents/windsurf.d.ts +0 -14
- package/dist/agents/windsurf.js +0 -102
- package/dist/api/__tests__/client.test.d.ts +0 -1
- package/dist/api/__tests__/client.test.js +0 -272
- package/dist/api/client.d.ts +0 -326
- package/dist/api/client.js +0 -427
- package/dist/auth/__tests__/login-flow.test.d.ts +0 -1
- package/dist/auth/__tests__/login-flow.test.js +0 -101
- package/dist/auth/__tests__/store.test.d.ts +0 -1
- package/dist/auth/__tests__/store.test.js +0 -132
- package/dist/auth/login-flow.d.ts +0 -26
- package/dist/auth/login-flow.js +0 -137
- package/dist/auth/store.d.ts +0 -5
- package/dist/auth/store.js +0 -62
- package/dist/commands/__tests__/clone-args.test.d.ts +0 -1
- package/dist/commands/__tests__/clone-args.test.js +0 -51
- package/dist/commands/__tests__/clone-source-result.test.d.ts +0 -1
- package/dist/commands/__tests__/clone-source-result.test.js +0 -45
- package/dist/commands/__tests__/info-merge.test.d.ts +0 -1
- package/dist/commands/__tests__/info-merge.test.js +0 -55
- package/dist/commands/__tests__/mcp-entries.test.d.ts +0 -1
- package/dist/commands/__tests__/mcp-entries.test.js +0 -48
- package/dist/commands/__tests__/setup-persona.test.d.ts +0 -1
- package/dist/commands/__tests__/setup-persona.test.js +0 -31
- package/dist/commands/__tests__/setup.test.d.ts +0 -1
- package/dist/commands/__tests__/setup.test.js +0 -81
- package/dist/commands/__tests__/sync-redetect.test.d.ts +0 -1
- package/dist/commands/__tests__/sync-redetect.test.js +0 -96
- package/dist/commands/__tests__/sync-telemetry.test.d.ts +0 -1
- package/dist/commands/__tests__/sync-telemetry.test.js +0 -281
- package/dist/commands/__tests__/upgrade.test.d.ts +0 -1
- package/dist/commands/__tests__/upgrade.test.js +0 -157
- package/dist/commands/agents.d.ts +0 -2
- package/dist/commands/agents.js +0 -160
- package/dist/commands/api-keys.d.ts +0 -2
- package/dist/commands/api-keys.js +0 -95
- package/dist/commands/apps.d.ts +0 -2
- package/dist/commands/apps.js +0 -82
- package/dist/commands/build-plugin.d.ts +0 -2
- package/dist/commands/build-plugin.js +0 -135
- package/dist/commands/clone.d.ts +0 -60
- package/dist/commands/clone.js +0 -271
- package/dist/commands/components.d.ts +0 -2
- package/dist/commands/components.js +0 -43
- package/dist/commands/deploy.d.ts +0 -2
- package/dist/commands/deploy.js +0 -279
- package/dist/commands/dev.d.ts +0 -11
- package/dist/commands/dev.js +0 -1011
- package/dist/commands/doctor.d.ts +0 -2
- package/dist/commands/doctor.js +0 -230
- package/dist/commands/endpoints.d.ts +0 -2
- package/dist/commands/endpoints.js +0 -189
- package/dist/commands/entities.d.ts +0 -2
- package/dist/commands/entities.js +0 -198
- package/dist/commands/files.d.ts +0 -2
- package/dist/commands/files.js +0 -193
- package/dist/commands/inbox.d.ts +0 -11
- package/dist/commands/inbox.js +0 -60
- package/dist/commands/info.d.ts +0 -178
- package/dist/commands/info.js +0 -455
- package/dist/commands/init.d.ts +0 -21
- package/dist/commands/init.js +0 -185
- package/dist/commands/integrations.d.ts +0 -2
- package/dist/commands/integrations.js +0 -233
- package/dist/commands/login.d.ts +0 -2
- package/dist/commands/login.js +0 -44
- package/dist/commands/logout.d.ts +0 -2
- package/dist/commands/logout.js +0 -13
- package/dist/commands/logs.d.ts +0 -2
- package/dist/commands/logs.js +0 -232
- package/dist/commands/mcp-entries.d.ts +0 -13
- package/dist/commands/mcp-entries.js +0 -26
- package/dist/commands/mcp.d.ts +0 -2
- package/dist/commands/mcp.js +0 -245
- package/dist/commands/open.d.ts +0 -2
- package/dist/commands/open.js +0 -53
- package/dist/commands/resume.d.ts +0 -2
- package/dist/commands/resume.js +0 -289
- package/dist/commands/save-convo.d.ts +0 -8
- package/dist/commands/save-convo.js +0 -20
- package/dist/commands/schedules.d.ts +0 -2
- package/dist/commands/schedules.js +0 -221
- package/dist/commands/setup.d.ts +0 -27
- package/dist/commands/setup.js +0 -177
- package/dist/commands/share-convo.d.ts +0 -24
- package/dist/commands/share-convo.js +0 -167
- package/dist/commands/skills.d.ts +0 -2
- package/dist/commands/skills.js +0 -352
- package/dist/commands/sync-telemetry.d.ts +0 -33
- package/dist/commands/sync-telemetry.js +0 -186
- package/dist/commands/sync.d.ts +0 -37
- package/dist/commands/sync.js +0 -702
- package/dist/commands/uninstall.d.ts +0 -2
- package/dist/commands/uninstall.js +0 -145
- package/dist/commands/upgrade.d.ts +0 -14
- package/dist/commands/upgrade.js +0 -217
- package/dist/commands/validate.d.ts +0 -2
- package/dist/commands/validate.js +0 -89
- package/dist/commands/welcome.d.ts +0 -1
- package/dist/commands/welcome.js +0 -86
- package/dist/commands/workflows.d.ts +0 -2
- package/dist/commands/workflows.js +0 -187
- package/dist/deploy/__tests__/deploy-state.test.d.ts +0 -1
- package/dist/deploy/__tests__/deploy-state.test.js +0 -80
- package/dist/deploy/__tests__/deploy-status.test.d.ts +0 -1
- package/dist/deploy/__tests__/deploy-status.test.js +0 -41
- package/dist/deploy/__tests__/detach-args.test.d.ts +0 -1
- package/dist/deploy/__tests__/detach-args.test.js +0 -30
- package/dist/deploy/deploy-state.d.ts +0 -36
- package/dist/deploy/deploy-state.js +0 -63
- package/dist/deploy/deploy-status.d.ts +0 -21
- package/dist/deploy/deploy-status.js +0 -36
- package/dist/deploy/detach.d.ts +0 -32
- package/dist/deploy/detach.js +0 -71
- package/dist/dev/__tests__/attach.test.d.ts +0 -1
- package/dist/dev/__tests__/attach.test.js +0 -296
- package/dist/dev/__tests__/detach.test.d.ts +0 -1
- package/dist/dev/__tests__/detach.test.js +0 -404
- package/dist/dev/__tests__/preview-url-poller.test.d.ts +0 -1
- package/dist/dev/__tests__/preview-url-poller.test.js +0 -149
- package/dist/dev/__tests__/session.test.d.ts +0 -1
- package/dist/dev/__tests__/session.test.js +0 -446
- package/dist/dev/__tests__/startup-sync.test.d.ts +0 -1
- package/dist/dev/__tests__/startup-sync.test.js +0 -32
- package/dist/dev/__tests__/stop.test.d.ts +0 -1
- package/dist/dev/__tests__/stop.test.js +0 -172
- package/dist/dev/attach.d.ts +0 -120
- package/dist/dev/attach.js +0 -269
- package/dist/dev/detach.d.ts +0 -187
- package/dist/dev/detach.js +0 -292
- package/dist/dev/preview-url-poller.d.ts +0 -35
- package/dist/dev/preview-url-poller.js +0 -50
- package/dist/dev/session.d.ts +0 -175
- package/dist/dev/session.js +0 -258
- package/dist/dev/startup-sync.d.ts +0 -23
- package/dist/dev/startup-sync.js +0 -14
- package/dist/dev/stop.d.ts +0 -52
- package/dist/dev/stop.js +0 -101
- package/dist/devtools/registry-data.d.ts +0 -46
- package/dist/devtools/registry-data.js +0 -40
- package/dist/generated/bundled-types.d.ts +0 -1
- package/dist/generated/bundled-types.js +0 -46
- package/dist/generated/version.d.ts +0 -1
- package/dist/generated/version.js +0 -2
- package/dist/git/__tests__/auto-commit.test.d.ts +0 -1
- package/dist/git/__tests__/auto-commit.test.js +0 -431
- package/dist/git/__tests__/classify-sync-error.test.d.ts +0 -1
- package/dist/git/__tests__/classify-sync-error.test.js +0 -57
- package/dist/git/__tests__/credential-helper-e2e.test.d.ts +0 -21
- package/dist/git/__tests__/credential-helper-e2e.test.js +0 -195
- package/dist/git/__tests__/credentials.test.d.ts +0 -1
- package/dist/git/__tests__/credentials.test.js +0 -141
- package/dist/git/__tests__/deploy-guard.test.d.ts +0 -1
- package/dist/git/__tests__/deploy-guard.test.js +0 -61
- package/dist/git/__tests__/ensure-git-credential-helper.test.d.ts +0 -1
- package/dist/git/__tests__/ensure-git-credential-helper.test.js +0 -133
- package/dist/git/__tests__/ensure-runwork-remote.test.d.ts +0 -1
- package/dist/git/__tests__/ensure-runwork-remote.test.js +0 -109
- package/dist/git/__tests__/identity.test.d.ts +0 -1
- package/dist/git/__tests__/identity.test.js +0 -146
- package/dist/git/__tests__/manifest.test.d.ts +0 -1
- package/dist/git/__tests__/manifest.test.js +0 -446
- package/dist/git/__tests__/preflight-resolution.test.d.ts +0 -1
- package/dist/git/__tests__/preflight-resolution.test.js +0 -366
- package/dist/git/__tests__/preflight.test.d.ts +0 -1
- package/dist/git/__tests__/preflight.test.js +0 -36
- package/dist/git/__tests__/repo-config.test.d.ts +0 -1
- package/dist/git/__tests__/repo-config.test.js +0 -26
- package/dist/git/__tests__/sync.test.d.ts +0 -1
- package/dist/git/__tests__/sync.test.js +0 -405
- package/dist/git/auto-commit.d.ts +0 -35
- package/dist/git/auto-commit.js +0 -293
- package/dist/git/classify-sync-error.d.ts +0 -21
- package/dist/git/classify-sync-error.js +0 -111
- package/dist/git/credentials.d.ts +0 -75
- package/dist/git/credentials.js +0 -164
- package/dist/git/critical-files.d.ts +0 -20
- package/dist/git/critical-files.js +0 -68
- package/dist/git/deploy-guard.d.ts +0 -53
- package/dist/git/deploy-guard.js +0 -78
- package/dist/git/identity.d.ts +0 -44
- package/dist/git/identity.js +0 -133
- package/dist/git/preflight.d.ts +0 -57
- package/dist/git/preflight.js +0 -276
- package/dist/git/remote.d.ts +0 -37
- package/dist/git/remote.js +0 -75
- package/dist/git/repo-config.d.ts +0 -8
- package/dist/git/repo-config.js +0 -29
- package/dist/git/sync.d.ts +0 -15
- package/dist/git/sync.js +0 -159
- package/dist/health/__tests__/checks.test.d.ts +0 -1
- package/dist/health/__tests__/checks.test.js +0 -357
- package/dist/health/__tests__/cli-distribution-checks.test.d.ts +0 -1
- package/dist/health/__tests__/cli-distribution-checks.test.js +0 -196
- package/dist/health/__tests__/fix.test.d.ts +0 -1
- package/dist/health/__tests__/fix.test.js +0 -72
- package/dist/health/__tests__/runner-filter.test.d.ts +0 -1
- package/dist/health/__tests__/runner-filter.test.js +0 -61
- package/dist/health/checks.d.ts +0 -42
- package/dist/health/checks.js +0 -636
- package/dist/health/fix.d.ts +0 -22
- package/dist/health/fix.js +0 -62
- package/dist/health/runner.d.ts +0 -8
- package/dist/health/runner.js +0 -47
- package/dist/health/types.d.ts +0 -12
- package/dist/health/types.js +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/logs/__tests__/tailer-format.test.d.ts +0 -1
- package/dist/logs/__tests__/tailer-format.test.js +0 -43
- package/dist/logs/tailer.d.ts +0 -17
- package/dist/logs/tailer.js +0 -217
- package/dist/sync/__tests__/change-detect.test.d.ts +0 -1
- package/dist/sync/__tests__/change-detect.test.js +0 -123
- package/dist/sync/__tests__/hash.test.d.ts +0 -1
- package/dist/sync/__tests__/hash.test.js +0 -18
- package/dist/sync/change-detect.d.ts +0 -19
- package/dist/sync/change-detect.js +0 -136
- package/dist/sync/conflict-ui.d.ts +0 -5
- package/dist/sync/conflict-ui.js +0 -78
- package/dist/sync/executor.d.ts +0 -23
- package/dist/sync/executor.js +0 -102
- package/dist/sync/hash.d.ts +0 -1
- package/dist/sync/hash.js +0 -4
- package/dist/sync/types.d.ts +0 -24
- package/dist/sync/types.js +0 -1
- package/dist/template/manifest.d.ts +0 -17
- package/dist/template/manifest.js +0 -145
- package/dist/tools/types.d.ts +0 -52
- package/dist/tools/types.js +0 -8
- package/dist/types-manager.d.ts +0 -1
- package/dist/types-manager.js +0 -35
- package/dist/types.d.ts +0 -349
- package/dist/types.js +0 -1
- package/dist/ui/__tests__/banner.test.d.ts +0 -1
- package/dist/ui/__tests__/banner.test.js +0 -98
- package/dist/ui/__tests__/colors.test.d.ts +0 -1
- package/dist/ui/__tests__/colors.test.js +0 -22
- package/dist/ui/__tests__/keyboard.test.d.ts +0 -1
- package/dist/ui/__tests__/keyboard.test.js +0 -34
- package/dist/ui/__tests__/status-line.test.d.ts +0 -1
- package/dist/ui/__tests__/status-line.test.js +0 -54
- package/dist/ui/banner.d.ts +0 -29
- package/dist/ui/banner.js +0 -153
- package/dist/ui/colors.d.ts +0 -4
- package/dist/ui/colors.js +0 -7
- package/dist/ui/keyboard.d.ts +0 -12
- package/dist/ui/keyboard.js +0 -61
- package/dist/ui/status-line.d.ts +0 -6
- package/dist/ui/status-line.js +0 -53
- package/dist/utils/__tests__/format-error.test.d.ts +0 -1
- package/dist/utils/__tests__/format-error.test.js +0 -43
- package/dist/utils/__tests__/help-json.test.d.ts +0 -1
- package/dist/utils/__tests__/help-json.test.js +0 -39
- package/dist/utils/__tests__/http.test.d.ts +0 -1
- package/dist/utils/__tests__/http.test.js +0 -381
- package/dist/utils/__tests__/ignore-matcher.test.d.ts +0 -1
- package/dist/utils/__tests__/ignore-matcher.test.js +0 -205
- package/dist/utils/__tests__/output.test.d.ts +0 -1
- package/dist/utils/__tests__/output.test.js +0 -38
- package/dist/utils/__tests__/prompt.test.d.ts +0 -1
- package/dist/utils/__tests__/prompt.test.js +0 -27
- package/dist/utils/__tests__/resolve.test.d.ts +0 -1
- package/dist/utils/__tests__/resolve.test.js +0 -95
- package/dist/utils/__tests__/sqlite.test.d.ts +0 -1
- package/dist/utils/__tests__/sqlite.test.js +0 -89
- package/dist/utils/agent-guidance.d.ts +0 -47
- package/dist/utils/agent-guidance.js +0 -128
- package/dist/utils/app-info.d.ts +0 -70
- package/dist/utils/app-info.js +0 -167
- package/dist/utils/curl-parser.d.ts +0 -12
- package/dist/utils/curl-parser.js +0 -162
- package/dist/utils/data-input.d.ts +0 -1
- package/dist/utils/data-input.js +0 -38
- package/dist/utils/format-error.d.ts +0 -10
- package/dist/utils/format-error.js +0 -38
- package/dist/utils/fs.d.ts +0 -5
- package/dist/utils/fs.js +0 -23
- package/dist/utils/help-json.d.ts +0 -70
- package/dist/utils/help-json.js +0 -88
- package/dist/utils/http.d.ts +0 -46
- package/dist/utils/http.js +0 -421
- package/dist/utils/ignore-matcher.d.ts +0 -47
- package/dist/utils/ignore-matcher.js +0 -132
- package/dist/utils/output.d.ts +0 -17
- package/dist/utils/output.js +0 -27
- package/dist/utils/prompt.d.ts +0 -5
- package/dist/utils/prompt.js +0 -42
- package/dist/utils/resolve.d.ts +0 -12
- package/dist/utils/resolve.js +0 -52
- package/dist/utils/setup-state.d.ts +0 -3
- package/dist/utils/setup-state.js +0 -19
- package/dist/utils/sqlite-adapter.d.ts +0 -48
- package/dist/utils/sqlite-adapter.js +0 -147
- package/dist/utils/sqlite.d.ts +0 -14
- package/dist/utils/sqlite.js +0 -14
- package/dist/utils/subprocess.d.ts +0 -19
- package/dist/utils/subprocess.js +0 -27
- package/dist/utils/which.d.ts +0 -14
- package/dist/utils/which.js +0 -47
- package/dist/utils/zip.d.ts +0 -7
- package/dist/utils/zip.js +0 -42
- package/dist/validate/__tests__/validate.test.d.ts +0 -1
- package/dist/validate/__tests__/validate.test.js +0 -42
- package/dist/validate/freshness.d.ts +0 -28
- package/dist/validate/freshness.js +0 -48
- package/dist/validate/preview.d.ts +0 -21
- package/dist/validate/preview.js +0 -39
- package/dist/workspace/resolve.d.ts +0 -19
- package/dist/workspace/resolve.js +0 -100
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
vi.mock('fs', async (importOriginal) => {
|
|
3
|
-
const actual = await importOriginal();
|
|
4
|
-
return { ...actual, existsSync: vi.fn(() => true) };
|
|
5
|
-
});
|
|
6
|
-
vi.mock('child_process', async (importOriginal) => {
|
|
7
|
-
const actual = await importOriginal();
|
|
8
|
-
return { ...actual, execFileSync: vi.fn() };
|
|
9
|
-
});
|
|
10
|
-
import { existsSync } from 'fs';
|
|
11
|
-
import { execFileSync } from 'child_process';
|
|
12
|
-
import { resetGitProbeCache } from '../../git/preflight.js';
|
|
13
|
-
const mockedExistsSync = vi.mocked(existsSync);
|
|
14
|
-
const mockedExecFileSync = vi.mocked(execFileSync);
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
vi.clearAllMocks();
|
|
17
|
-
vi.restoreAllMocks();
|
|
18
|
-
vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
19
|
-
vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
20
|
-
mockedExistsSync.mockReturnValue(true);
|
|
21
|
-
// checkSystemDeps now goes through probeGit() which caches the resolved
|
|
22
|
-
// git binary for the lifetime of the process. Reset between tests so each
|
|
23
|
-
// mock setup actually drives a fresh probe.
|
|
24
|
-
resetGitProbeCache();
|
|
25
|
-
});
|
|
26
|
-
afterEach(() => {
|
|
27
|
-
vi.restoreAllMocks();
|
|
28
|
-
});
|
|
29
|
-
// ── Helpers ──────────────────────────────────────────────────────────
|
|
30
|
-
function makeCtx(overrides = {}) {
|
|
31
|
-
return {
|
|
32
|
-
credentials: { apiKey: 'key', email: 'user@test.com', baseUrl: 'https://runwork.ai' },
|
|
33
|
-
client: null,
|
|
34
|
-
config: null,
|
|
35
|
-
cwd: '/test/dir',
|
|
36
|
-
...overrides,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function makeConfig(overrides = {}) {
|
|
40
|
-
return {
|
|
41
|
-
workspaceId: 'ws-1',
|
|
42
|
-
workspaceName: 'Test WS',
|
|
43
|
-
appId: 'app-1',
|
|
44
|
-
appName: 'Test App',
|
|
45
|
-
...overrides,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
// ── 1. System Dependencies ───────────────────────────────────────────
|
|
49
|
-
describe('checkSystemDeps', () => {
|
|
50
|
-
it('passes with modern git', async () => {
|
|
51
|
-
mockedExecFileSync.mockReturnValue(Buffer.from('git version 2.43.0'));
|
|
52
|
-
const { checkSystemDeps } = await import('../checks.js');
|
|
53
|
-
const result = await checkSystemDeps();
|
|
54
|
-
expect(result.status).toBe('pass');
|
|
55
|
-
expect(result.message).toContain('2.43.0');
|
|
56
|
-
});
|
|
57
|
-
it('warns with old git', async () => {
|
|
58
|
-
mockedExecFileSync.mockReturnValue(Buffer.from('git version 2.15.0'));
|
|
59
|
-
const { checkSystemDeps } = await import('../checks.js');
|
|
60
|
-
const result = await checkSystemDeps();
|
|
61
|
-
expect(result.status).toBe('warn');
|
|
62
|
-
expect(result.message).toContain('old');
|
|
63
|
-
});
|
|
64
|
-
it('fails when git not found', async () => {
|
|
65
|
-
mockedExecFileSync.mockImplementation(() => { throw new Error('not found'); });
|
|
66
|
-
const { checkSystemDeps } = await import('../checks.js');
|
|
67
|
-
const result = await checkSystemDeps();
|
|
68
|
-
expect(result.status).toBe('fail');
|
|
69
|
-
expect(result.fix).toBeDefined();
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
// ── 4a. Git credential helper ────────────────────────────────────────
|
|
73
|
-
describe('checkGitCredentialHelper', () => {
|
|
74
|
-
function gitConfigOutput(line) {
|
|
75
|
-
return Buffer.from(`${line}\n`);
|
|
76
|
-
}
|
|
77
|
-
function gitConfigEmpty() {
|
|
78
|
-
// git config --get-regexp returns 1 with no output when nothing matches
|
|
79
|
-
const err = new Error('git config exited with code 1');
|
|
80
|
-
err.status = 1;
|
|
81
|
-
throw err;
|
|
82
|
-
}
|
|
83
|
-
it('skips when not logged in', async () => {
|
|
84
|
-
const { checkGitCredentialHelper } = await import('../checks.js');
|
|
85
|
-
const ctx = makeCtx({ credentials: null });
|
|
86
|
-
const result = await checkGitCredentialHelper(ctx);
|
|
87
|
-
expect(result.status).toBe('skip');
|
|
88
|
-
expect(result.message).toContain('not logged in');
|
|
89
|
-
});
|
|
90
|
-
it('skips when git is not installed', async () => {
|
|
91
|
-
const { checkGitCredentialHelper } = await import('../checks.js');
|
|
92
|
-
// First call (probeGit -> git --version) throws ENOENT-shaped error.
|
|
93
|
-
mockedExecFileSync.mockImplementation((file) => {
|
|
94
|
-
if (file === 'git') {
|
|
95
|
-
const err = new Error('spawn git ENOENT');
|
|
96
|
-
err.code = 'ENOENT';
|
|
97
|
-
throw err;
|
|
98
|
-
}
|
|
99
|
-
throw new Error('unreachable');
|
|
100
|
-
});
|
|
101
|
-
const ctx = makeCtx();
|
|
102
|
-
const result = await checkGitCredentialHelper(ctx);
|
|
103
|
-
expect(result.status).toBe('skip');
|
|
104
|
-
expect(result.message).toContain('git not installed');
|
|
105
|
-
});
|
|
106
|
-
it('passes when helper is registered for the credentials origin', async () => {
|
|
107
|
-
const { checkGitCredentialHelper } = await import('../checks.js');
|
|
108
|
-
mockedExecFileSync.mockImplementation(((file, args) => {
|
|
109
|
-
if (file === 'git' && args?.[0] === '--version')
|
|
110
|
-
return Buffer.from('git version 2.43.0');
|
|
111
|
-
if (file === 'git' && args?.includes('--get-regexp')) {
|
|
112
|
-
return gitConfigOutput('credential.https://runwork.ai.helper !runwork git-credential-helper');
|
|
113
|
-
}
|
|
114
|
-
throw new Error(`unexpected call: ${file} ${args?.join(' ')}`);
|
|
115
|
-
}));
|
|
116
|
-
const ctx = makeCtx();
|
|
117
|
-
const result = await checkGitCredentialHelper(ctx);
|
|
118
|
-
expect(result.status).toBe('pass');
|
|
119
|
-
expect(result.message).toContain('https://runwork.ai');
|
|
120
|
-
});
|
|
121
|
-
it('fails when no entry exists in git config', async () => {
|
|
122
|
-
const { checkGitCredentialHelper } = await import('../checks.js');
|
|
123
|
-
mockedExecFileSync.mockImplementation(((file, args) => {
|
|
124
|
-
if (file === 'git' && args?.[0] === '--version')
|
|
125
|
-
return Buffer.from('git version 2.43.0');
|
|
126
|
-
if (file === 'git' && args?.includes('--get-regexp'))
|
|
127
|
-
gitConfigEmpty();
|
|
128
|
-
throw new Error(`unexpected call: ${file} ${args?.join(' ')}`);
|
|
129
|
-
}));
|
|
130
|
-
const ctx = makeCtx();
|
|
131
|
-
const result = await checkGitCredentialHelper(ctx);
|
|
132
|
-
expect(result.status).toBe('fail');
|
|
133
|
-
expect(result.fix).toBe('runwork doctor --fix');
|
|
134
|
-
});
|
|
135
|
-
it('fails when entries exist but none match the credentials origin', async () => {
|
|
136
|
-
const { checkGitCredentialHelper } = await import('../checks.js');
|
|
137
|
-
mockedExecFileSync.mockImplementation(((file, args) => {
|
|
138
|
-
if (file === 'git' && args?.[0] === '--version')
|
|
139
|
-
return Buffer.from('git version 2.43.0');
|
|
140
|
-
if (file === 'git' && args?.includes('--get-regexp')) {
|
|
141
|
-
// Stale entry from a previous staging URL.
|
|
142
|
-
return gitConfigOutput('credential.https://runwork-staging.example.com.helper !runwork git-credential-helper');
|
|
143
|
-
}
|
|
144
|
-
throw new Error(`unexpected call: ${file} ${args?.join(' ')}`);
|
|
145
|
-
}));
|
|
146
|
-
const ctx = makeCtx({
|
|
147
|
-
credentials: { apiKey: 'k', email: 'u@e.com', baseUrl: 'https://runwork.ai' },
|
|
148
|
-
});
|
|
149
|
-
const result = await checkGitCredentialHelper(ctx);
|
|
150
|
-
expect(result.status).toBe('fail');
|
|
151
|
-
expect(result.message).toContain('1 other runwork helper');
|
|
152
|
-
});
|
|
153
|
-
it('fails when the registered absolute path no longer exists', async () => {
|
|
154
|
-
const { checkGitCredentialHelper } = await import('../checks.js');
|
|
155
|
-
mockedExecFileSync.mockImplementation(((file, args) => {
|
|
156
|
-
if (file === 'git' && args?.[0] === '--version')
|
|
157
|
-
return Buffer.from('git version 2.43.0');
|
|
158
|
-
if (file === 'git' && args?.includes('--get-regexp')) {
|
|
159
|
-
return gitConfigOutput('credential.https://runwork.ai.helper !"/old/path/runwork" git-credential-helper');
|
|
160
|
-
}
|
|
161
|
-
throw new Error(`unexpected call: ${file} ${args?.join(' ')}`);
|
|
162
|
-
}));
|
|
163
|
-
mockedExistsSync.mockImplementation((p) => p !== '/old/path/runwork');
|
|
164
|
-
const ctx = makeCtx();
|
|
165
|
-
const result = await checkGitCredentialHelper(ctx);
|
|
166
|
-
expect(result.status).toBe('fail');
|
|
167
|
-
expect(result.message).toContain('binary not found');
|
|
168
|
-
expect(result.fix).toContain('runwork doctor --fix');
|
|
169
|
-
});
|
|
170
|
-
it('passes when the registered absolute path still exists', async () => {
|
|
171
|
-
const { checkGitCredentialHelper } = await import('../checks.js');
|
|
172
|
-
mockedExecFileSync.mockImplementation(((file, args) => {
|
|
173
|
-
if (file === 'git' && args?.[0] === '--version')
|
|
174
|
-
return Buffer.from('git version 2.43.0');
|
|
175
|
-
if (file === 'git' && args?.includes('--get-regexp')) {
|
|
176
|
-
return gitConfigOutput('credential.https://runwork.ai.helper !"/usr/local/bin/runwork" git-credential-helper');
|
|
177
|
-
}
|
|
178
|
-
throw new Error(`unexpected call: ${file} ${args?.join(' ')}`);
|
|
179
|
-
}));
|
|
180
|
-
mockedExistsSync.mockReturnValue(true);
|
|
181
|
-
const ctx = makeCtx();
|
|
182
|
-
const result = await checkGitCredentialHelper(ctx);
|
|
183
|
-
expect(result.status).toBe('pass');
|
|
184
|
-
});
|
|
185
|
-
it('passes a Windows-style absolute path that exists', async () => {
|
|
186
|
-
const { checkGitCredentialHelper } = await import('../checks.js');
|
|
187
|
-
mockedExecFileSync.mockImplementation(((file, args) => {
|
|
188
|
-
if (file === 'git' && args?.[0] === '--version')
|
|
189
|
-
return Buffer.from('git version 2.43.0');
|
|
190
|
-
if (file === 'git' && args?.includes('--get-regexp')) {
|
|
191
|
-
return gitConfigOutput('credential.https://runwork.ai.helper !"C:/Users/test/.runwork/bin/runwork.exe" git-credential-helper');
|
|
192
|
-
}
|
|
193
|
-
throw new Error(`unexpected call: ${file} ${args?.join(' ')}`);
|
|
194
|
-
}));
|
|
195
|
-
mockedExistsSync.mockReturnValue(true);
|
|
196
|
-
const ctx = makeCtx();
|
|
197
|
-
const result = await checkGitCredentialHelper(ctx);
|
|
198
|
-
expect(result.status).toBe('pass');
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
// ── 3 & 4. Auth + Network ───────────────────────────────────────────
|
|
202
|
-
describe('checkAuthAndNetwork', () => {
|
|
203
|
-
it('fails auth when no credentials', async () => {
|
|
204
|
-
const { checkAuthAndNetwork } = await import('../checks.js');
|
|
205
|
-
const ctx = makeCtx({ credentials: null, client: null });
|
|
206
|
-
const { auth, network } = await checkAuthAndNetwork(ctx);
|
|
207
|
-
expect(auth.status).toBe('fail');
|
|
208
|
-
expect(auth.fix).toContain('login');
|
|
209
|
-
expect(network.status).toBe('skip');
|
|
210
|
-
});
|
|
211
|
-
it('passes when API responds', async () => {
|
|
212
|
-
const { checkAuthAndNetwork } = await import('../checks.js');
|
|
213
|
-
const mockClient = {
|
|
214
|
-
listWorkspaces: vi.fn().mockResolvedValue([{ id: 'ws-1', name: 'WS' }]),
|
|
215
|
-
};
|
|
216
|
-
const ctx = makeCtx({ client: mockClient });
|
|
217
|
-
const { auth, network } = await checkAuthAndNetwork(ctx);
|
|
218
|
-
expect(auth.status).toBe('pass');
|
|
219
|
-
expect(auth.message).toContain('user@test.com');
|
|
220
|
-
expect(network.status).toBe('pass');
|
|
221
|
-
});
|
|
222
|
-
it('distinguishes auth error from network error', async () => {
|
|
223
|
-
const { checkAuthAndNetwork } = await import('../checks.js');
|
|
224
|
-
const mockClient = {
|
|
225
|
-
listWorkspaces: vi.fn().mockRejectedValue(new Error('API error 401: Unauthorized')),
|
|
226
|
-
};
|
|
227
|
-
const ctx = makeCtx({ client: mockClient });
|
|
228
|
-
const { auth, network } = await checkAuthAndNetwork(ctx);
|
|
229
|
-
expect(auth.status).toBe('fail');
|
|
230
|
-
expect(network.status).toBe('pass');
|
|
231
|
-
});
|
|
232
|
-
it('fails network on connection error', async () => {
|
|
233
|
-
const { checkAuthAndNetwork } = await import('../checks.js');
|
|
234
|
-
const mockClient = {
|
|
235
|
-
listWorkspaces: vi.fn().mockRejectedValue(new Error('fetch failed')),
|
|
236
|
-
};
|
|
237
|
-
const ctx = makeCtx({ client: mockClient });
|
|
238
|
-
const { auth, network } = await checkAuthAndNetwork(ctx);
|
|
239
|
-
expect(auth.status).toBe('skip');
|
|
240
|
-
expect(network.status).toBe('fail');
|
|
241
|
-
});
|
|
242
|
-
});
|
|
243
|
-
// ── 5. Project Config ────────────────────────────────────────────────
|
|
244
|
-
describe('checkProjectConfig', () => {
|
|
245
|
-
it('passes with valid config', async () => {
|
|
246
|
-
const { checkProjectConfig } = await import('../checks.js');
|
|
247
|
-
mockedExistsSync.mockReturnValue(true);
|
|
248
|
-
const ctx = makeCtx({ config: makeConfig() });
|
|
249
|
-
const result = await checkProjectConfig(ctx);
|
|
250
|
-
expect(result.status).toBe('pass');
|
|
251
|
-
expect(result.message).toContain('Test App');
|
|
252
|
-
expect(result.message).toContain('Test WS');
|
|
253
|
-
});
|
|
254
|
-
it('fails with malformed config (null)', async () => {
|
|
255
|
-
const { checkProjectConfig } = await import('../checks.js');
|
|
256
|
-
mockedExistsSync.mockReturnValue(true);
|
|
257
|
-
const ctx = makeCtx({ config: null });
|
|
258
|
-
const result = await checkProjectConfig(ctx);
|
|
259
|
-
expect(result.status).toBe('fail');
|
|
260
|
-
expect(result.message).toContain('malformed');
|
|
261
|
-
});
|
|
262
|
-
it('passes outside project when default workspace set', async () => {
|
|
263
|
-
const { checkProjectConfig } = await import('../checks.js');
|
|
264
|
-
mockedExistsSync.mockReturnValue(false);
|
|
265
|
-
const creds = {
|
|
266
|
-
apiKey: 'key',
|
|
267
|
-
email: 'user@test.com',
|
|
268
|
-
baseUrl: 'https://runwork.ai',
|
|
269
|
-
defaultWorkspaceId: 'ws-1',
|
|
270
|
-
defaultWorkspaceName: 'My WS',
|
|
271
|
-
};
|
|
272
|
-
const ctx = makeCtx({ credentials: creds, config: null });
|
|
273
|
-
const result = await checkProjectConfig(ctx);
|
|
274
|
-
expect(result.status).toBe('pass');
|
|
275
|
-
expect(result.message).toContain('My WS');
|
|
276
|
-
});
|
|
277
|
-
it('warns outside project when no default workspace', async () => {
|
|
278
|
-
const { checkProjectConfig } = await import('../checks.js');
|
|
279
|
-
mockedExistsSync.mockReturnValue(false);
|
|
280
|
-
const ctx = makeCtx({ config: null });
|
|
281
|
-
const result = await checkProjectConfig(ctx);
|
|
282
|
-
expect(result.status).toBe('warn');
|
|
283
|
-
expect(result.fix).toBeDefined();
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
// ── 6. App Exists ────────────────────────────────────────────────────
|
|
287
|
-
describe('checkAppExists', () => {
|
|
288
|
-
it('skips when no config or client', async () => {
|
|
289
|
-
const { checkAppExists } = await import('../checks.js');
|
|
290
|
-
const ctx = makeCtx({ config: null });
|
|
291
|
-
const result = await checkAppExists(ctx);
|
|
292
|
-
expect(result.status).toBe('skip');
|
|
293
|
-
});
|
|
294
|
-
it('passes when app found on server', async () => {
|
|
295
|
-
const { checkAppExists } = await import('../checks.js');
|
|
296
|
-
const mockClient = {
|
|
297
|
-
getApp: vi.fn().mockResolvedValue({ id: 'app-1', name: 'Test' }),
|
|
298
|
-
};
|
|
299
|
-
const ctx = makeCtx({ config: makeConfig(), client: mockClient });
|
|
300
|
-
const result = await checkAppExists(ctx);
|
|
301
|
-
expect(result.status).toBe('pass');
|
|
302
|
-
});
|
|
303
|
-
it('fails when app not found', async () => {
|
|
304
|
-
const { checkAppExists } = await import('../checks.js');
|
|
305
|
-
const mockClient = {
|
|
306
|
-
getApp: vi.fn().mockRejectedValue(new Error('404')),
|
|
307
|
-
};
|
|
308
|
-
const ctx = makeCtx({ config: makeConfig(), client: mockClient });
|
|
309
|
-
const result = await checkAppExists(ctx);
|
|
310
|
-
expect(result.status).toBe('fail');
|
|
311
|
-
expect(result.message).toContain('not found');
|
|
312
|
-
});
|
|
313
|
-
});
|
|
314
|
-
// ── 7. Git Remote ────────────────────────────────────────────────────
|
|
315
|
-
describe('checkGitRemote', () => {
|
|
316
|
-
it('skips when no project config', async () => {
|
|
317
|
-
const { checkGitRemote } = await import('../checks.js');
|
|
318
|
-
const ctx = makeCtx({ config: null });
|
|
319
|
-
const result = await checkGitRemote(ctx);
|
|
320
|
-
expect(result.status).toBe('skip');
|
|
321
|
-
});
|
|
322
|
-
it('fails when not a git repo', async () => {
|
|
323
|
-
const { checkGitRemote } = await import('../checks.js');
|
|
324
|
-
mockedExistsSync.mockReturnValue(false);
|
|
325
|
-
const ctx = makeCtx({ config: makeConfig() });
|
|
326
|
-
const result = await checkGitRemote(ctx);
|
|
327
|
-
expect(result.status).toBe('fail');
|
|
328
|
-
expect(result.message).toContain('not a git repository');
|
|
329
|
-
});
|
|
330
|
-
it('passes when remote configured correctly', async () => {
|
|
331
|
-
const { checkGitRemote } = await import('../checks.js');
|
|
332
|
-
mockedExistsSync.mockReturnValue(true);
|
|
333
|
-
mockedExecFileSync.mockReturnValue(Buffer.from('https://runwork.ai/git/ws-1/app-1'));
|
|
334
|
-
const ctx = makeCtx({ config: makeConfig() });
|
|
335
|
-
const result = await checkGitRemote(ctx);
|
|
336
|
-
expect(result.status).toBe('pass');
|
|
337
|
-
});
|
|
338
|
-
it('fails when remote not configured', async () => {
|
|
339
|
-
const { checkGitRemote } = await import('../checks.js');
|
|
340
|
-
mockedExistsSync.mockReturnValue(true);
|
|
341
|
-
mockedExecFileSync.mockImplementation(() => { throw new Error('No such remote'); });
|
|
342
|
-
const ctx = makeCtx({ config: makeConfig() });
|
|
343
|
-
const result = await checkGitRemote(ctx);
|
|
344
|
-
expect(result.status).toBe('fail');
|
|
345
|
-
expect(result.fix).toContain('git remote add');
|
|
346
|
-
});
|
|
347
|
-
});
|
|
348
|
-
// ── 8. Agent Setup ───────────────────────────────────────────────────
|
|
349
|
-
describe('checkAgentSetup', () => {
|
|
350
|
-
it('fails when setup never run', async () => {
|
|
351
|
-
const { checkAgentSetup } = await import('../checks.js');
|
|
352
|
-
mockedExistsSync.mockReturnValue(false);
|
|
353
|
-
const result = await checkAgentSetup();
|
|
354
|
-
expect(result.status).toBe('fail');
|
|
355
|
-
expect(result.fix).toContain('runwork setup');
|
|
356
|
-
});
|
|
357
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for the first-party distribution checks added to runwork doctor.
|
|
3
|
-
*
|
|
4
|
-
* These checks rely on `httpFetch` (our node:https-backed fetch wrapper) to
|
|
5
|
-
* hit https://runwork.ai/cli/latest.json. We mock the wrapper to redirect
|
|
6
|
-
* those calls to a local HTTP server so the suite has no outbound network
|
|
7
|
-
* dependency. We still exercise the real wrapper end to end -- only the
|
|
8
|
-
* URL is rewritten -- so this remains an integration test of the network
|
|
9
|
-
* code path.
|
|
10
|
-
*/
|
|
11
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
12
|
-
import { createServer } from 'node:http';
|
|
13
|
-
let currentRedirectBase = null;
|
|
14
|
-
vi.mock('../../utils/http.js', async () => {
|
|
15
|
-
const actual = await vi.importActual('../../utils/http.js');
|
|
16
|
-
return {
|
|
17
|
-
...actual,
|
|
18
|
-
httpFetch: (url, init) => {
|
|
19
|
-
if (currentRedirectBase && url.startsWith('https://runwork.ai')) {
|
|
20
|
-
const redirected = url.replace('https://runwork.ai', currentRedirectBase);
|
|
21
|
-
return actual.httpFetch(redirected, init);
|
|
22
|
-
}
|
|
23
|
-
return actual.httpFetch(url, init);
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
});
|
|
27
|
-
import { checkCliVersion, checkCliArtifactReachable, checkCliInstallLocation, } from '../checks.js';
|
|
28
|
-
function startServer() {
|
|
29
|
-
return new Promise((resolvePromise) => {
|
|
30
|
-
let currentManifest = null;
|
|
31
|
-
let manifestStatus = 200;
|
|
32
|
-
const artifactStatuses = new Map();
|
|
33
|
-
const server = createServer((req, res) => {
|
|
34
|
-
const url = req.url ?? '/';
|
|
35
|
-
if (url === '/cli/latest.json') {
|
|
36
|
-
if (currentManifest === null) {
|
|
37
|
-
res.writeHead(manifestStatus, { 'Content-Type': 'application/json' });
|
|
38
|
-
res.end('{"error": "not available"}');
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
res.writeHead(manifestStatus, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
42
|
-
res.end(JSON.stringify(currentManifest));
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
// Artifact route: /cli/releases/<version>/<artifact>
|
|
46
|
-
const releaseMatch = url.match(/^\/cli\/releases\/[^/]+\/runwork-([^/]+)\.(tar\.gz|zip)$/);
|
|
47
|
-
if (releaseMatch) {
|
|
48
|
-
const key = releaseMatch[1];
|
|
49
|
-
const status = artifactStatuses.get(key) ?? 200;
|
|
50
|
-
res.writeHead(status, { 'Content-Type': 'application/octet-stream' });
|
|
51
|
-
res.end('');
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
res.writeHead(404);
|
|
55
|
-
res.end('not found');
|
|
56
|
-
});
|
|
57
|
-
server.listen(0, '127.0.0.1', () => {
|
|
58
|
-
const addr = server.address();
|
|
59
|
-
if (addr && typeof addr === 'object') {
|
|
60
|
-
const port = addr.port;
|
|
61
|
-
resolvePromise({
|
|
62
|
-
url: `http://127.0.0.1:${port}`,
|
|
63
|
-
stop: () => new Promise((stop) => server.close(() => stop())),
|
|
64
|
-
setManifest(m, s = 200) {
|
|
65
|
-
currentManifest = m;
|
|
66
|
-
manifestStatus = s;
|
|
67
|
-
},
|
|
68
|
-
setArtifactStatus(key, s) {
|
|
69
|
-
artifactStatuses.set(key, s);
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
describe('checkCliVersion (runwork.ai backed)', () => {
|
|
77
|
-
let server;
|
|
78
|
-
beforeEach(async () => {
|
|
79
|
-
server = await startServer();
|
|
80
|
-
currentRedirectBase = server.url;
|
|
81
|
-
});
|
|
82
|
-
afterEach(async () => {
|
|
83
|
-
currentRedirectBase = null;
|
|
84
|
-
await server.stop();
|
|
85
|
-
});
|
|
86
|
-
it('skips with an explicit unreachable message when the manifest 404s', async () => {
|
|
87
|
-
server.setManifest(null, 404);
|
|
88
|
-
const result = await checkCliVersion();
|
|
89
|
-
expect(result.status).toBe('skip');
|
|
90
|
-
expect(result.message).toContain('unreachable');
|
|
91
|
-
});
|
|
92
|
-
it('passes when manifest version matches current VERSION', async () => {
|
|
93
|
-
// We cannot read VERSION here without depending on the generated file
|
|
94
|
-
// at test time; instead, serve "0.0.0" and expect the test to compare.
|
|
95
|
-
// To make this deterministic, we fetch the current VERSION from the
|
|
96
|
-
// module under test via a side channel. See the runner for the value.
|
|
97
|
-
const { VERSION } = await import('../../generated/version.js');
|
|
98
|
-
server.setManifest({ version: VERSION });
|
|
99
|
-
const result = await checkCliVersion();
|
|
100
|
-
expect(result.status).toBe('pass');
|
|
101
|
-
expect(result.message).toContain(VERSION);
|
|
102
|
-
});
|
|
103
|
-
it('warns when the manifest has a newer version', async () => {
|
|
104
|
-
server.setManifest({ version: '99.99.99' });
|
|
105
|
-
const result = await checkCliVersion();
|
|
106
|
-
expect(result.status).toBe('warn');
|
|
107
|
-
expect(result.message).toContain('99.99.99');
|
|
108
|
-
expect(result.fix).toBe('runwork upgrade');
|
|
109
|
-
});
|
|
110
|
-
it('skips when the manifest has no version field', async () => {
|
|
111
|
-
server.setManifest({ name: 'runwork' });
|
|
112
|
-
const result = await checkCliVersion();
|
|
113
|
-
expect(result.status).toBe('skip');
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
describe('checkCliArtifactReachable', () => {
|
|
117
|
-
let server;
|
|
118
|
-
beforeEach(async () => {
|
|
119
|
-
server = await startServer();
|
|
120
|
-
currentRedirectBase = server.url;
|
|
121
|
-
});
|
|
122
|
-
afterEach(async () => {
|
|
123
|
-
currentRedirectBase = null;
|
|
124
|
-
await server.stop();
|
|
125
|
-
});
|
|
126
|
-
function platformKey() {
|
|
127
|
-
const os = process.platform === 'darwin' ? 'darwin' : process.platform === 'linux' ? 'linux' : 'windows';
|
|
128
|
-
const arch = process.arch === 'arm64' ? 'arm64' : 'x64';
|
|
129
|
-
return `${os}-${arch}`;
|
|
130
|
-
}
|
|
131
|
-
it('fails when the manifest is unreachable', async () => {
|
|
132
|
-
server.setManifest(null, 500);
|
|
133
|
-
const result = await checkCliArtifactReachable();
|
|
134
|
-
expect(result.status).toBe('fail');
|
|
135
|
-
expect(result.message).toContain('unreachable');
|
|
136
|
-
});
|
|
137
|
-
it('fails when the manifest has no artifact for the host platform', async () => {
|
|
138
|
-
server.setManifest({
|
|
139
|
-
version: '99.99.99',
|
|
140
|
-
artifacts: {
|
|
141
|
-
'unknown-unknown': { path: '/cli/releases/99.99.99/runwork-unknown-unknown.tar.gz', sha256: 'deadbeef' },
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
const result = await checkCliArtifactReachable();
|
|
145
|
-
expect(result.status).toBe('fail');
|
|
146
|
-
expect(result.message).toMatch(/no artifact for/);
|
|
147
|
-
});
|
|
148
|
-
it('passes when the artifact HEAD returns 200', async () => {
|
|
149
|
-
const key = platformKey();
|
|
150
|
-
const ext = key === 'windows-x64' ? 'zip' : 'tar.gz';
|
|
151
|
-
server.setManifest({
|
|
152
|
-
version: '99.99.99',
|
|
153
|
-
artifacts: {
|
|
154
|
-
[key]: {
|
|
155
|
-
path: `/cli/releases/99.99.99/runwork-${key}.${ext}`,
|
|
156
|
-
sha256: 'deadbeef',
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
server.setArtifactStatus(key, 200);
|
|
161
|
-
const result = await checkCliArtifactReachable();
|
|
162
|
-
expect(result.status).toBe('pass');
|
|
163
|
-
expect(result.message).toContain(key);
|
|
164
|
-
});
|
|
165
|
-
it('fails when the artifact HEAD returns a non-2xx status', async () => {
|
|
166
|
-
const key = platformKey();
|
|
167
|
-
const ext = key === 'windows-x64' ? 'zip' : 'tar.gz';
|
|
168
|
-
server.setManifest({
|
|
169
|
-
version: '99.99.99',
|
|
170
|
-
artifacts: {
|
|
171
|
-
[key]: {
|
|
172
|
-
path: `/cli/releases/99.99.99/runwork-${key}.${ext}`,
|
|
173
|
-
sha256: 'deadbeef',
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
});
|
|
177
|
-
server.setArtifactStatus(key, 404);
|
|
178
|
-
const result = await checkCliArtifactReachable();
|
|
179
|
-
expect(result.status).toBe('fail');
|
|
180
|
-
expect(result.message).toContain('404');
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
describe('checkCliInstallLocation', () => {
|
|
184
|
-
const originalExecPath = process.execPath;
|
|
185
|
-
const originalArgv1 = process.argv[1];
|
|
186
|
-
afterEach(() => {
|
|
187
|
-
Object.defineProperty(process, 'execPath', { value: originalExecPath, writable: true });
|
|
188
|
-
process.argv[1] = originalArgv1;
|
|
189
|
-
});
|
|
190
|
-
it('warns when process does not run from the canonical ~/.runwork/bin/runwork', async () => {
|
|
191
|
-
Object.defineProperty(process, 'execPath', { value: '/usr/local/bin/node', writable: true });
|
|
192
|
-
process.argv[1] = '/opt/homebrew/bin/runwork';
|
|
193
|
-
const result = await checkCliInstallLocation();
|
|
194
|
-
expect(result.status).toBe('warn');
|
|
195
|
-
});
|
|
196
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
vi.mock('fs', () => ({ existsSync: vi.fn() }));
|
|
3
|
-
vi.mock('../../git/credentials.js', () => ({ ensureGitCredentialHelper: vi.fn(async () => { }) }));
|
|
4
|
-
vi.mock('../../git/remote.js', () => ({ ensureRunworkRemote: vi.fn(() => ({ action: 'added' })) }));
|
|
5
|
-
const { existsSync: mockExistsSync } = await import('fs');
|
|
6
|
-
const { ensureGitCredentialHelper } = await import('../../git/credentials.js');
|
|
7
|
-
const { ensureRunworkRemote } = await import('../../git/remote.js');
|
|
8
|
-
const { applyDoctorFixes, FIXABLE_CHECKS } = await import('../fix.js');
|
|
9
|
-
const creds = { apiKey: 'k', email: 'u@e.com', baseUrl: 'https://runwork.ai' };
|
|
10
|
-
const config = { workspaceId: 'ws', appId: 'app', workspaceName: 'WS', appName: 'App' };
|
|
11
|
-
function makeCtx(overrides = {}) {
|
|
12
|
-
return {
|
|
13
|
-
credentials: creds,
|
|
14
|
-
client: { getGitRemoteUrl: (ws, app) => `https://runwork.ai/api/git/${ws}/${app}` },
|
|
15
|
-
config,
|
|
16
|
-
cwd: '/repo',
|
|
17
|
-
...overrides,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
describe('health/fix applyDoctorFixes', () => {
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
vi.clearAllMocks();
|
|
23
|
-
vi.mocked(mockExistsSync).mockReturnValue(true); // .git present by default
|
|
24
|
-
});
|
|
25
|
-
it('only advertises the two fixable checks', () => {
|
|
26
|
-
expect(FIXABLE_CHECKS).toEqual(['git-credential-helper', 'git-remote']);
|
|
27
|
-
});
|
|
28
|
-
it('registers the git credential helper when that check is failing', async () => {
|
|
29
|
-
const outcomes = await applyDoctorFixes(makeCtx(), ['git-credential-helper']);
|
|
30
|
-
expect(ensureGitCredentialHelper).toHaveBeenCalledWith('https://runwork.ai');
|
|
31
|
-
expect(outcomes).toEqual([
|
|
32
|
-
{ name: 'git-credential-helper', applied: true, message: expect.stringContaining('registered') },
|
|
33
|
-
]);
|
|
34
|
-
});
|
|
35
|
-
it('cannot fix the credential helper when not logged in', async () => {
|
|
36
|
-
const outcomes = await applyDoctorFixes(makeCtx({ credentials: null }), ['git-credential-helper']);
|
|
37
|
-
expect(ensureGitCredentialHelper).not.toHaveBeenCalled();
|
|
38
|
-
expect(outcomes[0]).toMatchObject({ name: 'git-credential-helper', applied: false });
|
|
39
|
-
expect(outcomes[0].message).toContain('runwork login');
|
|
40
|
-
});
|
|
41
|
-
it('configures the runwork remote with the canonical /api/git URL', async () => {
|
|
42
|
-
const outcomes = await applyDoctorFixes(makeCtx(), ['git-remote']);
|
|
43
|
-
expect(ensureRunworkRemote).toHaveBeenCalledWith('/repo', 'https://runwork.ai/api/git/ws/app');
|
|
44
|
-
expect(outcomes[0]).toMatchObject({ name: 'git-remote', applied: true });
|
|
45
|
-
});
|
|
46
|
-
it('cannot fix the remote outside a project (no config)', async () => {
|
|
47
|
-
const outcomes = await applyDoctorFixes(makeCtx({ config: null }), ['git-remote']);
|
|
48
|
-
expect(ensureRunworkRemote).not.toHaveBeenCalled();
|
|
49
|
-
expect(outcomes[0]).toMatchObject({ name: 'git-remote', applied: false });
|
|
50
|
-
expect(outcomes[0].message).toContain('project');
|
|
51
|
-
});
|
|
52
|
-
it('cannot fix the remote when the directory is not a git repo', async () => {
|
|
53
|
-
vi.mocked(mockExistsSync).mockReturnValue(false);
|
|
54
|
-
const outcomes = await applyDoctorFixes(makeCtx(), ['git-remote']);
|
|
55
|
-
expect(ensureRunworkRemote).not.toHaveBeenCalled();
|
|
56
|
-
expect(outcomes[0]).toMatchObject({ name: 'git-remote', applied: false });
|
|
57
|
-
expect(outcomes[0].message).toContain('git init');
|
|
58
|
-
});
|
|
59
|
-
it('ignores failing checks it cannot remediate', async () => {
|
|
60
|
-
const outcomes = await applyDoctorFixes(makeCtx(), ['auth', 'network', 'cli-version']);
|
|
61
|
-
expect(ensureGitCredentialHelper).not.toHaveBeenCalled();
|
|
62
|
-
expect(ensureRunworkRemote).not.toHaveBeenCalled();
|
|
63
|
-
expect(outcomes).toEqual([]);
|
|
64
|
-
});
|
|
65
|
-
it('applies both fixes when both checks fail', async () => {
|
|
66
|
-
const outcomes = await applyDoctorFixes(makeCtx(), ['git-credential-helper', 'git-remote']);
|
|
67
|
-
expect(ensureGitCredentialHelper).toHaveBeenCalledOnce();
|
|
68
|
-
expect(ensureRunworkRemote).toHaveBeenCalledOnce();
|
|
69
|
-
expect(outcomes.map(o => o.name)).toEqual(['git-credential-helper', 'git-remote']);
|
|
70
|
-
expect(outcomes.every(o => o.applied)).toBe(true);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|