failproofai 1.0.12-beta.0 → 1.0.12-beta.2
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 +1 -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 +2 -2
- 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 +2 -2
- 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 +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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/_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 +22 -22
- 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/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js +1 -1
- 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 +3 -3
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- 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 +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +4 -4
- 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/app/settings/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/settings/page.js +7 -7
- 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]__0-ha5ydqq0mws._.js → [externals]__0vt9i6fqinjne._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__20xe05hm5u8_1._.js → [root-of-the-server]__0f_4g-vazrvxv._.js} +1 -1
- package/.next/standalone/.next/server/chunks/_0hf5q4xzyg40j._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0tovk6q0miud6._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3ycqv17cl._.js +1 -1
- package/.next/standalone/.next/server/chunks/_19011-u44143j._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68lnh2daur._.js +2 -2
- package/.next/standalone/.next/server/chunks/{_0kc-piux3hady._.js → _1y590mjzpkbos._.js} +2 -2
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4vkpxxu7._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_0_em82s16zfym._.js +5 -0
- package/.next/standalone/.next/server/chunks/src_hooks_builtin-policies_ts_1yq5ybgx1v98e._.js +3 -0
- package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3h94wnn._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_daemon-client_ts_0il_yu1zc0291._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn_4twyh._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ts0fg5yih1c1._.js → [root-of-the-server]__00l2u6j86cb8y._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08ch3staq3ejw._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0bigvy9xmv2ka._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h86bl2qf0k_o._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1_xbg2kx30wp-._.js → [root-of-the-server]__0naevg4bcoh54._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pia5h3p8re4c._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pwj2h2efe5f-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-ey9fgesfbc0._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15usvs-ms-0hf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16gb3_92zvk5x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1avi2kypusyb_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1r7drlqli-ado._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0vcyntk4snf0f._.js → [root-of-the-server]__1vofkb2oq7p7e._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0bseae9u7g98r._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0eoura3_9gjb-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0mouhaflye1ro._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1gb0ifpkowwub._.js → _0ouu886szh6yj._.js} +10 -9
- package/.next/standalone/.next/server/chunks/ssr/_0pedwblqdoh58._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1djqs9i27e4kg._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1ve8y151ua7qx._.js → _1fw0_nzkae5ao._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_20hn4gn87nsjc._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo5_vm5f.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9snixb5fd-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47q0xq5w._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_components_project-list_tsx_1ke6anr0vt64h._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x33jqb6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpcz0uxur._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h47jmhhd._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_00jxj8foxqz5t.js → node_modules_html-to-image_es_index_1fv03uigni8s5.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_0janj0i6dqko7._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/package_json_[json]_cjs_013tntikdft59._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_06kzv9ddabn3_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0agdmptetetdt._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0mdwgjisv4fub._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4l1tzrm._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_daemon-client_ts_0k41_i7jg6ndv._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589gp4p60j._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me658zqmh_._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- 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/preview-props.json +3 -3
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +23 -23
- package/.next/standalone/.next/static/chunks/{0pjxnp927y7_j.js → 0ay__-d2h1xb5.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2znt1784j9gd9.js → 0tbm3vjm62h-2.js} +1 -1
- package/.next/standalone/.next/static/chunks/1c_jf6g5wnslk.js +1 -0
- package/.next/standalone/.next/static/chunks/{3tn1bzk094e8e.js → 1my3-5p47t7r-.js} +1 -1
- package/.next/standalone/.next/static/chunks/20py8k1gd_i1m.js +1 -0
- package/.next/standalone/.next/static/chunks/21hckpmsc4g70.js +6 -0
- package/.next/standalone/.next/static/chunks/2hmex93ppj1ke.js +1 -0
- package/.next/standalone/.next/static/chunks/2o6d37j425uci.js +1 -0
- package/.next/standalone/.next/static/chunks/40oworn_-x12p.js +1 -0
- package/.next/standalone/.next/static/chunks/{2qz5piiir-qqm.js → 42xptz88mtq3x.js} +1 -1
- package/.next/standalone/app/actions/update-jev-config.ts +5 -3
- package/.next/standalone/app/settings/jev-panel.tsx +1 -1
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +32 -0
- package/.next/standalone/fp-cloud-cli/README.md +40 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/client.py +91 -12
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +78 -16
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +84 -18
- package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +124 -6
- package/.next/standalone/fp-cloud-cli/fp_cli/models.py +89 -6
- package/.next/standalone/fp-cloud-cli/fp_cli/output.py +189 -20
- package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +8 -5
- package/.next/standalone/fp-cloud-cli/skill/SKILL.md +2 -2
- package/.next/standalone/fp-cloud-cli/skill/references/commands.md +14 -7
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_jev.py +527 -0
- package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +46 -0
- package/.next/standalone/fp-cloud-cli/tests/test_output.py +23 -0
- package/.next/standalone/hermes-plugin/README.md +6 -4
- package/.next/standalone/hermes-plugin/__init__.py +27 -0
- package/.next/standalone/hermes-plugin/client.py +5 -1
- package/.next/standalone/hermes-plugin/plugin.yaml +1 -1
- package/.next/standalone/package.json +11 -10
- package/.next/standalone/sdk/python/uv.lock +31 -8
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +17 -1
- package/dist/cli.mjs +3507 -1250
- package/dist/worker.mjs +1779 -169
- package/hermes-plugin/README.md +6 -4
- package/hermes-plugin/__init__.py +27 -0
- package/hermes-plugin/client.py +5 -1
- package/hermes-plugin/plugin.yaml +1 -1
- package/package.json +11 -10
- package/src/audit/open-browser.ts +6 -7
- package/src/hooks/agent-roster.ts +180 -0
- package/src/hooks/agent-skill.ts +203 -0
- package/src/hooks/agent-targets.ts +60 -0
- package/src/hooks/cloud-connection.ts +91 -45
- package/src/hooks/cloud-enrollment-cli.ts +7 -4
- package/src/hooks/cloud-login.ts +6 -3
- package/src/hooks/cloud-managed-policies.ts +430 -20
- package/src/hooks/cloud-policy-errors.ts +481 -0
- package/src/hooks/codex-system-hooks.ts +10 -4
- package/src/hooks/config-status.ts +10 -4
- package/src/hooks/configure-wizard.ts +54 -26
- package/src/hooks/custom-hooks-loader.ts +26 -3
- package/src/hooks/daemon-client.ts +9 -4
- package/src/hooks/daemon-service.ts +2 -1
- package/src/hooks/delivery-health.ts +47 -2
- package/src/hooks/effective-reviewers.ts +36 -1
- package/src/hooks/fp-config.ts +19 -0
- package/src/hooks/fp-home.ts +6 -0
- package/src/hooks/handler.ts +255 -19
- package/src/hooks/health-probe.ts +15 -0
- package/src/hooks/hook-activity-store.ts +7 -1
- package/src/hooks/integrations.ts +36 -14
- package/src/hooks/jev-activity.ts +117 -1
- package/src/hooks/jev-cli.ts +196 -1
- package/src/hooks/jev-cloud-connection.ts +92 -0
- package/src/hooks/manager.ts +174 -34
- package/src/hooks/pack-failclosed.ts +2 -0
- package/src/hooks/pack-manifest.ts +8 -1
- package/src/hooks/policy-evaluator.ts +22 -1
- package/src/hooks/policy-registry.ts +3 -2
- package/src/hooks/policy-reviewability.ts +23 -5
- package/src/hooks/semantic/cloud-jev-health.ts +227 -0
- package/src/hooks/semantic/cloud-jev.ts +868 -0
- package/src/hooks/semantic/combine.ts +26 -1
- package/src/hooks/semantic/jev-client.ts +19 -2
- package/src/hooks/semantic/jev-config.ts +179 -30
- package/src/hooks/semantic/jev-review.ts +217 -10
- package/src/hooks/semantic/pack-policies.ts +2 -1
- package/src/hooks/semantic/types.ts +8 -0
- package/src/hooks/session-pause-cli.ts +1 -1
- package/src/hooks/tui.ts +13 -4
- package/src/hooks/uninstall-cli.ts +12 -1
- package/src/hooks/worker-server.ts +5 -1
- package/.next/standalone/.next/server/chunks/src_hooks_1aveq0uhs2t1b._.js +0 -5
- package/.next/standalone/.next/server/chunks/src_hooks_1eem5a7cyqlb2._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_effective-reviewers_ts_1h4wtvo55j16x._.js +0 -5
- package/.next/standalone/.next/static/chunks/0a-ccsp3xmypz.js +0 -1
- package/.next/standalone/.next/static/chunks/13jv-xvabeb62.js +0 -6
- package/.next/standalone/.next/static/chunks/2ank2u3apy8m6.js +0 -1
- package/.next/standalone/.next/static/chunks/2meqb9kygrncb.js +0 -1
- package/.next/standalone/.next/static/chunks/2ohktp_-lkv3x.js +0 -1
- package/.next/standalone/.next/static/chunks/2py0eldhwz8ay.js +0 -1
- /package/.next/standalone/.next/static/{SNr7o_-G4kNBQTR-iLZMM → LHCtFxVG0mdvd4lsXAOFC}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{SNr7o_-G4kNBQTR-iLZMM → LHCtFxVG0mdvd4lsXAOFC}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{SNr7o_-G4kNBQTR-iLZMM → LHCtFxVG0mdvd4lsXAOFC}/_ssgManifest.js +0 -0
|
@@ -55,7 +55,6 @@ import {
|
|
|
55
55
|
PERMISSION_POLICIES,
|
|
56
56
|
} from "./cloud-introspect";
|
|
57
57
|
import type { CloudJevConfigWrite } from "./jev-cloud-connection";
|
|
58
|
-
import { jevChecksDeclared } from "./effective-reviewers";
|
|
59
58
|
import {
|
|
60
59
|
ingestPath,
|
|
61
60
|
validateIngestKey,
|
|
@@ -63,7 +62,7 @@ import {
|
|
|
63
62
|
type CollectorSettings,
|
|
64
63
|
} from "./collector-config";
|
|
65
64
|
import { recoverCredentialRefusals } from "./credential-recovery";
|
|
66
|
-
import { clearActiveCloudManagedPolicies } from "./cloud-managed-policies";
|
|
65
|
+
import { clearActiveCloudManagedPolicies, readCloudJevState } from "./cloud-managed-policies";
|
|
67
66
|
import { rmSync } from "node:fs";
|
|
68
67
|
import { backfillRequestPath } from "./backfill-request";
|
|
69
68
|
|
|
@@ -141,6 +140,10 @@ export interface ConnectInput {
|
|
|
141
140
|
*/
|
|
142
141
|
sessions?: boolean;
|
|
143
142
|
environment?: string;
|
|
143
|
+
/** The org the key is already known to report into — a browser login's
|
|
144
|
+
* claim names it. Used only when introspect gives no answer (unreachable
|
|
145
|
+
* just then, or an older server), so the saved org is not left stale. */
|
|
146
|
+
org?: { id?: string; slug?: string; name?: string };
|
|
144
147
|
/** Injected by tests so nothing reaches the network. */
|
|
145
148
|
verifyPolicy?: typeof verifyCloudCredentials;
|
|
146
149
|
verifyIngest?: typeof validateIngestKey;
|
|
@@ -162,7 +165,8 @@ export interface ConnectOutcome {
|
|
|
162
165
|
* server has no introspect endpoint to say whether the key carries it.
|
|
163
166
|
*/
|
|
164
167
|
jev?: JevConnectOutcome;
|
|
165
|
-
/** Which organisation the key belongs to. Absent
|
|
168
|
+
/** Which organisation the key belongs to. Absent when neither introspect nor
|
|
169
|
+
* the caller (`ConnectInput.org`) named one. */
|
|
166
170
|
org?: { id?: string; slug?: string; name?: string };
|
|
167
171
|
/** True when at least one capability was configured. */
|
|
168
172
|
anyConfigured: boolean;
|
|
@@ -180,11 +184,18 @@ export interface JevConnectOutcome {
|
|
|
180
184
|
* is stored; `jev setup --provider failproofai` switches Jev on.
|
|
181
185
|
*/
|
|
182
186
|
optIn?: true;
|
|
187
|
+
/**
|
|
188
|
+
* A `--no-transcripts` connection removed this `jev.json`: it named the
|
|
189
|
+
* FailproofAI Cloud provider and was not switched off (the rule `config
|
|
190
|
+
* --disconnect` uses), so Jev through FailproofAI Cloud stays off here.
|
|
191
|
+
* Set with `optIn`.
|
|
192
|
+
*/
|
|
193
|
+
removedCloudConfig?: string;
|
|
183
194
|
/**
|
|
184
195
|
* A `--no-transcripts` connection found a `jev.json` already in place that
|
|
185
|
-
* keeps Jev running through FailproofAI Cloud, in this mode
|
|
186
|
-
*
|
|
187
|
-
*
|
|
196
|
+
* keeps Jev running through FailproofAI Cloud, in this mode, and could not
|
|
197
|
+
* remove it (see `removedCloudConfig`). A connection that asked for
|
|
198
|
+
* decisions only is told that Jev still sends more than that.
|
|
188
199
|
*/
|
|
189
200
|
stillOn?: "observe" | "enforce";
|
|
190
201
|
/**
|
|
@@ -264,6 +275,7 @@ export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcom
|
|
|
264
275
|
// CLI ships independently of whatever server a customer runs, so a good key
|
|
265
276
|
// on an older deployment must still connect.
|
|
266
277
|
const known = identity.kind === "ok" ? identity.identity : null;
|
|
278
|
+
const org = known ? { id: known.orgId, slug: known.orgSlug, name: known.orgName } : input.org;
|
|
267
279
|
|
|
268
280
|
// Skip a probe the key provably cannot pass. This is not an optimisation: a
|
|
269
281
|
// permission failure surfaces from `/events` as a 403 whose message reads
|
|
@@ -294,9 +306,7 @@ export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcom
|
|
|
294
306
|
: { ok: false, reason: policyResult.reason },
|
|
295
307
|
ingest: ingestResult.ok ? { ok: true } : { ok: false, reason: ingestResult.reason },
|
|
296
308
|
anyConfigured: false,
|
|
297
|
-
org
|
|
298
|
-
? { id: known.orgId, slug: known.orgSlug, name: known.orgName }
|
|
299
|
-
: undefined,
|
|
309
|
+
org,
|
|
300
310
|
};
|
|
301
311
|
|
|
302
312
|
outcome.anyConfigured = policyResult.ok || ingestResult.ok;
|
|
@@ -316,9 +326,14 @@ export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcom
|
|
|
316
326
|
prev.ingest ? { token: prev.ingest.key, url: prev.ingest.url } : undefined,
|
|
317
327
|
prev.jev ? { token: prev.jev.key, url: prev.jev.url } : undefined,
|
|
318
328
|
].some((slot) => slot && (slot.token !== input.token || !sameOrigin(slot.url)));
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
329
|
+
// Both sides name their organisation: that alone decides. The same org on
|
|
330
|
+
// a new URL (http → https, a new host for the same Cloud) is a reconnect,
|
|
331
|
+
// not a switch — clearing its deployment would drop enforcement until the
|
|
332
|
+
// next poll, and "Switched from X to X" says nothing true.
|
|
333
|
+
const sameKnownOrg = Boolean(prev.org?.id && org?.id && prev.org.id === org.id);
|
|
334
|
+
const orgSwitch = hadConnection && !sameKnownOrg && (
|
|
335
|
+
(prev.org?.id && org?.id && prev.org.id !== org.id) ||
|
|
336
|
+
((!prev.org?.id || !org?.id) && keyChanged) ||
|
|
322
337
|
(previousPolicy && !sameOrigin(previousPolicy.url)) ||
|
|
323
338
|
(prev.ingest && !sameOrigin(prev.ingest.url))
|
|
324
339
|
);
|
|
@@ -329,7 +344,7 @@ export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcom
|
|
|
329
344
|
if (orgSwitch) {
|
|
330
345
|
rmSync(backfillRequestPath(), { force: true });
|
|
331
346
|
const oldOrg = prev.org?.name ?? prev.org?.slug ?? prev.org?.id ?? "the previous org";
|
|
332
|
-
const newOrg =
|
|
347
|
+
const newOrg = org?.name ?? org?.slug ?? org?.id ?? "the new org";
|
|
333
348
|
changes.push(`Switched from ${oldOrg} to ${newOrg}. Nothing recorded before now will be sent to ${newOrg}; ${oldOrg}'s cloud policies stop within ~30 s.`);
|
|
334
349
|
// Remove local references, not the daemon's policy-floor state (#899).
|
|
335
350
|
clearActiveCloudManagedPolicies();
|
|
@@ -362,8 +377,8 @@ export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcom
|
|
|
362
377
|
if (prev.jev) changes.push(`Removed the previous Jev key ${maskToken(prev.jev.key)}: this key's ${PERMISSION_JEV} permission was not verified.`);
|
|
363
378
|
}
|
|
364
379
|
const since = boundary === undefined ? {} : { since: boundary };
|
|
365
|
-
if (
|
|
366
|
-
next.org = {
|
|
380
|
+
if (org) {
|
|
381
|
+
next.org = { ...org, ...since };
|
|
367
382
|
} else {
|
|
368
383
|
const { since: _drop, ...kept } = orgSwitch ? {} : (prev.org ?? {});
|
|
369
384
|
next.org = { ...kept, ...since };
|
|
@@ -373,7 +388,7 @@ export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcom
|
|
|
373
388
|
// a flush served before the daemon sees the new key retries these batches
|
|
374
389
|
// with the key that was refused, re-parking them as definitive refusals.
|
|
375
390
|
if (ingestResult.ok && prev.ingest && prev.ingest.key !== input.token) {
|
|
376
|
-
recoverCredentialRefusals(Boolean(prev.org?.id && prev.org.id ===
|
|
391
|
+
recoverCredentialRefusals(Boolean(prev.org?.id && prev.org.id === org?.id && sameOrigin(prev.ingest.url)), prev.org?.id);
|
|
377
392
|
}
|
|
378
393
|
// Legacy JSON override remains supported; the consolidated file above is
|
|
379
394
|
// published once, never as a mixture of previous and replacement slots.
|
|
@@ -425,23 +440,37 @@ export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcom
|
|
|
425
440
|
writeJevCloudCredential({ url: new URL(input.url).origin, key: input.token });
|
|
426
441
|
// Loaded here and nowhere earlier: a connect whose key does not carry Jev
|
|
427
442
|
// pulls in none of the Jev modules.
|
|
428
|
-
const { cloudJevRunningMode, existingJevConfig,
|
|
443
|
+
const { cloudJevRunningMode, existingJevConfig, removeCloudJevConfig, writeCloudJevConfig } = await import(
|
|
444
|
+
"./jev-cloud-connection"
|
|
445
|
+
);
|
|
429
446
|
if (input.sessions === true) {
|
|
430
|
-
outcome.jev = { ok: true, config:
|
|
447
|
+
outcome.jev = { ok: true, config: writeCloudJevConfig(input.url) };
|
|
431
448
|
} else {
|
|
432
449
|
// `--no-transcripts` never switches Jev on (the user's decision). Jev
|
|
433
450
|
// sends each checked tool call and the recent prompt to FailproofAI
|
|
434
451
|
// Cloud, and someone who just asked for decisions only has not asked
|
|
435
452
|
// for that. The key is stored all the same, so opting in later is one
|
|
436
|
-
// command with no key to paste; jev.json is not written.
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
//
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
453
|
+
// command with no key to paste; jev.json is not written.
|
|
454
|
+
//
|
|
455
|
+
// And a jev.json naming the FailproofAI Cloud provider — left by an
|
|
456
|
+
// earlier connect with transcripts on — is removed, by exactly the rule
|
|
457
|
+
// `config --disconnect` uses (`removeCloudJevConfig`: the Cloud's file,
|
|
458
|
+
// not switched off). Kept, it went on sending this machine's tool calls
|
|
459
|
+
// to FailproofAI Cloud with no Cloud mode set (e2e-c10 O2, user
|
|
460
|
+
// decision). A BYOK file is its owner's and stays, reported as ever; so
|
|
461
|
+
// does a Cloud file switched off, the owner's opt-out.
|
|
462
|
+
const removal = removeCloudJevConfig();
|
|
463
|
+
if (removal.status === "removed") {
|
|
464
|
+
outcome.jev = { ok: true, optIn: true, removedCloudConfig: removal.path };
|
|
465
|
+
} else {
|
|
466
|
+
const existing = existingJevConfig(input.url);
|
|
467
|
+
// Still sending (the removal failed): said, or "Decisions only."
|
|
468
|
+
// would be the last word while Jev keeps sending.
|
|
469
|
+
const stillOn = existing ? cloudJevRunningMode() : null;
|
|
470
|
+
outcome.jev = existing
|
|
471
|
+
? { ok: true, config: existing, ...(stillOn ? { stillOn } : {}) }
|
|
472
|
+
: { ok: true, optIn: true };
|
|
473
|
+
}
|
|
445
474
|
}
|
|
446
475
|
} else if (known) {
|
|
447
476
|
// Introspect ANSWERED, and the key does not carry Jev. This connection
|
|
@@ -491,6 +520,14 @@ export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcom
|
|
|
491
520
|
* this machine now, and — the one case worth a second line — why an existing
|
|
492
521
|
* `jev.json` was not touched.
|
|
493
522
|
*/
|
|
523
|
+
function safeCloudJev(): ReturnType<typeof readCloudJevState> {
|
|
524
|
+
try {
|
|
525
|
+
return readCloudJevState();
|
|
526
|
+
} catch {
|
|
527
|
+
return { jevMode: null, deployment: null, errors: [] };
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
494
531
|
export function jevLines(outcome: ConnectOutcome): string[] {
|
|
495
532
|
const jev = outcome.jev;
|
|
496
533
|
if (!jev) return [];
|
|
@@ -503,6 +540,11 @@ export function jevLines(outcome: ConnectOutcome): string[] {
|
|
|
503
540
|
if (!jev.ok) {
|
|
504
541
|
return [` Jev not through FailproofAI Cloud: ${jev.reason ?? `that key does not carry \`${PERMISSION_JEV}\``}.`];
|
|
505
542
|
}
|
|
543
|
+
if (jev.optIn && jev.removedCloudConfig) {
|
|
544
|
+
return [
|
|
545
|
+
` Jev removed ${jev.removedCloudConfig}, which sent each checked tool call and the recent prompt to FailproofAI Cloud: Jev via FailproofAI Cloud stays off on this machine (--no-transcripts). To switch it on: \`failproofai jev setup --provider failproofai\`.`,
|
|
546
|
+
];
|
|
547
|
+
}
|
|
506
548
|
if (jev.optIn) {
|
|
507
549
|
// One line, and nothing that reads as "on": this connection asked for
|
|
508
550
|
// decisions only, and Jev would send more than that.
|
|
@@ -511,28 +553,32 @@ export function jevLines(outcome: ConnectOutcome): string[] {
|
|
|
511
553
|
];
|
|
512
554
|
}
|
|
513
555
|
const config = jev.config;
|
|
514
|
-
if (!config || config.status === "written") {
|
|
515
|
-
//
|
|
516
|
-
//
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
556
|
+
if (!config || config.status === "written" || config.status === "repointed") {
|
|
557
|
+
// What happened to jev.json, said first on a re-point; the mode lines
|
|
558
|
+
// below are the same for both (N1).
|
|
559
|
+
const lead =
|
|
560
|
+
config?.status === "repointed" ? [` Jev ${config.path} now points here instead of ${config.from}.`] : [];
|
|
561
|
+
const indent = lead.length > 0 ? " " : " Jev ";
|
|
562
|
+
// FailproofAI Cloud already set this machine's Jev mode (a reconnect, or a
|
|
563
|
+
// deployment that arrived first): that mode and Cloud's own checks decide,
|
|
564
|
+
// not the local jev.json or the packs installed here.
|
|
565
|
+
const cloudJev = safeCloudJev();
|
|
566
|
+
// One line: FailproofAI Cloud decides the mode, whatever jev.json says, so
|
|
567
|
+
// the local fallback and the packs installed here are not worth a line.
|
|
568
|
+
const state =
|
|
569
|
+
cloudJev.jevMode === null
|
|
570
|
+
? "waiting for its first deployment"
|
|
571
|
+
: cloudJev.jevMode === "off"
|
|
572
|
+
? "off — no Jev policy deployed"
|
|
573
|
+
: cloudJev.jevMode;
|
|
574
|
+
return [...lead, `${indent}managed by FailproofAI Cloud · ${state}`];
|
|
529
575
|
}
|
|
530
576
|
if (config.status === "kept") {
|
|
531
577
|
const lines = [` Jev key stored; ${config.path} already exists and was left as configured.`];
|
|
532
578
|
if (config.otherOrigin) {
|
|
533
579
|
lines.push(
|
|
534
580
|
config.jevOff?.why === "off"
|
|
535
|
-
? ` It points at ${config.otherOrigin} and has Jev switched off (mode off), so Jev is off. To point it here
|
|
581
|
+
? ` It points at ${config.otherOrigin} and has Jev switched off (mode off), so Jev is off. To point it here: \`failproofai jev setup --provider failproofai\`.`
|
|
536
582
|
: ` It points at ${config.otherOrigin}, so Jev stays off until you run \`failproofai jev setup --provider failproofai\`.`,
|
|
537
583
|
);
|
|
538
584
|
} else if (config.jevOff?.why === "refused") {
|
|
@@ -540,11 +586,11 @@ export function jevLines(outcome: ConnectOutcome): string[] {
|
|
|
540
586
|
` It was refused (${config.jevOff.problem}), so Jev is off. ${config.jevOff.fix ? `Fix: \`${config.jevOff.fix}\`.` : "Write a valid one: `failproofai jev setup --provider failproofai`."}`,
|
|
541
587
|
);
|
|
542
588
|
} else if (config.jevOff?.why === "off") {
|
|
543
|
-
lines.push(" It has Jev switched off (mode off)
|
|
589
|
+
lines.push(" It has Jev switched off (mode off) until FailproofAI Cloud's Jev mode arrives with this machine's first deployment; from then on FailproofAI Cloud decides.");
|
|
544
590
|
}
|
|
545
591
|
if (jev.stillOn) {
|
|
546
592
|
lines.push(
|
|
547
|
-
` Jev is still on through FailproofAI Cloud (${jev.stillOn} mode): it sends each checked tool call and the recent prompt to FailproofAI Cloud.
|
|
593
|
+
` Jev is still on through FailproofAI Cloud (${jev.stillOn} mode): it sends each checked tool call and the recent prompt to FailproofAI Cloud. FailproofAI Cloud sets its mode: off when no Jev policy is deployed here.`,
|
|
548
594
|
);
|
|
549
595
|
}
|
|
550
596
|
return lines;
|
|
@@ -409,12 +409,13 @@ export function runDisconnectCommand(): CommandResult {
|
|
|
409
409
|
// machine that had deliberately left its organisation went on being governed
|
|
410
410
|
// by whatever deployment was current when it left, indefinitely, while
|
|
411
411
|
// `--status` reported it as unconnected.
|
|
412
|
-
const stoppedManaged = clearActiveCloudManagedPolicies();
|
|
413
412
|
// Back to OSS. Leaving mode = "cloud" with no credentials would describe a
|
|
414
413
|
// machine that does not exist, and every cloud code path keys off this flag
|
|
415
414
|
// rather than off "is a token lying around" precisely so that a disconnected
|
|
416
415
|
// machine is provably silent instead of silent-by-happenstance.
|
|
417
416
|
updateConfig({ mode: "oss" });
|
|
417
|
+
const sharedPolicyRoot = process.env.FAILPROOFAI_CLOUD_POLICY_DIR !== undefined;
|
|
418
|
+
const stoppedManaged = clearActiveCloudManagedPolicies();
|
|
418
419
|
|
|
419
420
|
const jevLines =
|
|
420
421
|
jevConfig.status === "removed"
|
|
@@ -450,10 +451,12 @@ export function runDisconnectCommand(): CommandResult {
|
|
|
450
451
|
const lines = [
|
|
451
452
|
"Disconnected from FailproofAI Cloud.",
|
|
452
453
|
"",
|
|
453
|
-
stoppedManaged
|
|
454
|
-
? " Cloud-managed policies stop being enforced and stop being refreshed
|
|
454
|
+
stoppedManaged || (sharedPolicyRoot && !!existing)
|
|
455
|
+
? " Cloud-managed policies stop being enforced and stop being refreshed, and any Jev mode\n" +
|
|
456
|
+
" FailproofAI Cloud set goes with them (its Jev checks never ran on this machine).\n" +
|
|
455
457
|
" Local builtin, custom and convention policies are unaffected."
|
|
456
458
|
: " Local builtin, custom and convention policies are unaffected.",
|
|
459
|
+
...(sharedPolicyRoot ? [" An overridden Cloud policy directory was left untouched; OSS mode ignores its old deployment."] : []),
|
|
457
460
|
...jevLines,
|
|
458
461
|
];
|
|
459
462
|
if (removedIngest) {
|
|
@@ -589,4 +592,4 @@ export function connectionStatusLines(daemonStatus = daemonServiceStatus): strin
|
|
|
589
592
|
kitRows(report.rows, opts),
|
|
590
593
|
report.warnings.length > 0 ? warning(report.warnings, opts) : null,
|
|
591
594
|
);
|
|
592
|
-
}
|
|
595
|
+
}
|
package/src/hooks/cloud-login.ts
CHANGED
|
@@ -162,9 +162,12 @@ export async function startLogin(
|
|
|
162
162
|
fetchImpl,
|
|
163
163
|
);
|
|
164
164
|
if ("error" in r) return { kind: "unreachable", reason: r.error };
|
|
165
|
-
// No such route,
|
|
166
|
-
//
|
|
167
|
-
|
|
165
|
+
// No such route, the web app redirecting an unknown path, or serving a page
|
|
166
|
+
// (200, not JSON) in its place: a cloud from before this feature. Not an
|
|
167
|
+
// error — the key prompt still connects it.
|
|
168
|
+
if (r.status === 404 || (r.status >= 300 && r.status < 400) || (r.status === 200 && !r.json)) {
|
|
169
|
+
return { kind: "unsupported" };
|
|
170
|
+
}
|
|
168
171
|
if (r.status === 429) return { kind: "refused", reason: "too many login attempts from here — wait a minute" };
|
|
169
172
|
if (r.status !== 201 || !r.json) {
|
|
170
173
|
return r.status >= 500
|