@principles/pd-cli 1.152.12 → 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.
Files changed (296) hide show
  1. package/package.json +13 -7
  2. package/CHANGELOG.md +0 -23
  3. package/dist/commands/__tests__/intent-flag-wiring.test.d.ts +0 -9
  4. package/dist/commands/__tests__/intent-flag-wiring.test.d.ts.map +0 -1
  5. package/dist/commands/__tests__/intent-flag-wiring.test.js +0 -237
  6. package/dist/commands/__tests__/intent-flag-wiring.test.js.map +0 -1
  7. package/dist/commands/__tests__/legacy-cleanup.test.d.ts +0 -18
  8. package/dist/commands/__tests__/legacy-cleanup.test.d.ts.map +0 -1
  9. package/dist/commands/__tests__/legacy-cleanup.test.js +0 -467
  10. package/dist/commands/__tests__/legacy-cleanup.test.js.map +0 -1
  11. package/dist/commands/__tests__/mvp-smoke.test.d.ts +0 -15
  12. package/dist/commands/__tests__/mvp-smoke.test.d.ts.map +0 -1
  13. package/dist/commands/__tests__/mvp-smoke.test.js +0 -262
  14. package/dist/commands/__tests__/mvp-smoke.test.js.map +0 -1
  15. package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts +0 -2
  16. package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts.map +0 -1
  17. package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js +0 -60
  18. package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js.map +0 -1
  19. package/dist/commands/__tests__/rulecode-flag-wiring.test.d.ts +0 -21
  20. package/dist/commands/__tests__/rulecode-flag-wiring.test.d.ts.map +0 -1
  21. package/dist/commands/__tests__/rulecode-flag-wiring.test.js +0 -186
  22. package/dist/commands/__tests__/rulecode-flag-wiring.test.js.map +0 -1
  23. package/dist/commands/__tests__/rulecode-handler.test.d.ts +0 -16
  24. package/dist/commands/__tests__/rulecode-handler.test.d.ts.map +0 -1
  25. package/dist/commands/__tests__/rulecode-handler.test.js +0 -285
  26. package/dist/commands/__tests__/rulecode-handler.test.js.map +0 -1
  27. package/dist/commands/__tests__/run-rulehost-flag-wiring.test.d.ts +0 -24
  28. package/dist/commands/__tests__/run-rulehost-flag-wiring.test.d.ts.map +0 -1
  29. package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js +0 -232
  30. package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js.map +0 -1
  31. package/dist/commands/__tests__/runtime-activation-nextaction.test.d.ts +0 -15
  32. package/dist/commands/__tests__/runtime-activation-nextaction.test.d.ts.map +0 -1
  33. package/dist/commands/__tests__/runtime-activation-nextaction.test.js +0 -73
  34. package/dist/commands/__tests__/runtime-activation-nextaction.test.js.map +0 -1
  35. package/dist/commands/__tests__/runtime-probe-config.test.d.ts +0 -20
  36. package/dist/commands/__tests__/runtime-probe-config.test.d.ts.map +0 -1
  37. package/dist/commands/__tests__/runtime-probe-config.test.js +0 -389
  38. package/dist/commands/__tests__/runtime-probe-config.test.js.map +0 -1
  39. package/dist/commands/__tests__/telemetry-flag-wiring.test.d.ts +0 -9
  40. package/dist/commands/__tests__/telemetry-flag-wiring.test.d.ts.map +0 -1
  41. package/dist/commands/__tests__/telemetry-flag-wiring.test.js +0 -76
  42. package/dist/commands/__tests__/telemetry-flag-wiring.test.js.map +0 -1
  43. package/dist/commands/__tests__/version.test.d.ts +0 -2
  44. package/dist/commands/__tests__/version.test.d.ts.map +0 -1
  45. package/dist/commands/__tests__/version.test.js +0 -238
  46. package/dist/commands/__tests__/version.test.js.map +0 -1
  47. package/dist/commands/runtime-uat.guard.test.d.ts +0 -2
  48. package/dist/commands/runtime-uat.guard.test.d.ts.map +0 -1
  49. package/dist/commands/runtime-uat.guard.test.js +0 -153
  50. package/dist/commands/runtime-uat.guard.test.js.map +0 -1
  51. package/dist/services/__tests__/cli-output.test.d.ts +0 -18
  52. package/dist/services/__tests__/cli-output.test.d.ts.map +0 -1
  53. package/dist/services/__tests__/cli-output.test.js +0 -103
  54. package/dist/services/__tests__/cli-output.test.js.map +0 -1
  55. package/dist/services/__tests__/evaluator-runner-deps.test.d.ts +0 -2
  56. package/dist/services/__tests__/evaluator-runner-deps.test.d.ts.map +0 -1
  57. package/dist/services/__tests__/evaluator-runner-deps.test.js +0 -343
  58. package/dist/services/__tests__/evaluator-runner-deps.test.js.map +0 -1
  59. package/dist/services/__tests__/rulehost-readiness.test.d.ts +0 -2
  60. package/dist/services/__tests__/rulehost-readiness.test.d.ts.map +0 -1
  61. package/dist/services/__tests__/rulehost-readiness.test.js +0 -311
  62. package/dist/services/__tests__/rulehost-readiness.test.js.map +0 -1
  63. package/dist/services/__tests__/runtime-adapter-resolver.test.d.ts +0 -18
  64. package/dist/services/__tests__/runtime-adapter-resolver.test.d.ts.map +0 -1
  65. package/dist/services/__tests__/runtime-adapter-resolver.test.js +0 -673
  66. package/dist/services/__tests__/runtime-adapter-resolver.test.js.map +0 -1
  67. package/dist/utils/production-workspace-guard.test.d.ts +0 -2
  68. package/dist/utils/production-workspace-guard.test.d.ts.map +0 -1
  69. package/dist/utils/production-workspace-guard.test.js +0 -137
  70. package/dist/utils/production-workspace-guard.test.js.map +0 -1
  71. package/scripts/llm-dogfood.ts +0 -456
  72. package/scripts/migrate-illegal-expected-decision.ts +0 -252
  73. package/src/commands/__tests__/intent-flag-wiring.test.ts +0 -282
  74. package/src/commands/__tests__/legacy-cleanup.test.ts +0 -602
  75. package/src/commands/__tests__/mvp-smoke.test.ts +0 -300
  76. package/src/commands/__tests__/pain-retry-maxTokens-flag.test.ts +0 -73
  77. package/src/commands/__tests__/rulecode-flag-wiring.test.ts +0 -237
  78. package/src/commands/__tests__/rulecode-handler.test.ts +0 -369
  79. package/src/commands/__tests__/run-rulehost-flag-wiring.test.ts +0 -292
  80. package/src/commands/__tests__/runtime-activation-nextaction.test.ts +0 -81
  81. package/src/commands/__tests__/runtime-probe-config.test.ts +0 -432
  82. package/src/commands/__tests__/telemetry-flag-wiring.test.ts +0 -84
  83. package/src/commands/__tests__/version.test.ts +0 -251
  84. package/src/commands/admission-gate.ts +0 -30
  85. package/src/commands/artifact.ts +0 -82
  86. package/src/commands/build-trajectory-evidence.ts +0 -298
  87. package/src/commands/candidate.ts +0 -1250
  88. package/src/commands/codex-ingest-catchup.ts +0 -114
  89. package/src/commands/codex-ingest-quarantine.ts +0 -127
  90. package/src/commands/codex-reconcile.ts +0 -84
  91. package/src/commands/codex-setup.ts +0 -476
  92. package/src/commands/codex-worker.ts +0 -185
  93. package/src/commands/command-helpers.ts +0 -24
  94. package/src/commands/config-doctor.ts +0 -193
  95. package/src/commands/console.ts +0 -797
  96. package/src/commands/context.ts +0 -72
  97. package/src/commands/demo-story-a.ts +0 -241
  98. package/src/commands/diagnose.ts +0 -756
  99. package/src/commands/errors-list.ts +0 -441
  100. package/src/commands/evolution-tasks-list.ts +0 -48
  101. package/src/commands/evolution-tasks-show.ts +0 -62
  102. package/src/commands/flow.ts +0 -60
  103. package/src/commands/health-codex.ts +0 -576
  104. package/src/commands/health.ts +0 -315
  105. package/src/commands/history.ts +0 -63
  106. package/src/commands/intent.ts +0 -457
  107. package/src/commands/legacy-cleanup.ts +0 -531
  108. package/src/commands/legacy-import.ts +0 -104
  109. package/src/commands/mvp-smoke.ts +0 -160
  110. package/src/commands/pain-evidence.ts +0 -226
  111. package/src/commands/pain-list.ts +0 -239
  112. package/src/commands/pain-record.ts +0 -612
  113. package/src/commands/pain-retry.ts +0 -916
  114. package/src/commands/principles-stats.ts +0 -700
  115. package/src/commands/proven-channel-baseline.ts +0 -115
  116. package/src/commands/quality-scorecard.ts +0 -272
  117. package/src/commands/remediation-output.ts +0 -66
  118. package/src/commands/rulecode.ts +0 -465
  119. package/src/commands/run.ts +0 -89
  120. package/src/commands/runtime-activation.ts +0 -1629
  121. package/src/commands/runtime-artifact-repair.ts +0 -524
  122. package/src/commands/runtime-canary.ts +0 -395
  123. package/src/commands/runtime-compatibility-scan.ts +0 -105
  124. package/src/commands/runtime-diagnostics-export.ts +0 -229
  125. package/src/commands/runtime-features.ts +0 -153
  126. package/src/commands/runtime-gfi-snapshot.ts +0 -135
  127. package/src/commands/runtime-health-snapshot.ts +0 -108
  128. package/src/commands/runtime-init.ts +0 -429
  129. package/src/commands/runtime-internalization-enqueue-successors.ts +0 -530
  130. package/src/commands/runtime-internalization-integrity-repair.ts +0 -79
  131. package/src/commands/runtime-internalization-integrity.ts +0 -107
  132. package/src/commands/runtime-internalization-queue.ts +0 -135
  133. package/src/commands/runtime-internalization-retry.ts +0 -191
  134. package/src/commands/runtime-internalization-run-once.ts +0 -791
  135. package/src/commands/runtime-internalization-run-rulehost.ts +0 -749
  136. package/src/commands/runtime-internalization-wake-once.ts +0 -87
  137. package/src/commands/runtime-pain-flood-simulation.ts +0 -121
  138. package/src/commands/runtime-pruning.ts +0 -438
  139. package/src/commands/runtime-recovery-failed-tasks.ts +0 -258
  140. package/src/commands/runtime-recovery.ts +0 -109
  141. package/src/commands/runtime-synthetic-baseline.ts +0 -70
  142. package/src/commands/runtime-uat.guard.test.ts +0 -175
  143. package/src/commands/runtime-uat.ts +0 -380
  144. package/src/commands/runtime.ts +0 -473
  145. package/src/commands/samples-list.ts +0 -39
  146. package/src/commands/samples-review.ts +0 -36
  147. package/src/commands/task.ts +0 -271
  148. package/src/commands/telemetry.ts +0 -349
  149. package/src/commands/trace.ts +0 -174
  150. package/src/commands/trajectory-db-unavailable.ts +0 -19
  151. package/src/commands/trajectory.ts +0 -64
  152. package/src/commands/version.ts +0 -49
  153. package/src/config-reader.ts +0 -122
  154. package/src/index.ts +0 -1276
  155. package/src/legacy/legacy-import.ts +0 -179
  156. package/src/legacy/session-history-import.ts +0 -203
  157. package/src/resolve-workspace.ts +0 -106
  158. package/src/services/__tests__/cli-output.test.ts +0 -130
  159. package/src/services/__tests__/evaluator-runner-deps.test.ts +0 -382
  160. package/src/services/__tests__/rulehost-readiness.test.ts +0 -363
  161. package/src/services/__tests__/runtime-adapter-resolver.test.ts +0 -793
  162. package/src/services/cli-output.ts +0 -95
  163. package/src/services/config-doctor.ts +0 -697
  164. package/src/services/console-launcher.ts +0 -537
  165. package/src/services/demo-rule-compiler.ts +0 -40
  166. package/src/services/demo-story-a-runner.ts +0 -471
  167. package/src/services/mainline-snapshot-assembler.ts +0 -579
  168. package/src/services/pain-flood-simulation-runner.ts +0 -462
  169. package/src/services/pd-config-loader.ts +0 -433
  170. package/src/services/proven-channel-baseline-runner.ts +0 -157
  171. package/src/services/quality-scorecard/data-extractor.ts +0 -150
  172. package/src/services/quality-scorecard/local-evaluator.ts +0 -142
  173. package/src/services/quality-scorecard/strong-model-gate.ts +0 -202
  174. package/src/services/resolve-runtime-from-pd-config.ts +0 -230
  175. package/src/services/rulehost-pipeline-runner.ts +0 -1214
  176. package/src/services/rulehost-readiness.ts +0 -326
  177. package/src/services/runtime-adapter-resolver.ts +0 -364
  178. package/src/services/synthetic-baseline-runner.ts +0 -400
  179. package/src/services/version-report.ts +0 -253
  180. package/src/services/workspace-tool-semantics.ts +0 -26
  181. package/src/utils/path-security.ts +0 -96
  182. package/src/utils/production-workspace-guard.test.ts +0 -157
  183. package/src/utils/production-workspace-guard.ts +0 -210
  184. package/tests/bdd/cli-contract.steps.ts +0 -346
  185. package/tests/bdd/principles-stats.steps.ts +0 -211
  186. package/tests/bdd/support/gherkin-loader.ts +0 -147
  187. package/tests/bdd/support/repo-root.ts +0 -62
  188. package/tests/bdd/support/vitest-bdd.ts +0 -179
  189. package/tests/commands/admission-gate-coverage.test.ts +0 -79
  190. package/tests/commands/build-trajectory-evidence.test.ts +0 -508
  191. package/tests/commands/candidate-audit-repair.test.ts +0 -421
  192. package/tests/commands/candidate-intake.test.ts +0 -732
  193. package/tests/commands/candidate-internalization-backfill.test.ts +0 -548
  194. package/tests/commands/candidate-internalize-lineage.test.ts +0 -628
  195. package/tests/commands/candidate-internalize.test.ts +0 -299
  196. package/tests/commands/candidate-route.test.ts +0 -329
  197. package/tests/commands/candidate-show.test.ts +0 -95
  198. package/tests/commands/cli-command-tree.test.ts +0 -144
  199. package/tests/commands/cli-help-snapshot.test.ts +0 -155
  200. package/tests/commands/cli-skill-contract.test.ts +0 -121
  201. package/tests/commands/codex-consent-reversibility.steps.test.ts +0 -299
  202. package/tests/commands/codex-ingest-quarantine.test.ts +0 -136
  203. package/tests/commands/codex-setup.test.ts +0 -297
  204. package/tests/commands/codex-worker-daemon.test.ts +0 -95
  205. package/tests/commands/codex-worker-registration.test.ts +0 -207
  206. package/tests/commands/config-doctor.test.ts +0 -336
  207. package/tests/commands/console-launcher-edge-cases.test.ts +0 -388
  208. package/tests/commands/console-open.test.ts +0 -1381
  209. package/tests/commands/context.test.ts +0 -114
  210. package/tests/commands/demo-story-a.test.ts +0 -318
  211. package/tests/commands/diagnose.test.ts +0 -1803
  212. package/tests/commands/errors-list.test.ts +0 -679
  213. package/tests/commands/evolution-tasks-list.test.ts +0 -41
  214. package/tests/commands/evolution-tasks-show.test.ts +0 -46
  215. package/tests/commands/health-codex.test.ts +0 -349
  216. package/tests/commands/health-host-wiring.test.ts +0 -76
  217. package/tests/commands/health-reliability.test.ts +0 -99
  218. package/tests/commands/health.test.ts +0 -413
  219. package/tests/commands/intent.test.ts +0 -321
  220. package/tests/commands/legacy-cleanup.test.ts +0 -148
  221. package/tests/commands/pain-evidence.test.ts +0 -518
  222. package/tests/commands/pain-list.test.ts +0 -219
  223. package/tests/commands/pain-record-async.test.ts +0 -335
  224. package/tests/commands/pain-record-session-parser.test.ts +0 -228
  225. package/tests/commands/pain-record.test.ts +0 -851
  226. package/tests/commands/pain-retry.test.ts +0 -1121
  227. package/tests/commands/plugin-config-resolution-cutover.test.ts +0 -229
  228. package/tests/commands/pri-393-runtime-config-unification.test.ts +0 -443
  229. package/tests/commands/principles-stats-wiring.test.ts +0 -135
  230. package/tests/commands/principles-stats.test.ts +0 -274
  231. package/tests/commands/product-path-regression.test.ts +0 -86
  232. package/tests/commands/proven-channel-baseline.test.ts +0 -441
  233. package/tests/commands/rulecode.test.ts +0 -177
  234. package/tests/commands/run-rulehost-handler.test.ts +0 -908
  235. package/tests/commands/runtime-activation-approve-flag-wiring.test.ts +0 -227
  236. package/tests/commands/runtime-activation-deactivate-flag-wiring.test.ts +0 -167
  237. package/tests/commands/runtime-activation-dispatch-flag-wiring.test.ts +0 -262
  238. package/tests/commands/runtime-activation-governance-audit.test.ts +0 -95
  239. package/tests/commands/runtime-activation-list-flag-wiring.test.ts +0 -193
  240. package/tests/commands/runtime-activation-promote-flag-wiring.test.ts +0 -248
  241. package/tests/commands/runtime-activation-shadow-telemetry.test.ts +0 -130
  242. package/tests/commands/runtime-activation.test.ts +0 -2052
  243. package/tests/commands/runtime-artifact-repair-registration.test.ts +0 -74
  244. package/tests/commands/runtime-artifact-repair.test.ts +0 -404
  245. package/tests/commands/runtime-canary.test.ts +0 -430
  246. package/tests/commands/runtime-compatibility-scan.test.ts +0 -146
  247. package/tests/commands/runtime-diagnostics-export.test.ts +0 -175
  248. package/tests/commands/runtime-features.test.ts +0 -249
  249. package/tests/commands/runtime-health-snapshot.test.ts +0 -358
  250. package/tests/commands/runtime-init-empty-workspace.test.ts +0 -252
  251. package/tests/commands/runtime-init.test.ts +0 -420
  252. package/tests/commands/runtime-internalization-enqueue-successors.test.ts +0 -782
  253. package/tests/commands/runtime-internalization-integrity-repair.test.ts +0 -239
  254. package/tests/commands/runtime-internalization-integrity.test.ts +0 -140
  255. package/tests/commands/runtime-internalization-queue.test.ts +0 -380
  256. package/tests/commands/runtime-internalization-retry-owner-authority.test.ts +0 -431
  257. package/tests/commands/runtime-internalization-run-once-evaluator-parity.test.ts +0 -269
  258. package/tests/commands/runtime-internalization-run-once-language.test.ts +0 -303
  259. package/tests/commands/runtime-internalization-run-once-real-config.test.ts +0 -237
  260. package/tests/commands/runtime-internalization-run-once-rollout-parity.test.ts +0 -706
  261. package/tests/commands/runtime-internalization-run-once.test.ts +0 -1791
  262. package/tests/commands/runtime-internalization-wake-once.test.ts +0 -171
  263. package/tests/commands/runtime-internalization.test.ts +0 -53
  264. package/tests/commands/runtime-pain-flood-simulation.test.ts +0 -418
  265. package/tests/commands/runtime-pruning.test.ts +0 -694
  266. package/tests/commands/runtime-recovery-failed-tasks.test.ts +0 -328
  267. package/tests/commands/runtime-recovery.test.ts +0 -120
  268. package/tests/commands/runtime-synthetic-baseline.test.ts +0 -249
  269. package/tests/commands/runtime-uat.test.ts +0 -397
  270. package/tests/commands/runtime.test.ts +0 -392
  271. package/tests/commands/samples-list.test.ts +0 -43
  272. package/tests/commands/samples-review.test.ts +0 -41
  273. package/tests/commands/task.test.ts +0 -153
  274. package/tests/commands/telemetry.test.ts +0 -190
  275. package/tests/commands/trace.test.ts +0 -316
  276. package/tests/config-reader.test.ts +0 -143
  277. package/tests/e2e/candidate-intake-e2e.test.ts +0 -338
  278. package/tests/e2e/cli-full-flow.test.ts +0 -233
  279. package/tests/e2e/cross-package-acceptance.test.ts +0 -635
  280. package/tests/helpers/pd-cli-path.ts +0 -5
  281. package/tests/pd-cli-smoke.test.ts +0 -13
  282. package/tests/resolve-workspace.test.ts +0 -196
  283. package/tests/services/config-doctor-signal-health.test.ts +0 -109
  284. package/tests/services/console-launcher.test.ts +0 -114
  285. package/tests/services/demo-rule-compiler.test.ts +0 -242
  286. package/tests/services/mainline-snapshot-assembler.test.ts +0 -572
  287. package/tests/services/pd-config-loader.test.ts +0 -505
  288. package/tests/services/proven-channel-baseline-runner.test.ts +0 -30
  289. package/tests/services/quality-scorecard/strong-model-gate.test.ts +0 -133
  290. package/tests/services/resolve-runtime-from-pd-config.test.ts +0 -485
  291. package/tests/services/rulehost-pipeline-e2e.test.ts +0 -538
  292. package/tests/services/rulehost-pipeline-runner.test.ts +0 -1242
  293. package/tests/services/workspace-tool-semantics.test.ts +0 -76
  294. package/tests/utils/path-security.test.ts +0 -180
  295. package/tsconfig.json +0 -26
  296. package/vitest.config.ts +0 -10
@@ -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
- }