@tekmidian/pai 1.0.5 → 1.0.7

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 (247) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/{aibroker-client-CQPGqLxL.mjs → aibroker-client-CXsyC7A8.mjs} +2 -2
  3. package/dist/aibroker-client-ChB7gSxV.mjs +3 -0
  4. package/dist/auto-route-CAlZ5p-h.mjs +11 -0
  5. package/dist/{auto-route-DUYr57rs.mjs → auto-route-PDOAYszN.mjs} +2 -2
  6. package/dist/budget-BXmsSBLI.mjs +15 -0
  7. package/dist/{caller-identity-Bim8JT-D.mjs → caller-identity-DPDSrZE2.mjs} +4 -4
  8. package/dist/{capacity-CsZMkVyg.mjs → capacity-CThSydrd.mjs} +82 -10
  9. package/dist/{chain-Dzz6xcKH.mjs → chain-BvdKqRiZ.mjs} +5 -5
  10. package/dist/cli/index.mjs +2175 -284
  11. package/dist/{cockpit-actions-BTA1njMO.mjs → cockpit-actions-BgdKB9G3.mjs} +26 -26
  12. package/dist/{cockpit-actions-BgsiNcUY.mjs → cockpit-actions-DNl0rKfW.mjs} +18 -18
  13. package/dist/cockpit-memory-CqLPjcWe.mjs +20 -0
  14. package/dist/config/defaults/config.yaml +24 -1
  15. package/dist/config/defaults/workers.yaml +2 -0
  16. package/dist/{config-h5qK3fst.mjs → config-C2dd2SXS.mjs} +2 -2
  17. package/dist/{config-CST2uq1v.mjs → config-DGV64YJa.mjs} +11 -3
  18. package/dist/{config-Dv3t7xnT.mjs → config-DHgPx3ER.mjs} +1 -1
  19. package/dist/{config-DYbyEe4-.mjs → config-SlZ-Z52i.mjs} +3 -2
  20. package/dist/{controls-OiVVbe-f.mjs → controls-ByrcupcA.mjs} +59 -66
  21. package/dist/daemon/index.mjs +40 -40
  22. package/dist/{daemon-C1dycZ3W.mjs → daemon-BjgJyJMP.mjs} +79 -31
  23. package/dist/daemon-iymy18Ie.mjs +46 -0
  24. package/dist/daemon-mcp/index.mjs +49 -38
  25. package/dist/detect-C8nFP-kR.mjs +10 -0
  26. package/dist/{detect-CHuxFrLy.mjs → detect-DLjeYwnt.mjs} +2 -2
  27. package/dist/{embed-pass-DtVF2fOV.mjs → embed-pass-DdTMn4DH.mjs} +5 -5
  28. package/dist/{embedding-gate-B98PK_wz.mjs → embedding-gate-BVQvUmox.mjs} +2 -2
  29. package/dist/{embedding-gate-CTAk8STe.mjs → embedding-gate-iq0oQEkF.mjs} +3 -3
  30. package/dist/{endreason-BqyyU7BH.mjs → endreason-BjPom527.mjs} +4 -4
  31. package/dist/{factory-BV0MQA3T.mjs → factory-CYwa0jYj.mjs} +6 -6
  32. package/dist/factory-DFwez99M.mjs +9 -0
  33. package/dist/{fallback-Bj2cyVk_.mjs → fallback-CQ8_CuVu.mjs} +9 -231
  34. package/dist/{health-kufVIf6i.mjs → health-Bb2U4PNK.mjs} +5 -5
  35. package/dist/{helpers-CvbYEKoD.mjs → helpers-Dj2nY56t.mjs} +2 -2
  36. package/dist/{helpers-WV8tdQHM.mjs → helpers-DyHKAwrW.mjs} +6 -6
  37. package/dist/hooks/block-sleep-poll.mjs +9 -0
  38. package/dist/hooks/capture-all-events.mjs +99 -3
  39. package/dist/hooks/capture-tool-output.mjs +99 -3
  40. package/dist/hooks/handoff-link.mjs +159 -0
  41. package/dist/hooks/load-project-context.mjs +10 -1
  42. package/dist/hooks/local-model-hint.mjs +9 -0
  43. package/dist/hooks/route-agents-to-worker.mjs +9 -0
  44. package/dist/hooks/security-validator.mjs +940 -92
  45. package/dist/hooks/supervision-banner.mjs +219 -48
  46. package/dist/hooks/worker-edit-repair.mjs +9 -0
  47. package/dist/hooks/worker-guard.mjs +97 -1
  48. package/dist/hooks/worker-note.mjs +108 -3
  49. package/dist/hooks/worker-pool-host.mjs +1670 -315
  50. package/dist/hooks/worker-proxy.mjs +9 -0
  51. package/dist/hooks/worker-status-line.mjs +251 -70
  52. package/dist/hooks/worker-supervision.mjs +9 -0
  53. package/dist/hooks/worker-tool-name-repair.mjs +9 -0
  54. package/dist/index.mjs +9 -9
  55. package/dist/{ipc-client-CnYEF31R.mjs → ipc-client-CmagIFLh.mjs} +2 -2
  56. package/dist/{ipc-client-DN7EvoVo.mjs → ipc-client-DmbSIBUK.mjs} +1 -1
  57. package/dist/kill-tree-BlPw1vaU.mjs +10 -0
  58. package/dist/{kill-tree-CcpsRLCZ.mjs → kill-tree-DTmiEb3T.mjs} +8 -8
  59. package/dist/{launch-DpQPZGsT.mjs → launch-CV6oPHI2.mjs} +3 -3
  60. package/dist/{launch-UPQBoW2q.mjs → launch-Dy8qQnSD.mjs} +3 -3
  61. package/dist/{indexer-backend-B2vtFl38.mjs → lessons-CdmqZD0G.mjs} +262 -43
  62. package/dist/{local-candidate-Cp_bw4kK.mjs → local-candidate-C2FGhf54.mjs} +3 -3
  63. package/dist/{local-instances-bdCNNSsL.mjs → local-instances-DhF4_sV2.mjs} +2 -2
  64. package/dist/{local-instances-B-c-CxzP.mjs → local-instances-GgXydQ2P.mjs} +4 -4
  65. package/dist/{local-tiers-BtdEEe5n.mjs → local-tiers-CqwGUcEn.mjs} +3 -3
  66. package/dist/{local-tiers-OBqAAfOg.mjs → local-tiers-X_dQfI4Q.mjs} +3 -3
  67. package/dist/{main-config-ops-BYsK3BxM.mjs → main-config-ops-C_fkINDv.mjs} +3 -3
  68. package/dist/{main-resolver-CAC3xfZM.mjs → main-resolver-CN_sfG1R.mjs} +18 -18
  69. package/dist/{migrate-DrA_OwlZ.mjs → migrate-C2z1YmWC.mjs} +2 -2
  70. package/dist/{model-inventory-BuTpi9a6.mjs → model-inventory-BYNrSTaY.mjs} +13 -13
  71. package/dist/{models-add-CJx2YAwh.mjs → models-add-Dc7VXmcu.mjs} +38 -38
  72. package/dist/operator-IrLbk8hy.mjs +7 -0
  73. package/dist/{operator-DrwWItVt.mjs → operator-lRGvYr3D.mjs} +2 -2
  74. package/dist/{pai-marker-DLKJ9Yr5.mjs → pai-marker-kA0iM7rP.mjs} +2 -2
  75. package/dist/{pane-audit-wv9UkyCe.mjs → pane-audit-DUuilP1V.mjs} +13 -23
  76. package/dist/{paths-DS5VQKoR.mjs → paths-BwfwHOvN.mjs} +4 -4
  77. package/dist/{paths-DBd78I7Z.mjs → paths-CKGYM0Lb.mjs} +3 -3
  78. package/dist/{planner-K0mFrCpL.mjs → planner-BDHVUkIM.mjs} +29 -29
  79. package/dist/{pool-BZ94nFuq.mjs → pool-CFeHGX7q.mjs} +2 -2
  80. package/dist/{pool-cli-BjYQDfx6.mjs → pool-cli-Bj2TeA-d.mjs} +6 -6
  81. package/dist/{pool-cli-B1eKySeN.mjs → pool-cli-DOLgPgOM.mjs} +5 -5
  82. package/dist/{postgres-CBHY5PIi.mjs → postgres-B1B80bcj.mjs} +3 -3
  83. package/dist/{postgres-C-T5OWer.mjs → postgres-BL5oBBgY.mjs} +3 -3
  84. package/dist/{project-setup-pIPHZaXf.mjs → project-setup-i5D0xkTn.mjs} +24 -25
  85. package/dist/{providers-Dzx5GAUI.mjs → providers-BC2Myo_O.mjs} +6 -6
  86. package/dist/providers-C-mnlH1b.mjs +16 -0
  87. package/dist/{query-feedback-DN52N9C5.mjs → query-feedback-BWl2HRwN.mjs} +2 -2
  88. package/dist/registrable-Ct0DKNKS.mjs +9 -0
  89. package/dist/{registrable-CXBBpUcz.mjs → registrable-D_4qXCnq.mjs} +4 -4
  90. package/dist/render-CbifCwTb.mjs +2350 -0
  91. package/dist/render-FB4Fv_fi.mjs +13 -0
  92. package/dist/root-80ESg6b7.mjs +36 -0
  93. package/dist/{root-bG48WVpW.mjs → root-B-ZYiEgi.mjs} +10 -10
  94. package/dist/route-table-DcBZCuco.mjs +15 -0
  95. package/dist/{router-CQSc3lk3.mjs → router-D2476pOp.mjs} +2 -2
  96. package/dist/{router-DwHJRgG9.mjs → router-ghujlEFb.mjs} +1 -1
  97. package/dist/{routing-BlD_tKjW.mjs → routing-CF0wlR-_.mjs} +15 -15
  98. package/dist/{routing-D27jxj9x.mjs → routing-CJ8ioPl2.mjs} +13 -13
  99. package/dist/{run-AZVwceiU.mjs → run-CAjfKBQV.mjs} +74 -31
  100. package/dist/{runtime-paths-WsO9sJFc.mjs → runtime-paths-twfV23YL.mjs} +2 -2
  101. package/dist/{scan-C3uotg_f.mjs → scan-ew8M6tTH.mjs} +7 -7
  102. package/dist/{scope-D8HK6vU7.mjs → scope-BRcaPw5D.mjs} +4 -4
  103. package/dist/session-BmQsXF_a.mjs +35 -0
  104. package/dist/{session-BAfypQb9.mjs → session-C1JnDXvP.mjs} +28 -28
  105. package/dist/session-scan-DWCUKhDM.mjs +9 -0
  106. package/dist/{session-scan-BmnjSReu.mjs → session-scan-DihMibI-.mjs} +6 -6
  107. package/dist/{session-usage-oycSdiD3.mjs → session-usage-BRIWFYPS.mjs} +3 -3
  108. package/dist/status-Bj4yVKj0.mjs +6 -0
  109. package/dist/{status-COtzewKV.mjs → status-DTMT5lyn.mjs} +9 -5
  110. package/dist/{tool-errors-DXJ70-sr.mjs → tool-errors-BOW2n882.mjs} +2 -2
  111. package/dist/{tool-errors-B2LnIqR0.mjs → tool-errors-jNJj3Gzm.mjs} +6 -6
  112. package/dist/{viewer-BgGRqXfD.mjs → viewer-4BU8w_gK.mjs} +16 -14
  113. package/dist/viewer-D3VEdqhC.mjs +22 -0
  114. package/dist/{wait-ChpHKJZV.mjs → wait-BindDA5w.mjs} +3 -3
  115. package/dist/{worker-config-DgdT0iTB.mjs → worker-config-CdPmb4_2.mjs} +54 -103
  116. package/dist/worker-config-CfV2dgSb.mjs +42 -0
  117. package/dist/{workers-config-BhZBrR-Q.mjs → workers-config-2VipVOYk.mjs} +2 -2
  118. package/dist/{worktree-BY--Rgmy.mjs → worktree-7ZS3RBf8.mjs} +175 -9
  119. package/dist/{zettelkasten-BHVx5zj-.mjs → zettelkasten-BH8AdNh8.mjs} +4 -4
  120. package/dist/{zettelkasten-LoH9RBiN.mjs → zettelkasten-FFhQFESJ.mjs} +2 -2
  121. package/docs/commands/README.md +32 -2
  122. package/docs/commands/audit.md +34 -3
  123. package/docs/commands/backup.md +1 -1
  124. package/docs/commands/build.md +4 -4
  125. package/docs/commands/cd.md +1 -1
  126. package/docs/commands/clear-names.md +1 -1
  127. package/docs/commands/cockpit.md +1 -1
  128. package/docs/commands/config.md +1 -1
  129. package/docs/commands/daemon.md +1 -1
  130. package/docs/commands/db.md +1 -1
  131. package/docs/commands/doc.md +1 -1
  132. package/docs/commands/end.md +1 -1
  133. package/docs/commands/find.md +1 -1
  134. package/docs/commands/git.md +110 -0
  135. package/docs/commands/handoff.md +70 -0
  136. package/docs/commands/harness.md +74 -0
  137. package/docs/commands/help.md +1 -1
  138. package/docs/commands/home.md +1 -1
  139. package/docs/commands/hooks-db.md +1 -1
  140. package/docs/commands/identity.md +1 -1
  141. package/docs/commands/install.md +1 -1
  142. package/docs/commands/issue.md +1 -1
  143. package/docs/commands/kg.md +1 -1
  144. package/docs/commands/launch.md +1 -1
  145. package/docs/commands/lessons.md +201 -0
  146. package/docs/commands/list.md +1 -1
  147. package/docs/commands/ls.md +1 -1
  148. package/docs/commands/mcp.md +1 -1
  149. package/docs/commands/memory.md +1 -1
  150. package/docs/commands/models.md +1 -1
  151. package/docs/commands/more.md +1 -1
  152. package/docs/commands/new.md +1 -1
  153. package/docs/commands/notes.md +1 -1
  154. package/docs/commands/notify.md +1 -1
  155. package/docs/commands/observation.md +1 -1
  156. package/docs/commands/obsidian.md +1 -1
  157. package/docs/commands/open.md +1 -1
  158. package/docs/commands/pane.md +1 -1
  159. package/docs/commands/pause.md +1 -1
  160. package/docs/commands/plan.md +1 -1
  161. package/docs/commands/project.md +1 -1
  162. package/docs/commands/projects.md +1 -1
  163. package/docs/commands/providers.md +1 -1
  164. package/docs/commands/queue.md +1 -1
  165. package/docs/commands/registry.md +1 -1
  166. package/docs/commands/reload.md +1 -1
  167. package/docs/commands/rename.md +1 -1
  168. package/docs/commands/restore.md +1 -1
  169. package/docs/commands/resume.md +1 -1
  170. package/docs/commands/router.md +1 -1
  171. package/docs/commands/rules.md +70 -0
  172. package/docs/commands/session.md +1 -1
  173. package/docs/commands/sessions.md +1 -1
  174. package/docs/commands/setup.md +1 -1
  175. package/docs/commands/setups.md +1 -1
  176. package/docs/commands/shell-init.md +1 -1
  177. package/docs/commands/skill.md +1 -1
  178. package/docs/commands/task.md +1 -1
  179. package/docs/commands/tasks.md +1 -1
  180. package/docs/commands/topic.md +1 -1
  181. package/docs/commands/update.md +1 -1
  182. package/docs/commands/worker.md +1 -1
  183. package/docs/commands/zettel.md +1 -1
  184. package/docs/concepts/panes.md +3 -2
  185. package/gemini-extension.json +1 -1
  186. package/package.json +2 -2
  187. package/pai-plugin.json +1 -1
  188. package/plugins/context-preservation/plugin.json +1 -1
  189. package/plugins/core/plugin.json +1 -1
  190. package/plugins/creative/plugin.json +1 -1
  191. package/plugins/observability/plugin.json +1 -1
  192. package/plugins/productivity/plugin.json +1 -1
  193. package/plugins/semantic-search/plugin.json +1 -1
  194. package/plugins/ui/plugin.json +1 -1
  195. package/plugins/zettelkasten/plugin.json +1 -1
  196. package/src/hooks/ts/lib/loop-guard.ts +3 -5
  197. package/src/hooks/ts/lib/rules/builtin/base64-decode-to-shell.rule.yaml +15 -0
  198. package/src/hooks/ts/lib/rules/builtin/chmod-world-writable.rule.yaml +13 -0
  199. package/src/hooks/ts/lib/rules/builtin/chmod-zero.rule.yaml +13 -0
  200. package/src/hooks/ts/lib/rules/builtin/credential-file-read.rule.yaml +13 -0
  201. package/src/hooks/ts/lib/rules/builtin/credential-read-then-network.rule.yaml +29 -0
  202. package/src/hooks/ts/lib/rules/builtin/crontab-persistence.rule.yaml +10 -0
  203. package/src/hooks/ts/lib/rules/builtin/curl-piped-to-shell.rule.yaml +22 -0
  204. package/src/hooks/ts/lib/rules/builtin/download-then-execute.rule.yaml +30 -0
  205. package/src/hooks/ts/lib/rules/builtin/find-root-delete.rule.yaml +14 -0
  206. package/src/hooks/ts/lib/rules/builtin/git-push-force.rule.yaml +24 -0
  207. package/src/hooks/ts/lib/rules/builtin/git-push-plus-refspec.rule.yaml +12 -0
  208. package/src/hooks/ts/lib/rules/builtin/git-reset-hard.rule.yaml +12 -0
  209. package/src/hooks/ts/lib/rules/builtin/injection-then-tool.rule.yaml +30 -0
  210. package/src/hooks/ts/lib/rules/builtin/instruction-override-disregard.rule.yaml +14 -0
  211. package/src/hooks/ts/lib/rules/builtin/instruction-override-ignore.rule.yaml +13 -0
  212. package/src/hooks/ts/lib/rules/builtin/launchagents-write-persistence.rule.yaml +12 -0
  213. package/src/hooks/ts/lib/rules/builtin/launchctl-load-persistence.rule.yaml +10 -0
  214. package/src/hooks/ts/lib/rules/builtin/reverse-shell-dev-tcp.rule.yaml +14 -0
  215. package/src/hooks/ts/lib/rules/builtin/reverse-shell-interactive-bash.rule.yaml +12 -0
  216. package/src/hooks/ts/lib/rules/builtin/rm-recursive-parent.rule.yaml +13 -0
  217. package/src/hooks/ts/lib/rules/builtin/rm-recursive-relative.rule.yaml +12 -0
  218. package/src/hooks/ts/lib/rules/builtin/rm-recursive-root.rule.yaml +27 -0
  219. package/src/hooks/ts/lib/rules/builtin/rm-trailing-tilde.rule.yaml +12 -0
  220. package/src/hooks/ts/lib/rules/builtin/sudoers-nopasswd.rule.yaml +15 -0
  221. package/src/hooks/ts/lib/rules/builtin.generated.ts +557 -0
  222. package/src/hooks/ts/lib/rules/eval.ts +76 -0
  223. package/src/hooks/ts/lib/rules/lint.ts +108 -0
  224. package/src/hooks/ts/lib/rules/load.ts +264 -0
  225. package/src/hooks/ts/lib/rules/types.ts +58 -0
  226. package/src/hooks/ts/pre-tool-use/security-validator.ts +35 -96
  227. package/src/hooks/ts/user-prompt/handoff-link.ts +34 -0
  228. package/dist/aibroker-client-BxzeEHPF.mjs +0 -3
  229. package/dist/auto-route-DWYK_0i0.mjs +0 -11
  230. package/dist/budget-DX-VIWGf.mjs +0 -15
  231. package/dist/cockpit-memory-DIrNs4xr.mjs +0 -20
  232. package/dist/daemon-ieo0lnpq.mjs +0 -46
  233. package/dist/detect-C0ZBXqDL.mjs +0 -10
  234. package/dist/factory-DeASMV7Z.mjs +0 -9
  235. package/dist/kill-tree-CXZ0WfVf.mjs +0 -10
  236. package/dist/operator-Bpxq_WB5.mjs +0 -7
  237. package/dist/providers-QJzpaNhX.mjs +0 -16
  238. package/dist/registrable-Drn8wIDO.mjs +0 -9
  239. package/dist/render-C4KR8qQ8.mjs +0 -13
  240. package/dist/render-Cz3BWIBO.mjs +0 -903
  241. package/dist/root-Bfb1zgwE.mjs +0 -36
  242. package/dist/route-table-ZVMNBsy7.mjs +0 -15
  243. package/dist/session-BcnhepLZ.mjs +0 -35
  244. package/dist/session-scan-CqUQdCac.mjs +0 -9
  245. package/dist/status-D_SWmOlH.mjs +0 -6
  246. package/dist/viewer-CbGN2Ri4.mjs +0 -22
  247. package/dist/worker-config-D1KVAMp4.mjs +0 -42
@@ -1,234 +1,12 @@
1
- import { l as claudeHomeDir, t as aibrokerHomeDir } from "./runtime-paths-WsO9sJFc.mjs";
2
- import { F as resolveProviderKey, L as writeWorkersSection, M as readWorkersSection, P as resolveModelCapability, c as WorkersConfigError } from "./config-CST2uq1v.mjs";
3
- import { O as readJsonStrict, k as writeJsonAtomic } from "./config-DYbyEe4-.mjs";
4
- import { u as workersLogDir } from "./paths-DS5VQKoR.mjs";
5
- import { _ as resolveTarget } from "./routing-BlD_tKjW.mjs";
6
- import { yt as psTable } from "./controls-OiVVbe-f.mjs";
1
+ import { l as claudeHomeDir, t as aibrokerHomeDir } from "./runtime-paths-twfV23YL.mjs";
2
+ import { F as resolveProviderKey, L as writeWorkersSection, M as readWorkersSection, P as resolveModelCapability, c as WorkersConfigError } from "./config-DGV64YJa.mjs";
3
+ import { O as readJsonStrict, k as writeJsonAtomic } from "./config-SlZ-Z52i.mjs";
4
+ import { u as workersLogDir } from "./paths-BwfwHOvN.mjs";
5
+ import { _ as resolveTarget } from "./routing-CF0wlR-_.mjs";
7
6
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
8
- import { isAbsolute, join, relative } from "node:path";
9
- import { execFileSync, spawn } from "node:child_process";
10
- import { fileURLToPath } from "node:url";
7
+ import { join } from "node:path";
8
+ import { execFileSync } from "node:child_process";
11
9
 
12
- //#region src/workers/hang-probe.ts
13
- /**
14
- * hang-probe.ts — `pai worker test-run -- <command>`: runs a test command and, when it goes silent,
15
- * tells the model WHERE it is stuck.
16
- *
17
- * A synchronous infinite loop blocks Node's event loop, so the test runner cannot even report its own
18
- * timeout and the model sees nothing. Node's `--report-on-signal` does not help: it is serviced by the
19
- * event loop (measured: a `while(true){}` process never writes the report). The child therefore runs with a
20
- * preloaded watchdog (NODE_OPTIONS --require): a worker thread per process that, when a trigger file appears,
21
- * attaches the inspector to the main thread, pauses it (V8 interrupts a running loop) and writes the call
22
- * frames. After the command was silent for the idle threshold the wrapper triggers every node process under
23
- * it and maps the frames to the worktree's files. The worker guard rewrites test commands into this wrapper
24
- * (see wrapTestCommand).
25
- */
26
- /** Marker line every diagnosis carries; the loop guard keys on it. */
27
- const HANG_MARKER = "[pai hang probe]";
28
- const DEFAULT_IDLE_SECS = 60;
29
- /** Seconds of the first `timeout N` in the command, else null. */
30
- function timeoutSecs(command) {
31
- const m = /\b(?:g?timeout)\s+(?:-[ks]\s+\S+\s+|-\S+\s+)*(\d+(?:\.\d+)?)([smhd]?)\b/.exec(command);
32
- if (!m) return null;
33
- return Number(m[1]) * {
34
- "": 1,
35
- s: 1,
36
- m: 60,
37
- h: 3600,
38
- d: 86400
39
- }[m[2]];
40
- }
41
- function killQuiet(pid, sig) {
42
- try {
43
- process.kill(pid, sig);
44
- } catch {}
45
- }
46
- function rmQuiet(f) {
47
- try {
48
- unlinkSync(f);
49
- } catch {}
50
- }
51
- const shq = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
52
- /** The command line test-run executes: the decoded --cmd-b64 payload, else the words after `--`, each shell-quoted. */
53
- function commandToRun(cmdB64, words) {
54
- if (cmdB64) return Buffer.from(cmdB64, "base64").toString("utf8");
55
- return words.map(shq).join(" ");
56
- }
57
- /** Call frames of one probed process, user code only, files relative to cwd. */
58
- function userFrames(frames, cwd) {
59
- const out = [];
60
- for (const f of frames) {
61
- let file = f.url;
62
- if (file.startsWith("file://")) try {
63
- file = fileURLToPath(file);
64
- } catch {
65
- continue;
66
- }
67
- if (!file || file.startsWith("node:") || file.includes("node_modules") || !isAbsolute(file)) continue;
68
- const rel = relative(cwd, file);
69
- if (rel.startsWith("..")) continue;
70
- out.push({
71
- fn: f.functionName || "(anonymous)",
72
- file: rel,
73
- line: f.lineNumber + 1
74
- });
75
- }
76
- return out;
77
- }
78
- /** The model-facing diagnosis for the reports taken after `idleSecs` of silence. */
79
- function diagnose(probed, cwd, idleSecs) {
80
- const stuck = probed.map((r) => userFrames(r, cwd)).find((f) => f.length > 0);
81
- if (stuck) {
82
- const [top, ...rest] = stuck;
83
- const via = rest[0] ? ` (called from ${rest[0].file}:${rest[0].line} ${rest[0].fn}())` : "";
84
- return `${HANG_MARKER} test run hung with no output for ${idleSecs} s; the main thread is stuck in ${top.file}:${top.line} ${top.fn}()${via}. A synchronous loop that never ends blocks the runner's own timeout. Fix that loop; rerunning unchanged will hang again.\n` + stuck.slice(0, 6).map((f) => ` at ${f.fn} (${f.file}:${f.line})`).join("\n");
85
- }
86
- return `${HANG_MARKER} test run printed nothing for ${idleSecs} s and the main thread is idle in the event loop: it is waiting (async), not spinning. A test awaits a promise, timer, process or socket that never completes (vitest's own per-test timeout ends it unless the test raised it). Find the await and give it an end; rerunning unchanged will wait again.`;
87
- }
88
- function listProcs() {
89
- try {
90
- return psTable("pid=,ppid=,command=").split("\n").flatMap((l) => {
91
- const m = /^\s*(\d+)\s+(\d+)\s+(.*)$/.exec(l);
92
- return m ? [{
93
- pid: Number(m[1]),
94
- ppid: Number(m[2]),
95
- cmd: m[3]
96
- }] : [];
97
- });
98
- } catch {
99
- return [];
100
- }
101
- }
102
- /** Pids of node processes below `root`. */
103
- function nodeDescendants(root, procs = listProcs()) {
104
- const seen = new Set([root]);
105
- for (let grew = true; grew;) {
106
- grew = false;
107
- for (const p of procs) if (!seen.has(p.pid) && seen.has(p.ppid)) seen.add(p.pid), grew = true;
108
- }
109
- return procs.filter((p) => p.pid !== root && seen.has(p.pid) && /(?:^|\/)node(?:\s|$)/.test(p.cmd)).map((p) => p.pid);
110
- }
111
- /** Runs the command, passes its output through, probes once when it goes silent. Resolves to the exit code. */
112
- async function runProbed(o) {
113
- const out = o.out ?? ((s) => process.stdout.write(s));
114
- const err = o.err ?? ((s) => process.stderr.write(s));
115
- mkdirSync(o.reportDir, { recursive: true });
116
- const t = timeoutSecs(o.command);
117
- const idle = Math.max(2, Math.min(o.idleSecs ?? DEFAULT_IDLE_SECS, t !== null ? t - 8 : Infinity));
118
- const preload = join(o.reportDir, "preload.cjs");
119
- writeFileSync(preload, PRELOAD_SOURCE);
120
- const env = {
121
- ...process.env,
122
- PAI_HANG_PROBE_DIR: o.reportDir,
123
- NODE_OPTIONS: `${process.env.NODE_OPTIONS ?? ""} --require=${preload}`.trim()
124
- };
125
- const child = spawn("bash", ["-c", o.command], {
126
- cwd: o.cwd,
127
- env,
128
- stdio: [
129
- "ignore",
130
- "pipe",
131
- "pipe"
132
- ]
133
- });
134
- let last = Date.now();
135
- let probing = false;
136
- let announcedIdle = false;
137
- let spun = false;
138
- let pending = Promise.resolve();
139
- child.stdout.on("data", (d) => (last = Date.now(), out(d.toString())));
140
- child.stderr.on("data", (d) => (last = Date.now(), err(d.toString())));
141
- const timer = setInterval(() => {
142
- if (probing || spun || Date.now() - last < idle * 1e3 || !child.pid) return;
143
- probing = true;
144
- const pids = nodeDescendants(child.pid);
145
- for (const p of pids) writeFileSync(join(o.reportDir, `probe-${p}`), "");
146
- pending = collectFrames(o.reportDir, pids, 3e3).then((frames) => {
147
- for (const p of pids) rmQuiet(join(o.reportDir, `probe-${p}`));
148
- const stuck = frames.some((f) => userFrames(f, o.cwd).length > 0);
149
- if (stuck || !announcedIdle) err(`\n${diagnose(frames, o.cwd, idle)}\n`);
150
- announcedIdle = true;
151
- probing = false;
152
- if (stuck) {
153
- spun = true;
154
- for (const p of [...pids, child.pid]) killQuiet(p, "SIGTERM");
155
- setTimeout(() => pids.forEach((p) => killQuiet(p, "SIGKILL")), 2e3).unref();
156
- }
157
- });
158
- }, 1e3);
159
- return await new Promise((resolve) => {
160
- child.on("close", (code, sig) => {
161
- clearInterval(timer);
162
- pending.then(() => resolve(spun ? 124 : code ?? (sig ? 128 + (sig === "SIGKILL" ? 9 : 15) : 1)));
163
- });
164
- });
165
- }
166
- /** Frames each probed pid reported (empty list: its main thread was idle); pids that never answered are left out. */
167
- async function collectFrames(dir, pids, waitMs) {
168
- const got = /* @__PURE__ */ new Map();
169
- for (const end = Date.now() + waitMs; Date.now() < end && got.size < pids.length;) {
170
- for (const p of pids) {
171
- const f = join(dir, `frames-${p}.json`);
172
- if (got.has(p) || !existsSync(f)) continue;
173
- try {
174
- got.set(p, JSON.parse(readFileSync(f, "utf8")).frames);
175
- unlinkSync(f);
176
- } catch {}
177
- }
178
- await new Promise((r) => setTimeout(r, 100));
179
- }
180
- return [...got.values()];
181
- }
182
- /** Preloaded into every node process of the test run (CommonJS, so it also loads under --require). */
183
- const PRELOAD_SOURCE = `"use strict";
184
- const { Worker, isMainThread } = require("node:worker_threads");
185
- if (isMainThread) {
186
- const code = \`
187
- const { existsSync, unlinkSync, writeFileSync, renameSync } = require("node:fs");
188
- const { Session } = require("node:inspector");
189
- const { workerData } = require("node:worker_threads");
190
- const trigger = workerData.dir + "/probe-" + workerData.pid;
191
- const done = (frames) => {
192
- const tmp = workerData.dir + "/tmp-" + workerData.pid;
193
- writeFileSync(tmp, JSON.stringify({ frames }));
194
- renameSync(tmp, workerData.dir + "/frames-" + workerData.pid + ".json");
195
- };
196
- setInterval(() => {
197
- if (!existsSync(trigger)) return;
198
- try { unlinkSync(trigger); } catch {}
199
- const s = new Session();
200
- s.connectToMainThread();
201
- let answered = false;
202
- const urls = {}, maps = {};
203
- s.on("Debugger.scriptParsed", (m) => { urls[m.params.scriptId] = m.params.url; maps[m.params.scriptId] = m.params.sourceMapURL; });
204
- // transformed code (vite-node, tsx) has shifted lines: map back through its inline source map
205
- const where = (f) => {
206
- const id = f.location.scriptId;
207
- let url = urls[id] || f.url, line = f.location.lineNumber;
208
- const m = /^data:[^,]*;base64,(.*)$/.exec(maps[id] || "");
209
- if (m) try {
210
- const e = new (require("node:module").SourceMap)(JSON.parse(Buffer.from(m[1], "base64").toString())).findEntry(line, f.location.columnNumber || 0);
211
- if (e && e.originalSource) { url = new URL(e.originalSource, url).href; line = e.originalLine; }
212
- } catch {}
213
- return { functionName: f.functionName, url, lineNumber: line };
214
- };
215
- s.on("Debugger.paused", (m) => {
216
- answered = true;
217
- done(m.params.callFrames.map(where));
218
- s.post("Debugger.resume", () => s.post("Debugger.disable", () => s.disconnect()));
219
- });
220
- s.post("Debugger.enable", () => s.post("Debugger.pause"));
221
- // an idle event loop runs no JS to pause in
222
- setTimeout(() => { if (!answered) { done([]); s.post("Debugger.disable", () => s.disconnect()); } }, 800);
223
- }, 200);
224
- \`;
225
- try {
226
- new Worker(code, { eval: true, workerData: { dir: process.env.PAI_HANG_PROBE_DIR, pid: process.pid } }).unref();
227
- } catch {}
228
- }
229
- `;
230
-
231
- //#endregion
232
10
  //#region src/workers/fallback.ts
233
11
  /**
234
12
  * fallback.ts — machine-wide Claude Code fallback to a worker provider.
@@ -493,5 +271,5 @@ function fallbackStatusText(s) {
493
271
  }
494
272
 
495
273
  //#endregion
496
- export { DEFAULT_IDLE_SECS as a, fallbackStatusText as i, fallbackOn as n, commandToRun as o, fallbackStatus as r, runProbed as s, fallbackOff as t };
497
- //# sourceMappingURL=fallback-Bj2cyVk_.mjs.map
274
+ export { fallbackStatusText as i, fallbackOn as n, fallbackStatus as r, fallbackOff as t };
275
+ //# sourceMappingURL=fallback-CQ8_CuVu.mjs.map
@@ -1,7 +1,7 @@
1
- import { g as paiHomePath } from "./runtime-paths-WsO9sJFc.mjs";
2
- import { t as ANTHROPIC_NATIVE } from "./config-CST2uq1v.mjs";
3
- import { A as budgetSkipReason, D as budgetExceeded, E as budgetCache, M as budgetWall } from "./routing-BlD_tKjW.mjs";
4
- import { n as tierProviderNames, t as isLocalCandidate } from "./local-candidate-Cp_bw4kK.mjs";
1
+ import { _ as paiHomePath } from "./runtime-paths-twfV23YL.mjs";
2
+ import { t as ANTHROPIC_NATIVE } from "./config-DGV64YJa.mjs";
3
+ import { A as budgetSkipReason, D as budgetExceeded, E as budgetCache, M as budgetWall } from "./routing-CF0wlR-_.mjs";
4
+ import { n as tierProviderNames, t as isLocalCandidate } from "./local-candidate-C2FGhf54.mjs";
5
5
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
6
6
  import { Chalk } from "chalk";
7
7
 
@@ -311,4 +311,4 @@ function saveBudgetSnapshot(map, now = Date.now()) {
311
311
 
312
312
  //#endregion
313
313
  export { colorEnabled as a, renderCommandHelp as c, renderOverview as d, renderTable as f, saveBudgetSnapshot as i, renderMarkdown as l, usageLine as m, readBudget as n, makeStyle as o, suggest as p, readBudgetSnapshot as r, mdCommand as s, providerHealth as t, renderMore as u };
314
- //# sourceMappingURL=health-kufVIf6i.mjs.map
314
+ //# sourceMappingURL=health-Bb2U4PNK.mjs.map
@@ -1,4 +1,4 @@
1
- import { i as getRegistryBackend } from "./factory-BV0MQA3T.mjs";
1
+ import { i as getRegistryBackend } from "./factory-CYwa0jYj.mjs";
2
2
 
3
3
  //#region src/cli/impl/project/helpers.ts
4
4
  /**
@@ -66,4 +66,4 @@ async function upsertTag(tagName) {
66
66
 
67
67
  //#endregion
68
68
  export { getSessionCount as a, upsertTag as c, getProjectTags as i, getProject as n, requireProject as o, getProjectAliases as r, resolveIdentifier as s, getLastSessionDate as t };
69
- //# sourceMappingURL=helpers-CvbYEKoD.mjs.map
69
+ //# sourceMappingURL=helpers-Dj2nY56t.mjs.map
@@ -1,10 +1,10 @@
1
- import "./config-DYbyEe4-.mjs";
1
+ import "./config-SlZ-Z52i.mjs";
2
2
  import "./helpers-CRuBUas3.mjs";
3
- import "./factory-BV0MQA3T.mjs";
4
- import "./postgres-C-T5OWer.mjs";
5
- import "./embedding-gate-CTAk8STe.mjs";
3
+ import "./factory-CYwa0jYj.mjs";
4
+ import "./postgres-BL5oBBgY.mjs";
5
+ import "./embedding-gate-iq0oQEkF.mjs";
6
6
  import "./embeddings-BVq3esBF.mjs";
7
- import "./router-CQSc3lk3.mjs";
8
- import { a as getSessionCount, c as upsertTag, i as getProjectTags, n as getProject, o as requireProject, r as getProjectAliases, s as resolveIdentifier, t as getLastSessionDate } from "./helpers-CvbYEKoD.mjs";
7
+ import "./router-D2476pOp.mjs";
8
+ import { a as getSessionCount, c as upsertTag, i as getProjectTags, n as getProject, o as requireProject, r as getProjectAliases, s as resolveIdentifier, t as getLastSessionDate } from "./helpers-Dj2nY56t.mjs";
9
9
 
10
10
  export { getProject };
@@ -8174,6 +8174,7 @@ function defaultWorkersConfig() {
8174
8174
  cacheKeepaliveSecs: num(w.cacheKeepaliveSecs, 0),
8175
8175
  defaultMaxMinutes: num(w.defaultMaxMinutes, 60),
8176
8176
  waitAlarmMinutes: num(w.waitAlarmMinutes, 120),
8177
+ scanBudgetMs: num(w.scanBudgetMs, 5e3),
8177
8178
  stagger: {
8178
8179
  enabled: bool(stagger.enabled, true),
8179
8180
  localDefaultSec: num(stagger.localDefaultSec, 20),
@@ -8768,6 +8769,13 @@ function parseWorkersConfig(raw, yamlProviders = {}) {
8768
8769
  }
8769
8770
  waitAlarmMinutes = w.waitAlarmMinutes;
8770
8771
  }
8772
+ let scanBudgetMs = d.scanBudgetMs ?? 5e3;
8773
+ if (w.scanBudgetMs !== void 0) {
8774
+ if (typeof w.scanBudgetMs !== "number" || !Number.isInteger(w.scanBudgetMs) || w.scanBudgetMs < 1) {
8775
+ bad(".scanBudgetMs", "must be a positive integer (milliseconds)");
8776
+ }
8777
+ scanBudgetMs = w.scanBudgetMs;
8778
+ }
8771
8779
  const stagger = { ...d.stagger };
8772
8780
  if (w.stagger !== void 0) {
8773
8781
  if (typeof w.stagger !== "object" || w.stagger === null || Array.isArray(w.stagger)) {
@@ -8904,6 +8912,7 @@ function parseWorkersConfig(raw, yamlProviders = {}) {
8904
8912
  cacheKeepaliveSecs,
8905
8913
  defaultMaxMinutes,
8906
8914
  waitAlarmMinutes,
8915
+ scanBudgetMs,
8907
8916
  stagger,
8908
8917
  capacity,
8909
8918
  localIdle,
@@ -7724,6 +7724,104 @@ import { appendFileSync, chmodSync as chmodSync2, existsSync as existsSync4, mkd
7724
7724
  // src/workers/ps-table.ts
7725
7725
  var PS_MAX_BUFFER = 64 * 1024 * 1024;
7726
7726
 
7727
+ // src/utils/redact.ts
7728
+ var REDACTED = "[REDACTED]";
7729
+ var REDACTED_RE = String.raw`\[REDACTED\]`;
7730
+ var KEY_WORDS = "password|passwd|secret|token|api[_-]?key|credential|auth(?:orization)?|bearer";
7731
+ var PROSE = "is|are|was|were|be|to|of|in|on|for|and|or|the|a|an|that|this|it|not|out|here|there|so|as|at|by|with|if|when|which";
7732
+ var VAL = String.raw`(?:"[^"]*"|'[^']*'|[^\s'"]+)`;
7733
+ var REDACT_RULES = [
7734
+ {
7735
+ // `Authorization: Bearer xxx` / `Authorization: Basic xxx` — keep the scheme
7736
+ id: "authorization",
7737
+ re: /(Authorization\s*:\s*)(Bearer|Basic)\s+([^\s'"]+)/gi,
7738
+ replace: (_m, head, scheme) => `${head}${scheme} ${REDACTED}`
7739
+ },
7740
+ {
7741
+ // `Cookie: a=b; c=d` — keep the cookie names, redact every value
7742
+ id: "cookie",
7743
+ re: /((?:Set-)?Cookie\s*:\s*)([^\n]+)/gi,
7744
+ replace: (_m, head, rest) => `${head}${rest.replace(/([^=;\s'"]+=)[^;'"]*/g, `$1${REDACTED}`)}`
7745
+ },
7746
+ {
7747
+ // `key=value`, `key: value`, `"key": "value"` — key carries a secret-ish word;
7748
+ // the word must be a whole key segment, so `monkeys=` stays untouched.
7749
+ // The separator may start with the key's closing quote (JSON); it is kept.
7750
+ id: "key-value",
7751
+ re: new RegExp(String.raw`(?<![A-Za-z0-9])((?:[A-Za-z0-9]+[_-])?(?:${KEY_WORDS})(?![A-Za-z0-9])[A-Za-z0-9_-]*)(["']?\s*[:=]\s*)(?!(?:Bearer|Basic) ${REDACTED_RE})(?!(?:${PROSE})\b)(${VAL})`, "gi"),
7752
+ replace: (m, key, sep2, val) => {
7753
+ if (/[_-](?:count|length|len|size|limit|max|min|budget)$/i.test(key) && /^\d+$/.test(val)) return m;
7754
+ const q = sep2[0] === '"' || sep2[0] === "'" ? val[0] : "";
7755
+ return q && (q === '"' || q === "'") ? `${key}${sep2}${q}${REDACTED}${q}` : `${key}${sep2}${REDACTED}`;
7756
+ }
7757
+ },
7758
+ {
7759
+ // npmrc `//registry/:_authToken=xxx`
7760
+ id: "npm-auth-token",
7761
+ re: new RegExp(String.raw`(_authToken\s*=\s*)(${VAL})`, "g"),
7762
+ replace: (_m, head) => `${head}${REDACTED}`
7763
+ },
7764
+ {
7765
+ // `curl -u user:pass` / `--user user:pass` — keep the user
7766
+ id: "curl-user",
7767
+ re: /(\bcurl\b[^\n]{0,500}?\s(?:-u|--user)[\s=]+['"]?[^\s:'"]+:)([^\s'"]+)/g,
7768
+ replace: (_m, head) => `${head}${REDACTED}`
7769
+ },
7770
+ {
7771
+ // `mysql -phunter2` / `mysqldump -p'hunter2'` — attached password only
7772
+ id: "mysql-p",
7773
+ re: /(\bmysql(?:dump)?\b[^\n]{0,300}?\s-p)('[^']*'|"[^"]*"|[^\s'"-][^\s'"]*)/g,
7774
+ replace: (_m, head) => `${head}${REDACTED}`
7775
+ },
7776
+ {
7777
+ // `export FOO_TOKEN=...` / `FOO_KEY=...` — SCREAMING_SNAKE env style
7778
+ id: "env-style",
7779
+ re: new RegExp(String.raw`(?<![A-Za-z0-9])((?:export\s+)?[A-Z][A-Z0-9]*_(?:KEY|SECRET|TOKEN|PASS\w*|CRED\w*|AUTH\w*)\s*=\s*)(${VAL})`, "g"),
7780
+ replace: (_m, head) => `${head}${REDACTED}`
7781
+ },
7782
+ {
7783
+ // long CLI flags only: `--password X`, `--token X`, `--api-key X`, `--secret X`, `--passwd X`
7784
+ id: "cli-flag",
7785
+ re: new RegExp(String.raw`(--(?:password|token|api-key|secret|passwd)(?:\s*=\s*|\s+))(${VAL})`, "gi"),
7786
+ replace: (_m, head) => `${head}${REDACTED}`
7787
+ },
7788
+ {
7789
+ // `scheme://user:pass@host` — redact the password, keep user and host
7790
+ id: "url-credentials",
7791
+ re: /((?<![a-z0-9+.-])[a-z][a-z0-9+.-]*:\/\/[^\s/@:]+:)([^\s/@]+)(@)/gi,
7792
+ replace: (_m, head, _pass, at) => `${head}${REDACTED}${at}`
7793
+ },
7794
+ {
7795
+ // provider token prefixes: sk-ant-, sk-, ghp_/gho_/ghu_/ghs_/ghr_, github_pat_,
7796
+ // glpat-, xoxb-/xoxp-/xoxa-, AKIA/ASIA + 16 chars, AIza + 35 chars, JWT (eyJ… three segments)
7797
+ id: "token-prefix",
7798
+ re: /\b(?:sk-ant-[A-Za-z0-9_-]{20,}|sk-[A-Za-z0-9_-]{20,}|gh[pousr]_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{22,}|glpat-[A-Za-z0-9_-]{20,}|xox[bap]-[A-Za-z0-9-]{10,}|(?:AKIA|ASIA|AGPA|AIDA|AROA)[A-Z0-9]{16}|[sr]k_(?:live|test)_[A-Za-z0-9]{20,}|npm_[A-Za-z0-9]{36}|hf_[A-Za-z0-9]{30,}|AIza[A-Za-z0-9_-]{35}(?![A-Za-z0-9_-])|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)(?![A-Za-z0-9_-])/g,
7799
+ replace: REDACTED
7800
+ },
7801
+ {
7802
+ // 40+ char opaque base64/hex run, only on a line that also has a secret-ish key word
7803
+ id: "long-run",
7804
+ re: /(?<![A-Za-z0-9+/])[A-Za-z0-9+/]{40,4096}={0,2}(?![\w/])/g,
7805
+ // a bare 40-hex git SHA stays; it is only redacted as the value of a secret key
7806
+ replace: (m) => /^[0-9a-f]{40}$/i.test(m) ? m : REDACTED
7807
+ }
7808
+ ];
7809
+ var PEM_BLOCK = /-----BEGIN[^-\n]*PRIVATE KEY-----[\s\S]*?(?:-----END[^-\n]*PRIVATE KEY-----|$)/g;
7810
+ var LINE_RULES = REDACT_RULES.filter((r) => r.id !== "long-run");
7811
+ var LONG_RUN = REDACT_RULES.find((r) => r.id === "long-run");
7812
+ var LONG_RUN_MAX_LINE = 64 * 1024;
7813
+ var KEY_LINE = new RegExp(`(?:${KEY_WORDS})`, "i");
7814
+ function redactLine(line) {
7815
+ let out = line;
7816
+ for (const r of LINE_RULES) out = out.replace(r.re, r.replace);
7817
+ if (out.length <= LONG_RUN_MAX_LINE && KEY_LINE.test(out)) out = out.replace(LONG_RUN.re, LONG_RUN.replace);
7818
+ return out;
7819
+ }
7820
+ function redactSecrets(text) {
7821
+ const withPem = text.replace(PEM_BLOCK, `-----BEGIN PRIVATE KEY----- ${REDACTED}`);
7822
+ return withPem.split("\n").map(redactLine).join("\n");
7823
+ }
7824
+
7727
7825
  // src/hooks/ts/lib/loop-guard.ts
7728
7826
  var LOOP_MAX_REPEATS = 4;
7729
7827
  var LOOP_READ_STREAK = Number(process.env.PAI_LOOP_READ_STREAK) > 0 ? Number(process.env.PAI_LOOP_READ_STREAK) : 15;
@@ -7732,10 +7830,8 @@ var LOOP_DENY_REASON = `loop guard: you ran this command ${LOOP_MAX_REPEATS} tim
7732
7830
  var WAIT_CAP_MS = 45 * 6e4;
7733
7831
  var FP_FULL_READ_BYTES = 256 * 1024;
7734
7832
  var FP_TOTAL_BYTES = 4 * 1024 * 1024;
7735
- var SECRET_LINE = /pass(?:word|wd)|secret|token|api[_-]?key|credential|authorization|bearer|private key|^\s*(?:export\s+)?[A-Z][A-Z0-9_]*(?:KEY|SECRET|TOKEN|PASS\w*|CRED\w*|AUTH\w*)\s*=|\b(?:sk|ghp|gho|xox[bap])[-_][A-Za-z0-9_-]{16,}|\b[A-Za-z0-9+]{40,}={0,2}(?![\w/])|\b[a-z][a-z0-9+.-]*:\/\/[^\s\/:@]+:[^\s\/@]+@|\b(?:AKIA|ASIA|AGPA|AIDA|AROA)[A-Z0-9]{16}\b|\bgh[pousr]_[A-Za-z0-9]{20,}/i;
7736
- var MASKED_LINE = "[line masked: looks like a secret]";
7737
7833
  function maskSecrets(text) {
7738
- return text.split("\n").map((l) => SECRET_LINE.test(l) ? MASKED_LINE : l).join("\n");
7834
+ return redactSecrets(text);
7739
7835
  }
7740
7836
 
7741
7837
  // src/utils/hash.ts
@@ -141,6 +141,104 @@ import { appendFileSync, chmodSync, existsSync as existsSync3, mkdirSync as mkdi
141
141
  // src/workers/ps-table.ts
142
142
  var PS_MAX_BUFFER = 64 * 1024 * 1024;
143
143
 
144
+ // src/utils/redact.ts
145
+ var REDACTED = "[REDACTED]";
146
+ var REDACTED_RE = String.raw`\[REDACTED\]`;
147
+ var KEY_WORDS = "password|passwd|secret|token|api[_-]?key|credential|auth(?:orization)?|bearer";
148
+ var PROSE = "is|are|was|were|be|to|of|in|on|for|and|or|the|a|an|that|this|it|not|out|here|there|so|as|at|by|with|if|when|which";
149
+ var VAL = String.raw`(?:"[^"]*"|'[^']*'|[^\s'"]+)`;
150
+ var REDACT_RULES = [
151
+ {
152
+ // `Authorization: Bearer xxx` / `Authorization: Basic xxx` — keep the scheme
153
+ id: "authorization",
154
+ re: /(Authorization\s*:\s*)(Bearer|Basic)\s+([^\s'"]+)/gi,
155
+ replace: (_m, head, scheme) => `${head}${scheme} ${REDACTED}`
156
+ },
157
+ {
158
+ // `Cookie: a=b; c=d` — keep the cookie names, redact every value
159
+ id: "cookie",
160
+ re: /((?:Set-)?Cookie\s*:\s*)([^\n]+)/gi,
161
+ replace: (_m, head, rest) => `${head}${rest.replace(/([^=;\s'"]+=)[^;'"]*/g, `$1${REDACTED}`)}`
162
+ },
163
+ {
164
+ // `key=value`, `key: value`, `"key": "value"` — key carries a secret-ish word;
165
+ // the word must be a whole key segment, so `monkeys=` stays untouched.
166
+ // The separator may start with the key's closing quote (JSON); it is kept.
167
+ id: "key-value",
168
+ re: new RegExp(String.raw`(?<![A-Za-z0-9])((?:[A-Za-z0-9]+[_-])?(?:${KEY_WORDS})(?![A-Za-z0-9])[A-Za-z0-9_-]*)(["']?\s*[:=]\s*)(?!(?:Bearer|Basic) ${REDACTED_RE})(?!(?:${PROSE})\b)(${VAL})`, "gi"),
169
+ replace: (m, key, sep, val) => {
170
+ if (/[_-](?:count|length|len|size|limit|max|min|budget)$/i.test(key) && /^\d+$/.test(val)) return m;
171
+ const q = sep[0] === '"' || sep[0] === "'" ? val[0] : "";
172
+ return q && (q === '"' || q === "'") ? `${key}${sep}${q}${REDACTED}${q}` : `${key}${sep}${REDACTED}`;
173
+ }
174
+ },
175
+ {
176
+ // npmrc `//registry/:_authToken=xxx`
177
+ id: "npm-auth-token",
178
+ re: new RegExp(String.raw`(_authToken\s*=\s*)(${VAL})`, "g"),
179
+ replace: (_m, head) => `${head}${REDACTED}`
180
+ },
181
+ {
182
+ // `curl -u user:pass` / `--user user:pass` — keep the user
183
+ id: "curl-user",
184
+ re: /(\bcurl\b[^\n]{0,500}?\s(?:-u|--user)[\s=]+['"]?[^\s:'"]+:)([^\s'"]+)/g,
185
+ replace: (_m, head) => `${head}${REDACTED}`
186
+ },
187
+ {
188
+ // `mysql -phunter2` / `mysqldump -p'hunter2'` — attached password only
189
+ id: "mysql-p",
190
+ re: /(\bmysql(?:dump)?\b[^\n]{0,300}?\s-p)('[^']*'|"[^"]*"|[^\s'"-][^\s'"]*)/g,
191
+ replace: (_m, head) => `${head}${REDACTED}`
192
+ },
193
+ {
194
+ // `export FOO_TOKEN=...` / `FOO_KEY=...` — SCREAMING_SNAKE env style
195
+ id: "env-style",
196
+ re: new RegExp(String.raw`(?<![A-Za-z0-9])((?:export\s+)?[A-Z][A-Z0-9]*_(?:KEY|SECRET|TOKEN|PASS\w*|CRED\w*|AUTH\w*)\s*=\s*)(${VAL})`, "g"),
197
+ replace: (_m, head) => `${head}${REDACTED}`
198
+ },
199
+ {
200
+ // long CLI flags only: `--password X`, `--token X`, `--api-key X`, `--secret X`, `--passwd X`
201
+ id: "cli-flag",
202
+ re: new RegExp(String.raw`(--(?:password|token|api-key|secret|passwd)(?:\s*=\s*|\s+))(${VAL})`, "gi"),
203
+ replace: (_m, head) => `${head}${REDACTED}`
204
+ },
205
+ {
206
+ // `scheme://user:pass@host` — redact the password, keep user and host
207
+ id: "url-credentials",
208
+ re: /((?<![a-z0-9+.-])[a-z][a-z0-9+.-]*:\/\/[^\s/@:]+:)([^\s/@]+)(@)/gi,
209
+ replace: (_m, head, _pass, at) => `${head}${REDACTED}${at}`
210
+ },
211
+ {
212
+ // provider token prefixes: sk-ant-, sk-, ghp_/gho_/ghu_/ghs_/ghr_, github_pat_,
213
+ // glpat-, xoxb-/xoxp-/xoxa-, AKIA/ASIA + 16 chars, AIza + 35 chars, JWT (eyJ… three segments)
214
+ id: "token-prefix",
215
+ re: /\b(?:sk-ant-[A-Za-z0-9_-]{20,}|sk-[A-Za-z0-9_-]{20,}|gh[pousr]_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{22,}|glpat-[A-Za-z0-9_-]{20,}|xox[bap]-[A-Za-z0-9-]{10,}|(?:AKIA|ASIA|AGPA|AIDA|AROA)[A-Z0-9]{16}|[sr]k_(?:live|test)_[A-Za-z0-9]{20,}|npm_[A-Za-z0-9]{36}|hf_[A-Za-z0-9]{30,}|AIza[A-Za-z0-9_-]{35}(?![A-Za-z0-9_-])|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)(?![A-Za-z0-9_-])/g,
216
+ replace: REDACTED
217
+ },
218
+ {
219
+ // 40+ char opaque base64/hex run, only on a line that also has a secret-ish key word
220
+ id: "long-run",
221
+ re: /(?<![A-Za-z0-9+/])[A-Za-z0-9+/]{40,4096}={0,2}(?![\w/])/g,
222
+ // a bare 40-hex git SHA stays; it is only redacted as the value of a secret key
223
+ replace: (m) => /^[0-9a-f]{40}$/i.test(m) ? m : REDACTED
224
+ }
225
+ ];
226
+ var PEM_BLOCK = /-----BEGIN[^-\n]*PRIVATE KEY-----[\s\S]*?(?:-----END[^-\n]*PRIVATE KEY-----|$)/g;
227
+ var LINE_RULES = REDACT_RULES.filter((r) => r.id !== "long-run");
228
+ var LONG_RUN = REDACT_RULES.find((r) => r.id === "long-run");
229
+ var LONG_RUN_MAX_LINE = 64 * 1024;
230
+ var KEY_LINE = new RegExp(`(?:${KEY_WORDS})`, "i");
231
+ function redactLine(line) {
232
+ let out = line;
233
+ for (const r of LINE_RULES) out = out.replace(r.re, r.replace);
234
+ if (out.length <= LONG_RUN_MAX_LINE && KEY_LINE.test(out)) out = out.replace(LONG_RUN.re, LONG_RUN.replace);
235
+ return out;
236
+ }
237
+ function redactSecrets(text) {
238
+ const withPem = text.replace(PEM_BLOCK, `-----BEGIN PRIVATE KEY----- ${REDACTED}`);
239
+ return withPem.split("\n").map(redactLine).join("\n");
240
+ }
241
+
144
242
  // src/hooks/ts/lib/loop-guard.ts
145
243
  var LOOP_MAX_REPEATS = 4;
146
244
  var LOOP_READ_STREAK = Number(process.env.PAI_LOOP_READ_STREAK) > 0 ? Number(process.env.PAI_LOOP_READ_STREAK) : 15;
@@ -149,10 +247,8 @@ var LOOP_DENY_REASON = `loop guard: you ran this command ${LOOP_MAX_REPEATS} tim
149
247
  var WAIT_CAP_MS = 45 * 6e4;
150
248
  var FP_FULL_READ_BYTES = 256 * 1024;
151
249
  var FP_TOTAL_BYTES = 4 * 1024 * 1024;
152
- var SECRET_LINE = /pass(?:word|wd)|secret|token|api[_-]?key|credential|authorization|bearer|private key|^\s*(?:export\s+)?[A-Z][A-Z0-9_]*(?:KEY|SECRET|TOKEN|PASS\w*|CRED\w*|AUTH\w*)\s*=|\b(?:sk|ghp|gho|xox[bap])[-_][A-Za-z0-9_-]{16,}|\b[A-Za-z0-9+]{40,}={0,2}(?![\w/])|\b[a-z][a-z0-9+.-]*:\/\/[^\s\/:@]+:[^\s\/@]+@|\b(?:AKIA|ASIA|AGPA|AIDA|AROA)[A-Z0-9]{16}\b|\bgh[pousr]_[A-Za-z0-9]{20,}/i;
153
- var MASKED_LINE = "[line masked: looks like a secret]";
154
250
  function maskSecrets(text) {
155
- return text.split("\n").map((l) => SECRET_LINE.test(l) ? MASKED_LINE : l).join("\n");
251
+ return redactSecrets(text);
156
252
  }
157
253
 
158
254
  // src/utils/hash.ts