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
package/dist/dev/detach.js
DELETED
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `runwork dev --detach` orchestration.
|
|
3
|
-
*
|
|
4
|
-
* The detach contract:
|
|
5
|
-
*
|
|
6
|
-
* 1. Parent spawns the child via `process.execPath` with the same args
|
|
7
|
-
* plus `--internal-detached-child`. Child stdout/stderr are routed to
|
|
8
|
-
* log files in `.runwork/`. Parent calls `child.unref()` and exits as
|
|
9
|
-
* soon as the rendezvous signal arrives.
|
|
10
|
-
*
|
|
11
|
-
* 2. Child runs the same `runwork dev` code path as foreground, but in
|
|
12
|
-
* "detached" mode: no TUI, no keypress listener, JSON event stream
|
|
13
|
-
* to stdout (which is the log file).
|
|
14
|
-
*
|
|
15
|
-
* 3. Rendezvous channel is the session file, NOT a pipe. The child
|
|
16
|
-
* writes `.runwork/dev-session.json` once it has a `previewUrl`. The
|
|
17
|
-
* parent polls. When the parent observes a file whose `pid` matches
|
|
18
|
-
* the spawned child AND `previewUrl` is non-empty, parent prints +
|
|
19
|
-
* emits the URL and exits 0.
|
|
20
|
-
*
|
|
21
|
-
* 4. On timeout, parent attempts a single kill of the child, removes
|
|
22
|
-
* any partial session file, and exits 1 with a structured error.
|
|
23
|
-
*
|
|
24
|
-
* The file-as-IPC choice is intentional: pipes don't survive parent exit,
|
|
25
|
-
* and Node IPC has historical Bun-on-Windows quirks. The session file is
|
|
26
|
-
* already our durable lifecycle truth; reusing it for the handshake is
|
|
27
|
-
* free.
|
|
28
|
-
*/
|
|
29
|
-
import * as fs from 'fs';
|
|
30
|
-
import * as child_process from 'child_process';
|
|
31
|
-
import { getSessionPaths, readSessionFile, removeSessionFileIfOwned, } from './session.js';
|
|
32
|
-
/**
|
|
33
|
-
* Internal flag passed to the child. Hidden from `--help`. The child uses
|
|
34
|
-
* its presence to skip the parent-spawn branch and run the actual dev
|
|
35
|
-
* work in detached mode.
|
|
36
|
-
*/
|
|
37
|
-
export const INTERNAL_DETACHED_CHILD_FLAG = '--internal-detached-child';
|
|
38
|
-
const realPollDeps = {
|
|
39
|
-
now: () => Date.now(),
|
|
40
|
-
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
41
|
-
};
|
|
42
|
-
function resolvePollDeps(deps) {
|
|
43
|
-
if (!deps)
|
|
44
|
-
return realPollDeps;
|
|
45
|
-
return {
|
|
46
|
-
now: deps.now ?? realPollDeps.now,
|
|
47
|
-
sleep: deps.sleep ?? realPollDeps.sleep,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Poll the session file until either:
|
|
52
|
-
* - a file appears whose pid matches `expectedPid` AND has a non-empty
|
|
53
|
-
* `previewUrl` -> `ready`
|
|
54
|
-
* - the timeout elapses -> `timeout`
|
|
55
|
-
*
|
|
56
|
-
* If a file appears with a different pid (some other process won a write
|
|
57
|
-
* race), we treat it as the user's intent being satisfied and return
|
|
58
|
-
* `wrong-pid` so the caller can decide how to handle it -- usually that's
|
|
59
|
-
* "another `runwork dev` invocation already had a session running, fine."
|
|
60
|
-
*
|
|
61
|
-
* The function does not validate `bootTime` or `appId` -- those are the
|
|
62
|
-
* caller's job once they know which file matters. We only care about the
|
|
63
|
-
* shape of the file and the pid match here.
|
|
64
|
-
*/
|
|
65
|
-
export async function pollForSession(appDir, expectedPid, expectedAppId, opts = {}) {
|
|
66
|
-
const intervalMs = opts.intervalMs ?? 250;
|
|
67
|
-
const timeoutMs = opts.timeoutMs ?? 90_000;
|
|
68
|
-
const d = resolvePollDeps(opts.deps);
|
|
69
|
-
const deadline = d.now() + timeoutMs;
|
|
70
|
-
while (d.now() < deadline) {
|
|
71
|
-
if (opts.isChildAlive && !opts.isChildAlive()) {
|
|
72
|
-
return { result: 'child-exited' };
|
|
73
|
-
}
|
|
74
|
-
const file = readSessionFile(appDir);
|
|
75
|
-
if (file && file.previewUrl && file.appId === expectedAppId) {
|
|
76
|
-
if (file.pid === expectedPid) {
|
|
77
|
-
return { result: 'ready', file };
|
|
78
|
-
}
|
|
79
|
-
// Another process wrote a session file before our child did. The
|
|
80
|
-
// user's intent ("have a dev session running") is satisfied. Caller
|
|
81
|
-
// decides whether to log this and exit success or kill our child.
|
|
82
|
-
return { result: 'wrong-pid', file };
|
|
83
|
-
}
|
|
84
|
-
await d.sleep(intervalMs);
|
|
85
|
-
}
|
|
86
|
-
// One last child-alive probe before declaring timeout: a child that
|
|
87
|
-
// exited just before the deadline would otherwise be reported as
|
|
88
|
-
// "timeout" instead of "child-exited", losing diagnostic information.
|
|
89
|
-
if (opts.isChildAlive && !opts.isChildAlive()) {
|
|
90
|
-
return { result: 'child-exited' };
|
|
91
|
-
}
|
|
92
|
-
return { result: 'timeout' };
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Open the stdout/stderr log files in truncating mode and return their
|
|
96
|
-
* descriptors. We truncate (not append) so each new dev session starts
|
|
97
|
-
* fresh -- log rotation across sessions is out of scope.
|
|
98
|
-
*
|
|
99
|
-
* The descriptors must be closed by the caller after `spawn` so the
|
|
100
|
-
* parent process doesn't keep them open (which would prevent the child
|
|
101
|
-
* from being the sole owner).
|
|
102
|
-
*/
|
|
103
|
-
export function openLogFds(appDir) {
|
|
104
|
-
const paths = getSessionPaths(appDir);
|
|
105
|
-
fs.mkdirSync(paths.dir, { recursive: true });
|
|
106
|
-
const stdoutFd = fs.openSync(paths.stdoutLog, 'w');
|
|
107
|
-
const stderrFd = fs.openSync(paths.stderrLog, 'w');
|
|
108
|
-
return { stdoutFd, stderrFd };
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* The parent half of `runwork dev --detach`. Returns a structured outcome
|
|
112
|
-
* the caller can translate into stdout text and an exit code.
|
|
113
|
-
*
|
|
114
|
-
* IMPORTANT: this function does NOT print anything. It only orchestrates.
|
|
115
|
-
* The caller (in `dev.ts`) decides how to render the outcome -- human
|
|
116
|
-
* banner vs. JSON event vs. error stream.
|
|
117
|
-
*/
|
|
118
|
-
export async function runAsDetachedParent(opts) {
|
|
119
|
-
let child;
|
|
120
|
-
try {
|
|
121
|
-
child = (opts.spawn ?? defaultSpawnDetachedChild)(opts.childArgs);
|
|
122
|
-
}
|
|
123
|
-
catch (err) {
|
|
124
|
-
return { result: 'spawn-failed', error: err };
|
|
125
|
-
}
|
|
126
|
-
if (!child.pid) {
|
|
127
|
-
return { result: 'spawn-failed', error: new Error('Spawned child has no PID') };
|
|
128
|
-
}
|
|
129
|
-
const outcome = await pollForSession(opts.appDir, child.pid, opts.expectedAppId, {
|
|
130
|
-
intervalMs: opts.intervalMs,
|
|
131
|
-
timeoutMs: opts.timeoutMs,
|
|
132
|
-
deps: opts.pollDeps,
|
|
133
|
-
isChildAlive: child.isAlive,
|
|
134
|
-
});
|
|
135
|
-
if (outcome.result === 'ready') {
|
|
136
|
-
return { result: 'started', file: outcome.file };
|
|
137
|
-
}
|
|
138
|
-
if (outcome.result === 'wrong-pid') {
|
|
139
|
-
// Another process wrote a session file with a different PID before
|
|
140
|
-
// our child did. Our child is still happily running its own dev
|
|
141
|
-
// setup -- left alone, it will eventually overwrite the winner's
|
|
142
|
-
// session file with its own PID, defeating the duplicate-session
|
|
143
|
-
// guard. SIGTERM our child so the winner remains the unique owner.
|
|
144
|
-
// Best-effort: if the kill fails (race, EPERM), we still surface
|
|
145
|
-
// wrong-pid -- the user/agent will see two sessions briefly and
|
|
146
|
-
// can resolve via `runwork dev stop`.
|
|
147
|
-
try {
|
|
148
|
-
child.kill('SIGTERM');
|
|
149
|
-
}
|
|
150
|
-
catch { /* best-effort */ }
|
|
151
|
-
return { result: 'wrong-pid', file: outcome.file, ourPid: child.pid };
|
|
152
|
-
}
|
|
153
|
-
// For both timeout and child-exited, kill the child (no-op if already
|
|
154
|
-
// gone), clean up any partial session file we own, and surface the
|
|
155
|
-
// stderr tail for diagnostics.
|
|
156
|
-
try {
|
|
157
|
-
child.kill('SIGTERM');
|
|
158
|
-
}
|
|
159
|
-
catch { /* best-effort */ }
|
|
160
|
-
// Only remove the session file if it's ours -- a wrong-pid race could
|
|
161
|
-
// have replaced it with the winner's file between our last probe and
|
|
162
|
-
// here. removeSessionFileIfOwned guards against that.
|
|
163
|
-
try {
|
|
164
|
-
removeSessionFileIfOwned(opts.appDir, child.pid);
|
|
165
|
-
}
|
|
166
|
-
catch { /* best-effort */ }
|
|
167
|
-
const childLogTail = readChildStderrTail(opts.appDir);
|
|
168
|
-
if (outcome.result === 'child-exited') {
|
|
169
|
-
return { result: 'child-exited', ourPid: child.pid, childLogTail };
|
|
170
|
-
}
|
|
171
|
-
return { result: 'timeout', ourPid: child.pid, childLogTail };
|
|
172
|
-
}
|
|
173
|
-
function readChildStderrTail(appDir) {
|
|
174
|
-
try {
|
|
175
|
-
const paths = getSessionPaths(appDir);
|
|
176
|
-
if (!fs.existsSync(paths.stderrLog))
|
|
177
|
-
return undefined;
|
|
178
|
-
const raw = fs.readFileSync(paths.stderrLog, 'utf-8');
|
|
179
|
-
return raw.slice(-2000); // last ~2KB of stderr
|
|
180
|
-
}
|
|
181
|
-
catch {
|
|
182
|
-
return undefined;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Cross-platform detached self-spawn. Uses `process.execPath` so we never
|
|
187
|
-
* depend on PATH lookup -- a known failure mode for Bun standalone on
|
|
188
|
-
* Windows. The child gets the user-supplied args plus the internal child
|
|
189
|
-
* marker.
|
|
190
|
-
*/
|
|
191
|
-
export function defaultSpawnDetachedChild(childArgs) {
|
|
192
|
-
const cwd = process.cwd();
|
|
193
|
-
const { stdoutFd, stderrFd } = openLogFds(cwd);
|
|
194
|
-
try {
|
|
195
|
-
const proc = child_process.spawn(process.execPath, childArgs, {
|
|
196
|
-
cwd,
|
|
197
|
-
env: process.env,
|
|
198
|
-
detached: true,
|
|
199
|
-
stdio: ['ignore', stdoutFd, stderrFd],
|
|
200
|
-
windowsHide: true,
|
|
201
|
-
});
|
|
202
|
-
if (proc.pid !== undefined) {
|
|
203
|
-
proc.unref();
|
|
204
|
-
}
|
|
205
|
-
// Track exit via the 'exit' event. We can't depend on `proc.exitCode`
|
|
206
|
-
// alone because that's null until the process actually exits.
|
|
207
|
-
let exited = proc.exitCode !== null || proc.signalCode !== null;
|
|
208
|
-
proc.on('exit', () => { exited = true; });
|
|
209
|
-
proc.on('error', () => { exited = true; });
|
|
210
|
-
return {
|
|
211
|
-
pid: proc.pid ?? 0,
|
|
212
|
-
kill: (signal) => proc.kill(signal),
|
|
213
|
-
isAlive: () => !exited,
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
finally {
|
|
217
|
-
// The child has dup'd these descriptors; the parent should close its
|
|
218
|
-
// copies so it isn't holding onto the log files. Failure here is
|
|
219
|
-
// non-fatal -- the FDs will be reaped on parent exit anyway.
|
|
220
|
-
try {
|
|
221
|
-
fs.closeSync(stdoutFd);
|
|
222
|
-
}
|
|
223
|
-
catch { /* ignore */ }
|
|
224
|
-
try {
|
|
225
|
-
fs.closeSync(stderrFd);
|
|
226
|
-
}
|
|
227
|
-
catch { /* ignore */ }
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Detect whether the current `runwork dev` invocation is the detached
|
|
232
|
-
* child. Used by `dev.ts` to pick between the parent-spawn branch and
|
|
233
|
-
* the actual dev work.
|
|
234
|
-
*/
|
|
235
|
-
export function isInternalDetachedChild(argv = process.argv) {
|
|
236
|
-
return argv.includes(INTERNAL_DETACHED_CHILD_FLAG);
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Strip the internal marker from a list of args. Used when constructing
|
|
240
|
-
* the child's args from the parent's own args -- the parent already has
|
|
241
|
-
* the marker, the child needs it, but we want to avoid duplicates if for
|
|
242
|
-
* any reason the parent was itself launched with the marker (e.g., a
|
|
243
|
-
* misconfigured wrapper).
|
|
244
|
-
*/
|
|
245
|
-
export function stripInternalChildFlag(args) {
|
|
246
|
-
return args.filter((a) => a !== INTERNAL_DETACHED_CHILD_FLAG);
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Detect a Bun standalone virtual-filesystem path. Bun's compile mode
|
|
250
|
-
* on Windows injects the in-bundle script path as `process.argv[1]`
|
|
251
|
-
* (e.g., `B:/~BUN/root/runwork-windows-x64.exe`). When we self-spawn,
|
|
252
|
-
* the child Bun runtime re-injects an equivalent entry on its own --
|
|
253
|
-
* forwarding ours causes a duplicate that downstream parsers (commander
|
|
254
|
-
* here) misread as a stray positional command. macOS and Linux Bun
|
|
255
|
-
* standalone do NOT inject this entry, but the prefix is documented in
|
|
256
|
-
* Bun source as `/$bunfs/` if it ever appears, so we detect that too
|
|
257
|
-
* defensively.
|
|
258
|
-
*/
|
|
259
|
-
export function looksLikeBunStandaloneArtifact(p) {
|
|
260
|
-
// Windows: drive-letter paths under \~BUN\, e.g. "B:/~BUN/root/..." or "B:\~BUN\root\..."
|
|
261
|
-
if (/^[a-z]:[/\\]~BUN[/\\]/i.test(p))
|
|
262
|
-
return true;
|
|
263
|
-
// Linux/macOS bunfs prefix (documented; not currently emitted in user-visible argv).
|
|
264
|
-
if (p.startsWith('/$bunfs/'))
|
|
265
|
-
return true;
|
|
266
|
-
return false;
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Construct the args we should forward to the spawned child so it
|
|
270
|
-
* re-runs the same `runwork dev` invocation as the parent. Drops
|
|
271
|
-
* elements that the child runtime will re-inject on its own (notably
|
|
272
|
-
* the Bun-on-Windows virtual-FS path) and drops any pre-existing copy
|
|
273
|
-
* of the internal-child marker before we re-add exactly one.
|
|
274
|
-
*
|
|
275
|
-
* Pure function for testability -- accepts the parent's argv and returns
|
|
276
|
-
* what to hand to `spawn`. Real callers pass `process.argv`.
|
|
277
|
-
*/
|
|
278
|
-
export function buildChildArgs(parentArgv) {
|
|
279
|
-
// argv[0] is always the binary; the child gets it back via spawn's
|
|
280
|
-
// execPath argument. We start scanning from argv[1].
|
|
281
|
-
const rest = [];
|
|
282
|
-
for (let i = 1; i < parentArgv.length; i++) {
|
|
283
|
-
if (i === 1 && looksLikeBunStandaloneArtifact(parentArgv[i])) {
|
|
284
|
-
// Skip Bun-on-Windows's auto-injected virtual-FS path; the child
|
|
285
|
-
// runtime injects its own equivalent entry. Forwarding ours would
|
|
286
|
-
// duplicate it in the child's argv.
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
|
-
rest.push(parentArgv[i]);
|
|
290
|
-
}
|
|
291
|
-
return [...stripInternalChildFlag(rest), INTERNAL_DETACHED_CHILD_FLAG];
|
|
292
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { ApiClient } from '../api/client.js';
|
|
2
|
-
export interface PreviewUrlPollerOptions {
|
|
3
|
-
client: ApiClient;
|
|
4
|
-
appId: string;
|
|
5
|
-
initialUrl: string;
|
|
6
|
-
/**
|
|
7
|
-
* Polling interval in ms. Defaults to 15s -- low enough to catch a
|
|
8
|
-
* sandbox URL rotation within one keypress's worth of attention, high
|
|
9
|
-
* enough that an idle dev session isn't generating noticeable API load.
|
|
10
|
-
*/
|
|
11
|
-
intervalMs?: number;
|
|
12
|
-
onChange: (next: string, prev: string) => void;
|
|
13
|
-
onError?: (err: unknown) => void;
|
|
14
|
-
}
|
|
15
|
-
export interface PreviewUrlPoller {
|
|
16
|
-
getCurrent(): string;
|
|
17
|
-
stop(): void;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Periodically refreshes the preview URL from `GET /api/dev/status` so the
|
|
21
|
-
* `runwork dev` UI stays in agreement with `runwork info` even when the
|
|
22
|
-
* sandbox tunnel rotates mid-session.
|
|
23
|
-
*
|
|
24
|
-
* Two intentional behaviours:
|
|
25
|
-
*
|
|
26
|
-
* 1. Empty server responses are NOT propagated. The DO clears its
|
|
27
|
-
* preview-URL cache to "" while a sandbox is being replaced; flipping
|
|
28
|
-
* the status line to empty and back would just be flicker. We keep
|
|
29
|
-
* the last known good URL until the server reports a real new one.
|
|
30
|
-
*
|
|
31
|
-
* 2. Overlapping ticks are skipped. If a fetch takes longer than the
|
|
32
|
-
* interval (slow network, sandbox spin-up), we don't pile up requests
|
|
33
|
-
* -- the next tick simply no-ops until the in-flight one resolves.
|
|
34
|
-
*/
|
|
35
|
-
export declare function startPreviewUrlPoller(opts: PreviewUrlPollerOptions): PreviewUrlPoller;
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Periodically refreshes the preview URL from `GET /api/dev/status` so the
|
|
3
|
-
* `runwork dev` UI stays in agreement with `runwork info` even when the
|
|
4
|
-
* sandbox tunnel rotates mid-session.
|
|
5
|
-
*
|
|
6
|
-
* Two intentional behaviours:
|
|
7
|
-
*
|
|
8
|
-
* 1. Empty server responses are NOT propagated. The DO clears its
|
|
9
|
-
* preview-URL cache to "" while a sandbox is being replaced; flipping
|
|
10
|
-
* the status line to empty and back would just be flicker. We keep
|
|
11
|
-
* the last known good URL until the server reports a real new one.
|
|
12
|
-
*
|
|
13
|
-
* 2. Overlapping ticks are skipped. If a fetch takes longer than the
|
|
14
|
-
* interval (slow network, sandbox spin-up), we don't pile up requests
|
|
15
|
-
* -- the next tick simply no-ops until the in-flight one resolves.
|
|
16
|
-
*/
|
|
17
|
-
export function startPreviewUrlPoller(opts) {
|
|
18
|
-
let current = opts.initialUrl;
|
|
19
|
-
const interval = opts.intervalMs ?? 15000;
|
|
20
|
-
let stopped = false;
|
|
21
|
-
let inFlight = false;
|
|
22
|
-
const tick = async () => {
|
|
23
|
-
if (stopped || inFlight)
|
|
24
|
-
return;
|
|
25
|
-
inFlight = true;
|
|
26
|
-
try {
|
|
27
|
-
const status = await opts.client.getDevStatus(opts.appId);
|
|
28
|
-
const next = status.previewUrl ?? '';
|
|
29
|
-
if (next && next !== current) {
|
|
30
|
-
const prev = current;
|
|
31
|
-
current = next;
|
|
32
|
-
opts.onChange(next, prev);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
catch (err) {
|
|
36
|
-
opts.onError?.(err);
|
|
37
|
-
}
|
|
38
|
-
finally {
|
|
39
|
-
inFlight = false;
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
const handle = setInterval(() => { void tick(); }, interval);
|
|
43
|
-
return {
|
|
44
|
-
getCurrent: () => current,
|
|
45
|
-
stop: () => {
|
|
46
|
-
stopped = true;
|
|
47
|
-
clearInterval(handle);
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
}
|
package/dist/dev/session.d.ts
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-app `runwork dev` session lifecycle primitives.
|
|
3
|
-
*
|
|
4
|
-
* The session file at `<app-dir>/.runwork/dev-session.json` is the durable
|
|
5
|
-
* truth about whether a dev session is running for the current app, on the
|
|
6
|
-
* current machine. Every dev-related command reads from it, writes to it,
|
|
7
|
-
* or treats it as the IPC channel during detach.
|
|
8
|
-
*
|
|
9
|
-
* This module is intentionally pure: it does not start, stop, or signal
|
|
10
|
-
* processes. It only reads/writes the file and reports whether what it
|
|
11
|
-
* finds is alive, stale, or absent. Callers (dev.ts, dev stop, etc.) act
|
|
12
|
-
* on that information.
|
|
13
|
-
*
|
|
14
|
-
* See `docs/plans/2026-05-06-runwork-dev-lifecycle-design.md` for the full
|
|
15
|
-
* lifecycle contract.
|
|
16
|
-
*/
|
|
17
|
-
import type { StartupSyncSummary } from './startup-sync.js';
|
|
18
|
-
export declare const SESSION_FILE_SCHEMA_VERSION = 1;
|
|
19
|
-
/**
|
|
20
|
-
* Tolerance (ms) when comparing the stored bootTime to the currently
|
|
21
|
-
* computed one. NTP corrections can shift `Date.now()` by a few seconds
|
|
22
|
-
* relative to `os.uptime()`. A reboot is always orders of magnitude
|
|
23
|
-
* larger than this tolerance.
|
|
24
|
-
*/
|
|
25
|
-
export declare const BOOT_TIME_TOLERANCE_MS = 60000;
|
|
26
|
-
export type SessionMode = 'foreground' | 'detached';
|
|
27
|
-
export interface SessionFile {
|
|
28
|
-
version: number;
|
|
29
|
-
pid: number;
|
|
30
|
-
sessionId: string;
|
|
31
|
-
appId: string;
|
|
32
|
-
previewUrl: string;
|
|
33
|
-
startedAt: number;
|
|
34
|
-
bootTime: number;
|
|
35
|
-
cliVersion: string;
|
|
36
|
-
mode: SessionMode;
|
|
37
|
-
/**
|
|
38
|
-
* Whether this session runs in no-sync mode (no file watcher, no
|
|
39
|
-
* auto-commit, no working-tree-mutating startup). Optional for
|
|
40
|
-
* back-compat with v1 session files that predate the field; absent is
|
|
41
|
-
* treated as `false`.
|
|
42
|
-
*/
|
|
43
|
-
noSync?: boolean;
|
|
44
|
-
/**
|
|
45
|
-
* Startup sync outcome, so a detached parent can relay stale-code warnings
|
|
46
|
-
* to the caller instead of those signals only living in the child's log
|
|
47
|
-
* (friction log #2). Optional for back-compat with session files that
|
|
48
|
-
* predate the field.
|
|
49
|
-
*/
|
|
50
|
-
startupSync?: StartupSyncSummary;
|
|
51
|
-
}
|
|
52
|
-
export type SessionStaleReason = 'malformed' | 'version-mismatch' | 'app-id-mismatch' | 'boot-time-mismatch' | 'pid-dead';
|
|
53
|
-
export type SessionState = {
|
|
54
|
-
state: 'none';
|
|
55
|
-
} | {
|
|
56
|
-
state: 'alive';
|
|
57
|
-
file: SessionFile;
|
|
58
|
-
} | {
|
|
59
|
-
state: 'stale';
|
|
60
|
-
reason: SessionStaleReason;
|
|
61
|
-
file?: SessionFile;
|
|
62
|
-
};
|
|
63
|
-
export interface SessionPaths {
|
|
64
|
-
/** `<app-dir>/.runwork` */
|
|
65
|
-
dir: string;
|
|
66
|
-
/** `<app-dir>/.runwork/dev-session.json` */
|
|
67
|
-
file: string;
|
|
68
|
-
/** `<app-dir>/.runwork/dev-session.json.tmp` -- staging path for atomic writes */
|
|
69
|
-
tmpFile: string;
|
|
70
|
-
/** `<app-dir>/.runwork/dev-stdout.log` -- detached child's stdout sink */
|
|
71
|
-
stdoutLog: string;
|
|
72
|
-
/** `<app-dir>/.runwork/dev-stderr.log` -- detached child's stderr sink */
|
|
73
|
-
stderrLog: string;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Inject-able dependencies for testing. The defaults call into real OS
|
|
77
|
-
* primitives. Tests pass overrides to simulate dead PIDs, reboots, or
|
|
78
|
-
* arbitrary clocks without monkey-patching `process` or `os`.
|
|
79
|
-
*/
|
|
80
|
-
export interface SessionDeps {
|
|
81
|
-
bootTime?: () => number;
|
|
82
|
-
pidAlive?: (pid: number) => boolean;
|
|
83
|
-
}
|
|
84
|
-
export declare function getSessionPaths(appDir: string): SessionPaths;
|
|
85
|
-
/**
|
|
86
|
-
* System boot timestamp in ms. Computed purely from Node's `os.uptime()`
|
|
87
|
-
* (monotonic seconds since boot, available on every platform Node runs
|
|
88
|
-
* on) and the wall clock. No spawning, no native modules.
|
|
89
|
-
*
|
|
90
|
-
* Drift between `Date.now()` and `os.uptime()` from NTP corrections is
|
|
91
|
-
* absorbed by `BOOT_TIME_TOLERANCE_MS` at the comparison site.
|
|
92
|
-
*/
|
|
93
|
-
export declare function currentBootTime(): number;
|
|
94
|
-
export declare function isBootTimeStale(stored: number, deps?: SessionDeps): boolean;
|
|
95
|
-
/**
|
|
96
|
-
* Cross-platform PID liveness probe.
|
|
97
|
-
*
|
|
98
|
-
* - Unix: `process.kill(pid, 0)` is signal-zero, a permissions+existence
|
|
99
|
-
* probe. ESRCH for dead, EPERM for "alive but not ours."
|
|
100
|
-
* - Windows: Node maps signal 0 to `OpenProcess(PROCESS_QUERY_LIMITED_-`
|
|
101
|
-
* `INFORMATION)`. Returns success iff the PID exists and the caller
|
|
102
|
-
* has rights, throws otherwise.
|
|
103
|
-
*
|
|
104
|
-
* On any error we return `false`. EPERM in particular ("alive but
|
|
105
|
-
* unprivileged") means the PID belongs to someone else now, so it's not
|
|
106
|
-
* our session even if it's a real process. The bootTime guard catches the
|
|
107
|
-
* rare "PID got reused after a reboot, we still own it" case.
|
|
108
|
-
*/
|
|
109
|
-
export declare function isPidAlive(pid: number): boolean;
|
|
110
|
-
/**
|
|
111
|
-
* Read and parse the session file. Returns `null` for a missing file or
|
|
112
|
-
* any IO/parse error. The caller decides what "missing" vs "malformed"
|
|
113
|
-
* means -- use `getSessionState()` for that distinction.
|
|
114
|
-
*/
|
|
115
|
-
export declare function readSessionFile(appDir: string): SessionFile | null;
|
|
116
|
-
/**
|
|
117
|
-
* Write the session file atomically. Writes to `<file>.tmp` first then
|
|
118
|
-
* renames into place. POSIX `rename()` and Windows `MoveFileEx` are both
|
|
119
|
-
* atomic on the same volume, which a sibling temp file always is.
|
|
120
|
-
*
|
|
121
|
-
* Creates `.runwork/` if it doesn't exist. The directory is gitignored at
|
|
122
|
-
* the project-template level.
|
|
123
|
-
*/
|
|
124
|
-
export declare function writeSessionFile(appDir: string, data: SessionFile): void;
|
|
125
|
-
/**
|
|
126
|
-
* Conditional removal: only deletes the session file when its `pid`
|
|
127
|
-
* field matches the caller's. Use this from per-process cleanup paths
|
|
128
|
-
* where another process (e.g., the winner of a startup race) may have
|
|
129
|
-
* overwritten the file with a different owner. Returns `true` when we
|
|
130
|
-
* actually removed it.
|
|
131
|
-
*
|
|
132
|
-
* `removeSessionFile` is the unconditional primitive; this is the
|
|
133
|
-
* "be-a-good-citizen" wrapper for in-process cleanup handlers.
|
|
134
|
-
*/
|
|
135
|
-
export declare function removeSessionFileIfOwned(appDir: string, pid: number): boolean;
|
|
136
|
-
/**
|
|
137
|
-
* Idempotent removal. Missing file is not an error. We also clear any
|
|
138
|
-
* leftover temp file from a crashed mid-write -- those aren't load-bearing
|
|
139
|
-
* but they're noise and they confuse `ls -la .runwork/`.
|
|
140
|
-
*/
|
|
141
|
-
export declare function removeSessionFile(appDir: string): void;
|
|
142
|
-
/**
|
|
143
|
-
* Determine the live state of the session file in the given app directory.
|
|
144
|
-
*
|
|
145
|
-
* Checks are ordered cheap-to-expensive. The `pidAlive` syscall is last
|
|
146
|
-
* so a malformed or wrong-version file short-circuits before we touch
|
|
147
|
-
* the OS at all.
|
|
148
|
-
*
|
|
149
|
-
* 1. File missing -> `none`
|
|
150
|
-
* 2. File malformed / truncated -> `stale: malformed`
|
|
151
|
-
* 3. Schema version mismatch -> `stale: version-mismatch`
|
|
152
|
-
* 4. App ID mismatch -> `stale: app-id-mismatch`
|
|
153
|
-
* 5. Boot time outside tolerance -> `stale: boot-time-mismatch`
|
|
154
|
-
* 6. PID not alive -> `stale: pid-dead`
|
|
155
|
-
* 7. Otherwise -> `alive`
|
|
156
|
-
*/
|
|
157
|
-
export declare function getSessionState(appDir: string, expectedAppId: string, deps?: SessionDeps): SessionState;
|
|
158
|
-
/**
|
|
159
|
-
* Convenience constructor for a fresh session file. Fills in version,
|
|
160
|
-
* startedAt, and bootTime from the runtime; everything else is supplied
|
|
161
|
-
* by the caller. Test code can pass a `deps.bootTime` override to inject
|
|
162
|
-
* a deterministic value.
|
|
163
|
-
*/
|
|
164
|
-
export declare function buildSessionFile(input: {
|
|
165
|
-
pid: number;
|
|
166
|
-
sessionId: string;
|
|
167
|
-
appId: string;
|
|
168
|
-
previewUrl: string;
|
|
169
|
-
cliVersion: string;
|
|
170
|
-
mode: SessionMode;
|
|
171
|
-
noSync?: boolean;
|
|
172
|
-
startupSync?: StartupSyncSummary;
|
|
173
|
-
startedAt?: number;
|
|
174
|
-
deps?: SessionDeps;
|
|
175
|
-
}): SessionFile;
|