failproofai 1.0.0-beta.12 → 1.0.0-beta.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +2 -3
- 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 +2 -3
- 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 +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- 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 +10 -10
- 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/api/audit/invite/route.js +2 -2
- 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 +2 -2
- 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 +3 -2
- 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 +2 -2
- 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 +2 -2
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js +2 -2
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js +2 -2
- 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 +2 -3
- 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 +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- 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 +2 -3
- 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 +10 -10
- package/.next/standalone/.next/server/app/policies/page.js +2 -3
- 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 +3 -4
- 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 +3 -4
- 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 +3 -4
- 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 +4 -4
- package/.next/standalone/.next/server/app/settings/page.js +2 -3
- 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/{[root-of-the-server]__1a7i2q4._.js → [root-of-the-server]__00ni9za._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__08y94xv._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1_ps0jw._.js → [root-of-the-server]__0effm9m._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0lk_935._.js → [root-of-the-server]__0ix8gm7._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0ky_n0r._.js → [root-of-the-server]__0ji11a-._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0v2fpxe._.js → [root-of-the-server]__0ojwjxl._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vb5fl2._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1uyc-z4._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__20d06rh._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +1 -3
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +1 -3
- 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]__1rdruw4._.js → [root-of-the-server]__0_1tiya._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ha3gul._.js → [root-of-the-server]__0eeipso._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spkm68._.js +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1tyf0tc._.js → [root-of-the-server]__17itmx-._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1io8n-9._.js → [root-of-the-server]__1dx-a-c._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1mmefsr._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1yw368c._.js → [root-of-the-server]__1n2vcv4._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1a2hcb_._.js → [root-of-the-server]__1tlvv9a._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1fahg9e._.js → _0vo2qyt._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_0uz7o9y._.js → _0xej-0e._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_04mekdy._.js → _10399ge._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_0se484m._.js → _149k9oy._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_1vsgpqt._.js → _166jvu8._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0r-dsre._.js → _1_hwp06._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_1w8sola._.js → _1mnhi_r._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_1w_6p9g._.js → _1ror7k9._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_1gmzkwj._.js → _1sdgprl._.js} +1 -1
- 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_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.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/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +15 -15
- package/.next/standalone/.next/static/chunks/{2_h--2ame3r83.js → 01s3vpzky_qew.js} +1 -1
- package/.next/standalone/.next/static/chunks/0km-jn9o5xyqs.css +2 -0
- package/.next/standalone/.next/static/chunks/{23hp7hekegpe5.js → 0zbcd_n8ku2ov.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0cakk77ppmlo7.js → 1erji0ratofla.js} +1 -1
- package/.next/standalone/.next/static/chunks/{40-zk44qsg5y-.js → 23hnlzqx44vbe.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1wb8cnmzx69rz.js → 2cwsiyhc7d2cw.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0_rdbv_k5j51p.js → 2y42ebzynyxdv.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1spk1bub0vd-h.js → 315lncj7dsp9c.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0-zh1umoq6ppc.js → 31_px0dfgdg9j.js} +2 -2
- package/.next/standalone/.next/static/chunks/{2bie7ajv70p9b.js → 3l4-fu3xt75j8.js} +1 -1
- package/.next/standalone/.next/static/chunks/{298v78ypf8edl.js → 3poscoxn69x9g.js} +1 -1
- package/.next/standalone/app/policies/hooks-client.tsx +4 -4
- package/.next/standalone/ino.ujh3/.failproofai/VERSION +4 -0
- package/.next/standalone/ino.ujh3/.failproofai/state/last-version +1 -0
- package/.next/standalone/ino.ujh3/.failproofai/state/telemetry-id +1 -0
- package/.next/standalone/package.json +10 -10
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +186 -5
- package/dist/cli.mjs +1130 -1352
- package/dist/worker.mjs +107 -1000
- package/package.json +10 -10
- package/src/hooks/backfill-cli.ts +1 -1
- package/src/hooks/cloud-connection.ts +4 -4
- package/src/hooks/cloud-enrollment-cli.ts +1 -1
- package/src/hooks/cloud-enrollment.ts +3 -3
- package/src/hooks/cloud-managed-policies.ts +60 -22
- package/src/hooks/configure-wizard.ts +124 -53
- package/src/hooks/custom-hooks-loader.ts +57 -8
- package/src/hooks/daemon-service.ts +66 -11
- package/src/hooks/flush-cli.ts +180 -0
- package/src/hooks/fp-config.ts +242 -118
- package/src/hooks/fp-home.ts +138 -22
- package/src/hooks/fp-reset.ts +357 -43
- package/src/hooks/handler.ts +11 -11
- package/src/hooks/harness-cli.ts +309 -0
- package/src/hooks/hook-activity-store.ts +5 -5
- package/src/hooks/manager.ts +35 -0
- package/src/hooks/onboarding-attempt.ts +22 -6
- package/src/hooks/tui.ts +64 -7
- package/src/hooks/uninstall-cli.ts +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1iny24d._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tyedjt._.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +0 -5
- package/.next/standalone/.next/static/chunks/13vrxdsdihqd_.css +0 -2
- /package/.next/standalone/.next/static/{-3gDrXYxNRTVwrFnqXAWd → ziAsXfNE0cH_S7LHK-Py3}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{-3gDrXYxNRTVwrFnqXAWd → ziAsXfNE0cH_S7LHK-Py3}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{-3gDrXYxNRTVwrFnqXAWd → ziAsXfNE0cH_S7LHK-Py3}/_ssgManifest.js +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "failproofai",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.13",
|
|
4
4
|
"description": "The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously — for Claude Code & the Agents SDK",
|
|
5
5
|
"bin": {
|
|
6
6
|
"failproofai": "./dist/cli.mjs",
|
|
@@ -101,12 +101,12 @@
|
|
|
101
101
|
"html-to-image": "^1.11.13",
|
|
102
102
|
"html2canvas": "^1.4.1",
|
|
103
103
|
"posthog-node": "^5.47.7",
|
|
104
|
-
"smol-toml": "^1.7.1",
|
|
105
104
|
"sql.js": "^1.14.1",
|
|
106
105
|
"yaml": "^2.9.0"
|
|
107
106
|
},
|
|
108
107
|
"overrides": {
|
|
109
108
|
"postcss": "8.5.23",
|
|
109
|
+
"nanoid": "3.3.17",
|
|
110
110
|
"eslint-plugin-react-hooks": "7.0.1",
|
|
111
111
|
"vite": "8.0.16",
|
|
112
112
|
"undici": "7.29.0",
|
|
@@ -114,15 +114,15 @@
|
|
|
114
114
|
"sharp": "0.35.0"
|
|
115
115
|
},
|
|
116
116
|
"optionalDependencies": {
|
|
117
|
-
"@failproofai/failproofaid-linux-x64": "1.0.0-beta.
|
|
118
|
-
"@failproofai/failproofaid-linux-arm64": "1.0.0-beta.
|
|
119
|
-
"@failproofai/failproofaid-darwin-x64": "1.0.0-beta.
|
|
120
|
-
"@failproofai/failproofaid-darwin-arm64": "1.0.0-beta.
|
|
117
|
+
"@failproofai/failproofaid-linux-x64": "1.0.0-beta.13",
|
|
118
|
+
"@failproofai/failproofaid-linux-arm64": "1.0.0-beta.13",
|
|
119
|
+
"@failproofai/failproofaid-darwin-x64": "1.0.0-beta.13",
|
|
120
|
+
"@failproofai/failproofaid-darwin-arm64": "1.0.0-beta.13"
|
|
121
121
|
},
|
|
122
122
|
"failproofaidBinaries": {
|
|
123
|
-
"linux-x64": "
|
|
124
|
-
"linux-arm64": "
|
|
125
|
-
"darwin-x64": "
|
|
126
|
-
"darwin-arm64": "
|
|
123
|
+
"linux-x64": "b96dd035433adbf1290ad8b4ed32fc8134d4dfc03057b8f6a210c041ac7d8d93",
|
|
124
|
+
"linux-arm64": "0e11c7349de7d10488a6ea8f80714260e3dbedabcd06b0ab530119970b16404d",
|
|
125
|
+
"darwin-x64": "7d2943d6126430d9282ca106918be8aa2e0133c4c7189493996b14cbbb361d06",
|
|
126
|
+
"darwin-arm64": "dd6c646611ee81d7db88254917232bd5b0cfac514657bd2ce1062c7bb24cf960"
|
|
127
127
|
}
|
|
128
128
|
}
|
|
@@ -168,7 +168,7 @@ export function runBackfillCommand(opts: BackfillOptions = {}): BackfillResult {
|
|
|
168
168
|
exitCode: 1,
|
|
169
169
|
lines: [
|
|
170
170
|
"Collection is switched off, so a backfill would re-read history and send",
|
|
171
|
-
|
|
171
|
+
'none of it. Enable it in ~/.failproofai/config.json under "collector".',
|
|
172
172
|
],
|
|
173
173
|
};
|
|
174
174
|
}
|
|
@@ -137,7 +137,7 @@ export interface CapabilityOutcome {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
export interface ConnectOutcome {
|
|
140
|
-
policy: CapabilityOutcome & { policyCount?: number;
|
|
140
|
+
policy: CapabilityOutcome & { policyCount?: number; deployment?: number };
|
|
141
141
|
ingest: CapabilityOutcome;
|
|
142
142
|
/** Which organisation the key belongs to. Absent on a pre-introspect server. */
|
|
143
143
|
org?: { id?: string; slug?: string; name?: string };
|
|
@@ -232,7 +232,7 @@ export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcom
|
|
|
232
232
|
|
|
233
233
|
const outcome: ConnectOutcome = {
|
|
234
234
|
policy: policyResult.ok
|
|
235
|
-
? { ok: true, policyCount: policyResult.policyCount,
|
|
235
|
+
? { ok: true, policyCount: policyResult.policyCount, deployment: policyResult.deployment }
|
|
236
236
|
: { ok: false, reason: policyResult.reason },
|
|
237
237
|
ingest: ingestResult.ok ? { ok: true } : { ok: false, reason: ingestResult.reason },
|
|
238
238
|
anyConfigured: false,
|
|
@@ -309,7 +309,7 @@ export function describeOutcome(outcome: ConnectOutcome, machineId: string, url:
|
|
|
309
309
|
if (outcome.policy.ok && outcome.ingest.ok) {
|
|
310
310
|
lines.push(`Connected to ${url} as ${machineId}${into}.`);
|
|
311
311
|
const n = outcome.policy.policyCount ?? 0;
|
|
312
|
-
lines.push(` Policy ${n} polic${n === 1 ? "y" : "ies"} assigned (
|
|
312
|
+
lines.push(` Policy ${n} polic${n === 1 ? "y" : "ies"} assigned (deployment ${outcome.policy.deployment ?? 0}).`);
|
|
313
313
|
lines.push(` Dashboard hook activity will be sent to ${ingestUrlFor(url)}.`);
|
|
314
314
|
return lines;
|
|
315
315
|
}
|
|
@@ -317,7 +317,7 @@ export function describeOutcome(outcome: ConnectOutcome, machineId: string, url:
|
|
|
317
317
|
if (outcome.policy.ok && !outcome.ingest.ok) {
|
|
318
318
|
const n = outcome.policy.policyCount ?? 0;
|
|
319
319
|
lines.push(`Connected to ${url} as ${machineId}${into}, for policy only.`);
|
|
320
|
-
lines.push(` Policy ${n} polic${n === 1 ? "y" : "ies"} assigned (
|
|
320
|
+
lines.push(` Policy ${n} polic${n === 1 ? "y" : "ies"} assigned (deployment ${outcome.policy.deployment ?? 0}).`);
|
|
321
321
|
lines.push(` Dashboard NOT configured: ${outcome.ingest.reason}`);
|
|
322
322
|
lines.push("");
|
|
323
323
|
lines.push(" Enforcement works. Nothing will appear in the dashboard until this");
|
|
@@ -214,7 +214,7 @@ export function runDisconnectCommand(): CommandResult {
|
|
|
214
214
|
// credential ends the daemon's polling; every artifact already on disk stayed
|
|
215
215
|
// referenced by `active.json` and kept being loaded on every tool call — so a
|
|
216
216
|
// machine that had deliberately left its organisation went on being governed
|
|
217
|
-
// by whatever
|
|
217
|
+
// by whatever deployment was current when it left, indefinitely, while
|
|
218
218
|
// `--status` reported it as unconnected.
|
|
219
219
|
const stoppedManaged = clearActiveCloudManagedPolicies();
|
|
220
220
|
// Back to OSS. Leaving mode = "cloud" with no credentials would describe a
|
|
@@ -182,7 +182,7 @@ export function clearCloudCredentials(): boolean {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
export type VerifyResult =
|
|
185
|
-
| { ok: true; policyCount: number;
|
|
185
|
+
| { ok: true; policyCount: number; deployment: number }
|
|
186
186
|
| { ok: false; reason: string };
|
|
187
187
|
|
|
188
188
|
/**
|
|
@@ -228,11 +228,11 @@ export async function verifyCloudCredentials(creds: CloudCredentials): Promise<V
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
try {
|
|
231
|
-
const body = (await response.json()) as { policies?: unknown[];
|
|
231
|
+
const body = (await response.json()) as { policies?: unknown[]; deployment?: number };
|
|
232
232
|
return {
|
|
233
233
|
ok: true,
|
|
234
234
|
policyCount: Array.isArray(body.policies) ? body.policies.length : 0,
|
|
235
|
-
|
|
235
|
+
deployment: typeof body.deployment === "number" ? body.deployment : 0,
|
|
236
236
|
};
|
|
237
237
|
} catch {
|
|
238
238
|
return {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Reads the daemon-owned active cloud policy
|
|
2
|
+
* Reads the daemon-owned active cloud policy deployment for the TypeScript
|
|
3
3
|
* evaluator. The Rust reconciler is responsible for downloading and repairing
|
|
4
4
|
* artifacts; this boundary independently verifies the digest immediately
|
|
5
5
|
* before import so the worker never knowingly executes modified bytes.
|
|
@@ -10,7 +10,21 @@ import { homedir } from "node:os";
|
|
|
10
10
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
11
11
|
import { cloudPoliciesDir } from "./fp-home";
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Active-manifest schema versions this reader accepts.
|
|
15
|
+
*
|
|
16
|
+
* MUST stay in step with `SUPPORTED_SCHEMA_VERSIONS` in `cloud_policies.rs`,
|
|
17
|
+
* which is what WRITES the file this parses. They are two hand-maintained
|
|
18
|
+
* copies of one contract in two languages, and the failure is silent in the
|
|
19
|
+
* worst direction: the daemon reconciles happily, `active.json` is correct on
|
|
20
|
+
* disk, and only the hook path refuses it — so cloud policy stops being
|
|
21
|
+
* enforced while every other signal says the machine is healthy. Reproduced
|
|
22
|
+
* exactly that way while syncing this with AgentEye#559.
|
|
23
|
+
*
|
|
24
|
+
* 1 is accepted for files a pre-rename beta daemon left behind; 2 is what is
|
|
25
|
+
* written now.
|
|
26
|
+
*/
|
|
27
|
+
const ACCEPTED_ACTIVE_SCHEMA_VERSIONS: readonly number[] = [1, 2];
|
|
14
28
|
const SHA256_RE = /^[a-f0-9]{64}$/;
|
|
15
29
|
const POLICY_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
|
|
16
30
|
|
|
@@ -19,7 +33,7 @@ export type PolicyEffect = "enforce" | "observe";
|
|
|
19
33
|
|
|
20
34
|
export interface CloudManagedPolicyArtifact {
|
|
21
35
|
id: string;
|
|
22
|
-
|
|
36
|
+
version: number;
|
|
23
37
|
/**
|
|
24
38
|
* `observe` policies are evaluated exactly like any other and then have their
|
|
25
39
|
* verdict discarded, so a rollout can be measured against real traffic before
|
|
@@ -30,15 +44,15 @@ export interface CloudManagedPolicyArtifact {
|
|
|
30
44
|
effect: PolicyEffect;
|
|
31
45
|
sha256: string;
|
|
32
46
|
path: string;
|
|
33
|
-
|
|
47
|
+
deployment: number;
|
|
34
48
|
}
|
|
35
49
|
|
|
36
50
|
interface ActiveManifest {
|
|
37
51
|
schemaVersion: number;
|
|
38
|
-
|
|
52
|
+
deployment: number;
|
|
39
53
|
policies: Array<{
|
|
40
54
|
id: string;
|
|
41
|
-
|
|
55
|
+
version: number;
|
|
42
56
|
effect?: string;
|
|
43
57
|
sha256: string;
|
|
44
58
|
path: string;
|
|
@@ -52,17 +66,41 @@ export function cloudManagedPolicyRoot(): string {
|
|
|
52
66
|
);
|
|
53
67
|
}
|
|
54
68
|
|
|
69
|
+
/**
|
|
70
|
+
* A version-1 manifest, in the spelling a pre-rename daemon actually wrote.
|
|
71
|
+
*
|
|
72
|
+
* Accepting schema 1 while reading only the version-2 FIELD NAMES made the
|
|
73
|
+
* acceptance unreachable: every genuine v1 file threw "active manifest
|
|
74
|
+
* deployment is invalid". The Rust reader of this same file handles it with
|
|
75
|
+
* `#[serde(alias = "generation")]` / `#[serde(alias = "revision")]`; this is the
|
|
76
|
+
* TypeScript half of that pair, and it was missing.
|
|
77
|
+
*/
|
|
78
|
+
interface LegacyActiveManifest {
|
|
79
|
+
generation?: number;
|
|
80
|
+
policies?: Array<{ revision?: number }>;
|
|
81
|
+
}
|
|
82
|
+
|
|
55
83
|
function parseManifest(value: unknown): ActiveManifest {
|
|
56
84
|
if (!value || typeof value !== "object") throw new Error("active manifest is not an object");
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
59
|
-
throw new Error(
|
|
85
|
+
const raw = value as Partial<ActiveManifest> & LegacyActiveManifest;
|
|
86
|
+
if (!ACCEPTED_ACTIVE_SCHEMA_VERSIONS.includes(raw.schemaVersion as number)) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`unsupported active manifest schema ${String(raw.schemaVersion)} ` +
|
|
89
|
+
`(supported: ${ACCEPTED_ACTIVE_SCHEMA_VERSIONS.join(", ")})`,
|
|
90
|
+
);
|
|
60
91
|
}
|
|
61
|
-
|
|
62
|
-
|
|
92
|
+
// New name wins; the old one is a fallback, not an equal. A file carrying both
|
|
93
|
+
// (written by a mixed-version machine) must resolve to the current field.
|
|
94
|
+
const deployment = raw.deployment ?? raw.generation;
|
|
95
|
+
if (!Number.isSafeInteger(deployment) || (deployment ?? -1) < 0) {
|
|
96
|
+
throw new Error("active manifest deployment is invalid");
|
|
63
97
|
}
|
|
64
|
-
if (!Array.isArray(
|
|
65
|
-
|
|
98
|
+
if (!Array.isArray(raw.policies)) throw new Error("active manifest policies is not an array");
|
|
99
|
+
const policies = raw.policies.map((p) => {
|
|
100
|
+
const entry = p as ActiveManifest["policies"][number] & { revision?: number };
|
|
101
|
+
return { ...entry, version: entry.version ?? entry.revision } as ActiveManifest["policies"][number];
|
|
102
|
+
});
|
|
103
|
+
return { schemaVersion: raw.schemaVersion as number, deployment: deployment as number, policies };
|
|
66
104
|
}
|
|
67
105
|
|
|
68
106
|
function resolveManagedPath(root: string, candidate: string): string {
|
|
@@ -73,7 +111,7 @@ function resolveManagedPath(root: string, candidate: string): string {
|
|
|
73
111
|
throw new Error(`managed policy path escapes its root: ${JSON.stringify(candidate)}`);
|
|
74
112
|
}
|
|
75
113
|
|
|
76
|
-
// Reject a symlinked artifact or
|
|
114
|
+
// Reject a symlinked artifact or deployment directory escaping the managed
|
|
77
115
|
// root. The current daemon is same-user, but following an arbitrary path is
|
|
78
116
|
// still unnecessary ambient authority and becomes dangerous if its service
|
|
79
117
|
// identity is hardened later.
|
|
@@ -89,8 +127,8 @@ function resolveManagedPath(root: string, candidate: string): string {
|
|
|
89
127
|
/**
|
|
90
128
|
* Stop enforcing cloud-managed policies on this machine.
|
|
91
129
|
*
|
|
92
|
-
* Removes `active.json` only — the manifest that says which
|
|
93
|
-
* live. The
|
|
130
|
+
* Removes `active.json` only — the manifest that says which deployment is
|
|
131
|
+
* live. The deployment directories and content-addressed artifacts are left
|
|
94
132
|
* alone: they are large, hash-verified on use, and inert once nothing points
|
|
95
133
|
* at them, so keeping them makes a reconnect cheap and offline-safe.
|
|
96
134
|
*
|
|
@@ -98,7 +136,7 @@ function resolveManagedPath(root: string, candidate: string): string {
|
|
|
98
136
|
* credential stops the daemon REFRESHING policy; every artifact already on disk
|
|
99
137
|
* kept being loaded and enforced on every tool call, so a user who had
|
|
100
138
|
* deliberately left their organisation's cloud went on being governed by
|
|
101
|
-
* whatever
|
|
139
|
+
* whatever deployment happened to be current when they left — indefinitely,
|
|
102
140
|
* with `--status` reporting the machine as unconnected.
|
|
103
141
|
*
|
|
104
142
|
* Returns true when a manifest was actually removed.
|
|
@@ -134,8 +172,8 @@ export function readActiveCloudManagedPolicies(): CloudManagedPolicyArtifact[] {
|
|
|
134
172
|
if (!POLICY_ID_RE.test(policy.id) || policy.id === "." || policy.id === "..") {
|
|
135
173
|
throw new Error(`unsafe cloud-managed policy id ${JSON.stringify(policy.id)}`);
|
|
136
174
|
}
|
|
137
|
-
if (!Number.isSafeInteger(policy.
|
|
138
|
-
throw new Error(`invalid
|
|
175
|
+
if (!Number.isSafeInteger(policy.version) || policy.version < 0) {
|
|
176
|
+
throw new Error(`invalid version for cloud-managed policy ${policy.id}`);
|
|
139
177
|
}
|
|
140
178
|
if (!SHA256_RE.test(policy.sha256)) {
|
|
141
179
|
throw new Error(`invalid SHA-256 for cloud-managed policy ${policy.id}`);
|
|
@@ -154,16 +192,16 @@ export function readActiveCloudManagedPolicies(): CloudManagedPolicyArtifact[] {
|
|
|
154
192
|
if (policy.effect !== undefined && policy.effect !== "enforce" && policy.effect !== "observe") {
|
|
155
193
|
// Guessing would mean either enforcing something cloud did not ask to
|
|
156
194
|
// enforce, or observing something it wanted enforced. Both are worse than
|
|
157
|
-
// refusing the
|
|
195
|
+
// refusing the deployment.
|
|
158
196
|
throw new Error(`unknown effect ${JSON.stringify(policy.effect)} for cloud-managed policy ${policy.id}`);
|
|
159
197
|
}
|
|
160
198
|
return {
|
|
161
199
|
id: policy.id,
|
|
162
|
-
|
|
200
|
+
version: policy.version,
|
|
163
201
|
effect: (policy.effect as PolicyEffect | undefined) ?? "enforce",
|
|
164
202
|
sha256: policy.sha256,
|
|
165
203
|
path,
|
|
166
|
-
|
|
204
|
+
deployment: manifest.deployment,
|
|
167
205
|
};
|
|
168
206
|
});
|
|
169
207
|
}
|
|
@@ -33,6 +33,7 @@ import { dirname, resolve, sep } from "node:path";
|
|
|
33
33
|
import {
|
|
34
34
|
selectOne,
|
|
35
35
|
multiSelect,
|
|
36
|
+
BACK,
|
|
36
37
|
promptText,
|
|
37
38
|
intro,
|
|
38
39
|
outro,
|
|
@@ -126,6 +127,7 @@ export type WizardAbort =
|
|
|
126
127
|
| "cancelled"
|
|
127
128
|
| "needs_root"
|
|
128
129
|
| "daemon_failed"
|
|
130
|
+
| "unsupported_platform"
|
|
129
131
|
| "not_a_tty"
|
|
130
132
|
| "running_as_sudo";
|
|
131
133
|
|
|
@@ -415,6 +417,37 @@ export function describeCustomPolicies(cwd: string): {
|
|
|
415
417
|
return { active, warnings, fileCount, scopes };
|
|
416
418
|
}
|
|
417
419
|
|
|
420
|
+
/**
|
|
421
|
+
* The wizard's one-line completion summary. Pure and exported so the widest
|
|
422
|
+
* real combination — every policy, every CLI, custom/daemon/reporting all
|
|
423
|
+
* present — can be pinned by a test without having to drive the whole wizard
|
|
424
|
+
* through a real chdir + on-disk custom-policy fixture.
|
|
425
|
+
*
|
|
426
|
+
* Kept inside a standard 80-column terminal: `writeLines` truncates with a
|
|
427
|
+
* hard cut and no ellipsis, so an over-long line doesn't just lose its tail
|
|
428
|
+
* — it reads as broken output. Naming all ten CLIs once took it to 182
|
|
429
|
+
* characters; the count alone carries the same information, and the user
|
|
430
|
+
* picked them two screens ago. A single grouped "· a, b, c" clause bounds the
|
|
431
|
+
* optional notes to one separator and short tags, rather than three
|
|
432
|
+
* independent " · " clauses stacking up.
|
|
433
|
+
*/
|
|
434
|
+
export function buildCompletionSummary(
|
|
435
|
+
policiesCount: number,
|
|
436
|
+
harnessesCount: number,
|
|
437
|
+
customEnabled: boolean | undefined,
|
|
438
|
+
daemonInstalled: boolean,
|
|
439
|
+
connected: boolean,
|
|
440
|
+
): string {
|
|
441
|
+
const extras: string[] = [];
|
|
442
|
+
if (customEnabled === true) extras.push("custom");
|
|
443
|
+
else if (customEnabled === false) extras.push("custom off");
|
|
444
|
+
if (daemonInstalled) extras.push("daemon");
|
|
445
|
+
if (connected) extras.push("reporting");
|
|
446
|
+
const extrasNote = extras.length > 0 ? ` · ${extras.join(", ")}` : "";
|
|
447
|
+
const harnesses = `${harnessesCount} harness${harnessesCount === 1 ? "" : "es"}`;
|
|
448
|
+
return `Setup complete — ${policiesCount} policies · ${harnesses}${extrasNote}`;
|
|
449
|
+
}
|
|
450
|
+
|
|
418
451
|
export function reviewLines(state: {
|
|
419
452
|
/** What the scope step resolved to. Expands to one or two real scopes. */
|
|
420
453
|
target: SetupTarget;
|
|
@@ -441,9 +474,9 @@ export function reviewLines(state: {
|
|
|
441
474
|
? `This project (${homeify(cwd)})`
|
|
442
475
|
: "Everywhere (global)";
|
|
443
476
|
const lines: string[] = [];
|
|
444
|
-
const
|
|
477
|
+
const harnessNames = clis.map((c) => getIntegration(c).displayName);
|
|
445
478
|
lines.push(` Where : ${where}`);
|
|
446
|
-
lines.push(`
|
|
479
|
+
lines.push(` Harnesses : ${harnessNames.length ? summarize(harnessNames, "harnesses") : "(none)"}`);
|
|
447
480
|
// Zero is a deliberate answer, not a failed step — say so, and say where to
|
|
448
481
|
// change it, so the review screen doesn't read like the wizard lost the
|
|
449
482
|
// selection. Hooks still install; only the builtin set is empty.
|
|
@@ -712,7 +745,26 @@ export async function runConfigureWizard(io: WizardIO = {}): Promise<WizardResul
|
|
|
712
745
|
|
|
713
746
|
// Fire-and-forget: never block the wizard's first paint on telemetry.
|
|
714
747
|
void emit("configure_started", {});
|
|
715
|
-
|
|
748
|
+
|
|
749
|
+
// failproofaid — the only evaluator on a configured machine — only runs on
|
|
750
|
+
// Linux and macOS. Checked before intro() draws anything and before a
|
|
751
|
+
// single prompt is asked: completing setup anyway used to leave e.g. a
|
|
752
|
+
// Windows machine reading as configured while enforcing in-process with no
|
|
753
|
+
// fail-closed guarantee, which is worse than not being set up at all.
|
|
754
|
+
if (!isDaemonSupportedPlatform()) {
|
|
755
|
+
stdout.write(
|
|
756
|
+
`failproofai requires failproofaid, its background policy daemon, which runs on\n` +
|
|
757
|
+
`Linux and macOS only — not ${process.platform}. Setup cannot continue here: an\n` +
|
|
758
|
+
"installation with no daemon behind it would read as configured while enforcing\n" +
|
|
759
|
+
"nothing, which is worse than not being set up at all.\n\n" +
|
|
760
|
+
"Nothing was changed. This platform will be supported once failproofaid gains a\n" +
|
|
761
|
+
`${process.platform} service target.\n\n`,
|
|
762
|
+
);
|
|
763
|
+
void emit("configure_aborted", { reason: "unsupported_platform" });
|
|
764
|
+
return { applied: false, abort: "unsupported_platform" };
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
intro("let's set up failproofai", stdout);
|
|
716
768
|
|
|
717
769
|
const cancel = (): WizardResult => {
|
|
718
770
|
outro("Cancelled — nothing was changed.", { ok: false }, stdout);
|
|
@@ -733,9 +785,9 @@ export async function runConfigureWizard(io: WizardIO = {}): Promise<WizardResul
|
|
|
733
785
|
// Machine-level, so it is deliberately NOT gated on the scope chosen in the
|
|
734
786
|
// next step: one daemon serves every project on this machine.
|
|
735
787
|
//
|
|
736
|
-
//
|
|
737
|
-
//
|
|
738
|
-
//
|
|
788
|
+
// Always true here — the guard near the top of this function already
|
|
789
|
+
// refused setup on anything else. Kept as a real read (not a literal
|
|
790
|
+
// `true`) so this block still fails safe if that guard is ever moved.
|
|
739
791
|
const daemonSupported = isDaemonSupportedPlatform();
|
|
740
792
|
// An already-healthy daemon needs no install and no password. Re-running
|
|
741
793
|
// setup on a configured machine must not demand sudo for work that is
|
|
@@ -909,24 +961,38 @@ export async function runConfigureWizard(io: WizardIO = {}): Promise<WizardResul
|
|
|
909
961
|
// The assistants step below keeps its minimum deliberately: an empty CLI list
|
|
910
962
|
// does NOT mean "no assistants" there — `installHooksImpl` falls back to
|
|
911
963
|
// ["claude"], so letting it through would silently install for Claude.
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
964
|
+
// Steps 2 and 3 are navigable: ← on the harness step returns to the policy
|
|
965
|
+
// step with the previous answer still selected. Before this, changing an
|
|
966
|
+
// earlier answer meant abandoning setup and starting over, because a prompt
|
|
967
|
+
// had exactly one way out and it was `null`.
|
|
968
|
+
//
|
|
969
|
+
// The policy step itself takes no `allowBack`: the only thing before it is
|
|
970
|
+
// the scope question, which is frequently not asked at all (a single choice
|
|
971
|
+
// is stated, not prompted), so ← there would sometimes go nowhere.
|
|
972
|
+
let presets: string[] | null = null;
|
|
973
|
+
let clisSel: string[] | null = null;
|
|
974
|
+
while (clisSel === null) {
|
|
975
|
+
// Re-entering after a ← must show what was picked, not a blank slate.
|
|
976
|
+
// Selection state lives on each choice, so carry it back in.
|
|
977
|
+
// Loop-carried: narrowed to `null` on the first pass, repopulated on a ←.
|
|
978
|
+
const priorPresets = presets as string[] | null;
|
|
979
|
+
presets = await multiSelect<string>({
|
|
980
|
+
message: "What should we guard against?",
|
|
981
|
+
choices: priorPresets
|
|
982
|
+
? presetChoices.map((c) => ({ ...c, checked: priorPresets.includes(c.value) }))
|
|
983
|
+
: presetChoices,
|
|
984
|
+
summaryNoun: "bundles",
|
|
985
|
+
hint: "space toggles · combine presets · ↵ confirm · none is fine",
|
|
986
|
+
stdin,
|
|
987
|
+
stdout,
|
|
988
|
+
});
|
|
989
|
+
if (presets === null) return cancel();
|
|
925
990
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
991
|
+
// 3 — Which harnesses? An "Everything available" row protects every supported
|
|
992
|
+
// CLI (detected + set-up-ahead); when ticked it wins over the individual boxes.
|
|
993
|
+
const priorClis = clisSel as string[] | null;
|
|
994
|
+
const picked: string[] | typeof BACK | null = await multiSelect<string>({
|
|
995
|
+
message: "Which harnesses should it protect?",
|
|
930
996
|
choices: [
|
|
931
997
|
{
|
|
932
998
|
label: "Everything available",
|
|
@@ -934,19 +1000,30 @@ export async function runConfigureWizard(io: WizardIO = {}): Promise<WizardResul
|
|
|
934
1000
|
// Counts only what this scope can actually take — expanding to all 12
|
|
935
1001
|
// under project scope is what crashed the apply on Hermes.
|
|
936
1002
|
hint: `protect all ${clisSupportingScope(primaryScope).length} CLIs configurable here`,
|
|
937
|
-
// A selector, not
|
|
1003
|
+
// A selector, not a harness. Counting it gave "13 harnesses" for
|
|
938
1004
|
// the 12 supported CLIs, and listed "Everything available" among them.
|
|
939
1005
|
summaryExclude: true,
|
|
940
1006
|
},
|
|
941
1007
|
...buildAgentChoices(primaryScope, cwd),
|
|
942
|
-
],
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1008
|
+
].map((c) => (priorClis ? { ...c, checked: priorClis.includes(c.value) } : c)),
|
|
1009
|
+
minSelected: 1,
|
|
1010
|
+
summaryNoun: "harnesses",
|
|
1011
|
+
hint: "detected CLIs are pre-selected · space toggles · ctrl+a all · ← back · ↵ confirm",
|
|
1012
|
+
allowBack: true as const,
|
|
1013
|
+
stdin,
|
|
1014
|
+
stdout,
|
|
1015
|
+
});
|
|
1016
|
+
if (picked === null) return cancel();
|
|
1017
|
+
// ← re-runs the loop, which re-asks the policy step with its answer intact.
|
|
1018
|
+
if (picked === BACK) continue;
|
|
1019
|
+
clisSel = picked;
|
|
1020
|
+
}
|
|
1021
|
+
// Non-null by construction: the loop only exits once both are assigned.
|
|
1022
|
+
const chosenPresets: string[] = presets ?? [];
|
|
1023
|
+
const policies = resolvePresetSelection(chosenPresets);
|
|
1024
|
+
// Only meaningful when there are files to switch off; with none, the row is
|
|
1025
|
+
// locked-unchecked and must not write a disabling flag.
|
|
1026
|
+
const customEnabled = hasCustomFiles ? chosenPresets.includes(CUSTOM) : undefined;
|
|
950
1027
|
// Filter to what the chosen scopes support in BOTH branches: "Everything
|
|
951
1028
|
// available" must not expand to CLIs that cannot take any selected scope,
|
|
952
1029
|
// and a locked row can't be ticked but belt-and-braces keeps the invariant
|
|
@@ -987,17 +1064,21 @@ export async function runConfigureWizard(io: WizardIO = {}): Promise<WizardResul
|
|
|
987
1064
|
" transcripts — prompts, file contents and command output — to your",
|
|
988
1065
|
" dashboard. Staying local sends nothing, anywhere, ever.",
|
|
989
1066
|
],
|
|
1067
|
+
// Cloud first, and therefore preselected: connecting is what most people
|
|
1068
|
+
// running this wizard came to do, and the local path stays one keystroke
|
|
1069
|
+
// away. Reversing these two is the whole change — neither option's copy
|
|
1070
|
+
// moved, so "stay local" is still stated as plainly as it was.
|
|
990
1071
|
choices: [
|
|
991
|
-
{
|
|
992
|
-
label: "Not now — stay local",
|
|
993
|
-
value: "local",
|
|
994
|
-
hint: "policies still enforce · connect later with failproofai config --connect",
|
|
995
|
-
},
|
|
996
1072
|
{
|
|
997
1073
|
label: "Paste an API key",
|
|
998
1074
|
value: "key",
|
|
999
1075
|
hint: "reports decisions and transcripts to your dashboard",
|
|
1000
1076
|
},
|
|
1077
|
+
{
|
|
1078
|
+
label: "Not now — stay local",
|
|
1079
|
+
value: "local",
|
|
1080
|
+
hint: "policies still enforce · connect later with failproofai config --connect",
|
|
1081
|
+
},
|
|
1001
1082
|
],
|
|
1002
1083
|
stdin,
|
|
1003
1084
|
stdout,
|
|
@@ -1319,7 +1400,7 @@ export async function runConfigureWizard(io: WizardIO = {}): Promise<WizardResul
|
|
|
1319
1400
|
cli: clis,
|
|
1320
1401
|
cli_count: clis.length,
|
|
1321
1402
|
policy_count: policies.length,
|
|
1322
|
-
source:
|
|
1403
|
+
source: chosenPresets.join("+"),
|
|
1323
1404
|
connected: connect !== null,
|
|
1324
1405
|
});
|
|
1325
1406
|
|
|
@@ -1411,22 +1492,12 @@ export async function runConfigureWizard(io: WizardIO = {}): Promise<WizardResul
|
|
|
1411
1492
|
// And any record of an earlier failure is now false: this machine got set up.
|
|
1412
1493
|
clearOnboardingAttempt();
|
|
1413
1494
|
|
|
1414
|
-
//
|
|
1415
|
-
//
|
|
1416
|
-
//
|
|
1417
|
-
//
|
|
1418
|
-
// screens ago.
|
|
1419
|
-
const customNote =
|
|
1420
|
-
customEnabled === true
|
|
1421
|
-
? " + your custom policies"
|
|
1422
|
-
: customEnabled === false
|
|
1423
|
-
? " · custom policies DISABLED"
|
|
1424
|
-
: "";
|
|
1425
|
-
const daemonNote = daemonInstalled ? " · daemon on" : "";
|
|
1426
|
-
const cloudNote = connected ? " · reporting on" : "";
|
|
1427
|
-
const assistants = `${clis.length} assistant${clis.length === 1 ? "" : "s"}`;
|
|
1495
|
+
// Every real completed setup is on a supported platform now (an unsupported
|
|
1496
|
+
// one aborts before this point), so the optional notes in the summary below
|
|
1497
|
+
// are no longer occasional additions — see buildCompletionSummary's own doc
|
|
1498
|
+
// comment for why the widest combination still fits in 80 columns.
|
|
1428
1499
|
outro(
|
|
1429
|
-
|
|
1500
|
+
buildCompletionSummary(policies.length, clis.length, customEnabled, daemonInstalled, connected),
|
|
1430
1501
|
{ ok: true },
|
|
1431
1502
|
stdout,
|
|
1432
1503
|
);
|