@principles/pd-cli 1.152.11 → 1.152.13
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/commands/candidate.d.ts +3 -3
- package/dist/commands/candidate.d.ts.map +1 -1
- package/dist/commands/candidate.js +9 -21
- package/dist/commands/candidate.js.map +1 -1
- package/dist/index.js +0 -0
- package/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
- package/dist/services/rulehost-pipeline-runner.js +4 -17
- package/dist/services/rulehost-pipeline-runner.js.map +1 -1
- package/package.json +13 -7
- package/dist/commands/__tests__/intent-flag-wiring.test.d.ts +0 -9
- package/dist/commands/__tests__/intent-flag-wiring.test.d.ts.map +0 -1
- package/dist/commands/__tests__/intent-flag-wiring.test.js +0 -237
- package/dist/commands/__tests__/intent-flag-wiring.test.js.map +0 -1
- package/dist/commands/__tests__/legacy-cleanup.test.d.ts +0 -18
- package/dist/commands/__tests__/legacy-cleanup.test.d.ts.map +0 -1
- package/dist/commands/__tests__/legacy-cleanup.test.js +0 -467
- package/dist/commands/__tests__/legacy-cleanup.test.js.map +0 -1
- package/dist/commands/__tests__/mvp-smoke.test.d.ts +0 -15
- package/dist/commands/__tests__/mvp-smoke.test.d.ts.map +0 -1
- package/dist/commands/__tests__/mvp-smoke.test.js +0 -262
- package/dist/commands/__tests__/mvp-smoke.test.js.map +0 -1
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts +0 -2
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts.map +0 -1
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js +0 -60
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js.map +0 -1
- package/dist/commands/__tests__/rulecode-flag-wiring.test.d.ts +0 -21
- package/dist/commands/__tests__/rulecode-flag-wiring.test.d.ts.map +0 -1
- package/dist/commands/__tests__/rulecode-flag-wiring.test.js +0 -186
- package/dist/commands/__tests__/rulecode-flag-wiring.test.js.map +0 -1
- package/dist/commands/__tests__/rulecode-handler.test.d.ts +0 -16
- package/dist/commands/__tests__/rulecode-handler.test.d.ts.map +0 -1
- package/dist/commands/__tests__/rulecode-handler.test.js +0 -285
- package/dist/commands/__tests__/rulecode-handler.test.js.map +0 -1
- package/dist/commands/__tests__/run-rulehost-flag-wiring.test.d.ts +0 -24
- package/dist/commands/__tests__/run-rulehost-flag-wiring.test.d.ts.map +0 -1
- package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js +0 -232
- package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js.map +0 -1
- package/dist/commands/__tests__/runtime-activation-nextaction.test.d.ts +0 -15
- package/dist/commands/__tests__/runtime-activation-nextaction.test.d.ts.map +0 -1
- package/dist/commands/__tests__/runtime-activation-nextaction.test.js +0 -73
- package/dist/commands/__tests__/runtime-activation-nextaction.test.js.map +0 -1
- package/dist/commands/__tests__/runtime-probe-config.test.d.ts +0 -20
- package/dist/commands/__tests__/runtime-probe-config.test.d.ts.map +0 -1
- package/dist/commands/__tests__/runtime-probe-config.test.js +0 -389
- package/dist/commands/__tests__/runtime-probe-config.test.js.map +0 -1
- package/dist/commands/__tests__/telemetry-flag-wiring.test.d.ts +0 -9
- package/dist/commands/__tests__/telemetry-flag-wiring.test.d.ts.map +0 -1
- package/dist/commands/__tests__/telemetry-flag-wiring.test.js +0 -76
- package/dist/commands/__tests__/telemetry-flag-wiring.test.js.map +0 -1
- package/dist/commands/__tests__/version.test.d.ts +0 -2
- package/dist/commands/__tests__/version.test.d.ts.map +0 -1
- package/dist/commands/__tests__/version.test.js +0 -238
- package/dist/commands/__tests__/version.test.js.map +0 -1
- package/dist/commands/runtime-uat.guard.test.d.ts +0 -2
- package/dist/commands/runtime-uat.guard.test.d.ts.map +0 -1
- package/dist/commands/runtime-uat.guard.test.js +0 -153
- package/dist/commands/runtime-uat.guard.test.js.map +0 -1
- package/dist/services/__tests__/cli-output.test.d.ts +0 -18
- package/dist/services/__tests__/cli-output.test.d.ts.map +0 -1
- package/dist/services/__tests__/cli-output.test.js +0 -103
- package/dist/services/__tests__/cli-output.test.js.map +0 -1
- package/dist/services/__tests__/evaluator-runner-deps.test.d.ts +0 -2
- package/dist/services/__tests__/evaluator-runner-deps.test.d.ts.map +0 -1
- package/dist/services/__tests__/evaluator-runner-deps.test.js +0 -343
- package/dist/services/__tests__/evaluator-runner-deps.test.js.map +0 -1
- package/dist/services/__tests__/rulehost-readiness.test.d.ts +0 -2
- package/dist/services/__tests__/rulehost-readiness.test.d.ts.map +0 -1
- package/dist/services/__tests__/rulehost-readiness.test.js +0 -311
- package/dist/services/__tests__/rulehost-readiness.test.js.map +0 -1
- package/dist/services/__tests__/runtime-adapter-resolver.test.d.ts +0 -18
- package/dist/services/__tests__/runtime-adapter-resolver.test.d.ts.map +0 -1
- package/dist/services/__tests__/runtime-adapter-resolver.test.js +0 -673
- package/dist/services/__tests__/runtime-adapter-resolver.test.js.map +0 -1
- package/dist/utils/production-workspace-guard.test.d.ts +0 -2
- package/dist/utils/production-workspace-guard.test.d.ts.map +0 -1
- package/dist/utils/production-workspace-guard.test.js +0 -137
- package/dist/utils/production-workspace-guard.test.js.map +0 -1
- package/scripts/llm-dogfood.ts +0 -456
- package/scripts/migrate-illegal-expected-decision.ts +0 -252
- package/src/commands/__tests__/intent-flag-wiring.test.ts +0 -282
- package/src/commands/__tests__/legacy-cleanup.test.ts +0 -602
- package/src/commands/__tests__/mvp-smoke.test.ts +0 -300
- package/src/commands/__tests__/pain-retry-maxTokens-flag.test.ts +0 -73
- package/src/commands/__tests__/rulecode-flag-wiring.test.ts +0 -237
- package/src/commands/__tests__/rulecode-handler.test.ts +0 -369
- package/src/commands/__tests__/run-rulehost-flag-wiring.test.ts +0 -292
- package/src/commands/__tests__/runtime-activation-nextaction.test.ts +0 -81
- package/src/commands/__tests__/runtime-probe-config.test.ts +0 -432
- package/src/commands/__tests__/telemetry-flag-wiring.test.ts +0 -84
- package/src/commands/__tests__/version.test.ts +0 -251
- package/src/commands/admission-gate.ts +0 -30
- package/src/commands/artifact.ts +0 -82
- package/src/commands/build-trajectory-evidence.ts +0 -298
- package/src/commands/candidate.ts +0 -1260
- package/src/commands/codex-ingest-catchup.ts +0 -114
- package/src/commands/codex-ingest-quarantine.ts +0 -127
- package/src/commands/codex-reconcile.ts +0 -84
- package/src/commands/codex-setup.ts +0 -476
- package/src/commands/codex-worker.ts +0 -185
- package/src/commands/command-helpers.ts +0 -24
- package/src/commands/config-doctor.ts +0 -193
- package/src/commands/console.ts +0 -797
- package/src/commands/context.ts +0 -72
- package/src/commands/demo-story-a.ts +0 -241
- package/src/commands/diagnose.ts +0 -756
- package/src/commands/errors-list.ts +0 -441
- package/src/commands/evolution-tasks-list.ts +0 -48
- package/src/commands/evolution-tasks-show.ts +0 -62
- package/src/commands/flow.ts +0 -60
- package/src/commands/health-codex.ts +0 -576
- package/src/commands/health.ts +0 -315
- package/src/commands/history.ts +0 -63
- package/src/commands/intent.ts +0 -457
- package/src/commands/legacy-cleanup.ts +0 -531
- package/src/commands/legacy-import.ts +0 -104
- package/src/commands/mvp-smoke.ts +0 -160
- package/src/commands/pain-evidence.ts +0 -226
- package/src/commands/pain-list.ts +0 -239
- package/src/commands/pain-record.ts +0 -612
- package/src/commands/pain-retry.ts +0 -916
- package/src/commands/principles-stats.ts +0 -700
- package/src/commands/proven-channel-baseline.ts +0 -115
- package/src/commands/quality-scorecard.ts +0 -272
- package/src/commands/remediation-output.ts +0 -66
- package/src/commands/rulecode.ts +0 -465
- package/src/commands/run.ts +0 -89
- package/src/commands/runtime-activation.ts +0 -1629
- package/src/commands/runtime-artifact-repair.ts +0 -524
- package/src/commands/runtime-canary.ts +0 -395
- package/src/commands/runtime-compatibility-scan.ts +0 -105
- package/src/commands/runtime-diagnostics-export.ts +0 -229
- package/src/commands/runtime-features.ts +0 -153
- package/src/commands/runtime-gfi-snapshot.ts +0 -135
- package/src/commands/runtime-health-snapshot.ts +0 -108
- package/src/commands/runtime-init.ts +0 -429
- package/src/commands/runtime-internalization-enqueue-successors.ts +0 -530
- package/src/commands/runtime-internalization-integrity-repair.ts +0 -79
- package/src/commands/runtime-internalization-integrity.ts +0 -107
- package/src/commands/runtime-internalization-queue.ts +0 -135
- package/src/commands/runtime-internalization-retry.ts +0 -191
- package/src/commands/runtime-internalization-run-once.ts +0 -791
- package/src/commands/runtime-internalization-run-rulehost.ts +0 -749
- package/src/commands/runtime-internalization-wake-once.ts +0 -87
- package/src/commands/runtime-pain-flood-simulation.ts +0 -121
- package/src/commands/runtime-pruning.ts +0 -438
- package/src/commands/runtime-recovery-failed-tasks.ts +0 -258
- package/src/commands/runtime-recovery.ts +0 -109
- package/src/commands/runtime-synthetic-baseline.ts +0 -70
- package/src/commands/runtime-uat.guard.test.ts +0 -175
- package/src/commands/runtime-uat.ts +0 -380
- package/src/commands/runtime.ts +0 -473
- package/src/commands/samples-list.ts +0 -39
- package/src/commands/samples-review.ts +0 -36
- package/src/commands/task.ts +0 -271
- package/src/commands/telemetry.ts +0 -349
- package/src/commands/trace.ts +0 -174
- package/src/commands/trajectory-db-unavailable.ts +0 -19
- package/src/commands/trajectory.ts +0 -64
- package/src/commands/version.ts +0 -49
- package/src/config-reader.ts +0 -122
- package/src/index.ts +0 -1276
- package/src/legacy/legacy-import.ts +0 -179
- package/src/legacy/session-history-import.ts +0 -203
- package/src/resolve-workspace.ts +0 -106
- package/src/services/__tests__/cli-output.test.ts +0 -130
- package/src/services/__tests__/evaluator-runner-deps.test.ts +0 -382
- package/src/services/__tests__/rulehost-readiness.test.ts +0 -363
- package/src/services/__tests__/runtime-adapter-resolver.test.ts +0 -793
- package/src/services/cli-output.ts +0 -95
- package/src/services/config-doctor.ts +0 -697
- package/src/services/console-launcher.ts +0 -537
- package/src/services/demo-rule-compiler.ts +0 -40
- package/src/services/demo-story-a-runner.ts +0 -471
- package/src/services/mainline-snapshot-assembler.ts +0 -579
- package/src/services/pain-flood-simulation-runner.ts +0 -462
- package/src/services/pd-config-loader.ts +0 -433
- package/src/services/proven-channel-baseline-runner.ts +0 -157
- package/src/services/quality-scorecard/data-extractor.ts +0 -150
- package/src/services/quality-scorecard/local-evaluator.ts +0 -142
- package/src/services/quality-scorecard/strong-model-gate.ts +0 -202
- package/src/services/resolve-runtime-from-pd-config.ts +0 -230
- package/src/services/rulehost-pipeline-runner.ts +0 -1216
- package/src/services/rulehost-readiness.ts +0 -326
- package/src/services/runtime-adapter-resolver.ts +0 -364
- package/src/services/synthetic-baseline-runner.ts +0 -400
- package/src/services/version-report.ts +0 -253
- package/src/services/workspace-tool-semantics.ts +0 -26
- package/src/utils/path-security.ts +0 -96
- package/src/utils/production-workspace-guard.test.ts +0 -157
- package/src/utils/production-workspace-guard.ts +0 -210
- package/tests/bdd/cli-contract.steps.ts +0 -346
- package/tests/bdd/principles-stats.steps.ts +0 -211
- package/tests/bdd/support/gherkin-loader.ts +0 -147
- package/tests/bdd/support/repo-root.ts +0 -62
- package/tests/bdd/support/vitest-bdd.ts +0 -179
- package/tests/commands/admission-gate-coverage.test.ts +0 -79
- package/tests/commands/build-trajectory-evidence.test.ts +0 -508
- package/tests/commands/candidate-audit-repair.test.ts +0 -421
- package/tests/commands/candidate-intake.test.ts +0 -732
- package/tests/commands/candidate-internalization-backfill.test.ts +0 -548
- package/tests/commands/candidate-internalize-lineage.test.ts +0 -565
- package/tests/commands/candidate-internalize.test.ts +0 -299
- package/tests/commands/candidate-route.test.ts +0 -329
- package/tests/commands/candidate-show.test.ts +0 -95
- package/tests/commands/cli-command-tree.test.ts +0 -144
- package/tests/commands/cli-help-snapshot.test.ts +0 -155
- package/tests/commands/cli-skill-contract.test.ts +0 -121
- package/tests/commands/codex-consent-reversibility.steps.test.ts +0 -299
- package/tests/commands/codex-ingest-quarantine.test.ts +0 -136
- package/tests/commands/codex-setup.test.ts +0 -297
- package/tests/commands/codex-worker-daemon.test.ts +0 -95
- package/tests/commands/codex-worker-registration.test.ts +0 -207
- package/tests/commands/config-doctor.test.ts +0 -336
- package/tests/commands/console-launcher-edge-cases.test.ts +0 -388
- package/tests/commands/console-open.test.ts +0 -1381
- package/tests/commands/context.test.ts +0 -114
- package/tests/commands/demo-story-a.test.ts +0 -318
- package/tests/commands/diagnose.test.ts +0 -1803
- package/tests/commands/errors-list.test.ts +0 -679
- package/tests/commands/evolution-tasks-list.test.ts +0 -41
- package/tests/commands/evolution-tasks-show.test.ts +0 -46
- package/tests/commands/health-codex.test.ts +0 -349
- package/tests/commands/health-host-wiring.test.ts +0 -76
- package/tests/commands/health-reliability.test.ts +0 -99
- package/tests/commands/health.test.ts +0 -413
- package/tests/commands/intent.test.ts +0 -321
- package/tests/commands/legacy-cleanup.test.ts +0 -148
- package/tests/commands/pain-evidence.test.ts +0 -518
- package/tests/commands/pain-list.test.ts +0 -219
- package/tests/commands/pain-record-async.test.ts +0 -335
- package/tests/commands/pain-record-session-parser.test.ts +0 -228
- package/tests/commands/pain-record.test.ts +0 -851
- package/tests/commands/pain-retry.test.ts +0 -1121
- package/tests/commands/plugin-config-resolution-cutover.test.ts +0 -229
- package/tests/commands/pri-393-runtime-config-unification.test.ts +0 -443
- package/tests/commands/principles-stats-wiring.test.ts +0 -135
- package/tests/commands/principles-stats.test.ts +0 -274
- package/tests/commands/product-path-regression.test.ts +0 -86
- package/tests/commands/proven-channel-baseline.test.ts +0 -441
- package/tests/commands/rulecode.test.ts +0 -177
- package/tests/commands/run-rulehost-handler.test.ts +0 -908
- package/tests/commands/runtime-activation-approve-flag-wiring.test.ts +0 -227
- package/tests/commands/runtime-activation-deactivate-flag-wiring.test.ts +0 -167
- package/tests/commands/runtime-activation-dispatch-flag-wiring.test.ts +0 -262
- package/tests/commands/runtime-activation-governance-audit.test.ts +0 -95
- package/tests/commands/runtime-activation-list-flag-wiring.test.ts +0 -193
- package/tests/commands/runtime-activation-promote-flag-wiring.test.ts +0 -248
- package/tests/commands/runtime-activation-shadow-telemetry.test.ts +0 -130
- package/tests/commands/runtime-activation.test.ts +0 -2052
- package/tests/commands/runtime-artifact-repair-registration.test.ts +0 -74
- package/tests/commands/runtime-artifact-repair.test.ts +0 -404
- package/tests/commands/runtime-canary.test.ts +0 -430
- package/tests/commands/runtime-compatibility-scan.test.ts +0 -146
- package/tests/commands/runtime-diagnostics-export.test.ts +0 -175
- package/tests/commands/runtime-features.test.ts +0 -249
- package/tests/commands/runtime-health-snapshot.test.ts +0 -358
- package/tests/commands/runtime-init-empty-workspace.test.ts +0 -252
- package/tests/commands/runtime-init.test.ts +0 -420
- package/tests/commands/runtime-internalization-enqueue-successors.test.ts +0 -782
- package/tests/commands/runtime-internalization-integrity-repair.test.ts +0 -239
- package/tests/commands/runtime-internalization-integrity.test.ts +0 -140
- package/tests/commands/runtime-internalization-queue.test.ts +0 -380
- package/tests/commands/runtime-internalization-retry-owner-authority.test.ts +0 -431
- package/tests/commands/runtime-internalization-run-once-evaluator-parity.test.ts +0 -269
- package/tests/commands/runtime-internalization-run-once-language.test.ts +0 -303
- package/tests/commands/runtime-internalization-run-once-real-config.test.ts +0 -237
- package/tests/commands/runtime-internalization-run-once-rollout-parity.test.ts +0 -706
- package/tests/commands/runtime-internalization-run-once.test.ts +0 -1791
- package/tests/commands/runtime-internalization-wake-once.test.ts +0 -171
- package/tests/commands/runtime-internalization.test.ts +0 -53
- package/tests/commands/runtime-pain-flood-simulation.test.ts +0 -418
- package/tests/commands/runtime-pruning.test.ts +0 -694
- package/tests/commands/runtime-recovery-failed-tasks.test.ts +0 -328
- package/tests/commands/runtime-recovery.test.ts +0 -120
- package/tests/commands/runtime-synthetic-baseline.test.ts +0 -249
- package/tests/commands/runtime-uat.test.ts +0 -397
- package/tests/commands/runtime.test.ts +0 -392
- package/tests/commands/samples-list.test.ts +0 -43
- package/tests/commands/samples-review.test.ts +0 -41
- package/tests/commands/task.test.ts +0 -153
- package/tests/commands/telemetry.test.ts +0 -190
- package/tests/commands/trace.test.ts +0 -316
- package/tests/config-reader.test.ts +0 -143
- package/tests/e2e/candidate-intake-e2e.test.ts +0 -338
- package/tests/e2e/cli-full-flow.test.ts +0 -233
- package/tests/e2e/cross-package-acceptance.test.ts +0 -635
- package/tests/helpers/pd-cli-path.ts +0 -5
- package/tests/pd-cli-smoke.test.ts +0 -13
- package/tests/resolve-workspace.test.ts +0 -196
- package/tests/services/config-doctor-signal-health.test.ts +0 -109
- package/tests/services/console-launcher.test.ts +0 -114
- package/tests/services/demo-rule-compiler.test.ts +0 -242
- package/tests/services/mainline-snapshot-assembler.test.ts +0 -572
- package/tests/services/pd-config-loader.test.ts +0 -505
- package/tests/services/proven-channel-baseline-runner.test.ts +0 -30
- package/tests/services/quality-scorecard/strong-model-gate.test.ts +0 -133
- package/tests/services/resolve-runtime-from-pd-config.test.ts +0 -485
- package/tests/services/rulehost-pipeline-e2e.test.ts +0 -538
- package/tests/services/rulehost-pipeline-runner.test.ts +0 -1175
- package/tests/services/workspace-tool-semantics.test.ts +0 -76
- package/tests/utils/path-security.test.ts +0 -180
- package/tsconfig.json +0 -26
- package/vitest.config.ts +0 -9
|
@@ -1,537 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PD Console Launcher — seed-friendly Console startup with port detection,
|
|
3
|
-
* reuse, and browser opening.
|
|
4
|
-
*
|
|
5
|
-
* PRI-300 MVP UX:
|
|
6
|
-
* - Default port 3100; auto-falls back to next available if 3100 is busy
|
|
7
|
-
* - Reuses a running Console if one is already serving on the chosen port
|
|
8
|
-
* - Refuses non-loopback hosts (ERR-049 loopback safety)
|
|
9
|
-
* - Opens the system browser on success (skipped under --json)
|
|
10
|
-
* - Emits `reason` + `nextAction` for every failure
|
|
11
|
-
*
|
|
12
|
-
* Constraints (no daemonization, no public bind, no manual port):
|
|
13
|
-
* - Does NOT start a background service
|
|
14
|
-
* - Does NOT bind to non-loopback hosts
|
|
15
|
-
* - Does NOT require user-configured ports (uses 3100 then 3101..)
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import * as net from 'net';
|
|
19
|
-
import * as http from 'http';
|
|
20
|
-
|
|
21
|
-
// ─── Public types ────────────────────────────────────────────────────────────
|
|
22
|
-
|
|
23
|
-
export type ConsoleStatus = 'reused' | 'started' | 'failed' | 'refused';
|
|
24
|
-
export type ConsoleAuthenticationMode = 'authenticated' | 'no_auth';
|
|
25
|
-
|
|
26
|
-
export interface ConsoleLaunchResult {
|
|
27
|
-
status: ConsoleStatus;
|
|
28
|
-
url: string;
|
|
29
|
-
port: number;
|
|
30
|
-
host: string;
|
|
31
|
-
workspaceDir: string;
|
|
32
|
-
reason?: string;
|
|
33
|
-
nextAction?: string;
|
|
34
|
-
/** True when an existing console was detected and reused. */
|
|
35
|
-
reused: boolean;
|
|
36
|
-
/** True when a browser should/has been opened (skipped in --json mode). */
|
|
37
|
-
browserOpened: boolean;
|
|
38
|
-
/** Verified for successful launch/reuse; omitted when no server was reached. */
|
|
39
|
-
authenticationMode?: ConsoleAuthenticationMode;
|
|
40
|
-
/**
|
|
41
|
-
* PID of the freshly spawned console server process. Present only when
|
|
42
|
-
* status === 'started'; absent on 'reused' (the server was started by
|
|
43
|
-
* another process, so this launcher cannot know its PID).
|
|
44
|
-
*/
|
|
45
|
-
serverPid?: number;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface ConsoleLaunchOptions {
|
|
49
|
-
workspaceDir: string;
|
|
50
|
-
/** Preferred port. Defaults to 3100. */
|
|
51
|
-
preferredPort?: number;
|
|
52
|
-
/** Optional override host. Must be loopback; non-loopback is refused. */
|
|
53
|
-
host?: string;
|
|
54
|
-
/** When true, the launcher does not open the browser. */
|
|
55
|
-
skipBrowser?: boolean;
|
|
56
|
-
/** Timeout in ms to wait for a freshly-spawned console to become ready. */
|
|
57
|
-
readyTimeoutMs?: number;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const DEFAULT_PORT = 3100;
|
|
61
|
-
const DEFAULT_HOST = '127.0.0.1';
|
|
62
|
-
const PORT_FALLBACK_LIMIT = 20; // try 3100..3119 before giving up
|
|
63
|
-
|
|
64
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
65
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// ─── Loopback safety (ERR-049) ──────────────────────────────────────────────
|
|
69
|
-
|
|
70
|
-
/** Returns true if the host resolves to a loopback address. */
|
|
71
|
-
export function isLoopbackHost(host: string): boolean {
|
|
72
|
-
if (host === 'localhost' || host === '127.0.0.1' || host === '::1' || host === '[::1]' || host === '0:0:0:0:0:0:0:1' || host === '[0:0:0:0:0:0:0:1]') return true;
|
|
73
|
-
// Reject any IPv4 host that doesn't start with 127.
|
|
74
|
-
if (/^127\./.test(host)) return true;
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Normalize loopback host for safe use in listen/http.request.
|
|
80
|
-
* - [::1] → ::1 (strip brackets; Node net/http don't want brackets in host field)
|
|
81
|
-
* - localhost, 127.x.x.x, ::1 → pass through
|
|
82
|
-
* - non-loopback → return as-is (caller must reject via isLoopbackHost first)
|
|
83
|
-
*/
|
|
84
|
-
export function normalizeLoopbackHost(host: string): string {
|
|
85
|
-
if (host === '[::1]') return '::1';
|
|
86
|
-
return host;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Build a valid Console URL from a normalized loopback host and port.
|
|
91
|
-
* - IPv6 ::1 → http://[::1]:port (brackets required for valid URL)
|
|
92
|
-
* - IPv4/localhost → http://host:port (unchanged)
|
|
93
|
-
*/
|
|
94
|
-
export function buildConsoleUrl(host: string, port: number): string {
|
|
95
|
-
if (host === '::1') return `http://[::1]:${port}`;
|
|
96
|
-
return `http://${host}:${port}`;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// ─── Port detection (ERR-022: bounded) ───────────────────────────────────────
|
|
100
|
-
|
|
101
|
-
/** Returns true if the port on the given host accepts a TCP connection. */
|
|
102
|
-
export async function isPortInUse(host: string, port: number, timeoutMs = 800): Promise<boolean> {
|
|
103
|
-
if (Object.hasOwn(globalThis, '__mockIsPortInUse')) {
|
|
104
|
-
const mock = Reflect.get(globalThis, '__mockIsPortInUse') as (
|
|
105
|
-
h: string,
|
|
106
|
-
p: number,
|
|
107
|
-
t?: number
|
|
108
|
-
) => Promise<boolean>;
|
|
109
|
-
return mock(host, port, timeoutMs);
|
|
110
|
-
}
|
|
111
|
-
return new Promise((resolve) => {
|
|
112
|
-
const socket = new net.Socket();
|
|
113
|
-
let settled = false;
|
|
114
|
-
const done = (inUse: boolean) => {
|
|
115
|
-
if (settled) return;
|
|
116
|
-
settled = true;
|
|
117
|
-
try { socket.destroy(); } catch { /* ignore */ }
|
|
118
|
-
resolve(inUse);
|
|
119
|
-
};
|
|
120
|
-
socket.setTimeout(timeoutMs);
|
|
121
|
-
socket.once('connect', () => done(true));
|
|
122
|
-
socket.once('timeout', () => done(false));
|
|
123
|
-
socket.once('error', (err: { code?: string; message?: string }) => {
|
|
124
|
-
// ECONNREFUSED → port is free; EHOSTUNREACH/ENETUNREACH → also free.
|
|
125
|
-
if (err.code === 'ECONNREFUSED' || err.code === 'EHOSTUNREACH' || err.code === 'ENETUNREACH') {
|
|
126
|
-
done(false);
|
|
127
|
-
} else {
|
|
128
|
-
done(false);
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
try {
|
|
132
|
-
socket.connect(port, host);
|
|
133
|
-
} catch {
|
|
134
|
-
done(false);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export interface HealthProbeOptions {
|
|
140
|
-
host: string;
|
|
141
|
-
port: number;
|
|
142
|
-
timeoutMs?: number;
|
|
143
|
-
/** Optional auth token (PD_CONSOLE_TOKEN) for authenticated health probes. */
|
|
144
|
-
token?: string;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** Probe a port to see if it serves a healthy PD Console. */
|
|
148
|
-
export interface ConsoleHealthProbeResult {
|
|
149
|
-
healthy: boolean;
|
|
150
|
-
authenticationMode?: ConsoleAuthenticationMode;
|
|
151
|
-
reason?: string;
|
|
152
|
-
failureKind?: 'unauthorized' | 'invalid_response' | 'unreachable';
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export async function probeConsoleHealth(opts: HealthProbeOptions): Promise<ConsoleHealthProbeResult> {
|
|
156
|
-
const { host, port, timeoutMs = 1500, token } = opts;
|
|
157
|
-
|
|
158
|
-
if (Object.hasOwn(globalThis, '__mockProbeConsoleHealth')) {
|
|
159
|
-
const mock = Reflect.get(globalThis, '__mockProbeConsoleHealth') as (
|
|
160
|
-
o: HealthProbeOptions
|
|
161
|
-
) => Promise<ConsoleHealthProbeResult>;
|
|
162
|
-
return mock(opts);
|
|
163
|
-
}
|
|
164
|
-
return new Promise((resolve) => {
|
|
165
|
-
const headers: Record<string, string> = {};
|
|
166
|
-
if (token) {
|
|
167
|
-
headers.Authorization = `Bearer ${token}`;
|
|
168
|
-
}
|
|
169
|
-
const req = http.request(
|
|
170
|
-
{ host, port, path: '/api/health', method: 'GET', timeout: timeoutMs, headers },
|
|
171
|
-
(res) => {
|
|
172
|
-
// 401 means auth required — treat as unhealthy, not a generic error
|
|
173
|
-
if (res.statusCode === 401) {
|
|
174
|
-
resolve({ healthy: false, failureKind: 'unauthorized', reason: 'console health endpoint returned 401 (unauthorized) — check PD_CONSOLE_TOKEN' });
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
if (res.statusCode !== 200) {
|
|
178
|
-
resolve({ healthy: false, reason: `console health endpoint returned status ${res.statusCode ?? 'no-status'}` });
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
let data = '';
|
|
182
|
-
res.on('data', (chunk: string) => {
|
|
183
|
-
data += chunk;
|
|
184
|
-
});
|
|
185
|
-
res.on('end', () => {
|
|
186
|
-
try {
|
|
187
|
-
const body: unknown = JSON.parse(data);
|
|
188
|
-
if (isRecord(body)) {
|
|
189
|
-
const isHealthy =
|
|
190
|
-
(Object.hasOwn(body, 'healthy') && Reflect.get(body, 'healthy') === true) ||
|
|
191
|
-
(Object.hasOwn(body, 'success') && Reflect.get(body, 'success') === true);
|
|
192
|
-
if (isHealthy) {
|
|
193
|
-
const dataValue = Object.hasOwn(body, 'data') ? Reflect.get(body, 'data') : undefined;
|
|
194
|
-
const payloadRecord = isRecord(dataValue) ? dataValue : body;
|
|
195
|
-
const mode = Reflect.get(payloadRecord, 'authenticationMode');
|
|
196
|
-
resolve({
|
|
197
|
-
healthy: true,
|
|
198
|
-
...(mode === 'authenticated' || mode === 'no_auth' ? { authenticationMode: mode } : {}),
|
|
199
|
-
});
|
|
200
|
-
} else {
|
|
201
|
-
resolve({ healthy: false, reason: 'console health JSON was missing healthy/success markers' });
|
|
202
|
-
}
|
|
203
|
-
} else {
|
|
204
|
-
resolve({ healthy: false, reason: 'console health endpoint returned non-object JSON' });
|
|
205
|
-
}
|
|
206
|
-
} catch (err) {
|
|
207
|
-
resolve({ healthy: false, reason: `failed to parse console health JSON: ${err instanceof Error ? err.message : String(err)}` });
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
},
|
|
211
|
-
);
|
|
212
|
-
req.on('timeout', () => {
|
|
213
|
-
req.destroy();
|
|
214
|
-
resolve({ healthy: false, reason: 'console health probe timed out' });
|
|
215
|
-
});
|
|
216
|
-
req.on('error', (err: Error) => {
|
|
217
|
-
resolve({ healthy: false, reason: `console health probe error: ${err.message}` });
|
|
218
|
-
});
|
|
219
|
-
req.end();
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/** Find the first available port in [preferred..preferred+limit]. */
|
|
224
|
-
export async function findAvailablePort(
|
|
225
|
-
host: string,
|
|
226
|
-
preferred: number,
|
|
227
|
-
limit = PORT_FALLBACK_LIMIT,
|
|
228
|
-
): Promise<number | null> {
|
|
229
|
-
for (let i = 0; i < limit; i++) {
|
|
230
|
-
const candidate = preferred + i;
|
|
231
|
-
if (candidate > 65535 || candidate < 1) {
|
|
232
|
-
break;
|
|
233
|
-
}
|
|
234
|
-
if (!(await isPortInUse(host, candidate))) return candidate;
|
|
235
|
-
}
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// ─── Browser opener (best-effort, no throw) ──────────────────────────────────
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Validate a browser URL before it is handed to a system opener.
|
|
243
|
-
* Only http/https targets are allowed — other schemes (file:, javascript:,
|
|
244
|
-
* custom protocols) could be abused. This is the primary defense for the
|
|
245
|
-
* win32 opener path, which no longer routes through a shell.
|
|
246
|
-
*/
|
|
247
|
-
function assertSafeBrowserUrl(rawUrl: string): string {
|
|
248
|
-
if (!rawUrl || rawUrl.trim().length === 0) {
|
|
249
|
-
throw new Error('browser URL is empty');
|
|
250
|
-
}
|
|
251
|
-
let url: URL;
|
|
252
|
-
try {
|
|
253
|
-
url = new URL(rawUrl);
|
|
254
|
-
} catch {
|
|
255
|
-
throw new Error(`invalid browser URL: "${rawUrl}"`);
|
|
256
|
-
}
|
|
257
|
-
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
258
|
-
throw new Error(`invalid browser URL: protocol must be http(s), got "${url.protocol}"`);
|
|
259
|
-
}
|
|
260
|
-
return url.toString();
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Open the system browser. Best-effort — failures are reported but do not
|
|
265
|
-
* crash the launcher.
|
|
266
|
-
*
|
|
267
|
-
* Security: the URL is validated (http/https only) and every opener is
|
|
268
|
-
* invoked with a parameterized `spawn` (no shell). In particular the win32
|
|
269
|
-
* path uses `explorer.exe` with an argument array instead of the previous
|
|
270
|
-
* `cmd.exe /c start "" <url>` form, which ran the URL through the cmd shell
|
|
271
|
-
* and allowed shell metacharacters in the URL to be interpreted as commands
|
|
272
|
-
* (command injection).
|
|
273
|
-
*/
|
|
274
|
-
export async function openBrowser(rawUrl: string): Promise<{ opened: boolean; reason?: string; nextAction?: string }> {
|
|
275
|
-
const { spawn } = await import('child_process');
|
|
276
|
-
const { platform } = process;
|
|
277
|
-
|
|
278
|
-
let url: string;
|
|
279
|
-
try {
|
|
280
|
-
url = assertSafeBrowserUrl(rawUrl);
|
|
281
|
-
} catch (err) {
|
|
282
|
-
return {
|
|
283
|
-
opened: false,
|
|
284
|
-
reason: err instanceof Error ? err.message : String(err),
|
|
285
|
-
nextAction: 'Use an http:// or https:// URL.',
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
let cmd: string;
|
|
290
|
-
let args: string[];
|
|
291
|
-
|
|
292
|
-
if (platform === 'win32') {
|
|
293
|
-
// Parameterized spawn (no shell): explorer.exe receives the validated
|
|
294
|
-
// URL as a plain argument and opens it in the default browser.
|
|
295
|
-
cmd = 'explorer.exe';
|
|
296
|
-
args = [url];
|
|
297
|
-
} else if (platform === 'darwin') {
|
|
298
|
-
cmd = 'open';
|
|
299
|
-
args = [url];
|
|
300
|
-
} else {
|
|
301
|
-
cmd = 'xdg-open';
|
|
302
|
-
args = [url];
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
return new Promise((resolve) => {
|
|
306
|
-
try {
|
|
307
|
-
const child = spawn(cmd, args, { detached: true, stdio: 'ignore' });
|
|
308
|
-
|
|
309
|
-
let settled = false;
|
|
310
|
-
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
311
|
-
const done = (result: { opened: boolean; reason?: string; nextAction?: string }) => {
|
|
312
|
-
if (settled) return;
|
|
313
|
-
settled = true;
|
|
314
|
-
if (timer) clearTimeout(timer);
|
|
315
|
-
resolve(result);
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
timer = setTimeout(() => {
|
|
319
|
-
child.unref();
|
|
320
|
-
done({ opened: true });
|
|
321
|
-
}, 1500);
|
|
322
|
-
|
|
323
|
-
child.on('error', (err) => {
|
|
324
|
-
done({
|
|
325
|
-
opened: false,
|
|
326
|
-
reason: `Failed to spawn browser process: ${err.message}`,
|
|
327
|
-
nextAction: `Ensure your system has '${cmd}' available in PATH or open the URL manually.`
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
child.on('close', (code) => {
|
|
332
|
-
if (code === 0) {
|
|
333
|
-
done({ opened: true });
|
|
334
|
-
} else {
|
|
335
|
-
done({
|
|
336
|
-
opened: false,
|
|
337
|
-
reason: `Browser command exited with code ${code}`,
|
|
338
|
-
nextAction: `Open the URL manually: ${url}`
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
} catch (err) {
|
|
343
|
-
resolve({
|
|
344
|
-
opened: false,
|
|
345
|
-
reason: err instanceof Error ? err.message : String(err),
|
|
346
|
-
nextAction: 'Ensure child_process is available or open the URL manually.'
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// ─── Main launcher orchestrator ──────────────────────────────────────────────
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Orchestrates the seed-friendly Console launch:
|
|
356
|
-
* 1. Validate the host is loopback (refuse otherwise)
|
|
357
|
-
* 2. If a healthy console is already running on the preferred port, reuse it
|
|
358
|
-
* 3. Otherwise find the next available port starting at the preferred one
|
|
359
|
-
*
|
|
360
|
-
* NOTE: This function does NOT spawn the server. The caller (CLI command) is
|
|
361
|
-
* responsible for actually launching the child process; the result returned
|
|
362
|
-
* here tells the caller which port to bind to and whether the existing port
|
|
363
|
-
* already serves a console.
|
|
364
|
-
*/
|
|
365
|
-
export interface OrchestratorResult {
|
|
366
|
-
status: ConsoleStatus;
|
|
367
|
-
url: string;
|
|
368
|
-
port: number;
|
|
369
|
-
host: string;
|
|
370
|
-
reused: boolean;
|
|
371
|
-
reason?: string;
|
|
372
|
-
nextAction?: string;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
export interface OrchestratorInput {
|
|
376
|
-
workspaceDir: string;
|
|
377
|
-
preferredPort?: number;
|
|
378
|
-
host?: string;
|
|
379
|
-
/** Optional auth token for health probes (PD_CONSOLE_TOKEN). */
|
|
380
|
-
token?: string;
|
|
381
|
-
/**
|
|
382
|
-
* Where `token` came from ('flag' = --token, 'env' = PD_CONSOLE_TOKEN).
|
|
383
|
-
* PRI-784: mismatch guidance is only actionable when it names the source —
|
|
384
|
-
* a persistently set env var applies to every run, so "reopen without a
|
|
385
|
-
* token" cannot work until the variable is removed at its source. When
|
|
386
|
-
* omitted, guidance stays source-neutral and never mentions
|
|
387
|
-
* PD_CONSOLE_TOKEN (PRI-695: the default install has none).
|
|
388
|
-
*/
|
|
389
|
-
tokenSource?: 'flag' | 'env';
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* PRI-784: action-oriented guidance for the auth-mode mismatch refusal.
|
|
394
|
-
* Names what is running, why this command was refused, and both exits as
|
|
395
|
-
* concrete commands. Source-aware: only the env source mentions
|
|
396
|
-
* PD_CONSOLE_TOKEN (and how to remove it durably).
|
|
397
|
-
*/
|
|
398
|
-
function buildAuthMismatchNextAction(port: number, tokenSource?: 'flag' | 'env'): string {
|
|
399
|
-
let why: string;
|
|
400
|
-
let reuse: string;
|
|
401
|
-
let authenticated: string;
|
|
402
|
-
if (tokenSource === 'env') {
|
|
403
|
-
why =
|
|
404
|
-
'but this command requested authenticated access because the PD_CONSOLE_TOKEN environment variable is set. ' +
|
|
405
|
-
'If it was saved as a permanent user-level environment variable, it silently applies to every run — ' +
|
|
406
|
-
'no terminal can "reopen without a token" until it is removed at its source.';
|
|
407
|
-
reuse =
|
|
408
|
-
'Option 1 — reuse the running Console without authentication:\n' +
|
|
409
|
-
' pd console open --no-auth\n' +
|
|
410
|
-
' and remove the variable at its source so this error stops returning:\n' +
|
|
411
|
-
" Windows PowerShell: [Environment]::SetEnvironmentVariable('PD_CONSOLE_TOKEN', $null, 'User')\n" +
|
|
412
|
-
" macOS/Linux: delete the 'export PD_CONSOLE_TOKEN=...' line in your shell profile, then reopen the terminal";
|
|
413
|
-
authenticated =
|
|
414
|
-
'Option 2 — keep token authentication: stop the running Console (close the terminal or window it runs in), then pass the variable\'s value explicitly:\n' +
|
|
415
|
-
' pd console open --token "$PD_CONSOLE_TOKEN" (PowerShell: --token $env:PD_CONSOLE_TOKEN)';
|
|
416
|
-
} else if (tokenSource === 'flag') {
|
|
417
|
-
why = 'but this command requested authenticated access via --token.';
|
|
418
|
-
reuse =
|
|
419
|
-
'Option 1 — reuse the running Console without authentication:\n' +
|
|
420
|
-
' pd console open --no-auth (drop --token)';
|
|
421
|
-
authenticated =
|
|
422
|
-
'Option 2 — keep token authentication: stop the running Console (close the terminal or window it runs in), then run pd console open again with --token <YOUR_TOKEN>.';
|
|
423
|
-
} else {
|
|
424
|
-
why = 'but this command requested authenticated access by presenting a token.';
|
|
425
|
-
reuse =
|
|
426
|
-
'Option 1 — reuse the running Console without authentication: run pd console open again with --no-auth';
|
|
427
|
-
authenticated =
|
|
428
|
-
'Option 2 — keep token authentication: stop the running Console (close the terminal or window it runs in), then run pd console open again with --token <YOUR_TOKEN>.';
|
|
429
|
-
}
|
|
430
|
-
return (
|
|
431
|
-
`A Console is already running on port ${port} without authentication (the post-install default), ` +
|
|
432
|
-
`${why}\n\n${reuse}\n\n${authenticated}`
|
|
433
|
-
);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
export async function planConsoleLaunch(input: OrchestratorInput): Promise<OrchestratorResult> {
|
|
437
|
-
if (Object.hasOwn(globalThis, '__mockPlanConsoleLaunch')) {
|
|
438
|
-
const mock = Reflect.get(globalThis, '__mockPlanConsoleLaunch') as (
|
|
439
|
-
i: OrchestratorInput
|
|
440
|
-
) => Promise<OrchestratorResult>;
|
|
441
|
-
return mock(input);
|
|
442
|
-
}
|
|
443
|
-
const { token } = input;
|
|
444
|
-
const rawHost = input.host ?? DEFAULT_HOST;
|
|
445
|
-
const host = normalizeLoopbackHost(rawHost);
|
|
446
|
-
const preferredPort = input.preferredPort ?? DEFAULT_PORT;
|
|
447
|
-
|
|
448
|
-
if (!isLoopbackHost(host)) {
|
|
449
|
-
return {
|
|
450
|
-
status: 'refused',
|
|
451
|
-
url: '',
|
|
452
|
-
port: preferredPort,
|
|
453
|
-
host,
|
|
454
|
-
reused: false,
|
|
455
|
-
reason: `Non-loopback host refused: '${host}'. Only 127.0.0.1, ::1, and 'localhost' are allowed.`,
|
|
456
|
-
nextAction: 'Use the default loopback host (do not pass --host 0.0.0.0 or any LAN address).',
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
// Step 1: Is there already a healthy console on the preferred port?
|
|
461
|
-
const health = await probeConsoleHealth({ host, port: preferredPort, token });
|
|
462
|
-
if (health.healthy) {
|
|
463
|
-
if (token && health.authenticationMode !== 'authenticated') {
|
|
464
|
-
// PRI-695: a token-bearing caller must not land on a no_auth server
|
|
465
|
-
// (governance writes would be unauthenticated). The refusal stays, but
|
|
466
|
-
// the guidance must tell the user WHAT is running and the two ways out —
|
|
467
|
-
// the old text referenced "Companion", which a CLI-only user has never
|
|
468
|
-
// configured. PRI-784: the guidance also names where the caller's token
|
|
469
|
-
// came from, because a persistently set PD_CONSOLE_TOKEN makes the
|
|
470
|
-
// "reopen without a token" advice impossible to follow.
|
|
471
|
-
return {
|
|
472
|
-
status: 'refused',
|
|
473
|
-
url: '',
|
|
474
|
-
port: preferredPort,
|
|
475
|
-
host,
|
|
476
|
-
reused: false,
|
|
477
|
-
reason: 'console_authentication_mode_mismatch',
|
|
478
|
-
nextAction: buildAuthMismatchNextAction(preferredPort, input.tokenSource),
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
return {
|
|
482
|
-
status: 'reused',
|
|
483
|
-
url: buildConsoleUrl(host, preferredPort),
|
|
484
|
-
port: preferredPort,
|
|
485
|
-
host,
|
|
486
|
-
reused: true,
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
if (token && health.failureKind === 'unauthorized') {
|
|
491
|
-
return {
|
|
492
|
-
status: 'refused',
|
|
493
|
-
url: '',
|
|
494
|
-
port: preferredPort,
|
|
495
|
-
host,
|
|
496
|
-
reused: false,
|
|
497
|
-
reason: 'console_authentication_failed',
|
|
498
|
-
nextAction: 'Verify PD_CONSOLE_TOKEN matches the running Console, stop that Console, then retry.',
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// Step 2: Is the preferred port simply occupied by something else?
|
|
503
|
-
const preferredInUse = await isPortInUse(host, preferredPort);
|
|
504
|
-
|
|
505
|
-
if (preferredInUse) {
|
|
506
|
-
// Try to find a free port in the fallback range.
|
|
507
|
-
const freePort = await findAvailablePort(host, preferredPort + 1, PORT_FALLBACK_LIMIT - 1);
|
|
508
|
-
if (freePort === null) {
|
|
509
|
-
return {
|
|
510
|
-
status: 'failed',
|
|
511
|
-
url: '',
|
|
512
|
-
port: preferredPort,
|
|
513
|
-
host,
|
|
514
|
-
reused: false,
|
|
515
|
-
reason: `No available port in ${preferredPort + 1}..${preferredPort + PORT_FALLBACK_LIMIT}`,
|
|
516
|
-
nextAction: `Stop the process holding port ${preferredPort} (e.g., another Console instance), or free a port in the fallback range.`,
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
return {
|
|
520
|
-
status: 'started',
|
|
521
|
-
url: buildConsoleUrl(host, freePort),
|
|
522
|
-
port: freePort,
|
|
523
|
-
host,
|
|
524
|
-
reused: false,
|
|
525
|
-
reason: `Preferred port ${preferredPort} was busy; using ${freePort} instead`,
|
|
526
|
-
};
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// Preferred port is free → bind there.
|
|
530
|
-
return {
|
|
531
|
-
status: 'started',
|
|
532
|
-
url: buildConsoleUrl(host, preferredPort),
|
|
533
|
-
port: preferredPort,
|
|
534
|
-
host,
|
|
535
|
-
reused: false,
|
|
536
|
-
};
|
|
537
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Demo Rule Compiler — Minimal bridge for Story A demo runner and the
|
|
3
|
-
* rulehost pipeline behavior checks.
|
|
4
|
-
*
|
|
5
|
-
* PURPOSE: Compile rule implementation code strings into typed evaluate
|
|
6
|
-
* functions for use in `evaluateInRefinerSandbox`.
|
|
7
|
-
*
|
|
8
|
-
* ARCHITECTURE (PRI-809): compilation delegates to the canonical hardened
|
|
9
|
-
* replay evaluator in @principles/core — the vm realm with the JSON-string
|
|
10
|
-
* trust-boundary crossing (input + helpers rebuilt inside the realm), the
|
|
11
|
-
* same primitive the production gate uses. The previous local vm duplicate
|
|
12
|
-
* handed host-realm objects to the vm-realm evaluate function, which let
|
|
13
|
-
* unapproved rule code escape via `input.constructor.constructor`.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import type { RuleHostInput, RuleHostResult } from '@principles/core/runtime-v2';
|
|
17
|
-
import type { RuleHostHelpers } from '@principles/core/runtime-v2';
|
|
18
|
-
import type { ReplayEvaluateFn } from '@principles/core/runtime-v2';
|
|
19
|
-
import { compileHardenedRuleEvaluator, safeStringifyPreview, validateRuleHostResult } from '@principles/core/runtime-v2';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Compile rule implementation code and return a typed evaluate function.
|
|
23
|
-
*
|
|
24
|
-
* @throws if the code fails to compile or does not define a function evaluate
|
|
25
|
-
*/
|
|
26
|
-
export function compileDemoRule(code: string, sourceLabel: string): ReplayEvaluateFn {
|
|
27
|
-
const evaluateFn = compileHardenedRuleEvaluator(code, sourceLabel);
|
|
28
|
-
return (input: RuleHostInput, helpers: RuleHostHelpers): RuleHostResult => {
|
|
29
|
-
const result = evaluateFn(input, helpers);
|
|
30
|
-
const validation = validateRuleHostResult(result);
|
|
31
|
-
if (!validation.valid) {
|
|
32
|
-
throw new Error(
|
|
33
|
-
`[${sourceLabel}]: evaluate returned invalid RuleHostResult — ${validation.errors.join('; ')} (got ${
|
|
34
|
-
typeof result === 'object' && result !== null ? safeStringifyPreview(result) : String(result)
|
|
35
|
-
})`,
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
return result;
|
|
39
|
-
};
|
|
40
|
-
}
|