failproofai 1.0.5 → 1.0.6-beta.1
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 +5 -5
- package/.next/standalone/.next/prerender-manifest.json +5 -5
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +2 -2
- 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 +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +2 -2
- 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 +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -4
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/build-manifest.json +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/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +6 -5
- 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/build-manifest.json +2 -2
- 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 +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/build-manifest.json +2 -2
- 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 +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/build-manifest.json +2 -2
- 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/app/settings/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
- package/.next/standalone/.next/server/app/settings/page.js +3 -2
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[externals]__0sds_16._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zlqo2t._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__112b4go._.js → [root-of-the-server]__1_r2rbg._.js} +4 -4
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1anxb5k._.js +1 -1
- package/.next/standalone/.next/server/chunks/_09dz7xv._.js +3 -3
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -2
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -2
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-_ki57._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0576iuy._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0bym1os._.js → [root-of-the-server]__0cg-bgc._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__00y9ww2._.js → [root-of-the-server]__0u3w0ll._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0sz_5na._.js → [root-of-the-server]__1ouobn4._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1py795m._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qapotl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__15k-dqb._.js → [root-of-the-server]__1x3j9mj._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_06imw3p._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0guaevg._.js → _0qlygw6._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0wso5d3._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1-3unkg._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1b78kcp._.js → _1_ddpm3._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_14c6q09._.js → _1_qswah._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1ddpc5g._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +3 -3
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +12 -11
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_components_project-list_tsx_1ke6anr._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_1hl81y4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -2
- package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
- package/.next/standalone/.next/server/pages/404.html +1 -1
- 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 +19 -19
- package/.next/standalone/.next/static/chunks/{3w46euc60dx39.js → 02vwr5js1x28j.js} +1 -1
- package/.next/standalone/.next/static/chunks/06fjx8-0_qufs.js +1 -0
- package/.next/standalone/.next/static/chunks/{2zafgs90r_leq.js → 06jxjnw8bfczc.js} +1 -1
- package/.next/standalone/.next/static/chunks/0cbvytf8epn34.js +1 -0
- package/.next/standalone/.next/static/chunks/0y227_ohdqwvn.js +1 -0
- package/.next/standalone/.next/static/chunks/{252xreaecy80h.js → 1p4xauf-zu6zk.js} +1 -1
- package/.next/standalone/.next/static/chunks/{021ydrq6rhpsi.js → 1qf2uiipatr80.js} +1 -1
- package/.next/standalone/.next/static/chunks/1uc_s_dftgxiq.js +6 -0
- package/.next/standalone/.next/static/chunks/1xacnwjkbekn7.js +1 -0
- package/.next/standalone/.next/static/chunks/{11s_v03ub6nss.js → 27_bwbg8sv6vj.js} +1 -1
- package/.next/standalone/.next/static/chunks/2hg_bp8z_vm2g.js +1 -0
- package/.next/standalone/.next/static/chunks/{3bleyy40s99f2.js → 2x776s0x3n_tu.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1vb33xa6j9y2q.js → 3ht0xsco46pul.js} +1 -1
- package/.next/standalone/.next/static/chunks/{turbopack-3p73obe0xmrxm.js → turbopack-38nzuvp95_2rv.js} +1 -1
- package/.next/standalone/fp-cloud-cli/uv.lock +3 -3
- package/.next/standalone/hermes-plugin/README.md +138 -0
- package/.next/standalone/hermes-plugin/__init__.py +248 -0
- package/.next/standalone/hermes-plugin/client.py +150 -0
- package/.next/standalone/hermes-plugin/ledger.py +187 -0
- package/.next/standalone/hermes-plugin/plugin.yaml +37 -0
- package/.next/standalone/lib/hermes-profiles.ts +67 -19
- package/.next/standalone/lib/hermes-sessions.ts +3 -2
- package/.next/standalone/node_modules/@next/env/package.json +1 -1
- package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo-experimental.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
- package/.next/standalone/node_modules/next/dist/server/app-render/create-component-styles-and-scripts.js +2 -1
- package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/image-optimizer.js +14 -0
- package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/disk-lru-cache.external.js +7 -1
- package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/use-cache/use-cache-wrapper.js +6 -1
- package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
- package/.next/standalone/node_modules/next/package.json +10 -10
- package/.next/standalone/node_modules/react/cjs/react.development.js +92 -46
- package/.next/standalone/node_modules/react/cjs/react.production.js +112 -89
- package/.next/standalone/node_modules/react/package.json +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1165 -790
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1171 -791
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +1294 -862
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +1305 -868
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +1315 -896
- package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +24 -5
- package/.next/standalone/node_modules/react-dom/package.json +4 -4
- package/.next/standalone/package.json +13 -12
- package/.next/standalone/sdk/python/uv.lock +18 -18
- package/.next/standalone/server.js +1 -1
- package/README.md +29 -13
- package/bin/failproofai.mjs +3 -1
- package/dist/cli.mjs +655 -360
- package/dist/worker.mjs +78 -34
- package/hermes-plugin/README.md +138 -0
- package/hermes-plugin/__init__.py +248 -0
- package/hermes-plugin/client.py +150 -0
- package/hermes-plugin/ledger.py +187 -0
- package/hermes-plugin/plugin.yaml +37 -0
- package/lib/hermes-profiles.ts +67 -19
- package/lib/hermes-sessions.ts +3 -2
- package/package.json +13 -12
- package/scripts/translate-docs/validate-translation.ts +48 -0
- package/src/hooks/handler.ts +29 -1
- package/src/hooks/integrations.ts +271 -73
- package/src/hooks/manager.ts +41 -7
- package/src/hooks/types.ts +8 -8
- package/src/hooks/worker-server.ts +1 -0
- package/.next/standalone/.next/server/chunks/[externals]__157pf8j._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +0 -6
- package/.next/standalone/.next/server/chunks/ssr/node_modules_13n822a._.js +0 -3
- package/.next/standalone/.next/static/chunks/0y8_1s45gbqmn.js +0 -6
- package/.next/standalone/.next/static/chunks/14b3ezufe5411.js +0 -1
- package/.next/standalone/.next/static/chunks/1oskk7pdo8aon.js +0 -1
- package/.next/standalone/.next/static/chunks/2xycd6mjg00fb.js +0 -1
- package/.next/standalone/.next/static/chunks/3l36d73kj18zw.js +0 -1
- package/.next/standalone/.next/static/chunks/40lpt2o2b283e.js +0 -1
- /package/.next/standalone/.next/static/{uFpAs_vIXSo6qBzmkXPWD → QCNYz2yCxxdeggg1k0iEq}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{uFpAs_vIXSo6qBzmkXPWD → QCNYz2yCxxdeggg1k0iEq}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{uFpAs_vIXSo6qBzmkXPWD → QCNYz2yCxxdeggg1k0iEq}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""Versioned Unix-socket client for failproofaid policy evaluation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import socket
|
|
8
|
+
import struct
|
|
9
|
+
import time
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, Mapping, Sequence
|
|
13
|
+
|
|
14
|
+
PROTOCOL_VERSION = 1
|
|
15
|
+
MAX_FRAME_BYTES = 16 * 1024 * 1024
|
|
16
|
+
MAX_REQUEST_BYTES = 1024 * 1024
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class EvaluationError(RuntimeError):
|
|
20
|
+
"""The daemon could not return a trustworthy policy verdict."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class PolicyVerdict:
|
|
25
|
+
decision: str
|
|
26
|
+
policy_names: tuple[str, ...]
|
|
27
|
+
reason: str | None
|
|
28
|
+
matched_policies: tuple[str, ...]
|
|
29
|
+
duration_ms: int
|
|
30
|
+
tool_name: str | None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def daemon_socket_path() -> Path:
|
|
34
|
+
override = os.environ.get("FAILPROOFAI_DAEMON_SOCKET", "").strip()
|
|
35
|
+
if override:
|
|
36
|
+
return Path(override).expanduser()
|
|
37
|
+
root = os.environ.get("FAILPROOFAI_HOME", "").strip()
|
|
38
|
+
home = Path(root).expanduser() if root else Path.home() / ".failproofai"
|
|
39
|
+
return home / "run" / "failproofaid.sock"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _remaining_timeout(deadline: float) -> float:
|
|
43
|
+
remaining = deadline - time.monotonic()
|
|
44
|
+
if remaining <= 0:
|
|
45
|
+
raise EvaluationError("failproofaid evaluation timed out")
|
|
46
|
+
return remaining
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _read_exact(sock: socket.socket, length: int, deadline: float) -> bytes:
|
|
50
|
+
chunks: list[bytes] = []
|
|
51
|
+
remaining = length
|
|
52
|
+
while remaining:
|
|
53
|
+
sock.settimeout(_remaining_timeout(deadline))
|
|
54
|
+
try:
|
|
55
|
+
chunk = sock.recv(remaining)
|
|
56
|
+
except TimeoutError as exc:
|
|
57
|
+
raise EvaluationError("failproofaid evaluation timed out") from exc
|
|
58
|
+
if not chunk:
|
|
59
|
+
raise EvaluationError("failproofaid closed the connection before returning a verdict")
|
|
60
|
+
chunks.append(chunk)
|
|
61
|
+
remaining -= len(chunk)
|
|
62
|
+
return b"".join(chunks)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _string_list(value: object, field: str) -> tuple[str, ...]:
|
|
66
|
+
if not isinstance(value, Sequence) or isinstance(value, (str, bytes, bytearray)):
|
|
67
|
+
raise EvaluationError(f"failproofaid returned an invalid {field}")
|
|
68
|
+
if not all(isinstance(item, str) for item in value):
|
|
69
|
+
raise EvaluationError(f"failproofaid returned an invalid {field}")
|
|
70
|
+
return tuple(value)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def evaluate_policy(
|
|
74
|
+
*,
|
|
75
|
+
event: str,
|
|
76
|
+
payload: Mapping[str, Any],
|
|
77
|
+
cwd: str | None,
|
|
78
|
+
connect_timeout_ms: int = 250,
|
|
79
|
+
evaluation_timeout_ms: int = 12_000,
|
|
80
|
+
) -> PolicyVerdict:
|
|
81
|
+
request = {
|
|
82
|
+
"type": "policyEvaluation",
|
|
83
|
+
"protocolVersion": PROTOCOL_VERSION,
|
|
84
|
+
"integration": "hermes",
|
|
85
|
+
"event": event,
|
|
86
|
+
"payload": dict(payload),
|
|
87
|
+
"cwd": cwd,
|
|
88
|
+
}
|
|
89
|
+
try:
|
|
90
|
+
body = json.dumps(
|
|
91
|
+
request,
|
|
92
|
+
ensure_ascii=False,
|
|
93
|
+
separators=(",", ":"),
|
|
94
|
+
default=lambda value: repr(value),
|
|
95
|
+
).encode("utf-8")
|
|
96
|
+
except (TypeError, ValueError) as exc:
|
|
97
|
+
raise EvaluationError(f"could not encode policy request: {exc}") from exc
|
|
98
|
+
if len(body) > MAX_REQUEST_BYTES:
|
|
99
|
+
raise EvaluationError("policy request exceeds the 1 MiB limit")
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
|
|
103
|
+
sock.settimeout(max(connect_timeout_ms, 1) / 1000)
|
|
104
|
+
sock.connect(str(daemon_socket_path()))
|
|
105
|
+
deadline = time.monotonic() + max(evaluation_timeout_ms, 1) / 1000
|
|
106
|
+
sock.settimeout(_remaining_timeout(deadline))
|
|
107
|
+
try:
|
|
108
|
+
sock.sendall(struct.pack(">I", len(body)) + body)
|
|
109
|
+
except TimeoutError as exc:
|
|
110
|
+
raise EvaluationError("failproofaid evaluation timed out") from exc
|
|
111
|
+
declared_length = struct.unpack(">I", _read_exact(sock, 4, deadline))[0]
|
|
112
|
+
if declared_length > MAX_FRAME_BYTES:
|
|
113
|
+
raise EvaluationError("failproofaid response exceeds the 16 MiB limit")
|
|
114
|
+
response = json.loads(_read_exact(sock, declared_length, deadline).decode("utf-8"))
|
|
115
|
+
except EvaluationError:
|
|
116
|
+
raise
|
|
117
|
+
except (OSError, UnicodeError, ValueError, struct.error) as exc:
|
|
118
|
+
raise EvaluationError(f"failproofaid evaluation failed: {exc}") from exc
|
|
119
|
+
|
|
120
|
+
if not isinstance(response, dict):
|
|
121
|
+
raise EvaluationError("failproofaid returned a non-object response")
|
|
122
|
+
if response.get("protocolVersion") != PROTOCOL_VERSION:
|
|
123
|
+
raise EvaluationError("failproofaid protocol version mismatch")
|
|
124
|
+
if response.get("type") == "error":
|
|
125
|
+
message = response.get("message")
|
|
126
|
+
raise EvaluationError(str(message or "failproofaid could not evaluate the policy"))
|
|
127
|
+
if response.get("type") != "policyResult":
|
|
128
|
+
raise EvaluationError("failproofaid does not support native policy evaluation")
|
|
129
|
+
|
|
130
|
+
decision = response.get("decision")
|
|
131
|
+
if decision not in {"allow", "deny", "instruct"}:
|
|
132
|
+
raise EvaluationError("failproofaid returned an invalid policy decision")
|
|
133
|
+
reason = response.get("reason")
|
|
134
|
+
if reason is not None and not isinstance(reason, str):
|
|
135
|
+
raise EvaluationError("failproofaid returned an invalid policy reason")
|
|
136
|
+
duration_ms = response.get("durationMs", 0)
|
|
137
|
+
if not isinstance(duration_ms, int) or duration_ms < 0:
|
|
138
|
+
raise EvaluationError("failproofaid returned an invalid evaluation duration")
|
|
139
|
+
tool_name = response.get("toolName")
|
|
140
|
+
if tool_name is not None and not isinstance(tool_name, str):
|
|
141
|
+
raise EvaluationError("failproofaid returned an invalid tool name")
|
|
142
|
+
|
|
143
|
+
return PolicyVerdict(
|
|
144
|
+
decision=decision,
|
|
145
|
+
policy_names=_string_list(response.get("policyNames", []), "policyNames"),
|
|
146
|
+
reason=reason,
|
|
147
|
+
matched_policies=_string_list(response.get("matchedPolicies", []), "matchedPolicies"),
|
|
148
|
+
duration_ms=duration_ms,
|
|
149
|
+
tool_name=tool_name,
|
|
150
|
+
)
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""Persistent, bounded delivery state for FailproofAI instruct decisions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import sqlite3
|
|
8
|
+
import threading
|
|
9
|
+
import time
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Sequence
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LedgerError(RuntimeError):
|
|
16
|
+
"""Instruction state could not be read or updated safely."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class InstructionAction:
|
|
21
|
+
block: bool
|
|
22
|
+
status: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class InstructionLedger:
|
|
26
|
+
def __init__(self, path: Path, *, ttl_seconds: int = 3600, max_rounds: int = 2) -> None:
|
|
27
|
+
self.path = path
|
|
28
|
+
self.ttl_seconds = max(60, int(ttl_seconds))
|
|
29
|
+
self.max_rounds = max(0, int(max_rounds))
|
|
30
|
+
self._init_lock = threading.Lock()
|
|
31
|
+
self._initialized = False
|
|
32
|
+
|
|
33
|
+
def _connect(self) -> sqlite3.Connection:
|
|
34
|
+
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
35
|
+
connection = sqlite3.connect(self.path, timeout=0.25, isolation_level=None)
|
|
36
|
+
connection.execute("PRAGMA busy_timeout = 250")
|
|
37
|
+
connection.execute("PRAGMA journal_mode = WAL")
|
|
38
|
+
connection.execute("PRAGMA synchronous = NORMAL")
|
|
39
|
+
return connection
|
|
40
|
+
|
|
41
|
+
def _ensure_schema(self) -> None:
|
|
42
|
+
if self._initialized:
|
|
43
|
+
return
|
|
44
|
+
with self._init_lock:
|
|
45
|
+
if self._initialized:
|
|
46
|
+
return
|
|
47
|
+
try:
|
|
48
|
+
with self._connect() as connection:
|
|
49
|
+
connection.execute(
|
|
50
|
+
"""
|
|
51
|
+
CREATE TABLE IF NOT EXISTS instruction_deliveries (
|
|
52
|
+
delivery_key TEXT PRIMARY KEY,
|
|
53
|
+
profile TEXT NOT NULL,
|
|
54
|
+
session_id TEXT NOT NULL,
|
|
55
|
+
task_id TEXT NOT NULL,
|
|
56
|
+
turn_id TEXT NOT NULL,
|
|
57
|
+
policy_fingerprint TEXT NOT NULL,
|
|
58
|
+
scope_key TEXT NOT NULL,
|
|
59
|
+
blocked_api_request_id TEXT NOT NULL,
|
|
60
|
+
acknowledged_api_request_id TEXT,
|
|
61
|
+
state TEXT NOT NULL,
|
|
62
|
+
created_at_ms INTEGER NOT NULL,
|
|
63
|
+
expires_at_ms INTEGER NOT NULL
|
|
64
|
+
)
|
|
65
|
+
"""
|
|
66
|
+
)
|
|
67
|
+
connection.execute(
|
|
68
|
+
"""
|
|
69
|
+
CREATE INDEX IF NOT EXISTS instruction_deliveries_turn
|
|
70
|
+
ON instruction_deliveries(profile, session_id, task_id, turn_id, expires_at_ms)
|
|
71
|
+
"""
|
|
72
|
+
)
|
|
73
|
+
except sqlite3.Error as exc:
|
|
74
|
+
raise LedgerError(f"could not initialize instruction state: {exc}") from exc
|
|
75
|
+
self._initialized = True
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def _digest(parts: Sequence[str]) -> str:
|
|
79
|
+
encoded = json.dumps(list(parts), ensure_ascii=False, separators=(",", ":")).encode("utf-8")
|
|
80
|
+
return hashlib.sha256(encoded).hexdigest()
|
|
81
|
+
|
|
82
|
+
def decide(
|
|
83
|
+
self,
|
|
84
|
+
*,
|
|
85
|
+
profile: str,
|
|
86
|
+
session_id: str,
|
|
87
|
+
task_id: str,
|
|
88
|
+
turn_id: str,
|
|
89
|
+
api_request_id: str,
|
|
90
|
+
policy_names: Sequence[str],
|
|
91
|
+
reason: str,
|
|
92
|
+
scope_key: str,
|
|
93
|
+
now_ms: int | None = None,
|
|
94
|
+
) -> InstructionAction:
|
|
95
|
+
if not session_id or not turn_id or not api_request_id:
|
|
96
|
+
return InstructionAction(False, "missing-correlation-id")
|
|
97
|
+
|
|
98
|
+
self._ensure_schema()
|
|
99
|
+
now = int(time.time() * 1000) if now_ms is None else int(now_ms)
|
|
100
|
+
expires_at = now + self.ttl_seconds * 1000
|
|
101
|
+
task = task_id or "root"
|
|
102
|
+
policy_fingerprint = self._digest([*sorted(policy_names), reason])
|
|
103
|
+
delivery_key = self._digest(
|
|
104
|
+
[profile, session_id, task, turn_id, policy_fingerprint, scope_key]
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
with self._connect() as connection:
|
|
109
|
+
connection.execute("BEGIN IMMEDIATE")
|
|
110
|
+
connection.execute(
|
|
111
|
+
"DELETE FROM instruction_deliveries WHERE expires_at_ms <= ?", (now,)
|
|
112
|
+
)
|
|
113
|
+
row = connection.execute(
|
|
114
|
+
"""
|
|
115
|
+
SELECT blocked_api_request_id, state
|
|
116
|
+
FROM instruction_deliveries
|
|
117
|
+
WHERE delivery_key = ?
|
|
118
|
+
""",
|
|
119
|
+
(delivery_key,),
|
|
120
|
+
).fetchone()
|
|
121
|
+
if row is not None:
|
|
122
|
+
blocked_request, state = row
|
|
123
|
+
if state == "issued" and blocked_request == api_request_id:
|
|
124
|
+
connection.commit()
|
|
125
|
+
return InstructionAction(True, "same-api-request")
|
|
126
|
+
if state == "issued":
|
|
127
|
+
connection.execute(
|
|
128
|
+
"""
|
|
129
|
+
UPDATE instruction_deliveries
|
|
130
|
+
SET state = 'acknowledged', acknowledged_api_request_id = ?
|
|
131
|
+
WHERE delivery_key = ?
|
|
132
|
+
""",
|
|
133
|
+
(api_request_id, delivery_key),
|
|
134
|
+
)
|
|
135
|
+
connection.commit()
|
|
136
|
+
return InstructionAction(False, "acknowledged")
|
|
137
|
+
|
|
138
|
+
rounds = connection.execute(
|
|
139
|
+
"""
|
|
140
|
+
SELECT COUNT(*)
|
|
141
|
+
FROM instruction_deliveries
|
|
142
|
+
WHERE profile = ? AND session_id = ? AND task_id = ? AND turn_id = ?
|
|
143
|
+
AND expires_at_ms > ?
|
|
144
|
+
""",
|
|
145
|
+
(profile, session_id, task, turn_id, now),
|
|
146
|
+
).fetchone()[0]
|
|
147
|
+
if rounds >= self.max_rounds:
|
|
148
|
+
connection.commit()
|
|
149
|
+
return InstructionAction(False, "turn-cap")
|
|
150
|
+
|
|
151
|
+
connection.execute(
|
|
152
|
+
"""
|
|
153
|
+
INSERT INTO instruction_deliveries (
|
|
154
|
+
delivery_key, profile, session_id, task_id, turn_id,
|
|
155
|
+
policy_fingerprint, scope_key, blocked_api_request_id,
|
|
156
|
+
acknowledged_api_request_id, state, created_at_ms, expires_at_ms
|
|
157
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL, 'issued', ?, ?)
|
|
158
|
+
""",
|
|
159
|
+
(
|
|
160
|
+
delivery_key,
|
|
161
|
+
profile,
|
|
162
|
+
session_id,
|
|
163
|
+
task,
|
|
164
|
+
turn_id,
|
|
165
|
+
policy_fingerprint,
|
|
166
|
+
scope_key,
|
|
167
|
+
api_request_id,
|
|
168
|
+
now,
|
|
169
|
+
expires_at,
|
|
170
|
+
),
|
|
171
|
+
)
|
|
172
|
+
connection.commit()
|
|
173
|
+
return InstructionAction(True, "issued")
|
|
174
|
+
except (sqlite3.Error, OSError) as exc:
|
|
175
|
+
raise LedgerError(f"could not update instruction state: {exc}") from exc
|
|
176
|
+
|
|
177
|
+
def clear_session(self, session_id: str) -> None:
|
|
178
|
+
if not session_id:
|
|
179
|
+
return
|
|
180
|
+
self._ensure_schema()
|
|
181
|
+
try:
|
|
182
|
+
with self._connect() as connection:
|
|
183
|
+
connection.execute(
|
|
184
|
+
"DELETE FROM instruction_deliveries WHERE session_id = ?", (session_id,)
|
|
185
|
+
)
|
|
186
|
+
except (sqlite3.Error, OSError) as exc:
|
|
187
|
+
raise LedgerError(f"could not clear instruction state: {exc}") from exc
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: failproofai
|
|
2
|
+
version: 1.0.6-beta.0
|
|
3
|
+
description: Real-time FailproofAI policy enforcement for Hermes
|
|
4
|
+
manifest_version: 2
|
|
5
|
+
api_version: 1
|
|
6
|
+
homepage: https://github.com/FailproofAI/failproofai
|
|
7
|
+
tags: [security, policy, observability]
|
|
8
|
+
provides_hooks:
|
|
9
|
+
- pre_tool_call
|
|
10
|
+
- post_tool_call
|
|
11
|
+
- pre_llm_call
|
|
12
|
+
- on_session_start
|
|
13
|
+
- on_session_end
|
|
14
|
+
- on_session_reset
|
|
15
|
+
- on_session_finalize
|
|
16
|
+
- subagent_stop
|
|
17
|
+
config_schema:
|
|
18
|
+
failure_mode:
|
|
19
|
+
type: str
|
|
20
|
+
default: deny
|
|
21
|
+
description: Block or allow when the local FailproofAI evaluator is unavailable.
|
|
22
|
+
connect_timeout_ms:
|
|
23
|
+
type: int
|
|
24
|
+
default: 250
|
|
25
|
+
description: Maximum time to connect to the local FailproofAI daemon.
|
|
26
|
+
evaluation_timeout_ms:
|
|
27
|
+
type: int
|
|
28
|
+
default: 12000
|
|
29
|
+
description: Maximum total time allowed for one policy evaluation.
|
|
30
|
+
instruction_ttl_seconds:
|
|
31
|
+
type: int
|
|
32
|
+
default: 3600
|
|
33
|
+
description: Maximum lifetime of an instruction-delivery record.
|
|
34
|
+
max_instruction_rounds:
|
|
35
|
+
type: int
|
|
36
|
+
default: 2
|
|
37
|
+
description: Maximum distinct instruction interruptions in one Hermes turn.
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A Hermes "profile" is not a column or a flag: it is a whole separate Hermes
|
|
5
5
|
* home directory, each with its own `config.yaml`, `.env`, `SOUL.md`, and
|
|
6
|
-
* `state.db`. The default profile lives at `~/.hermes`;
|
|
7
|
-
* `~/.hermes/profiles/<name
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* `state.db`. The default profile lives at `~/.hermes`; upstream named profiles
|
|
7
|
+
* live at `~/.hermes/profiles/<name>/`, while older/custom multi-install setups
|
|
8
|
+
* may use sibling homes such as `~/.hermes-work`. Selection is `hermes -p
|
|
9
|
+
* <name>`, a generated `~/.local/bin/<name>` alias that exports `HERMES_HOME`,
|
|
10
|
+
* or a sticky default recorded in `<root>/active_profile`.
|
|
10
11
|
*
|
|
11
12
|
* Upstream's own contributor guide warns that hardcoding `~/.hermes` breaks
|
|
12
13
|
* profiles — which is exactly what both pillars used to do:
|
|
@@ -21,12 +22,14 @@
|
|
|
21
22
|
* Home override: set `HERMES_HOME` (Hermes's own env var — respected here so a
|
|
22
23
|
* profile-scoped shell and failproofai agree on what "all profiles" means).
|
|
23
24
|
*/
|
|
24
|
-
import { readdirSync } from "node:fs";
|
|
25
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
25
26
|
import { homedir } from "node:os";
|
|
26
27
|
import { basename, dirname, join, resolve } from "node:path";
|
|
27
28
|
|
|
28
29
|
/** Directory under the Hermes root that holds non-default profiles. */
|
|
29
30
|
const PROFILES_DIR = "profiles";
|
|
31
|
+
const STANDARD_HOME = ".hermes";
|
|
32
|
+
const SIBLING_HOME_PREFIX = ".hermes-";
|
|
30
33
|
|
|
31
34
|
/** Name we give the root home (`~/.hermes`), which Hermes itself leaves unnamed. */
|
|
32
35
|
export const HERMES_DEFAULT_PROFILE = "default";
|
|
@@ -41,28 +44,45 @@ export interface HermesProfile {
|
|
|
41
44
|
/**
|
|
42
45
|
* The Hermes ROOT home — the directory that owns `profiles/`.
|
|
43
46
|
*
|
|
44
|
-
* `HERMES_HOME` may point AT
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
47
|
+
* `HERMES_HOME` may point AT an upstream profile (`<root>/profiles/<name>`) or
|
|
48
|
+
* at a sibling installation (`~/.hermes-<name>`). We normalize either standard
|
|
49
|
+
* layout back to `~/.hermes` so discovery covers every installation. A custom
|
|
50
|
+
* non-standard path remains authoritative; scanning arbitrary sibling paths
|
|
51
|
+
* would risk treating backups and unrelated directories as live agents.
|
|
48
52
|
*/
|
|
49
53
|
export function hermesRoot(): string {
|
|
50
54
|
const env = (process.env.HERMES_HOME || "").trim();
|
|
51
55
|
if (env) {
|
|
52
|
-
|
|
56
|
+
let home = resolve(env);
|
|
53
57
|
const parent = dirname(home);
|
|
54
|
-
if (basename(parent) === PROFILES_DIR)
|
|
58
|
+
if (basename(parent) === PROFILES_DIR) home = dirname(parent);
|
|
59
|
+
const base = basename(home);
|
|
60
|
+
if (base === STANDARD_HOME || base.startsWith(SIBLING_HOME_PREFIX)) {
|
|
61
|
+
return join(dirname(home), STANDARD_HOME);
|
|
62
|
+
}
|
|
55
63
|
return home;
|
|
56
64
|
}
|
|
57
|
-
return join(homedir(),
|
|
65
|
+
return join(homedir(), STANDARD_HOME);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function uniqueProfileName(preferred: string, seen: Set<string>): string {
|
|
69
|
+
if (!seen.has(preferred)) return preferred;
|
|
70
|
+
const base = `${preferred}-home`;
|
|
71
|
+
if (!seen.has(base)) return base;
|
|
72
|
+
let suffix = 2;
|
|
73
|
+
while (seen.has(`${base}-${suffix}`)) suffix += 1;
|
|
74
|
+
return `${base}-${suffix}`;
|
|
58
75
|
}
|
|
59
76
|
|
|
60
77
|
/**
|
|
61
78
|
* Every Hermes profile on disk: the root home first (as `"default"`), then each
|
|
62
|
-
* `<root>/profiles/<name
|
|
79
|
+
* `<root>/profiles/<name>/`, then valid `~/.hermes-<name>` sibling homes, in
|
|
80
|
+
* name order within each layout.
|
|
63
81
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
82
|
+
* A sibling home counts only when it contains `config.yaml`, matching OpenClaw
|
|
83
|
+
* discovery and avoiding arbitrary `.hermes-*` backup directories. Upstream
|
|
84
|
+
* `profiles/` directories are authoritative and remain discoverable before
|
|
85
|
+
* their config is first written.
|
|
66
86
|
*
|
|
67
87
|
* A profile directory literally named `default` would collide with the root's
|
|
68
88
|
* reserved name; the root wins and the directory is skipped (dedup by name keeps
|
|
@@ -73,22 +93,50 @@ export function listHermesProfiles(): HermesProfile[] {
|
|
|
73
93
|
const out: HermesProfile[] = [{ name: HERMES_DEFAULT_PROFILE, home: root }];
|
|
74
94
|
const seen = new Set<string>([HERMES_DEFAULT_PROFILE]);
|
|
75
95
|
|
|
76
|
-
let names: string[];
|
|
96
|
+
let names: string[] = [];
|
|
77
97
|
try {
|
|
78
98
|
names = readdirSync(join(root, PROFILES_DIR), { withFileTypes: true })
|
|
79
99
|
// Symlinked profile dirs are legitimate, and `isDirectory()` is false for them.
|
|
80
100
|
.filter((e) => (e.isDirectory() || e.isSymbolicLink()) && !e.name.startsWith("."))
|
|
81
101
|
.map((e) => e.name)
|
|
82
102
|
.sort();
|
|
83
|
-
} catch {
|
|
84
|
-
return out;
|
|
85
|
-
}
|
|
103
|
+
} catch {}
|
|
86
104
|
|
|
87
105
|
for (const name of names) {
|
|
88
106
|
if (seen.has(name)) continue;
|
|
89
107
|
seen.add(name);
|
|
90
108
|
out.push({ name, home: join(root, PROFILES_DIR, name) });
|
|
91
109
|
}
|
|
110
|
+
|
|
111
|
+
// Only the standard ~/.hermes layout defines `.hermes-*` sibling homes.
|
|
112
|
+
// A custom HERMES_HOME is intentionally not used as a prefix convention.
|
|
113
|
+
if (basename(root) !== STANDARD_HOME) return out;
|
|
114
|
+
|
|
115
|
+
let siblings: HermesProfile[] = [];
|
|
116
|
+
const parent = dirname(root);
|
|
117
|
+
try {
|
|
118
|
+
siblings = readdirSync(parent, { withFileTypes: true })
|
|
119
|
+
.filter(
|
|
120
|
+
(entry) =>
|
|
121
|
+
(entry.isDirectory() || entry.isSymbolicLink()) &&
|
|
122
|
+
entry.name.startsWith(SIBLING_HOME_PREFIX) &&
|
|
123
|
+
entry.name.length > SIBLING_HOME_PREFIX.length &&
|
|
124
|
+
existsSync(join(parent, entry.name, "config.yaml")),
|
|
125
|
+
)
|
|
126
|
+
.map((entry) => ({
|
|
127
|
+
name: entry.name.slice(SIBLING_HOME_PREFIX.length),
|
|
128
|
+
home: join(parent, entry.name),
|
|
129
|
+
}))
|
|
130
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
131
|
+
} catch {
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for (const sibling of siblings) {
|
|
136
|
+
const name = uniqueProfileName(sibling.name, seen);
|
|
137
|
+
seen.add(name);
|
|
138
|
+
out.push({ name, home: sibling.home });
|
|
139
|
+
}
|
|
92
140
|
return out;
|
|
93
141
|
}
|
|
94
142
|
|
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
*
|
|
15
15
|
* PROFILES: Hermes profiles are separate home dirs, each with its OWN state.db
|
|
16
16
|
* (`~/.hermes/state.db` for the default, `~/.hermes/profiles/<name>/state.db`
|
|
17
|
-
*
|
|
18
|
-
* profiles
|
|
17
|
+
* for upstream profiles, and `~/.hermes-<name>/state.db` for sibling homes) —
|
|
18
|
+
* see lib/hermes-profiles.ts. So every read here fans out across profiles
|
|
19
|
+
* instead of assuming one DB.
|
|
19
20
|
*
|
|
20
21
|
* DB path override: set `HERMES_DB_PATH` (used by tests and to point at a copied
|
|
21
22
|
* or remote state.db) — it collapses discovery to that single file. Set
|
|
@@ -139,7 +139,7 @@ var HmrTarget = /*#__PURE__*/ function(HmrTarget) {
|
|
|
139
139
|
HmrTarget["Server"] = "server";
|
|
140
140
|
return HmrTarget;
|
|
141
141
|
}({});
|
|
142
|
-
const nextVersion = "16.3.
|
|
142
|
+
const nextVersion = "16.3.5";
|
|
143
143
|
const ArchName = (0, _os.arch)();
|
|
144
144
|
const PlatformName = (0, _os.platform)();
|
|
145
145
|
function infoLog(...args) {
|