failproofai 0.0.6-beta.2 → 0.0.6-beta.4
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +7 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +2 -2
- package/.next/standalone/.next/server/app/_not-found.rsc +17 -17
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +17 -17
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +16 -16
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g72weg._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__092s1ta._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09icjsf._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g.lg8b._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h..k-e._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0okos0k._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0i5kvry._.js → [root-of-the-server]__0om-5pe._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__05akje6._.js → [root-of-the-server]__111.vxi._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11pa2ra._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12t-wym._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_0mebn66._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +2 -2
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +9 -9
- package/.next/standalone/.next/static/chunks/{05j1px0r8yzh6.js → 02dqjyv6_9mhq.js} +2 -2
- package/.next/standalone/.next/static/chunks/{14cl9poem30dq.js → 070orfsl6.xal.js} +1 -1
- package/.next/standalone/.next/static/chunks/0mir9jdxn35~s.css +1 -0
- package/.next/standalone/.next/static/chunks/{00j0rr7rh8ef8.js → 0o547jv-k_k35.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0ijk_kek9_wyx.js → 0pk2h2.mjxy.m.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0xpl.oscrakvx.js → 0rcwkbh24w38b.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1052sguyd-.ka.js → 140xx_tfr~lm_.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0npb~873.wvg3.js → 169_e4dq~1~b6.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0badv41uxa56..js → 17ne4p.1sw1jy.js} +1 -1
- package/.next/standalone/next.config.ts +6 -0
- package/.next/standalone/package.json +2 -2
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +91 -4
- package/dist/cli.mjs +1155 -54
- package/package.json +2 -2
- package/scripts/prune-standalone.mjs +128 -0
- package/src/auth/login.ts +104 -0
- package/src/auth/logout.ts +50 -0
- package/src/auth/token-store.ts +64 -0
- package/src/hooks/builtin-policies.ts +22 -20
- package/src/hooks/handler.ts +35 -15
- package/src/relay/daemon.ts +362 -0
- package/src/relay/pid.ts +76 -0
- package/src/relay/queue.ts +225 -0
- package/.next/standalone/.claude/settings.json +0 -316
- package/.next/standalone/.failproofai/policies/review-policies.mjs +0 -113
- package/.next/standalone/.failproofai/policies/workflow-policies.mjs +0 -63
- package/.next/standalone/.failproofai/policies-config.json +0 -39
- package/.next/standalone/.next/static/chunks/0gu_a.a80ritd.css +0 -1
- package/.next/standalone/AGENTS.md +0 -80
- package/.next/standalone/CHANGELOG.md +0 -151
- package/.next/standalone/CLAUDE.md +0 -165
- package/.next/standalone/CONTRIBUTING.md +0 -76
- package/.next/standalone/Dockerfile.docs +0 -12
- package/.next/standalone/LICENSE +0 -42
- package/.next/standalone/README.md +0 -301
- package/.next/standalone/bin/failproofai.mjs +0 -352
- package/.next/standalone/bun.lock +0 -1119
- package/.next/standalone/components.json +0 -23
- package/.next/standalone/dist/cli.mjs +0 -3595
- package/.next/standalone/dist/index.js +0 -80
- package/.next/standalone/docs/ar/architecture.mdx +0 -334
- package/.next/standalone/docs/ar/built-in-policies.mdx +0 -574
- package/.next/standalone/docs/ar/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/ar/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/ar/cli/hook.mdx +0 -31
- package/.next/standalone/docs/ar/cli/install-policies.mdx +0 -48
- package/.next/standalone/docs/ar/cli/list-policies.mdx +0 -31
- package/.next/standalone/docs/ar/cli/remove-policies.mdx +0 -43
- package/.next/standalone/docs/ar/cli/version.mdx +0 -13
- package/.next/standalone/docs/ar/configuration.mdx +0 -223
- package/.next/standalone/docs/ar/custom-policies.mdx +0 -354
- package/.next/standalone/docs/ar/dashboard.mdx +0 -142
- package/.next/standalone/docs/ar/examples.mdx +0 -307
- package/.next/standalone/docs/ar/for-agents.mdx +0 -39
- package/.next/standalone/docs/ar/getting-started.mdx +0 -187
- package/.next/standalone/docs/ar/introduction.mdx +0 -58
- package/.next/standalone/docs/ar/package-aliases.mdx +0 -82
- package/.next/standalone/docs/ar/testing.mdx +0 -261
- package/.next/standalone/docs/architecture.mdx +0 -332
- package/.next/standalone/docs/built-in-policies.mdx +0 -574
- package/.next/standalone/docs/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/cli/hook.mdx +0 -30
- package/.next/standalone/docs/cli/install-policies.mdx +0 -47
- package/.next/standalone/docs/cli/list-policies.mdx +0 -31
- package/.next/standalone/docs/cli/remove-policies.mdx +0 -43
- package/.next/standalone/docs/cli/version.mdx +0 -12
- package/.next/standalone/docs/configuration.mdx +0 -222
- package/.next/standalone/docs/custom-policies.mdx +0 -353
- package/.next/standalone/docs/dashboard.mdx +0 -142
- package/.next/standalone/docs/de/architecture.mdx +0 -332
- package/.next/standalone/docs/de/built-in-policies.mdx +0 -574
- package/.next/standalone/docs/de/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/de/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/de/cli/hook.mdx +0 -30
- package/.next/standalone/docs/de/cli/install-policies.mdx +0 -47
- package/.next/standalone/docs/de/cli/list-policies.mdx +0 -31
- package/.next/standalone/docs/de/cli/remove-policies.mdx +0 -43
- package/.next/standalone/docs/de/cli/version.mdx +0 -12
- package/.next/standalone/docs/de/configuration.mdx +0 -222
- package/.next/standalone/docs/de/custom-policies.mdx +0 -353
- package/.next/standalone/docs/de/dashboard.mdx +0 -142
- package/.next/standalone/docs/de/examples.mdx +0 -307
- package/.next/standalone/docs/de/for-agents.mdx +0 -38
- package/.next/standalone/docs/de/getting-started.mdx +0 -186
- package/.next/standalone/docs/de/introduction.mdx +0 -57
- package/.next/standalone/docs/de/package-aliases.mdx +0 -82
- package/.next/standalone/docs/de/testing.mdx +0 -260
- package/.next/standalone/docs/docs.json +0 -1002
- package/.next/standalone/docs/es/architecture.mdx +0 -332
- package/.next/standalone/docs/es/built-in-policies.mdx +0 -574
- package/.next/standalone/docs/es/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/es/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/es/cli/hook.mdx +0 -30
- package/.next/standalone/docs/es/cli/install-policies.mdx +0 -47
- package/.next/standalone/docs/es/cli/list-policies.mdx +0 -31
- package/.next/standalone/docs/es/cli/remove-policies.mdx +0 -43
- package/.next/standalone/docs/es/cli/version.mdx +0 -12
- package/.next/standalone/docs/es/configuration.mdx +0 -222
- package/.next/standalone/docs/es/custom-policies.mdx +0 -353
- package/.next/standalone/docs/es/dashboard.mdx +0 -142
- package/.next/standalone/docs/es/examples.mdx +0 -307
- package/.next/standalone/docs/es/for-agents.mdx +0 -38
- package/.next/standalone/docs/es/getting-started.mdx +0 -186
- package/.next/standalone/docs/es/introduction.mdx +0 -57
- package/.next/standalone/docs/es/package-aliases.mdx +0 -82
- package/.next/standalone/docs/es/testing.mdx +0 -260
- package/.next/standalone/docs/examples.mdx +0 -307
- package/.next/standalone/docs/favicon.ico +0 -0
- package/.next/standalone/docs/for-agents.mdx +0 -38
- package/.next/standalone/docs/fr/architecture.mdx +0 -332
- package/.next/standalone/docs/fr/built-in-policies.mdx +0 -574
- package/.next/standalone/docs/fr/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/fr/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/fr/cli/hook.mdx +0 -30
- package/.next/standalone/docs/fr/cli/install-policies.mdx +0 -47
- package/.next/standalone/docs/fr/cli/list-policies.mdx +0 -31
- package/.next/standalone/docs/fr/cli/remove-policies.mdx +0 -43
- package/.next/standalone/docs/fr/cli/version.mdx +0 -12
- package/.next/standalone/docs/fr/configuration.mdx +0 -222
- package/.next/standalone/docs/fr/custom-policies.mdx +0 -353
- package/.next/standalone/docs/fr/dashboard.mdx +0 -142
- package/.next/standalone/docs/fr/examples.mdx +0 -307
- package/.next/standalone/docs/fr/for-agents.mdx +0 -38
- package/.next/standalone/docs/fr/getting-started.mdx +0 -186
- package/.next/standalone/docs/fr/introduction.mdx +0 -57
- package/.next/standalone/docs/fr/package-aliases.mdx +0 -82
- package/.next/standalone/docs/fr/testing.mdx +0 -260
- package/.next/standalone/docs/getting-started.mdx +0 -186
- package/.next/standalone/docs/he/architecture.mdx +0 -333
- package/.next/standalone/docs/he/built-in-policies.mdx +0 -574
- package/.next/standalone/docs/he/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/he/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/he/cli/hook.mdx +0 -30
- package/.next/standalone/docs/he/cli/install-policies.mdx +0 -47
- package/.next/standalone/docs/he/cli/list-policies.mdx +0 -32
- package/.next/standalone/docs/he/cli/remove-policies.mdx +0 -43
- package/.next/standalone/docs/he/cli/version.mdx +0 -12
- package/.next/standalone/docs/he/configuration.mdx +0 -223
- package/.next/standalone/docs/he/custom-policies.mdx +0 -353
- package/.next/standalone/docs/he/dashboard.mdx +0 -142
- package/.next/standalone/docs/he/examples.mdx +0 -307
- package/.next/standalone/docs/he/for-agents.mdx +0 -38
- package/.next/standalone/docs/he/getting-started.mdx +0 -186
- package/.next/standalone/docs/he/introduction.mdx +0 -57
- package/.next/standalone/docs/he/package-aliases.mdx +0 -82
- package/.next/standalone/docs/he/testing.mdx +0 -260
- package/.next/standalone/docs/hi/architecture.mdx +0 -334
- package/.next/standalone/docs/hi/built-in-policies.mdx +0 -576
- package/.next/standalone/docs/hi/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/hi/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/hi/cli/hook.mdx +0 -30
- package/.next/standalone/docs/hi/cli/install-policies.mdx +0 -47
- package/.next/standalone/docs/hi/cli/list-policies.mdx +0 -31
- package/.next/standalone/docs/hi/cli/remove-policies.mdx +0 -43
- package/.next/standalone/docs/hi/cli/version.mdx +0 -12
- package/.next/standalone/docs/hi/configuration.mdx +0 -222
- package/.next/standalone/docs/hi/custom-policies.mdx +0 -354
- package/.next/standalone/docs/hi/dashboard.mdx +0 -142
- package/.next/standalone/docs/hi/examples.mdx +0 -309
- package/.next/standalone/docs/hi/for-agents.mdx +0 -38
- package/.next/standalone/docs/hi/getting-started.mdx +0 -187
- package/.next/standalone/docs/hi/introduction.mdx +0 -57
- package/.next/standalone/docs/hi/package-aliases.mdx +0 -82
- package/.next/standalone/docs/hi/testing.mdx +0 -260
- package/.next/standalone/docs/i18n/README.ar.md +0 -312
- package/.next/standalone/docs/i18n/README.de.md +0 -307
- package/.next/standalone/docs/i18n/README.es.md +0 -307
- package/.next/standalone/docs/i18n/README.fr.md +0 -307
- package/.next/standalone/docs/i18n/README.he.md +0 -312
- package/.next/standalone/docs/i18n/README.hi.md +0 -307
- package/.next/standalone/docs/i18n/README.it.md +0 -307
- package/.next/standalone/docs/i18n/README.ja.md +0 -307
- package/.next/standalone/docs/i18n/README.ko.md +0 -307
- package/.next/standalone/docs/i18n/README.pt-br.md +0 -307
- package/.next/standalone/docs/i18n/README.ru.md +0 -308
- package/.next/standalone/docs/i18n/README.tr.md +0 -307
- package/.next/standalone/docs/i18n/README.vi.md +0 -307
- package/.next/standalone/docs/i18n/README.zh.md +0 -307
- package/.next/standalone/docs/introduction.mdx +0 -57
- package/.next/standalone/docs/it/architecture.mdx +0 -334
- package/.next/standalone/docs/it/built-in-policies.mdx +0 -574
- package/.next/standalone/docs/it/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/it/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/it/cli/hook.mdx +0 -30
- package/.next/standalone/docs/it/cli/install-policies.mdx +0 -47
- package/.next/standalone/docs/it/cli/list-policies.mdx +0 -31
- package/.next/standalone/docs/it/cli/remove-policies.mdx +0 -43
- package/.next/standalone/docs/it/cli/version.mdx +0 -12
- package/.next/standalone/docs/it/configuration.mdx +0 -222
- package/.next/standalone/docs/it/custom-policies.mdx +0 -353
- package/.next/standalone/docs/it/dashboard.mdx +0 -142
- package/.next/standalone/docs/it/examples.mdx +0 -307
- package/.next/standalone/docs/it/for-agents.mdx +0 -38
- package/.next/standalone/docs/it/getting-started.mdx +0 -186
- package/.next/standalone/docs/it/introduction.mdx +0 -57
- package/.next/standalone/docs/it/package-aliases.mdx +0 -82
- package/.next/standalone/docs/it/testing.mdx +0 -260
- package/.next/standalone/docs/ja/architecture.mdx +0 -332
- package/.next/standalone/docs/ja/built-in-policies.mdx +0 -572
- package/.next/standalone/docs/ja/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/ja/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/ja/cli/hook.mdx +0 -30
- package/.next/standalone/docs/ja/cli/install-policies.mdx +0 -47
- package/.next/standalone/docs/ja/cli/list-policies.mdx +0 -31
- package/.next/standalone/docs/ja/cli/remove-policies.mdx +0 -43
- package/.next/standalone/docs/ja/cli/version.mdx +0 -12
- package/.next/standalone/docs/ja/configuration.mdx +0 -222
- package/.next/standalone/docs/ja/custom-policies.mdx +0 -353
- package/.next/standalone/docs/ja/dashboard.mdx +0 -142
- package/.next/standalone/docs/ja/examples.mdx +0 -307
- package/.next/standalone/docs/ja/for-agents.mdx +0 -38
- package/.next/standalone/docs/ja/getting-started.mdx +0 -186
- package/.next/standalone/docs/ja/introduction.mdx +0 -57
- package/.next/standalone/docs/ja/package-aliases.mdx +0 -82
- package/.next/standalone/docs/ja/testing.mdx +0 -260
- package/.next/standalone/docs/ko/architecture.mdx +0 -332
- package/.next/standalone/docs/ko/built-in-policies.mdx +0 -572
- package/.next/standalone/docs/ko/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/ko/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/ko/cli/hook.mdx +0 -30
- package/.next/standalone/docs/ko/cli/install-policies.mdx +0 -47
- package/.next/standalone/docs/ko/cli/list-policies.mdx +0 -31
- package/.next/standalone/docs/ko/cli/remove-policies.mdx +0 -43
- package/.next/standalone/docs/ko/cli/version.mdx +0 -12
- package/.next/standalone/docs/ko/configuration.mdx +0 -222
- package/.next/standalone/docs/ko/custom-policies.mdx +0 -353
- package/.next/standalone/docs/ko/dashboard.mdx +0 -142
- package/.next/standalone/docs/ko/examples.mdx +0 -307
- package/.next/standalone/docs/ko/for-agents.mdx +0 -38
- package/.next/standalone/docs/ko/getting-started.mdx +0 -186
- package/.next/standalone/docs/ko/introduction.mdx +0 -57
- package/.next/standalone/docs/ko/package-aliases.mdx +0 -82
- package/.next/standalone/docs/ko/testing.mdx +0 -260
- package/.next/standalone/docs/logo/dark.svg +0 -21
- package/.next/standalone/docs/logo/exosphere-dark.png +0 -0
- package/.next/standalone/docs/logo/exosphere-light.png +0 -0
- package/.next/standalone/docs/logo/light.svg +0 -21
- package/.next/standalone/docs/package-aliases.mdx +0 -82
- package/.next/standalone/docs/pt-br/architecture.mdx +0 -332
- package/.next/standalone/docs/pt-br/built-in-policies.mdx +0 -574
- package/.next/standalone/docs/pt-br/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/pt-br/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/pt-br/cli/hook.mdx +0 -30
- package/.next/standalone/docs/pt-br/cli/install-policies.mdx +0 -47
- package/.next/standalone/docs/pt-br/cli/list-policies.mdx +0 -31
- package/.next/standalone/docs/pt-br/cli/remove-policies.mdx +0 -43
- package/.next/standalone/docs/pt-br/cli/version.mdx +0 -12
- package/.next/standalone/docs/pt-br/configuration.mdx +0 -222
- package/.next/standalone/docs/pt-br/custom-policies.mdx +0 -353
- package/.next/standalone/docs/pt-br/dashboard.mdx +0 -142
- package/.next/standalone/docs/pt-br/examples.mdx +0 -307
- package/.next/standalone/docs/pt-br/for-agents.mdx +0 -38
- package/.next/standalone/docs/pt-br/getting-started.mdx +0 -186
- package/.next/standalone/docs/pt-br/introduction.mdx +0 -57
- package/.next/standalone/docs/pt-br/package-aliases.mdx +0 -82
- package/.next/standalone/docs/pt-br/testing.mdx +0 -260
- package/.next/standalone/docs/ru/architecture.mdx +0 -333
- package/.next/standalone/docs/ru/built-in-policies.mdx +0 -574
- package/.next/standalone/docs/ru/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/ru/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/ru/cli/hook.mdx +0 -30
- package/.next/standalone/docs/ru/cli/install-policies.mdx +0 -48
- package/.next/standalone/docs/ru/cli/list-policies.mdx +0 -32
- package/.next/standalone/docs/ru/cli/remove-policies.mdx +0 -43
- package/.next/standalone/docs/ru/cli/version.mdx +0 -12
- package/.next/standalone/docs/ru/configuration.mdx +0 -222
- package/.next/standalone/docs/ru/custom-policies.mdx +0 -354
- package/.next/standalone/docs/ru/dashboard.mdx +0 -142
- package/.next/standalone/docs/ru/examples.mdx +0 -309
- package/.next/standalone/docs/ru/for-agents.mdx +0 -38
- package/.next/standalone/docs/ru/getting-started.mdx +0 -186
- package/.next/standalone/docs/ru/introduction.mdx +0 -57
- package/.next/standalone/docs/ru/package-aliases.mdx +0 -82
- package/.next/standalone/docs/ru/testing.mdx +0 -260
- package/.next/standalone/docs/testing.mdx +0 -260
- package/.next/standalone/docs/tr/architecture.mdx +0 -333
- package/.next/standalone/docs/tr/built-in-policies.mdx +0 -574
- package/.next/standalone/docs/tr/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/tr/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/tr/cli/hook.mdx +0 -30
- package/.next/standalone/docs/tr/cli/install-policies.mdx +0 -47
- package/.next/standalone/docs/tr/cli/list-policies.mdx +0 -31
- package/.next/standalone/docs/tr/cli/remove-policies.mdx +0 -44
- package/.next/standalone/docs/tr/cli/version.mdx +0 -12
- package/.next/standalone/docs/tr/configuration.mdx +0 -222
- package/.next/standalone/docs/tr/custom-policies.mdx +0 -353
- package/.next/standalone/docs/tr/dashboard.mdx +0 -142
- package/.next/standalone/docs/tr/examples.mdx +0 -308
- package/.next/standalone/docs/tr/for-agents.mdx +0 -38
- package/.next/standalone/docs/tr/getting-started.mdx +0 -186
- package/.next/standalone/docs/tr/introduction.mdx +0 -57
- package/.next/standalone/docs/tr/package-aliases.mdx +0 -82
- package/.next/standalone/docs/tr/testing.mdx +0 -260
- package/.next/standalone/docs/vi/architecture.mdx +0 -334
- package/.next/standalone/docs/vi/built-in-policies.mdx +0 -575
- package/.next/standalone/docs/vi/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/vi/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/vi/cli/hook.mdx +0 -30
- package/.next/standalone/docs/vi/cli/install-policies.mdx +0 -47
- package/.next/standalone/docs/vi/cli/list-policies.mdx +0 -31
- package/.next/standalone/docs/vi/cli/remove-policies.mdx +0 -43
- package/.next/standalone/docs/vi/cli/version.mdx +0 -13
- package/.next/standalone/docs/vi/configuration.mdx +0 -222
- package/.next/standalone/docs/vi/custom-policies.mdx +0 -353
- package/.next/standalone/docs/vi/dashboard.mdx +0 -142
- package/.next/standalone/docs/vi/examples.mdx +0 -308
- package/.next/standalone/docs/vi/for-agents.mdx +0 -38
- package/.next/standalone/docs/vi/getting-started.mdx +0 -186
- package/.next/standalone/docs/vi/introduction.mdx +0 -57
- package/.next/standalone/docs/vi/package-aliases.mdx +0 -82
- package/.next/standalone/docs/vi/testing.mdx +0 -260
- package/.next/standalone/docs/zh/architecture.mdx +0 -332
- package/.next/standalone/docs/zh/built-in-policies.mdx +0 -572
- package/.next/standalone/docs/zh/cli/dashboard.mdx +0 -28
- package/.next/standalone/docs/zh/cli/environment-variables.mdx +0 -34
- package/.next/standalone/docs/zh/cli/hook.mdx +0 -30
- package/.next/standalone/docs/zh/cli/install-policies.mdx +0 -47
- package/.next/standalone/docs/zh/cli/list-policies.mdx +0 -31
- package/.next/standalone/docs/zh/cli/remove-policies.mdx +0 -43
- package/.next/standalone/docs/zh/cli/version.mdx +0 -12
- package/.next/standalone/docs/zh/configuration.mdx +0 -222
- package/.next/standalone/docs/zh/custom-policies.mdx +0 -353
- package/.next/standalone/docs/zh/dashboard.mdx +0 -142
- package/.next/standalone/docs/zh/examples.mdx +0 -307
- package/.next/standalone/docs/zh/for-agents.mdx +0 -38
- package/.next/standalone/docs/zh/getting-started.mdx +0 -186
- package/.next/standalone/docs/zh/introduction.mdx +0 -57
- package/.next/standalone/docs/zh/package-aliases.mdx +0 -82
- package/.next/standalone/docs/zh/testing.mdx +0 -260
- package/.next/standalone/eslint.config.mjs +0 -15
- package/.next/standalone/examples/convention-policies/security-policies.mjs +0 -40
- package/.next/standalone/examples/convention-policies/workflow-policies.mjs +0 -41
- package/.next/standalone/examples/policies-advanced/index.js +0 -103
- package/.next/standalone/examples/policies-advanced/utils.js +0 -35
- package/.next/standalone/examples/policies-basic.js +0 -77
- package/.next/standalone/examples/policies-notification.js +0 -104
- package/.next/standalone/node_modules/@img/colour/color.cjs +0 -1594
- package/.next/standalone/node_modules/@img/colour/index.cjs +0 -1
- package/.next/standalone/node_modules/@img/colour/package.json +0 -45
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/README.md +0 -46
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/glib-2.0/include/glibconfig.h +0 -221
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/index.js +0 -1
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/libvips-cpp.so.8.17.3 +0 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/package.json +0 -42
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/versions.json +0 -30
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/README.md +0 -46
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/glib-2.0/include/glibconfig.h +0 -221
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/index.js +0 -1
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/libvips-cpp.so.8.17.3 +0 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/package.json +0 -42
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/versions.json +0 -30
- package/.next/standalone/node_modules/@img/sharp-linux-x64/lib/sharp-linux-x64.node +0 -0
- package/.next/standalone/node_modules/@img/sharp-linux-x64/package.json +0 -46
- package/.next/standalone/node_modules/@img/sharp-linuxmusl-x64/lib/sharp-linuxmusl-x64.node +0 -0
- package/.next/standalone/node_modules/@img/sharp-linuxmusl-x64/package.json +0 -46
- package/.next/standalone/node_modules/detect-libc/lib/detect-libc.js +0 -313
- package/.next/standalone/node_modules/detect-libc/lib/elf.js +0 -39
- package/.next/standalone/node_modules/detect-libc/lib/filesystem.js +0 -51
- package/.next/standalone/node_modules/detect-libc/lib/process.js +0 -24
- package/.next/standalone/node_modules/detect-libc/package.json +0 -44
- package/.next/standalone/node_modules/sharp/lib/channel.js +0 -177
- package/.next/standalone/node_modules/sharp/lib/colour.js +0 -195
- package/.next/standalone/node_modules/sharp/lib/composite.js +0 -212
- package/.next/standalone/node_modules/sharp/lib/constructor.js +0 -499
- package/.next/standalone/node_modules/sharp/lib/index.js +0 -16
- package/.next/standalone/node_modules/sharp/lib/input.js +0 -809
- package/.next/standalone/node_modules/sharp/lib/is.js +0 -143
- package/.next/standalone/node_modules/sharp/lib/libvips.js +0 -207
- package/.next/standalone/node_modules/sharp/lib/operation.js +0 -1016
- package/.next/standalone/node_modules/sharp/lib/output.js +0 -1666
- package/.next/standalone/node_modules/sharp/lib/resize.js +0 -595
- package/.next/standalone/node_modules/sharp/lib/sharp.js +0 -121
- package/.next/standalone/node_modules/sharp/lib/utility.js +0 -291
- package/.next/standalone/node_modules/sharp/node_modules/semver/classes/comparator.js +0 -143
- package/.next/standalone/node_modules/sharp/node_modules/semver/classes/range.js +0 -557
- package/.next/standalone/node_modules/sharp/node_modules/semver/classes/semver.js +0 -333
- package/.next/standalone/node_modules/sharp/node_modules/semver/functions/cmp.js +0 -54
- package/.next/standalone/node_modules/sharp/node_modules/semver/functions/coerce.js +0 -62
- package/.next/standalone/node_modules/sharp/node_modules/semver/functions/compare.js +0 -7
- package/.next/standalone/node_modules/sharp/node_modules/semver/functions/eq.js +0 -5
- package/.next/standalone/node_modules/sharp/node_modules/semver/functions/gt.js +0 -5
- package/.next/standalone/node_modules/sharp/node_modules/semver/functions/gte.js +0 -5
- package/.next/standalone/node_modules/sharp/node_modules/semver/functions/lt.js +0 -5
- package/.next/standalone/node_modules/sharp/node_modules/semver/functions/lte.js +0 -5
- package/.next/standalone/node_modules/sharp/node_modules/semver/functions/neq.js +0 -5
- package/.next/standalone/node_modules/sharp/node_modules/semver/functions/parse.js +0 -18
- package/.next/standalone/node_modules/sharp/node_modules/semver/functions/satisfies.js +0 -12
- package/.next/standalone/node_modules/sharp/node_modules/semver/internal/constants.js +0 -37
- package/.next/standalone/node_modules/sharp/node_modules/semver/internal/debug.js +0 -11
- package/.next/standalone/node_modules/sharp/node_modules/semver/internal/identifiers.js +0 -29
- package/.next/standalone/node_modules/sharp/node_modules/semver/internal/lrucache.js +0 -42
- package/.next/standalone/node_modules/sharp/node_modules/semver/internal/parse-options.js +0 -17
- package/.next/standalone/node_modules/sharp/node_modules/semver/internal/re.js +0 -223
- package/.next/standalone/node_modules/sharp/node_modules/semver/package.json +0 -78
- package/.next/standalone/node_modules/sharp/package.json +0 -202
- package/.next/standalone/scripts/alias-proxy.js +0 -18
- package/.next/standalone/scripts/dev.ts +0 -3
- package/.next/standalone/scripts/install-telemetry.mjs +0 -108
- package/.next/standalone/scripts/launch.ts +0 -83
- package/.next/standalone/scripts/parse-script-args.ts +0 -87
- package/.next/standalone/scripts/postinstall.mjs +0 -121
- package/.next/standalone/scripts/preuninstall.mjs +0 -131
- package/.next/standalone/scripts/publish-aliases.mjs +0 -87
- package/.next/standalone/scripts/start.ts +0 -3
- package/.next/standalone/scripts/sync-hook-events-prompt.md +0 -60
- package/.next/standalone/scripts/translate-docs/cache.ts +0 -62
- package/.next/standalone/scripts/translate-docs/cli.ts +0 -357
- package/.next/standalone/scripts/translate-docs/config.ts +0 -248
- package/.next/standalone/scripts/translate-docs/mdx-translator.ts +0 -153
- package/.next/standalone/scripts/translate-docs/mintlify-nav.ts +0 -107
- package/.next/standalone/scripts/translate-docs/readme-translator.ts +0 -154
- package/.next/standalone/scripts/translate-docs/translator.ts +0 -68
- package/.next/standalone/scripts/translate-docs/types.ts +0 -43
- package/.next/standalone/src/cli-error.ts +0 -18
- package/.next/standalone/src/hooks/builtin-policies.ts +0 -1613
- package/.next/standalone/src/hooks/custom-hooks-loader.ts +0 -205
- package/.next/standalone/src/hooks/custom-hooks-registry.ts +0 -30
- package/.next/standalone/src/hooks/handler.ts +0 -202
- package/.next/standalone/src/hooks/hook-activity-store.ts +0 -349
- package/.next/standalone/src/hooks/hook-logger.ts +0 -133
- package/.next/standalone/src/hooks/hook-telemetry.ts +0 -43
- package/.next/standalone/src/hooks/hooks-config.ts +0 -166
- package/.next/standalone/src/hooks/install-prompt.ts +0 -357
- package/.next/standalone/src/hooks/llm-client.ts +0 -90
- package/.next/standalone/src/hooks/loader-utils.ts +0 -178
- package/.next/standalone/src/hooks/manager.ts +0 -692
- package/.next/standalone/src/hooks/policy-evaluator.ts +0 -224
- package/.next/standalone/src/hooks/policy-helpers.ts +0 -16
- package/.next/standalone/src/hooks/policy-registry.ts +0 -90
- package/.next/standalone/src/hooks/policy-types.ts +0 -77
- package/.next/standalone/src/hooks/types.ts +0 -63
- package/.next/standalone/src/index.ts +0 -19
- package/.next/standalone/src/posthog-key.ts +0 -5
- package/.next/standalone/tailwind.config.ts +0 -11
- package/.next/standalone/tsconfig.json +0 -42
- package/.next/standalone/vitest.config.e2e.mts +0 -24
- package/.next/standalone/vitest.config.mts +0 -23
- /package/.next/standalone/.next/static/{A9pNTZdoYJTVyPAYwQMx5 → wOkJXoch1UmRAmyIuKZWc}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{A9pNTZdoYJTVyPAYwQMx5 → wOkJXoch1UmRAmyIuKZWc}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{A9pNTZdoYJTVyPAYwQMx5 → wOkJXoch1UmRAmyIuKZWc}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { readTokens, writeTokens, isLoggedIn } from "../auth/token-store";
|
|
7
|
+
import { readPid, writePid, clearPid, isProcessAlive } from "./pid";
|
|
8
|
+
import {
|
|
9
|
+
claimPendingBatch,
|
|
10
|
+
readProcessingFile,
|
|
11
|
+
deleteProcessingFile,
|
|
12
|
+
findOrphanProcessingFiles,
|
|
13
|
+
type QueueEntry,
|
|
14
|
+
} from "./queue";
|
|
15
|
+
|
|
16
|
+
const QUEUE_DIR = join(homedir(), ".failproofai", "cache", "server-queue");
|
|
17
|
+
const BATCH_SIZE = 100;
|
|
18
|
+
const FLUSH_INTERVAL_MS = 2000;
|
|
19
|
+
const RECONNECT_BASE_MS = 1000;
|
|
20
|
+
const RECONNECT_MAX_MS = 60_000;
|
|
21
|
+
const HTTP_TIMEOUT_MS = 10_000;
|
|
22
|
+
const WS_CONNECT_TIMEOUT_MS = 15_000;
|
|
23
|
+
const ACK_TIMEOUT_MS = 30_000;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Lazy-start check: call on every hook invocation. Near-zero cost when daemon
|
|
27
|
+
* is already running (~1ms PID check); spawns daemon once after reboots.
|
|
28
|
+
*/
|
|
29
|
+
export function ensureRelayRunning(): void {
|
|
30
|
+
if (!isLoggedIn()) return;
|
|
31
|
+
|
|
32
|
+
const pid = readPid();
|
|
33
|
+
if (pid !== null && isProcessAlive(pid)) return;
|
|
34
|
+
|
|
35
|
+
if (pid !== null) clearPid();
|
|
36
|
+
spawnDaemon();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function spawnDaemon(): void {
|
|
40
|
+
const entrypoint = process.env.FAILPROOFAI_RELAY_ENTRYPOINT ?? process.argv[1];
|
|
41
|
+
if (!entrypoint) return;
|
|
42
|
+
|
|
43
|
+
const child = spawn(process.execPath, [entrypoint, "--relay-daemon"], {
|
|
44
|
+
detached: true,
|
|
45
|
+
stdio: "ignore",
|
|
46
|
+
env: { ...process.env, FAILPROOFAI_DAEMON: "1" },
|
|
47
|
+
});
|
|
48
|
+
child.unref();
|
|
49
|
+
|
|
50
|
+
if (typeof child.pid === "number") {
|
|
51
|
+
writePid(child.pid);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Block until the spawned daemon has been observed running, or until the
|
|
57
|
+
* timeout elapses. Used by `relay start` so we don't falsely report
|
|
58
|
+
* "Failed to start daemon" in the split-second window before the child
|
|
59
|
+
* has finished exec-ing.
|
|
60
|
+
*/
|
|
61
|
+
export async function waitForRelayAlive(timeoutMs = 2_000): Promise<boolean> {
|
|
62
|
+
const deadline = Date.now() + timeoutMs;
|
|
63
|
+
while (Date.now() < deadline) {
|
|
64
|
+
const pid = readPid();
|
|
65
|
+
if (pid !== null && isProcessAlive(pid)) return true;
|
|
66
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function refreshTokenIfNeeded(): Promise<string | null> {
|
|
72
|
+
const tokens = readTokens();
|
|
73
|
+
if (!tokens) return null;
|
|
74
|
+
|
|
75
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
76
|
+
if (tokens.expires_at - nowSec > 300) {
|
|
77
|
+
return tokens.access_token;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const resp = await fetch(`${tokens.server_url}/api/v1/auth/refresh`, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: { "Content-Type": "application/json" },
|
|
84
|
+
body: JSON.stringify({ refresh_token: tokens.refresh_token }),
|
|
85
|
+
signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),
|
|
86
|
+
});
|
|
87
|
+
if (!resp.ok) return tokens.access_token;
|
|
88
|
+
const refreshed = (await resp.json()) as {
|
|
89
|
+
access_token: string;
|
|
90
|
+
refresh_token: string;
|
|
91
|
+
expires_in: number;
|
|
92
|
+
};
|
|
93
|
+
writeTokens({
|
|
94
|
+
...tokens,
|
|
95
|
+
access_token: refreshed.access_token,
|
|
96
|
+
refresh_token: refreshed.refresh_token,
|
|
97
|
+
expires_at: nowSec + refreshed.expires_in,
|
|
98
|
+
});
|
|
99
|
+
return refreshed.access_token;
|
|
100
|
+
} catch {
|
|
101
|
+
return tokens.access_token;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
type WebSocketLike = {
|
|
106
|
+
send(data: string): void;
|
|
107
|
+
close(): void;
|
|
108
|
+
readyState: number;
|
|
109
|
+
onopen: (() => void) | null;
|
|
110
|
+
onmessage: ((ev: { data: string }) => void) | null;
|
|
111
|
+
onerror: ((ev: unknown) => void) | null;
|
|
112
|
+
onclose: (() => void) | null;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
class Relay {
|
|
116
|
+
private readonly ws: WebSocketLike;
|
|
117
|
+
private readonly pendingAcks = new Map<string, (ok: boolean) => void>();
|
|
118
|
+
private closed = false;
|
|
119
|
+
|
|
120
|
+
constructor(ws: WebSocketLike) {
|
|
121
|
+
this.ws = ws;
|
|
122
|
+
ws.onmessage = (ev) => this.handleMessage(ev.data);
|
|
123
|
+
ws.onclose = () => this.handleClose();
|
|
124
|
+
ws.onerror = () => this.handleClose();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private handleMessage(data: string): void {
|
|
128
|
+
try {
|
|
129
|
+
const msg = JSON.parse(data) as { ack?: string; error?: string };
|
|
130
|
+
if (msg.ack && this.pendingAcks.has(msg.ack)) {
|
|
131
|
+
const resolve = this.pendingAcks.get(msg.ack)!;
|
|
132
|
+
this.pendingAcks.delete(msg.ack);
|
|
133
|
+
resolve(true);
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
// Ignore unparseable server messages
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private handleClose(): void {
|
|
141
|
+
this.closed = true;
|
|
142
|
+
// Reject all outstanding acks so callers can retry
|
|
143
|
+
for (const [, resolve] of this.pendingAcks) {
|
|
144
|
+
resolve(false);
|
|
145
|
+
}
|
|
146
|
+
this.pendingAcks.clear();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
isClosed(): boolean {
|
|
150
|
+
return this.closed;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
close(): void {
|
|
154
|
+
try {
|
|
155
|
+
this.ws.close();
|
|
156
|
+
} catch {
|
|
157
|
+
// ignore
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Send a batch and wait for the server's ack (keyed on batch_id).
|
|
163
|
+
* Returns true only when the server confirms the insert.
|
|
164
|
+
*/
|
|
165
|
+
async sendBatchAndWaitAck(events: QueueEntry[]): Promise<boolean> {
|
|
166
|
+
if (this.closed) return false;
|
|
167
|
+
const batchId = randomUUID();
|
|
168
|
+
|
|
169
|
+
const ackPromise = new Promise<boolean>((resolve) => {
|
|
170
|
+
this.pendingAcks.set(batchId, resolve);
|
|
171
|
+
setTimeout(() => {
|
|
172
|
+
if (this.pendingAcks.delete(batchId)) resolve(false);
|
|
173
|
+
}, ACK_TIMEOUT_MS);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
this.ws.send(JSON.stringify({ batch_id: batchId, events }));
|
|
178
|
+
} catch {
|
|
179
|
+
this.pendingAcks.delete(batchId);
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return ackPromise;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function connect(wsUrl: string, token: string): Promise<WebSocketLike> {
|
|
188
|
+
const WSCtor: any = (globalThis as any).WebSocket;
|
|
189
|
+
if (!WSCtor) {
|
|
190
|
+
throw new Error("WebSocket not available in this Node version. Requires Node 22+.");
|
|
191
|
+
}
|
|
192
|
+
const ws: WebSocketLike = new WSCtor(wsUrl);
|
|
193
|
+
|
|
194
|
+
await new Promise<void>((resolve, reject) => {
|
|
195
|
+
let settled = false;
|
|
196
|
+
const timeout = setTimeout(() => {
|
|
197
|
+
if (settled) return;
|
|
198
|
+
settled = true;
|
|
199
|
+
try {
|
|
200
|
+
ws.close();
|
|
201
|
+
} catch {
|
|
202
|
+
// ignore
|
|
203
|
+
}
|
|
204
|
+
reject(new Error("WebSocket connect timeout"));
|
|
205
|
+
}, WS_CONNECT_TIMEOUT_MS);
|
|
206
|
+
|
|
207
|
+
ws.onopen = () => {
|
|
208
|
+
if (settled) return;
|
|
209
|
+
settled = true;
|
|
210
|
+
clearTimeout(timeout);
|
|
211
|
+
try {
|
|
212
|
+
ws.send(token);
|
|
213
|
+
resolve();
|
|
214
|
+
} catch (e) {
|
|
215
|
+
reject(e);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
ws.onerror = (e) => {
|
|
219
|
+
if (settled) return;
|
|
220
|
+
settled = true;
|
|
221
|
+
clearTimeout(timeout);
|
|
222
|
+
reject(e);
|
|
223
|
+
};
|
|
224
|
+
ws.onclose = () => {
|
|
225
|
+
if (settled) return;
|
|
226
|
+
settled = true;
|
|
227
|
+
clearTimeout(timeout);
|
|
228
|
+
reject(new Error("WebSocket closed before opening"));
|
|
229
|
+
};
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return ws;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Send all events from a processing file and wait for server acks on every
|
|
237
|
+
* batch. Returns true only when every batch was acknowledged — in that
|
|
238
|
+
* case the caller may delete the processing file.
|
|
239
|
+
*/
|
|
240
|
+
async function sendProcessingFile(relay: Relay, path: string): Promise<boolean> {
|
|
241
|
+
const events = readProcessingFile(path);
|
|
242
|
+
if (events.length === 0) return true;
|
|
243
|
+
|
|
244
|
+
for (let i = 0; i < events.length; i += BATCH_SIZE) {
|
|
245
|
+
const batch = events.slice(i, i + BATCH_SIZE);
|
|
246
|
+
const ok = await relay.sendBatchAndWaitAck(batch);
|
|
247
|
+
if (!ok) return false;
|
|
248
|
+
}
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export async function runDaemon(): Promise<void> {
|
|
253
|
+
let reconnectDelay = RECONNECT_BASE_MS;
|
|
254
|
+
|
|
255
|
+
while (true) {
|
|
256
|
+
const token = await refreshTokenIfNeeded();
|
|
257
|
+
const tokens = readTokens();
|
|
258
|
+
if (!token || !tokens) {
|
|
259
|
+
await new Promise((r) => setTimeout(r, 30_000));
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const wsUrl = `${tokens.server_url.replace(/^http/, "ws")}/ws/events/ingest`;
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
const ws = await connect(wsUrl, token);
|
|
267
|
+
const relay = new Relay(ws);
|
|
268
|
+
reconnectDelay = RECONNECT_BASE_MS;
|
|
269
|
+
|
|
270
|
+
// Drain any orphaned processing files from a prior crash first
|
|
271
|
+
for (const orphan of findOrphanProcessingFiles()) {
|
|
272
|
+
if (relay.isClosed()) break;
|
|
273
|
+
const ok = await sendProcessingFile(relay, orphan);
|
|
274
|
+
if (ok) deleteProcessingFile(orphan);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
while (!relay.isClosed()) {
|
|
278
|
+
let processingFile: string | null = null;
|
|
279
|
+
try {
|
|
280
|
+
processingFile = claimPendingBatch();
|
|
281
|
+
} catch {
|
|
282
|
+
// Transient FS error — retry on next tick
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (processingFile) {
|
|
286
|
+
const ok = await sendProcessingFile(relay, processingFile);
|
|
287
|
+
if (ok) {
|
|
288
|
+
deleteProcessingFile(processingFile);
|
|
289
|
+
} else {
|
|
290
|
+
// Ack failed or connection dropped — leave file for retry
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
await new Promise((r) => setTimeout(r, FLUSH_INTERVAL_MS));
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
relay.close();
|
|
298
|
+
} catch {
|
|
299
|
+
// Connection failed — wait and retry with backoff
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (existsSync(QUEUE_DIR)) {
|
|
303
|
+
// noop; QUEUE_DIR referenced to preserve import when tree-shaking
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
await new Promise((r) => setTimeout(r, reconnectDelay));
|
|
307
|
+
reconnectDelay = Math.min(reconnectDelay * 2, RECONNECT_MAX_MS);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* One-shot: POST all pending events to the server via REST batch endpoint.
|
|
313
|
+
* Used by `failproofai sync` — same rotate-then-delete pattern, but with
|
|
314
|
+
* HTTP response status as the ack mechanism.
|
|
315
|
+
*/
|
|
316
|
+
export async function runOneShotSync(): Promise<number> {
|
|
317
|
+
const token = await refreshTokenIfNeeded();
|
|
318
|
+
const tokens = readTokens();
|
|
319
|
+
if (!token || !tokens) {
|
|
320
|
+
throw new Error("Not logged in. Run `failproofai login` first.");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
let total = 0;
|
|
324
|
+
|
|
325
|
+
async function postBatch(events: QueueEntry[]): Promise<void> {
|
|
326
|
+
const resp = await fetch(`${tokens!.server_url}/api/v1/events/batch`, {
|
|
327
|
+
method: "POST",
|
|
328
|
+
headers: {
|
|
329
|
+
"Content-Type": "application/json",
|
|
330
|
+
Authorization: `Bearer ${token}`,
|
|
331
|
+
},
|
|
332
|
+
body: JSON.stringify({ events }),
|
|
333
|
+
signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),
|
|
334
|
+
});
|
|
335
|
+
if (!resp.ok) {
|
|
336
|
+
throw new Error(`Sync failed: ${resp.status} ${resp.statusText}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Drain orphans first
|
|
341
|
+
for (const orphan of findOrphanProcessingFiles()) {
|
|
342
|
+
const events = readProcessingFile(orphan);
|
|
343
|
+
if (events.length > 0) {
|
|
344
|
+
await postBatch(events);
|
|
345
|
+
total += events.length;
|
|
346
|
+
}
|
|
347
|
+
deleteProcessingFile(orphan);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Drain fresh pending batch
|
|
351
|
+
const processingFile = claimPendingBatch();
|
|
352
|
+
if (processingFile) {
|
|
353
|
+
const events = readProcessingFile(processingFile);
|
|
354
|
+
if (events.length > 0) {
|
|
355
|
+
await postBatch(events);
|
|
356
|
+
total += events.length;
|
|
357
|
+
}
|
|
358
|
+
deleteProcessingFile(processingFile);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return total;
|
|
362
|
+
}
|
package/src/relay/pid.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, unlinkSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
|
|
5
|
+
const PID_FILE = join(homedir(), ".failproofai", "relay.pid");
|
|
6
|
+
|
|
7
|
+
export function readPid(): number | null {
|
|
8
|
+
if (!existsSync(PID_FILE)) return null;
|
|
9
|
+
try {
|
|
10
|
+
const raw = readFileSync(PID_FILE, "utf8").trim();
|
|
11
|
+
const pid = parseInt(raw, 10);
|
|
12
|
+
if (Number.isNaN(pid) || pid <= 0) return null;
|
|
13
|
+
return pid;
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function writePid(pid: number): void {
|
|
20
|
+
const dir = dirname(PID_FILE);
|
|
21
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
22
|
+
writeFileSync(PID_FILE, String(pid));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function clearPid(): void {
|
|
26
|
+
if (existsSync(PID_FILE)) unlinkSync(PID_FILE);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ErrnoError extends Error {
|
|
30
|
+
code?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* `process.kill(pid, 0)` sends signal 0 as an existence probe.
|
|
35
|
+
*
|
|
36
|
+
* no throw → PID exists and we can signal it
|
|
37
|
+
* ESRCH → PID doesn't exist (process is gone)
|
|
38
|
+
* EPERM → PID exists but belongs to a different user — still ALIVE,
|
|
39
|
+
* just unsignalable by us. Treating this as "dead" would cause
|
|
40
|
+
* us to clear the PID file and spawn a second daemon while
|
|
41
|
+
* the first keeps running.
|
|
42
|
+
*/
|
|
43
|
+
export function isProcessAlive(pid: number): boolean {
|
|
44
|
+
try {
|
|
45
|
+
process.kill(pid, 0);
|
|
46
|
+
return true;
|
|
47
|
+
} catch (err) {
|
|
48
|
+
const e = err as ErrnoError;
|
|
49
|
+
// EPERM means the process exists but we can't signal it — still alive
|
|
50
|
+
if (e?.code === "EPERM") return true;
|
|
51
|
+
// ESRCH or anything else → treat as dead
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function stopRelay(): boolean {
|
|
57
|
+
const pid = readPid();
|
|
58
|
+
if (pid === null) return false;
|
|
59
|
+
if (!isProcessAlive(pid)) {
|
|
60
|
+
clearPid();
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
process.kill(pid, "SIGTERM");
|
|
65
|
+
clearPid();
|
|
66
|
+
return true;
|
|
67
|
+
} catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function relayStatus(): { running: boolean; pid: number | null } {
|
|
73
|
+
const pid = readPid();
|
|
74
|
+
if (pid === null) return { running: false, pid: null };
|
|
75
|
+
return { running: isProcessAlive(pid), pid };
|
|
76
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import {
|
|
2
|
+
appendFileSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
statSync,
|
|
7
|
+
renameSync,
|
|
8
|
+
unlinkSync,
|
|
9
|
+
readdirSync,
|
|
10
|
+
chmodSync,
|
|
11
|
+
} from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
15
|
+
import { isLoggedIn } from "../auth/token-store";
|
|
16
|
+
|
|
17
|
+
const QUEUE_DIR = join(homedir(), ".failproofai", "cache", "server-queue");
|
|
18
|
+
const PENDING_FILE = join(QUEUE_DIR, "pending.jsonl");
|
|
19
|
+
const PROCESSING_PREFIX = "processing-";
|
|
20
|
+
|
|
21
|
+
// Cap — if the queue file exceeds this, `appendToServerQueue` is a no-op.
|
|
22
|
+
// Prevents unbounded growth when the daemon is down for a long time or the
|
|
23
|
+
// user installed the CLI but never logged in.
|
|
24
|
+
const MAX_QUEUE_BYTES = 50 * 1024 * 1024; // 50 MB
|
|
25
|
+
|
|
26
|
+
export interface RawEntry {
|
|
27
|
+
timestamp: number;
|
|
28
|
+
eventType: string;
|
|
29
|
+
toolName?: string | null;
|
|
30
|
+
policyName?: string | null;
|
|
31
|
+
policyNames?: string[];
|
|
32
|
+
decision: string;
|
|
33
|
+
reason?: string | null;
|
|
34
|
+
durationMs: number;
|
|
35
|
+
sessionId?: string | null;
|
|
36
|
+
transcriptPath?: string | null;
|
|
37
|
+
cwd?: string | null;
|
|
38
|
+
permissionMode?: string | null;
|
|
39
|
+
hookEventName?: string | null;
|
|
40
|
+
toolInput?: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* What actually gets persisted and sent to the server. Intentionally a
|
|
45
|
+
* narrower shape than RawEntry — we drop / hash anything that could leak
|
|
46
|
+
* secrets or paths:
|
|
47
|
+
* - toolInput: dropped entirely (can contain credentials, file contents, commands)
|
|
48
|
+
* - cwd: replaced with cwd_hash (SHA-256) so the server can group by project
|
|
49
|
+
* - transcriptPath: dropped (local-only filesystem path)
|
|
50
|
+
* - reason: passed through a redactor for common credential patterns
|
|
51
|
+
*/
|
|
52
|
+
export interface QueueEntry {
|
|
53
|
+
client_event_id: string;
|
|
54
|
+
timestamp: number;
|
|
55
|
+
event_type: string;
|
|
56
|
+
tool_name: string | null;
|
|
57
|
+
policy_name: string | null;
|
|
58
|
+
policy_names: string[];
|
|
59
|
+
decision: string;
|
|
60
|
+
reason: string | null;
|
|
61
|
+
duration_ms: number;
|
|
62
|
+
session_id: string | null;
|
|
63
|
+
cwd_hash: string | null;
|
|
64
|
+
permission_mode: string | null;
|
|
65
|
+
hook_event_name: string | null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function hashCwd(cwd: string | null | undefined): string | null {
|
|
69
|
+
if (!cwd) return null;
|
|
70
|
+
return createHash("sha256").update(cwd).digest("hex");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function redactReason(reason: string | null | undefined): string | null {
|
|
74
|
+
if (!reason) return reason ?? null;
|
|
75
|
+
return reason
|
|
76
|
+
.replace(/AKIA[0-9A-Z]{16}/g, "[REDACTED-AWS-KEY]")
|
|
77
|
+
.replace(/eyJ[A-Za-z0-9_=-]+\.[A-Za-z0-9_=-]+\.[A-Za-z0-9_=-]+/g, "[REDACTED-JWT]")
|
|
78
|
+
.replace(/ghp_[A-Za-z0-9]{36,}/g, "[REDACTED-GH-TOKEN]")
|
|
79
|
+
.replace(/sk-[A-Za-z0-9]{20,}/g, "[REDACTED-API-KEY]")
|
|
80
|
+
.replace(/Bearer\s+[A-Za-z0-9_.=+-]+/gi, "Bearer [REDACTED]");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function sanitize(entry: RawEntry): QueueEntry {
|
|
84
|
+
return {
|
|
85
|
+
client_event_id: randomUUID(),
|
|
86
|
+
timestamp: entry.timestamp,
|
|
87
|
+
event_type: entry.eventType,
|
|
88
|
+
tool_name: entry.toolName ?? null,
|
|
89
|
+
policy_name: entry.policyName ?? null,
|
|
90
|
+
policy_names: entry.policyNames ?? [],
|
|
91
|
+
decision: entry.decision,
|
|
92
|
+
reason: redactReason(entry.reason),
|
|
93
|
+
duration_ms: entry.durationMs,
|
|
94
|
+
session_id: entry.sessionId ?? null,
|
|
95
|
+
cwd_hash: hashCwd(entry.cwd),
|
|
96
|
+
permission_mode: entry.permissionMode ?? null,
|
|
97
|
+
hook_event_name: entry.hookEventName ?? null,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function ensureDir(): void {
|
|
102
|
+
if (!existsSync(QUEUE_DIR)) {
|
|
103
|
+
mkdirSync(QUEUE_DIR, { recursive: true, mode: 0o700 });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Hook-side API — append one event to the pending queue.
|
|
109
|
+
*
|
|
110
|
+
* Uses `appendFileSync` (O_APPEND) which is atomic for small writes, so
|
|
111
|
+
* concurrent hook processes interleave lines correctly without clobbering
|
|
112
|
+
* each other. Sanitizes sensitive fields before persisting.
|
|
113
|
+
*
|
|
114
|
+
* No-op cases (keeps hook path fast and safe):
|
|
115
|
+
* - User not logged in (no auth.json exists)
|
|
116
|
+
* - Queue file already exceeds MAX_QUEUE_BYTES (prevents unbounded growth)
|
|
117
|
+
*/
|
|
118
|
+
export function appendToServerQueue(entry: RawEntry): void {
|
|
119
|
+
if (!isLoggedIn()) return;
|
|
120
|
+
ensureDir();
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
if (existsSync(PENDING_FILE) && statSync(PENDING_FILE).size > MAX_QUEUE_BYTES) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
// existsSync/statSync races are fine; proceed
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const sanitized = sanitize(entry);
|
|
131
|
+
appendFileSync(PENDING_FILE, JSON.stringify(sanitized) + "\n", { mode: 0o600 });
|
|
132
|
+
|
|
133
|
+
// Tighten perms on first create in case the umask allowed wider access
|
|
134
|
+
try {
|
|
135
|
+
chmodSync(PENDING_FILE, 0o600);
|
|
136
|
+
} catch {
|
|
137
|
+
// Windows or non-critical; skip
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function queueSizeBytes(): number {
|
|
142
|
+
try {
|
|
143
|
+
return statSync(PENDING_FILE).size;
|
|
144
|
+
} catch {
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
interface ClaimError extends Error {
|
|
150
|
+
code?: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Daemon-side API — atomically claim all pending events into a new
|
|
155
|
+
* processing file. Returns the processing file path, or null ONLY when
|
|
156
|
+
* there's nothing to claim (ENOENT). Other errors throw so we don't
|
|
157
|
+
* silently strand events.
|
|
158
|
+
*/
|
|
159
|
+
export function claimPendingBatch(): string | null {
|
|
160
|
+
if (!existsSync(PENDING_FILE)) return null;
|
|
161
|
+
try {
|
|
162
|
+
const size = statSync(PENDING_FILE).size;
|
|
163
|
+
if (size === 0) return null;
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const seq = `${Date.now()}-${process.pid}`;
|
|
169
|
+
const processingFile = join(QUEUE_DIR, `${PROCESSING_PREFIX}${seq}.jsonl`);
|
|
170
|
+
try {
|
|
171
|
+
renameSync(PENDING_FILE, processingFile);
|
|
172
|
+
try {
|
|
173
|
+
chmodSync(processingFile, 0o600);
|
|
174
|
+
} catch {
|
|
175
|
+
// non-critical
|
|
176
|
+
}
|
|
177
|
+
return processingFile;
|
|
178
|
+
} catch (err) {
|
|
179
|
+
const e = err as ClaimError;
|
|
180
|
+
if (e?.code === "ENOENT") return null;
|
|
181
|
+
// Real failure (EACCES, EIO, etc.) — surface to caller; don't lose events
|
|
182
|
+
throw err;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function findOrphanProcessingFiles(): string[] {
|
|
187
|
+
ensureDir();
|
|
188
|
+
try {
|
|
189
|
+
return readdirSync(QUEUE_DIR)
|
|
190
|
+
.filter((n) => n.startsWith(PROCESSING_PREFIX) && n.endsWith(".jsonl"))
|
|
191
|
+
.map((n) => join(QUEUE_DIR, n))
|
|
192
|
+
.sort();
|
|
193
|
+
} catch {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Parse a processing file into structured events, skipping (and logging
|
|
200
|
+
* via stderr) any malformed JSON lines so one bad entry doesn't wedge
|
|
201
|
+
* the entire file forever.
|
|
202
|
+
*/
|
|
203
|
+
export function readProcessingFile(path: string): QueueEntry[] {
|
|
204
|
+
if (!existsSync(path)) return [];
|
|
205
|
+
const content = readFileSync(path, "utf8");
|
|
206
|
+
const out: QueueEntry[] = [];
|
|
207
|
+
for (const line of content.split("\n")) {
|
|
208
|
+
const trimmed = line.trim();
|
|
209
|
+
if (!trimmed) continue;
|
|
210
|
+
try {
|
|
211
|
+
out.push(JSON.parse(trimmed) as QueueEntry);
|
|
212
|
+
} catch {
|
|
213
|
+
// Skip malformed line — we can't recover it
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return out;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function deleteProcessingFile(path: string): void {
|
|
220
|
+
try {
|
|
221
|
+
unlinkSync(path);
|
|
222
|
+
} catch {
|
|
223
|
+
// best-effort; stale processing files are cleaned up on next run
|
|
224
|
+
}
|
|
225
|
+
}
|