pi-harness-runtime 1.1.439 → 1.1.441
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/harness/e2e/auto-test-loop.ts +8 -2
- package/harness/e2e/browser-scrapers/base-browser-scraper.ts +2 -0
- package/harness/e2e/browser-scrapers/chrome-profile-manager.ts +2 -0
- package/harness/e2e/browser-scrapers/codex-browser-scraper.ts +2 -0
- package/harness/e2e/tools-detector.ts +2 -0
- package/harness/index.ts +2 -1
- package/harness/key-loader.ts +2 -0
- package/harness/langchain/checkpointer.ts +2 -0
- package/harness/langchain/cron-cli.ts +2 -0
- package/harness/langchain/graph.ts +2 -0
- package/harness/master-planner.ts +1 -1
- package/harness/notification-center.ts +2 -0
- package/harness/output-limit-handler.ts +1 -1
- package/harness/project-detector/detector.ts +2 -4
- package/harness/secret-provider.ts +2 -0
- package/harness/task-graph.ts +460 -113
- package/index.ts +16 -7
- package/package.json +7 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/dist/extension.d.ts.map +1 -1
- package/packages/agent-policy/dist/extension.js +2 -0
- package/packages/agent-policy/dist/extension.js.map +1 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/agent-policy/src/extension.ts +2 -0
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/dist/minimax-browser-auth.d.ts.map +1 -1
- package/packages/auth/dist/minimax-browser-auth.js +23 -1
- package/packages/auth/dist/minimax-browser-auth.js.map +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/auth/src/minimax-browser-auth.ts +30 -5
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/dist/inbox.d.ts.map +1 -1
- package/packages/autonomous-runtime/dist/inbox.js +2 -0
- package/packages/autonomous-runtime/dist/inbox.js.map +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/autonomous-runtime/src/inbox.ts +2 -0
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/dist/src/clipboard.d.ts.map +1 -1
- package/packages/clipboard/dist/src/clipboard.js +2 -0
- package/packages/clipboard/dist/src/clipboard.js.map +1 -1
- package/packages/clipboard/dist/src/copy-sync.d.ts.map +1 -1
- package/packages/clipboard/dist/src/copy-sync.js +2 -0
- package/packages/clipboard/dist/src/copy-sync.js.map +1 -1
- package/packages/clipboard/dist/src/gist-relay.d.ts.map +1 -1
- package/packages/clipboard/dist/src/gist-relay.js +2 -0
- package/packages/clipboard/dist/src/gist-relay.js.map +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard/src/clipboard.ts +2 -0
- package/packages/clipboard/src/copy-sync.ts +2 -0
- package/packages/clipboard/src/gist-relay.ts +2 -0
- package/packages/clipboard-plugin/dist/types.d.ts +9 -2
- package/packages/clipboard-plugin/dist/types.d.ts.map +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/clipboard-plugin/src/types.ts +9 -2
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/connection-runtime/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/contracts/dist/delegation-store.js +1 -4
- package/packages/contracts/dist/workspace-env.js +28 -1
- package/packages/contracts/package.json +1 -1
- package/packages/contracts/src/delegation-store.ts +9 -4
- package/packages/contracts/src/workspace-env.ts +29 -4
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/durable-review/package.json +1 -1
- package/packages/e2e-tester/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-sink/package.json +1 -1
- package/packages/event-store/dist/src/store.d.ts.map +1 -1
- package/packages/event-store/dist/src/store.js +2 -0
- package/packages/event-store/dist/src/store.js.map +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/event-store/src/store.ts +3 -0
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/dist/analyzer.d.ts.map +1 -1
- package/packages/generic-web-plugin/dist/analyzer.js +2 -0
- package/packages/generic-web-plugin/dist/analyzer.js.map +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/generic-web-plugin/src/analyzer.ts +3 -0
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/dist/mcp-client.d.ts.map +1 -1
- package/packages/knowledge-retrieval/dist/mcp-client.js +0 -1
- package/packages/knowledge-retrieval/dist/mcp-client.js.map +1 -1
- package/packages/knowledge-retrieval/package.json +1 -4
- package/packages/knowledge-retrieval/src/mcp-client.ts +0 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/logging/package.json +1 -1
- package/packages/mcp-adapter/dist/server.d.ts.map +1 -1
- package/packages/mcp-adapter/dist/server.js +2 -0
- package/packages/mcp-adapter/dist/server.js.map +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/mcp-adapter/src/server.ts +3 -0
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/dist/index.d.ts.map +1 -1
- package/packages/paperclip-integration/dist/index.js +2 -0
- package/packages/paperclip-integration/dist/index.js.map +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/paperclip-integration/src/index.ts +2 -0
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/permission-mode/package.json +1 -1
- package/packages/privilege-broker/dist/audit.d.ts.map +1 -1
- package/packages/privilege-broker/dist/audit.js +2 -0
- package/packages/privilege-broker/dist/audit.js.map +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/privilege-broker/src/audit.ts +2 -0
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/dist/compiler.d.ts.map +1 -1
- package/packages/prompt-compiler/dist/compiler.js +2 -0
- package/packages/prompt-compiler/dist/compiler.js.map +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-compiler/src/compiler.ts +2 -0
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/relay/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/dist/client.d.ts.map +1 -1
- package/packages/skill-mcp-client/dist/client.js +0 -1
- package/packages/skill-mcp-client/dist/client.js.map +1 -1
- package/packages/skill-mcp-client/package.json +1 -4
- package/packages/skill-mcp-client/src/client.ts +0 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/dist/parser.d.ts.map +1 -1
- package/packages/skills/dist/parser.js +2 -0
- package/packages/skills/dist/parser.js.map +1 -1
- package/packages/skills/dist/skill-versioning.d.ts.map +1 -1
- package/packages/skills/dist/skill-versioning.js +2 -0
- package/packages/skills/dist/skill-versioning.js.map +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/skills/src/parser.ts +3 -0
- package/packages/skills/src/skill-versioning.ts +2 -0
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/tooling-preference/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/two-phase-review/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/voice-api/package.json +1 -1
- package/packages/webmcp-tester/dist/index.js +0 -2
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/webmcp-tester/src/index.ts +0 -3
- package/packages/wiki/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/dist/trigger.d.ts.map +1 -1
- package/packages/write-review/dist/trigger.js +1 -2
- package/packages/write-review/dist/trigger.js.map +1 -1
- package/packages/write-review/package.json +1 -1
- package/packages/write-review/src/trigger.ts +1 -2
- package/scripts/lib/runtime-imports.ts +189 -3
- package/scripts/release-all.ts +5 -3
- package/scripts/telegram-webhook-server.ts +2 -0
- package/scripts/typecheck-packages.mjs +7 -4
- package/scripts/verify-package-contents.ts +161 -2
- package/harness/e2e/tools-detector.test.ts +0 -156
- package/harness/glm-quota-countdown.test.ts +0 -323
- package/harness/langchain/agents.test.ts +0 -39
- package/harness/langchain/checkpointer.test.ts +0 -243
- package/harness/langchain/daemon.test.ts +0 -459
- package/harness/langchain/model-router.test.ts +0 -191
- package/harness/langchain/ping-pong-middleware.test.ts +0 -69
- package/harness/langchain/scoreboard.test.ts +0 -245
- package/harness/langchain/status-line.test.ts +0 -106
- package/harness/langchain/surge.test.ts +0 -467
- package/harness/langchain/widget.test.ts +0 -251
- package/harness/skill-sync.test.ts +0 -57
- package/harness/ssh-detach-interceptor.test.ts +0 -263
- package/harness/ssh-hang-recovery.test.ts +0 -86
- package/packages/a2a-adapter/test/a2a.test.ts +0 -151
- package/packages/agent-policy/test/interlock.test.ts +0 -101
- package/packages/agent-policy/test/policy-retention.test.ts +0 -380
- package/packages/agent-policy/test/policy.test.ts +0 -387
- package/packages/auth/test/auth.test.ts +0 -238
- package/packages/autonomous-refactor/test/refactor.test.ts +0 -202
- package/packages/autonomous-runtime/test/types.test.ts +0 -95
- package/packages/cache-strategy/test/cache.test.ts +0 -137
- package/packages/capability-registry/test/registry.test.ts +0 -204
- package/packages/checkpoint/test/engine.test.ts +0 -265
- package/packages/cli-plugin-sdk/test/cli.test.ts +0 -45
- package/packages/code-generation/test/generator.test.ts +0 -238
- package/packages/code-review/test/engine.test.ts +0 -302
- package/packages/codex-adapter/test/provider.test.ts +0 -142
- package/packages/codex-watcher/test/watch.test.ts +0 -453
- package/packages/context-compiler/test/context-compiler.test.ts +0 -746
- package/packages/context-manager/test/context.test.ts +0 -198
- package/packages/contracts/test/delegation.test.ts +0 -350
- package/packages/contracts/test/workspace-env.test.ts +0 -802
- package/packages/cookie-sanitizer/test/infer.test.ts +0 -110
- package/packages/cookie-sanitizer/test/normalize.test.ts +0 -113
- package/packages/cookie-sanitizer/test/parse.test.ts +0 -204
- package/packages/cookie-sanitizer/test/sync.test.ts +0 -237
- package/packages/cookie-sanitizer/test/watcher.test.ts +0 -27
- package/packages/cost-optimizer/test/cost-optimizer.test.ts +0 -438
- package/packages/dependency-analyzer/test/analyzer.test.ts +0 -49
- package/packages/django-plugin/test/analyzer.test.ts +0 -195
- package/packages/doc-generator/test/docs.test.ts +0 -168
- package/packages/durable-review/test/acceptance.test.ts +0 -357
- package/packages/durable-review/test/smoke.test.ts +0 -107
- package/packages/evaluation-engine/test/engine.test.ts +0 -277
- package/packages/evaluation-runner/test/runner.test.ts +0 -168
- package/packages/event-bus/test/bus.test.ts +0 -100
- package/packages/event-store/test/store.test.ts +0 -115
- package/packages/experience-replay/test/replay.test.ts +0 -267
- package/packages/feedback-collector/test/collector.test.ts +0 -142
- package/packages/framework-detector/test/detector.test.ts +0 -75
- package/packages/framework-plugin-sdk/test/manager.test.ts +0 -320
- package/packages/frappe-plugin/test/analyzer.test.ts +0 -250
- package/packages/generic-web-plugin/test/analyzer.test.ts +0 -134
- package/packages/health-monitor/test/health.test.ts +0 -231
- package/packages/intent-analyzer/test/analyzer.test.ts +0 -72
- package/packages/intent-analyzer/test/ping-pong.test.ts +0 -96
- package/packages/jev-judge/tests/auto-continue-context.test.ts +0 -280
- package/packages/jev-judge/tests/continuation-controller.test.ts +0 -1396
- package/packages/jev-judge/tests/jev-provider-chain.test.ts +0 -536
- package/packages/jev-judge/tests/outcome-application.test.ts +0 -132
- package/packages/jev-judge/tests/question-risk.test.ts +0 -129
- package/packages/jev-judge/tests/typesafe-provider.test.ts +0 -640
- package/packages/jev-judge/tests/validation-ledger.test.ts +0 -181
- package/packages/jev-judge/tests/validation-plan.test.ts +0 -170
- package/packages/jev-judge/tests/validation-tie-breaker.test.ts +0 -88
- package/packages/laravel-plugin/test/analyzer.test.ts +0 -220
- package/packages/learning-engine/test/engine.test.ts +0 -331
- package/packages/logging/test/buffer.test.ts +0 -44
- package/packages/logging/test/classification.test.ts +0 -137
- package/packages/logging/test/config.test.ts +0 -97
- package/packages/logging/test/fanout.test.ts +0 -186
- package/packages/logging/test/identity-transport.test.ts +0 -197
- package/packages/logging/test/incidents.test.ts +0 -404
- package/packages/logging/test/ingest-schema.test.ts +0 -184
- package/packages/logging/test/integration.test.ts +0 -140
- package/packages/logging/test/log-http.test.ts +0 -246
- package/packages/logging/test/redact.test.ts +0 -109
- package/packages/logging/test/sampler.test.ts +0 -57
- package/packages/logging/test/server.test.ts +0 -444
- package/packages/logging/test/shipper.test.ts +0 -152
- package/packages/logging/test/spool.test.ts +0 -96
- package/packages/mcp-adapter/test/server.test.ts +0 -91
- package/packages/memory-engine/test/engine.test.ts +0 -456
- package/packages/model-registry/test/registry.test.ts +0 -342
- package/packages/nextjs-plugin/test/analyzer.test.ts +0 -149
- package/packages/notification/test/approval-payload.test.ts +0 -269
- package/packages/notification/test/detail-format.test.ts +0 -106
- package/packages/notification/test/notification.test.ts +0 -375
- package/packages/notification/test/pending-question-lifecycle.test.ts +0 -195
- package/packages/notification/test/pending-question.test.ts +0 -331
- package/packages/notification/test/tooling-proposal-payload.test.ts +0 -212
- package/packages/observability/test/observability.test.ts +0 -1350
- package/packages/performance-optimizer/test/optimizer.test.ts +0 -267
- package/packages/project-analyzer/test/index.test.ts +0 -1719
- package/packages/prompt-compiler/test/prompt-compiler.test.ts +0 -813
- package/packages/prompt-versioning/test/versioning.test.ts +0 -112
- package/packages/provider-adapter-sdk/test/sdk.test.ts +0 -220
- package/packages/provider-router/test/provider-router.test.ts +0 -405
- package/packages/provider-selector/test/selector.test.ts +0 -192
- package/packages/providers/test/providers.test.ts +0 -225
- package/packages/qdrant-skills/test/collection-info.test.ts +0 -165
- package/packages/qdrant-skills/test/embedder-batch.test.ts +0 -184
- package/packages/qdrant-skills/test/embedder.test.ts +0 -97
- package/packages/quota-manager/test/quota.test.ts +0 -432
- package/packages/rate-limiter/test/limiter.test.ts +0 -128
- package/packages/react-vite-plugin/test/analyzer.test.ts +0 -139
- package/packages/relay/test/client.test.ts +0 -446
- package/packages/relay/test/relay-e2e.test.ts +0 -299
- package/packages/relay/test/relay-endpoint.test.ts +0 -278
- package/packages/release-manager/test/release.test.ts +0 -215
- package/packages/requirement-compiler/test/requirement-compiler.test.ts +0 -743
- package/packages/runtime/test/runtime-api.test.ts +0 -169
- package/packages/runtime/test/runtime.test.ts +0 -297
- package/packages/scheduler/test/scheduler.test.ts +0 -125
- package/packages/session/test/manager.test.ts +0 -233
- package/packages/shared-context/test/shared-context.test.ts +0 -114
- package/packages/skill-registry/test/registry.test.ts +0 -508
- package/packages/skills/test/init-skills.test.ts +0 -114
- package/packages/skills/test/remote-skills.test.ts +0 -360
- package/packages/skills/test/remote-source.test.ts +0 -248
- package/packages/skills/test/sanitize.test.ts +0 -250
- package/packages/skills/test/vector-indexing.test.ts +0 -205
- package/packages/skills/test/vector-schema.test.ts +0 -180
- package/packages/skills/test/vector-threshold.test.ts +0 -108
- package/packages/sprint-planner/test/planner.test.ts +0 -187
- package/packages/task-compiler/test/task-compiler.test.ts +0 -628
- package/packages/test-data-generator/test/generator.test.ts +0 -265
- package/packages/test-generator/test/generator.test.ts +0 -156
- package/packages/token-estimation/test/token-estimation.test.ts +0 -207
- package/packages/token-optimizer/test/optimizer.test.ts +0 -141
- package/packages/tooling-preference/tests/changed-files.test.ts +0 -124
- package/packages/tooling-preference/tests/detect.test.ts +0 -188
- package/packages/tooling-preference/tests/proposal.test.ts +0 -107
- package/packages/tooling-preference/tests/quality-check.test.ts +0 -272
- package/packages/tooling-preference/tests/registry.test.ts +0 -51
- package/packages/tooling-preference/tests/store.test.ts +0 -99
- package/packages/trajectory/test/store.test.ts +0 -250
- package/packages/tui/test/harness-footer.test.ts +0 -559
- package/packages/tui/test/harness-header.test.ts +0 -590
- package/packages/two-phase-review/test/classify.test.ts +0 -102
- package/packages/two-phase-review/test/helpers.ts +0 -72
- package/packages/two-phase-review/test/rules.test.ts +0 -150
- package/packages/two-phase-review/test/runtime.test.ts +0 -362
- package/packages/two-phase-review/test/scope.test.ts +0 -186
- package/packages/two-phase-review/test/signals.test.ts +0 -183
- package/packages/two-phase-review/test/state.test.ts +0 -201
- package/packages/wiki/test/basic.test.ts +0 -242
- package/packages/workspace-scanner/test/scanner.test.ts +0 -43
- package/packages/worktree/test/worktree.test.ts +0 -163
|
@@ -1,802 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import {
|
|
6
|
-
buildAllowanceReason,
|
|
7
|
-
canShowModeValue,
|
|
8
|
-
collectDotenvVars,
|
|
9
|
-
collectProcessEnvVars,
|
|
10
|
-
describeCwdCommand,
|
|
11
|
-
describeOs,
|
|
12
|
-
describeServerState,
|
|
13
|
-
describeWorkspaceMode,
|
|
14
|
-
detectModeHint,
|
|
15
|
-
filterOwnPids,
|
|
16
|
-
formatEnvForAgentResult,
|
|
17
|
-
formatEnvStatusLine,
|
|
18
|
-
formatWorkspacePath,
|
|
19
|
-
isLauncherCommand,
|
|
20
|
-
isLowRamWarning,
|
|
21
|
-
isSessionVariableKey,
|
|
22
|
-
MASKED_MODE_VALUE,
|
|
23
|
-
modeFromRepositoryFacts,
|
|
24
|
-
modeFromValue,
|
|
25
|
-
orderDotenvFileNames,
|
|
26
|
-
parseDotenvVars,
|
|
27
|
-
resolveWorkspaceMode,
|
|
28
|
-
} from "../src/workspace-env-format.ts";
|
|
29
|
-
import {
|
|
30
|
-
DOTENV_MAX_BYTES,
|
|
31
|
-
isTemplateDotenvName,
|
|
32
|
-
readDotenvFiles,
|
|
33
|
-
} from "../src/workspace-dotenv.ts";
|
|
34
|
-
import type { EnvEvidence, WorkspaceEnvResult } from "../src/workspace-env-format.ts";
|
|
35
|
-
|
|
36
|
-
function makeEnv(overrides: Partial<WorkspaceEnvResult> = {}): WorkspaceEnvResult {
|
|
37
|
-
return {
|
|
38
|
-
workspacePath: "/home/tester/pi-harness-runtime",
|
|
39
|
-
mode: "unknown",
|
|
40
|
-
evidence: [],
|
|
41
|
-
hasDevScript: false,
|
|
42
|
-
hasBuildScript: false,
|
|
43
|
-
packageManager: "bun",
|
|
44
|
-
devServerRunning: false,
|
|
45
|
-
buildServerRunning: false,
|
|
46
|
-
ramTotalGb: 15,
|
|
47
|
-
ramAvailableGb: 7.7,
|
|
48
|
-
ramEffectiveLimitGb: null,
|
|
49
|
-
lowRamWarning: true,
|
|
50
|
-
buildAllowed: false,
|
|
51
|
-
detectedAt: 0,
|
|
52
|
-
...overrides,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
describe("workspace mode wording", () => {
|
|
57
|
-
test("an unknown mode carries its reason", () => {
|
|
58
|
-
expect(describeWorkspaceMode(makeEnv())).toBe(
|
|
59
|
-
"UNKNOWN (no NODE_ENV or environment variable found)",
|
|
60
|
-
);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test("a known mode names the variable it came from", () => {
|
|
64
|
-
const evidence: EnvEvidence[] = [
|
|
65
|
-
{
|
|
66
|
-
source: ".env file",
|
|
67
|
-
key: "AMOS_ENV",
|
|
68
|
-
value: "development",
|
|
69
|
-
timestamp: 0,
|
|
70
|
-
file: ".env",
|
|
71
|
-
},
|
|
72
|
-
];
|
|
73
|
-
|
|
74
|
-
expect(describeWorkspaceMode(makeEnv({ mode: "development", evidence }))).toBe(
|
|
75
|
-
"DEVELOPMENT (from AMOS_ENV in .env)",
|
|
76
|
-
);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test("an empty *_ENV variable names itself", () => {
|
|
80
|
-
const evidence: EnvEvidence[] = [
|
|
81
|
-
{ source: ".env file", key: "AMOS_ENV", value: "", timestamp: 0, file: ".env" },
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
expect(describeWorkspaceMode(makeEnv({ evidence }))).toBe(
|
|
85
|
-
"UNKNOWN (AMOS_ENV is empty in .env)",
|
|
86
|
-
);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test("an unrecognized NODE_ENV names the value that could not be read", () => {
|
|
90
|
-
const evidence: EnvEvidence[] = [
|
|
91
|
-
{ source: "process.env", key: "NODE_ENV", value: "staging", timestamp: 0 },
|
|
92
|
-
];
|
|
93
|
-
|
|
94
|
-
expect(describeWorkspaceMode(makeEnv({ evidence }))).toBe(
|
|
95
|
-
"UNKNOWN (NODE_ENV=staging is not a known mode)",
|
|
96
|
-
);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test("a known mode is reported plainly", () => {
|
|
100
|
-
expect(describeWorkspaceMode(makeEnv({ mode: "development" }))).toBe("DEVELOPMENT");
|
|
101
|
-
expect(describeWorkspaceMode(makeEnv({ mode: "production" }))).toBe("PRODUCTION");
|
|
102
|
-
expect(describeWorkspaceMode(makeEnv({ mode: "test" }))).toBe("TEST");
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test("a dev script is a capability, not a state", () => {
|
|
106
|
-
expect(describeServerState("running")).toBe("running");
|
|
107
|
-
expect(describeServerState("not-detected")).toBe("not detected");
|
|
108
|
-
expect(describeServerState("not-checked")).toBe("not checked");
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe("the operating system", () => {
|
|
113
|
-
test("the OS is named in a readable form", () => {
|
|
114
|
-
expect(describeOs("linux")).toBe("Linux");
|
|
115
|
-
expect(describeOs("darwin")).toBe("macOS");
|
|
116
|
-
expect(describeOs("win32")).toBe("Windows");
|
|
117
|
-
expect(describeOs("aix")).toBe("aix");
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
test("the command that prints the working directory depends on the OS", () => {
|
|
121
|
-
expect(describeCwdCommand("linux")).toBe("pwd");
|
|
122
|
-
expect(describeCwdCommand("darwin")).toBe("pwd");
|
|
123
|
-
expect(describeCwdCommand("win32")).toContain("echo %cd%");
|
|
124
|
-
expect(describeCwdCommand("win32")).toContain("PowerShell");
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
test("the home directory is shortened on a path boundary only", () => {
|
|
128
|
-
expect(formatWorkspacePath("/home/tester/app", "/home/tester")).toBe("~/app");
|
|
129
|
-
expect(formatWorkspacePath("/home/tester", "/home/tester")).toBe("~");
|
|
130
|
-
expect(formatWorkspacePath("/home/testerX/app", "/home/tester")).toBe("/home/testerX/app");
|
|
131
|
-
expect(formatWorkspacePath("/home/tester/app/", "/home/tester")).toBe("~/app");
|
|
132
|
-
expect(formatWorkspacePath("/app", "/")).toBe("/app");
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
describe("the process check", () => {
|
|
137
|
-
test("our own processes are not counted as a running server", () => {
|
|
138
|
-
// The shell that runs pgrep carries the pattern in its own command
|
|
139
|
-
// line, so it always matches.
|
|
140
|
-
expect(filterOwnPids(["4242", "4243"], ["4242"])).toEqual(["4243"]);
|
|
141
|
-
expect(filterOwnPids(["", " "], [])).toEqual([]);
|
|
142
|
-
expect(filterOwnPids(["4242"], [])).toEqual(["4242"]);
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
describe("the server detection", () => {
|
|
147
|
-
test("a launcher is told apart from a server", () => {
|
|
148
|
-
// "npm run dev" only STARTS a server - the server is its child.
|
|
149
|
-
expect(isLauncherCommand("bun run dev")).toBe(true);
|
|
150
|
-
expect(isLauncherCommand("npm run start")).toBe(true);
|
|
151
|
-
expect(isLauncherCommand("yarn dev")).toBe(true);
|
|
152
|
-
// A real server is not a launcher.
|
|
153
|
-
expect(isLauncherCommand("node /srv/app/vite/bin/vite.js")).toBe(false);
|
|
154
|
-
expect(isLauncherCommand("next dev")).toBe(false);
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
describe("the mode detection of a workspace", () => {
|
|
159
|
-
test("the mode is read from a dotenv file of the workspace", () => {
|
|
160
|
-
const vars = collectDotenvVars([{ name: ".env", content: "AMOS_ENV=development\n" }]);
|
|
161
|
-
const resolved = resolveWorkspaceMode([], vars);
|
|
162
|
-
|
|
163
|
-
expect(resolved.mode).toBe("development");
|
|
164
|
-
expect(resolved.hint?.key).toBe("AMOS_ENV");
|
|
165
|
-
expect(resolved.hint?.file).toBe(".env");
|
|
166
|
-
expect(resolved.source).toBe(".env file");
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test("a .env.<name> file is read too", () => {
|
|
170
|
-
// The real reading of the files: a variant file of the workspace
|
|
171
|
-
// must be found on disk, not only in a hand made list.
|
|
172
|
-
const dir = mkdtempSync(join(tmpdir(), "wstest-"));
|
|
173
|
-
try {
|
|
174
|
-
writeFileSync(join(dir, ".env.production"), "NODE_ENV=production\n");
|
|
175
|
-
const vars = collectDotenvVars(readDotenvFiles(dir));
|
|
176
|
-
|
|
177
|
-
expect(resolveWorkspaceMode([], vars).mode).toBe("production");
|
|
178
|
-
} finally {
|
|
179
|
-
rmSync(dir, { recursive: true, force: true });
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
test("the base .env wins over a variant", () => {
|
|
184
|
-
const vars = collectDotenvVars([
|
|
185
|
-
{ name: ".env.production", content: "NODE_ENV=production\n" },
|
|
186
|
-
{ name: ".env", content: "NODE_ENV=development\n" },
|
|
187
|
-
]);
|
|
188
|
-
|
|
189
|
-
expect(vars.find((v) => v.key === "NODE_ENV")?.value).toBe("development");
|
|
190
|
-
expect(resolveWorkspaceMode([], vars).mode).toBe("development");
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
test("a workspace without a variable keeps the mode unknown", () => {
|
|
194
|
-
const vars = collectDotenvVars([
|
|
195
|
-
{ name: ".env", content: "DATABASE_URL=postgres://localhost\n" },
|
|
196
|
-
]);
|
|
197
|
-
const resolved = resolveWorkspaceMode([], vars);
|
|
198
|
-
|
|
199
|
-
expect(resolved.mode).toBe("unknown");
|
|
200
|
-
expect(resolved.hint).toBeNull();
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
test("the process env wins over the dotenv files", () => {
|
|
204
|
-
const vars = collectDotenvVars([{ name: ".env", content: "NODE_ENV=production\n" }]);
|
|
205
|
-
const resolved = resolveWorkspaceMode(
|
|
206
|
-
collectProcessEnvVars({ NODE_ENV: "development" }),
|
|
207
|
-
vars,
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
expect(resolved.mode).toBe("development");
|
|
211
|
-
expect(resolved.source).toBe("process.env");
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
test("an explicit process NODE_ENV decides even when it names no known mode", () => {
|
|
215
|
-
const vars = collectDotenvVars([{ name: ".env", content: "AMOS_ENV=production\n" }]);
|
|
216
|
-
const resolved = resolveWorkspaceMode(collectProcessEnvVars({ NODE_ENV: "staging" }), vars);
|
|
217
|
-
|
|
218
|
-
// The explicit "staging" is not overwritten by a file: the mode stays
|
|
219
|
-
// unknown and the label names the value.
|
|
220
|
-
expect(resolved.mode).toBe("unknown");
|
|
221
|
-
expect(resolved.source).toBe("process.env");
|
|
222
|
-
expect(resolved.hint?.value).toBe("staging");
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
test("an unrelated environment variable of the process env does not outrank the workspace", () => {
|
|
226
|
-
const vars = collectDotenvVars([{ name: ".env", content: "NODE_ENV=production\n" }]);
|
|
227
|
-
const resolved = resolveWorkspaceMode(
|
|
228
|
-
collectProcessEnvVars({ FOO_ENV: "development" }),
|
|
229
|
-
vars,
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
expect(resolved.mode).toBe("production");
|
|
233
|
-
expect(resolved.source).toBe(".env file");
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
test("an environment variable of the process env is the last resort", () => {
|
|
237
|
-
const resolved = resolveWorkspaceMode(
|
|
238
|
-
collectProcessEnvVars({ AMOS_ENV: "production" }),
|
|
239
|
-
[],
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
expect(resolved.mode).toBe("production");
|
|
243
|
-
expect(resolved.source).toBe("process.env");
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
test("an unusable environment variable does not shadow a usable one", () => {
|
|
247
|
-
// The scenario of the user: an early "APP_MODE=" in the same .env
|
|
248
|
-
// must not hide the "AMOS_ENV=development" of line 46.
|
|
249
|
-
const vars = collectDotenvVars([
|
|
250
|
-
{ name: ".env", content: "APP_MODE=staging\nAPP_ENV=\nAMOS_ENV=development\n" },
|
|
251
|
-
]);
|
|
252
|
-
const resolved = resolveWorkspaceMode([], vars);
|
|
253
|
-
|
|
254
|
-
expect(resolved.mode).toBe("development");
|
|
255
|
-
expect(resolved.hint?.key).toBe("AMOS_ENV");
|
|
256
|
-
expect(resolved.source).toBe(".env file");
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
test("the dotenv file names are ordered", () => {
|
|
260
|
-
expect(
|
|
261
|
-
orderDotenvFileNames([".env.production", ".env", ".env.local", ".env.zzz", "other"]),
|
|
262
|
-
).toEqual([".env", ".env.local", ".env.production", ".env.zzz"]);
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
describe("the mode detection helpers", () => {
|
|
267
|
-
test("a dotenv value is read with its quotes and trailing comment removed", () => {
|
|
268
|
-
const vars = parseDotenvVars(
|
|
269
|
-
[
|
|
270
|
-
"# a comment",
|
|
271
|
-
"",
|
|
272
|
-
"NODE_ENV=production",
|
|
273
|
-
'APP_ENV="development"',
|
|
274
|
-
"export AMOS_ENV=staging",
|
|
275
|
-
"RAILS_ENV: test",
|
|
276
|
-
"BARE",
|
|
277
|
-
"QUOTED_WITH_HASH=abc # trailing",
|
|
278
|
-
].join("\n"),
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
expect(vars).toEqual([
|
|
282
|
-
{ key: "NODE_ENV", value: "production" },
|
|
283
|
-
{ key: "APP_ENV", value: "development" },
|
|
284
|
-
{ key: "AMOS_ENV", value: "staging" },
|
|
285
|
-
{ key: "RAILS_ENV", value: "test" },
|
|
286
|
-
{ key: "QUOTED_WITH_HASH", value: "abc" },
|
|
287
|
-
]);
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
test("a known mode is read from a value", () => {
|
|
291
|
-
expect(modeFromValue("development")).toBe("development");
|
|
292
|
-
expect(modeFromValue("DEV")).toBe("development");
|
|
293
|
-
expect(modeFromValue(" prod ")).toBe("production");
|
|
294
|
-
expect(modeFromValue("testing")).toBe("test");
|
|
295
|
-
expect(modeFromValue("staging")).toBeNull();
|
|
296
|
-
expect(modeFromValue("")).toBeNull();
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
test("NODE_ENV wins over another *_ENV key", () => {
|
|
300
|
-
const hint = detectModeHint([
|
|
301
|
-
{ key: "AMOS_ENV", value: "production" },
|
|
302
|
-
{ key: "NODE_ENV", value: "development" },
|
|
303
|
-
]);
|
|
304
|
-
|
|
305
|
-
expect(hint?.key).toBe("NODE_ENV");
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
test("another *_ENV key is used when there is no NODE_ENV", () => {
|
|
309
|
-
const hint = detectModeHint([
|
|
310
|
-
{ key: "DATABASE_URL", value: "postgres://localhost" },
|
|
311
|
-
{ key: "AMOS_ENV", value: "production" },
|
|
312
|
-
]);
|
|
313
|
-
|
|
314
|
-
expect(hint?.key).toBe("AMOS_ENV");
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
test("a value that names a mode is only used in the dotenv files", () => {
|
|
318
|
-
// The process env never decides by a value: an unrelated variable
|
|
319
|
-
// that holds "production" says nothing about the environment.
|
|
320
|
-
const resolved = resolveWorkspaceMode(
|
|
321
|
-
collectProcessEnvVars({ BRANCH: "production" }),
|
|
322
|
-
[],
|
|
323
|
-
);
|
|
324
|
-
|
|
325
|
-
expect(resolved.mode).toBe("unknown");
|
|
326
|
-
expect(resolved.hint).toBeNull();
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
test("the workspace file wins over the process env", () => {
|
|
330
|
-
// The workspace says "APP_MODE=staging": that is explicit and it
|
|
331
|
-
// must not be replaced by an unrelated variable of the process env.
|
|
332
|
-
const resolved = resolveWorkspaceMode(
|
|
333
|
-
collectProcessEnvVars({ AMOS_ENV: "development" }),
|
|
334
|
-
collectDotenvVars([{ name: ".env", content: "APP_MODE=staging\n" }]),
|
|
335
|
-
);
|
|
336
|
-
|
|
337
|
-
expect(resolved.mode).toBe("unknown");
|
|
338
|
-
expect(resolved.hint?.key).toBe("APP_MODE");
|
|
339
|
-
expect(resolved.source).toBe(".env file");
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
test("a usable environment shaped key wins over an unusable one", () => {
|
|
343
|
-
const vars = [
|
|
344
|
-
{ key: "APP_MODE", value: "staging" },
|
|
345
|
-
{ key: "AMOS_ENV", value: "development" },
|
|
346
|
-
];
|
|
347
|
-
|
|
348
|
-
// The value match is opt-in: a caller has to ask for it.
|
|
349
|
-
expect(detectModeHint(vars, { allowValueMatch: true })?.key).toBe("AMOS_ENV");
|
|
350
|
-
expect(detectModeHint(vars)?.key).toBe("APP_MODE");
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
test("only an environment shaped key may decide the mode by its value", () => {
|
|
354
|
-
// BRANCH=production is a branch name, not the environment.
|
|
355
|
-
expect(detectModeHint([{ key: "BRANCH", value: "production" }], { allowValueMatch: true })).toBeNull();
|
|
356
|
-
expect(detectModeHint([{ key: "DEBUG", value: "dev" }], { allowValueMatch: true })).toBeNull();
|
|
357
|
-
expect(detectModeHint([{ key: "APP_ENV", value: "production" }], { allowValueMatch: true })?.key).toBe("APP_ENV");
|
|
358
|
-
});
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
describe("the masking of the mode value", () => {
|
|
362
|
-
test("a secret like key is never shown", () => {
|
|
363
|
-
expect(canShowModeValue("PASSWORD", "production", ".env file")).toBe(false);
|
|
364
|
-
expect(canShowModeValue("API_KEY", "production", ".env file")).toBe(false);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
test("the NODE_ENV of the process env is shown", () => {
|
|
368
|
-
expect(canShowModeValue("NODE_ENV", "staging", "process.env")).toBe(true);
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
test("a dotenv value that names no known mode is not shown", () => {
|
|
372
|
-
expect(canShowModeValue("NODE_ENV", "staging", ".env file")).toBe(false);
|
|
373
|
-
expect(canShowModeValue("AMOS_ENV", "production", ".env file")).toBe(true);
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
test("an evidence value that names a mode is never called unknown", () => {
|
|
377
|
-
// A stale cache row (the stored mode and the evidence disagree) must
|
|
378
|
-
// not produce "NODE_ENV=development is not a known mode".
|
|
379
|
-
const env = makeEnv({
|
|
380
|
-
mode: "unknown",
|
|
381
|
-
evidence: [
|
|
382
|
-
{
|
|
383
|
-
source: ".env file",
|
|
384
|
-
key: "NODE_ENV",
|
|
385
|
-
value: "development",
|
|
386
|
-
timestamp: 0,
|
|
387
|
-
file: ".env",
|
|
388
|
-
},
|
|
389
|
-
],
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
expect(describeWorkspaceMode(env)).toBe("DEVELOPMENT (from NODE_ENV in .env)");
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
test("a masked value is named by its key only", () => {
|
|
396
|
-
const env = makeEnv({
|
|
397
|
-
mode: "unknown",
|
|
398
|
-
evidence: [
|
|
399
|
-
{
|
|
400
|
-
source: ".env file",
|
|
401
|
-
key: "NODE_ENV",
|
|
402
|
-
value: MASKED_MODE_VALUE,
|
|
403
|
-
timestamp: 0,
|
|
404
|
-
file: ".env",
|
|
405
|
-
},
|
|
406
|
-
],
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
expect(describeWorkspaceMode(env)).toBe("UNKNOWN (NODE_ENV in .env names no known mode)");
|
|
410
|
-
});
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
describe("reading the dotenv files of a workspace", () => {
|
|
414
|
-
test("a template file is not an environment file", () => {
|
|
415
|
-
expect(isTemplateDotenvName(".env.example")).toBe(true);
|
|
416
|
-
expect(isTemplateDotenvName(".env.sample")).toBe(true);
|
|
417
|
-
expect(isTemplateDotenvName(".env.template")).toBe(true);
|
|
418
|
-
expect(isTemplateDotenvName(".env")).toBe(false);
|
|
419
|
-
expect(isTemplateDotenvName(".env.production")).toBe(false);
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
test("a template marker in any segment makes the file a template", () => {
|
|
423
|
-
// ".env.example.local" documents a template: it must never decide
|
|
424
|
-
// the mode.
|
|
425
|
-
expect(isTemplateDotenvName(".env.example.local")).toBe(true);
|
|
426
|
-
expect(isTemplateDotenvName(".env.local.sample")).toBe(true);
|
|
427
|
-
expect(isTemplateDotenvName(".env.tpl")).toBe(true);
|
|
428
|
-
expect(isTemplateDotenvName(".env.local.development")).toBe(false);
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
test("a symlink out of the workspace is not read", () => {
|
|
432
|
-
const outside = mkdtempSync(join(tmpdir(), "wstest-outside-"));
|
|
433
|
-
const dir = mkdtempSync(join(tmpdir(), "wstest-"));
|
|
434
|
-
try {
|
|
435
|
-
writeFileSync(join(outside, "secret.env"), "AMOS_ENV=production\n");
|
|
436
|
-
// A ".env" that points outside the workspace is not the
|
|
437
|
-
// configuration of the workspace.
|
|
438
|
-
symlinkSync(join(outside, "secret.env"), join(dir, ".env"));
|
|
439
|
-
expect(readDotenvFiles(dir)).toEqual([]);
|
|
440
|
-
|
|
441
|
-
// A link that stays inside the workspace is fine.
|
|
442
|
-
writeFileSync(join(dir, "real.env"), "AMOS_ENV=development\n");
|
|
443
|
-
symlinkSync(join(dir, "real.env"), join(dir, ".env.local"));
|
|
444
|
-
const files = readDotenvFiles(dir);
|
|
445
|
-
expect(files.map((f) => f.name)).toEqual([".env.local"]);
|
|
446
|
-
} finally {
|
|
447
|
-
rmSync(dir, { recursive: true, force: true });
|
|
448
|
-
rmSync(outside, { recursive: true, force: true });
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
test("the files are read from the workspace, not from the working directory", () => {
|
|
453
|
-
const dir = mkdtempSync(join(tmpdir(), "wstest-"));
|
|
454
|
-
try {
|
|
455
|
-
writeFileSync(join(dir, ".env"), "AMOS_ENV=development\n");
|
|
456
|
-
writeFileSync(join(dir, ".env.example"), "BRANCH=production\n");
|
|
457
|
-
|
|
458
|
-
const files = readDotenvFiles(dir);
|
|
459
|
-
expect(files.map((f) => f.name)).toEqual([".env"]);
|
|
460
|
-
expect(files[0].content).toContain("AMOS_ENV=development");
|
|
461
|
-
} finally {
|
|
462
|
-
rmSync(dir, { recursive: true, force: true });
|
|
463
|
-
}
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
test("a missing workspace has no environment files", () => {
|
|
467
|
-
expect(readDotenvFiles(join(tmpdir(), "wstest-does-not-exist-12345"))).toEqual([]);
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
test("a file of exactly the maximum size is read", () => {
|
|
471
|
-
const dir = mkdtempSync(join(tmpdir(), "wstest-"));
|
|
472
|
-
try {
|
|
473
|
-
// The limit is a limit, not an off by one: "X=" + N + "\n" is
|
|
474
|
-
// exactly DOTENV_MAX_BYTES.
|
|
475
|
-
writeFileSync(join(dir, ".env"), "X=" + "a".repeat(DOTENV_MAX_BYTES - 3) + "\n");
|
|
476
|
-
const files = readDotenvFiles(dir);
|
|
477
|
-
expect(files).toHaveLength(1);
|
|
478
|
-
expect(files[0].content).toContain("X=aaa");
|
|
479
|
-
} finally {
|
|
480
|
-
rmSync(dir, { recursive: true, force: true });
|
|
481
|
-
}
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
test("a file that is too big is not read", () => {
|
|
485
|
-
const dir = mkdtempSync(join(tmpdir(), "wstest-"));
|
|
486
|
-
try {
|
|
487
|
-
writeFileSync(join(dir, ".env"), `X=${"a".repeat(DOTENV_MAX_BYTES + 10)}\n`);
|
|
488
|
-
expect(readDotenvFiles(dir)).toEqual([]);
|
|
489
|
-
} finally {
|
|
490
|
-
rmSync(dir, { recursive: true, force: true });
|
|
491
|
-
}
|
|
492
|
-
});
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
describe("formatEnvStatusLine", () => {
|
|
496
|
-
test("does not show a bare UNKNOWN next to an available dev script", () => {
|
|
497
|
-
const line = formatEnvStatusLine(makeEnv({ hasDevScript: true, hasBuildScript: true }));
|
|
498
|
-
|
|
499
|
-
expect(line).toContain("mode: UNKNOWN (no NODE_ENV or environment variable found)");
|
|
500
|
-
expect(line).not.toContain("Workspace: UNKNOWN |");
|
|
501
|
-
// Without a stored state the honest answer is "not checked" - the
|
|
502
|
-
// old cache cannot tell a missing server from a platform where no
|
|
503
|
-
// check was possible.
|
|
504
|
-
expect(line).toContain("dev server: not checked");
|
|
505
|
-
expect(line).toContain("build server: not checked");
|
|
506
|
-
expect(line).toContain("dev script: available");
|
|
507
|
-
expect(line).toContain("build script: available");
|
|
508
|
-
expect(line).toContain("RAM: 7.7/15");
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
test("a stored server state is reported as it is", () => {
|
|
512
|
-
const line = formatEnvStatusLine(
|
|
513
|
-
makeEnv({ devServerState: "running", buildServerState: "not-detected" }),
|
|
514
|
-
);
|
|
515
|
-
|
|
516
|
-
expect(line).toContain("dev server: running");
|
|
517
|
-
expect(line).toContain("build server: not detected");
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
test("the legacy server state signature still works", () => {
|
|
521
|
-
expect(describeServerState(true, false)).toBe("running");
|
|
522
|
-
expect(describeServerState(false, true)).toBe("not running (script available)");
|
|
523
|
-
expect(describeServerState(false, false)).toBe("no script");
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
test("the line names the workspace path and the operating system", () => {
|
|
527
|
-
const line = formatEnvStatusLine(makeEnv());
|
|
528
|
-
|
|
529
|
-
expect(line).toContain(`Workspace: ${formatWorkspacePath("/home/tester/pi-harness-runtime")}`);
|
|
530
|
-
expect(line).toContain(`OS: ${describeOs()}`);
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
test("an unchecked server is not reported as not running", () => {
|
|
534
|
-
const line = formatEnvStatusLine(
|
|
535
|
-
makeEnv({ devServerState: "not-checked", buildServerState: "not-checked" }),
|
|
536
|
-
);
|
|
537
|
-
|
|
538
|
-
expect(line).toContain("dev server: not checked");
|
|
539
|
-
expect(line).not.toContain("dev server: not detected");
|
|
540
|
-
expect(line).not.toContain("not running");
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
test("a running dev server is reported as running", () => {
|
|
544
|
-
const line = formatEnvStatusLine(makeEnv({ mode: "development", hasDevScript: true, devServerRunning: true }));
|
|
545
|
-
|
|
546
|
-
expect(line).toContain("mode: DEVELOPMENT");
|
|
547
|
-
expect(line).toContain("dev server: running");
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
test("a missing RAM reading omits the RAM segment", () => {
|
|
551
|
-
const line = formatEnvStatusLine(makeEnv({ ramTotalGb: null, ramAvailableGb: null }));
|
|
552
|
-
|
|
553
|
-
expect(line).not.toContain("RAM:");
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
test("a partial RAM reading never renders a null value", () => {
|
|
557
|
-
const available = formatEnvStatusLine(makeEnv({ ramTotalGb: null, ramAvailableGb: 7.7 }));
|
|
558
|
-
const total = formatEnvStatusLine(makeEnv({ ramTotalGb: 15, ramAvailableGb: null }));
|
|
559
|
-
|
|
560
|
-
expect(available).toContain("RAM: 7.7GB available");
|
|
561
|
-
expect(available).not.toContain("null");
|
|
562
|
-
expect(total).toContain("RAM: 15GB total");
|
|
563
|
-
expect(total).not.toContain("null");
|
|
564
|
-
});
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
describe("export surface of workspace-env.ts", () => {
|
|
568
|
-
// The module itself needs node:sqlite, which bun 1.3.x does not provide.
|
|
569
|
-
// Assert the re-export surface on the source text instead.
|
|
570
|
-
const source = readFileSync(
|
|
571
|
-
join(import.meta.dir, "..", "src", "workspace-env.ts"),
|
|
572
|
-
"utf8",
|
|
573
|
-
);
|
|
574
|
-
|
|
575
|
-
test("keeps re-exporting the formatting helpers", () => {
|
|
576
|
-
for (const name of [
|
|
577
|
-
"describeServerState",
|
|
578
|
-
"describeWorkspaceMode",
|
|
579
|
-
"formatEnvForAgentResult",
|
|
580
|
-
]) {
|
|
581
|
-
// The name must be part of an export statement, not only a word
|
|
582
|
-
// somewhere in the file.
|
|
583
|
-
expect(source).toMatch(new RegExp(`export\\s*\\{[^}]*\\b${name}\\b`, "s"));
|
|
584
|
-
}
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
test("keeps exporting the detector functions", () => {
|
|
588
|
-
for (const name of [
|
|
589
|
-
"detectWorkspaceEnv",
|
|
590
|
-
"loadWorkspaceEnv",
|
|
591
|
-
"getWorkspaceEnv",
|
|
592
|
-
"revalidateWorkspaceEnv",
|
|
593
|
-
"formatEnvForAgent",
|
|
594
|
-
"formatEnvStatusLine",
|
|
595
|
-
]) {
|
|
596
|
-
expect(source).toContain(`export function ${name}`);
|
|
597
|
-
}
|
|
598
|
-
});
|
|
599
|
-
});
|
|
600
|
-
|
|
601
|
-
describe("formatEnvForAgentResult", () => {
|
|
602
|
-
test("explains that a dev script does not establish the mode", () => {
|
|
603
|
-
const text = formatEnvForAgentResult(makeEnv({ hasDevScript: true }));
|
|
604
|
-
|
|
605
|
-
expect(text).toContain("Mode: UNKNOWN (no NODE_ENV or environment variable found)");
|
|
606
|
-
expect(text).toContain("Dev script: yes");
|
|
607
|
-
expect(text).toContain("Build allowed: no - requires explicit request");
|
|
608
|
-
expect(text).toContain("a dev/build script only means a server CAN be started");
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
test("a known mode drops the unknown-mode note", () => {
|
|
612
|
-
const text = formatEnvForAgentResult(makeEnv({ mode: "production", buildAllowed: true }));
|
|
613
|
-
|
|
614
|
-
expect(text).toContain("Mode: PRODUCTION");
|
|
615
|
-
expect(text).toContain("Build allowed: yes");
|
|
616
|
-
expect(text).not.toContain("Environment unknown");
|
|
617
|
-
});
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
describe("the template files of a workspace", () => {
|
|
621
|
-
test("a plural template name is a template too", () => {
|
|
622
|
-
expect(isTemplateDotenvName(".env.examples")).toBe(true);
|
|
623
|
-
expect(isTemplateDotenvName(".env.samples")).toBe(true);
|
|
624
|
-
expect(isTemplateDotenvName(".env.templates")).toBe(true);
|
|
625
|
-
expect(isTemplateDotenvName(".env.example")).toBe(true);
|
|
626
|
-
expect(isTemplateDotenvName(".env.local")).toBe(false);
|
|
627
|
-
expect(isTemplateDotenvName(".env.production")).toBe(false);
|
|
628
|
-
});
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
describe("the session variables of a desktop", () => {
|
|
632
|
-
test("a session variable is not an environment selector", () => {
|
|
633
|
-
expect(isSessionVariableKey("GNOME_SHELL_SESSION_MODE")).toBe(true);
|
|
634
|
-
expect(isSessionVariableKey("XDG_SESSION_TYPE")).toBe(true);
|
|
635
|
-
expect(isSessionVariableKey("DESKTOP_SESSION")).toBe(true);
|
|
636
|
-
expect(isSessionVariableKey("DBUS_SESSION_BUS_ADDRESS")).toBe(true);
|
|
637
|
-
});
|
|
638
|
-
|
|
639
|
-
test("a real environment selector is not a session variable", () => {
|
|
640
|
-
expect(isSessionVariableKey("NODE_ENV")).toBe(false);
|
|
641
|
-
expect(isSessionVariableKey("AMOS_ENV")).toBe(false);
|
|
642
|
-
expect(isSessionVariableKey("APP_MODE")).toBe(false);
|
|
643
|
-
expect(isSessionVariableKey("RAILS_ENV")).toBe(false);
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
test("a session variable never decides the mode of a workspace", () => {
|
|
647
|
-
// The scenario of the user: GNOME_SHELL_SESSION_MODE=ubuntu is set in
|
|
648
|
-
// every GNOME shell and must not make a source repository unknown.
|
|
649
|
-
const processVars = collectProcessEnvVars({
|
|
650
|
-
GNOME_SHELL_SESSION_MODE: "ubuntu",
|
|
651
|
-
});
|
|
652
|
-
|
|
653
|
-
expect(processVars).toEqual([]);
|
|
654
|
-
expect(resolveWorkspaceMode(processVars, []).mode).toBe("unknown");
|
|
655
|
-
expect(resolveWorkspaceMode(processVars, []).hint).toBeNull();
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
test("a session variable in a dotenv file does not decide the mode either", () => {
|
|
659
|
-
const vars = collectDotenvVars([
|
|
660
|
-
{ name: ".env", content: "XDG_SESSION_TYPE=x11\n" },
|
|
661
|
-
]);
|
|
662
|
-
|
|
663
|
-
expect(resolveWorkspaceMode([], vars).hint).toBeNull();
|
|
664
|
-
});
|
|
665
|
-
|
|
666
|
-
test("a session variable does not shadow a real environment selector", () => {
|
|
667
|
-
const vars = collectDotenvVars([
|
|
668
|
-
{ name: ".env", content: "GNOME_SHELL_SESSION_MODE=ubuntu\nAMOS_ENV=production\n" },
|
|
669
|
-
]);
|
|
670
|
-
const resolved = resolveWorkspaceMode([], vars);
|
|
671
|
-
|
|
672
|
-
expect(resolved.mode).toBe("production");
|
|
673
|
-
expect(resolved.hint?.key).toBe("AMOS_ENV");
|
|
674
|
-
});
|
|
675
|
-
});
|
|
676
|
-
|
|
677
|
-
describe("the mode detection of a source repository", () => {
|
|
678
|
-
const published = {
|
|
679
|
-
isGitRepository: true,
|
|
680
|
-
branch: "main",
|
|
681
|
-
isPublishedPackage: true,
|
|
682
|
-
hasDeploymentMarker: false,
|
|
683
|
-
hasRunningServer: false,
|
|
684
|
-
};
|
|
685
|
-
|
|
686
|
-
test("a published package without a runtime is a development checkout", () => {
|
|
687
|
-
expect(modeFromRepositoryFacts(published)).toEqual({
|
|
688
|
-
mode: "development",
|
|
689
|
-
key: "source-only repository",
|
|
690
|
-
});
|
|
691
|
-
});
|
|
692
|
-
|
|
693
|
-
test("a deployment configuration keeps the mode unknown", () => {
|
|
694
|
-
const detection = modeFromRepositoryFacts({
|
|
695
|
-
...published,
|
|
696
|
-
hasDeploymentMarker: true,
|
|
697
|
-
});
|
|
698
|
-
|
|
699
|
-
expect(detection).toBeNull();
|
|
700
|
-
});
|
|
701
|
-
|
|
702
|
-
test("a development branch is the fallback", () => {
|
|
703
|
-
expect(
|
|
704
|
-
modeFromRepositoryFacts({
|
|
705
|
-
...published,
|
|
706
|
-
branch: "develop",
|
|
707
|
-
isPublishedPackage: false,
|
|
708
|
-
}),
|
|
709
|
-
).toEqual({ mode: "development", key: "git branch develop" });
|
|
710
|
-
expect(
|
|
711
|
-
modeFromRepositoryFacts({
|
|
712
|
-
...published,
|
|
713
|
-
branch: "development",
|
|
714
|
-
isPublishedPackage: false,
|
|
715
|
-
}),
|
|
716
|
-
).toEqual({ mode: "development", key: "git branch development" });
|
|
717
|
-
});
|
|
718
|
-
|
|
719
|
-
test("a production branch is never a development checkout", () => {
|
|
720
|
-
expect(
|
|
721
|
-
modeFromRepositoryFacts({
|
|
722
|
-
...published,
|
|
723
|
-
branch: "main",
|
|
724
|
-
isPublishedPackage: false,
|
|
725
|
-
}),
|
|
726
|
-
).toBeNull();
|
|
727
|
-
expect(
|
|
728
|
-
modeFromRepositoryFacts({
|
|
729
|
-
...published,
|
|
730
|
-
branch: "production",
|
|
731
|
-
isPublishedPackage: false,
|
|
732
|
-
}),
|
|
733
|
-
).toBeNull();
|
|
734
|
-
});
|
|
735
|
-
|
|
736
|
-
test("a directory that is not a repository names no mode", () => {
|
|
737
|
-
expect(
|
|
738
|
-
modeFromRepositoryFacts({
|
|
739
|
-
...published,
|
|
740
|
-
isGitRepository: false,
|
|
741
|
-
}),
|
|
742
|
-
).toBeNull();
|
|
743
|
-
});
|
|
744
|
-
|
|
745
|
-
test("the label names the reason of the detection", () => {
|
|
746
|
-
const env = makeEnv({
|
|
747
|
-
mode: "development",
|
|
748
|
-
buildAllowed: true,
|
|
749
|
-
evidence: [
|
|
750
|
-
{ source: "detection", key: "source-only repository", value: "development", timestamp: 0 },
|
|
751
|
-
],
|
|
752
|
-
});
|
|
753
|
-
|
|
754
|
-
expect(describeWorkspaceMode(env)).toBe("DEVELOPMENT (source-only repository)");
|
|
755
|
-
});
|
|
756
|
-
});
|
|
757
|
-
|
|
758
|
-
describe("the low memory rule", () => {
|
|
759
|
-
test("flags a machine with almost no free memory", () => {
|
|
760
|
-
expect(isLowRamWarning(64, 2)).toBe(true);
|
|
761
|
-
expect(isLowRamWarning(16, 3.9)).toBe(true);
|
|
762
|
-
});
|
|
763
|
-
|
|
764
|
-
test("does not flag a small machine with real headroom", () => {
|
|
765
|
-
expect(isLowRamWarning(15, 6.6)).toBe(false);
|
|
766
|
-
expect(isLowRamWarning(8, 8)).toBe(false);
|
|
767
|
-
});
|
|
768
|
-
|
|
769
|
-
test("flags a big machine that ran out of memory", () => {
|
|
770
|
-
expect(isLowRamWarning(64, 3)).toBe(true);
|
|
771
|
-
});
|
|
772
|
-
|
|
773
|
-
test("does not flag an unknown memory state", () => {
|
|
774
|
-
expect(isLowRamWarning(null, null)).toBe(false);
|
|
775
|
-
expect(isLowRamWarning(null, 12)).toBe(false);
|
|
776
|
-
});
|
|
777
|
-
});
|
|
778
|
-
|
|
779
|
-
describe("the build permission", () => {
|
|
780
|
-
test("allows a build when the environment allows it", () => {
|
|
781
|
-
expect(buildAllowanceReason(makeEnv({ mode: "development", buildAllowed: true }))).toBe("yes");
|
|
782
|
-
});
|
|
783
|
-
|
|
784
|
-
test("asks for an explicit request when the mode is unknown", () => {
|
|
785
|
-
expect(
|
|
786
|
-
buildAllowanceReason(makeEnv({ mode: "unknown", buildAllowed: false, lowRamWarning: false })),
|
|
787
|
-
).toBe("no - requires explicit request");
|
|
788
|
-
});
|
|
789
|
-
|
|
790
|
-
test("names the memory shortage when the memory is the reason", () => {
|
|
791
|
-
expect(
|
|
792
|
-
buildAllowanceReason(makeEnv({ mode: "development", buildAllowed: false, lowRamWarning: true })),
|
|
793
|
-
).toBe("no - low memory");
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
test("appears in the agent context", () => {
|
|
797
|
-
const message = formatEnvForAgentResult(
|
|
798
|
-
makeEnv({ mode: "development", buildAllowed: false, lowRamWarning: true }),
|
|
799
|
-
);
|
|
800
|
-
expect(message).toContain("Build allowed: no - low memory");
|
|
801
|
-
});
|
|
802
|
-
});
|