failproofai 1.0.2-beta.0 → 1.0.2-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 +5 -5
- 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/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.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.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.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.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.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]__0o07qi9._.js +1 -1
- 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/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- 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]__07gm3zl._.js +14 -14
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__090sxxw._.js +3 -0
- 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]__0oa1lav._.js +1 -1
- 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]__0s740oi._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0wb8osu._.js +23 -0
- package/.next/standalone/.next/server/chunks/ssr/{_12l4a_k._.js → _1oggjel._.js} +1 -1
- 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/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/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- 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/{3y54nw0i7dsjl.js → 0nnbtp0w4sxrr.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0sa9v5mhc8861.js → 0ykd_go4oahan.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0eqky-7n7dgdm.js → 140ip21rqmelt.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2i2yt3hzjredm.js → 1da5a_a-3btas.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1oml7c7env4-g.js → 1fac8krph-zgp.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2hs7ycvsav7v2.js → 1nzy5jc_vf9e0.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1sl2xmn9inx3y.js → 1yw71gn8vqco1.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1nuqytbta44af.js → 29eh4k77otcw6.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2aawsjld9ied_.js → 2axi5ehjbld9h.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2gn7ut4svofm2.js → 2fzxl7n717pbc.js} +1 -1
- package/.next/standalone/.next/static/chunks/{35xg6retpe6rp.js → 3jjmdqqzem21x.js} +1 -1
- package/.next/standalone/.next/static/chunks/{turbopack-3kjgdgwci4tqf.js → turbopack-2vqpa-dts1gy4.js} +1 -1
- package/.next/standalone/app/actions/pack-actions.ts +21 -11
- package/.next/standalone/app/policies/hooks-client.tsx +3 -4
- package/.next/standalone/fp-cloud-cli/fp_cli/_version.py +1 -1
- package/.next/standalone/package.json +10 -11
- package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -1
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +455 -316
- package/dist/cli.mjs +2374 -1873
- package/dist/worker.mjs +3 -13
- package/package.json +10 -11
- package/src/hooks/configure-wizard.ts +67 -444
- package/src/hooks/fp-reset.ts +10 -19
- package/src/hooks/handler.ts +2 -2
- package/src/hooks/manager.ts +58 -24
- package/src/hooks/pack-cli.ts +488 -43
- package/src/hooks/pack-failclosed.ts +1 -1
- package/src/hooks/pack-manifest.ts +14 -6
- package/src/hooks/pack-store.ts +89 -128
- package/src/hooks/tui.ts +150 -48
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0zig5sv._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_00e9tnf._.js +0 -23
- package/policy-pack/SHA256SUMS +0 -2
- package/policy-pack/failproofai-pack.json +0 -732
- package/policy-pack/failproofai-pack.mjs +0 -2017
- package/src/hooks/policy-presets.ts +0 -137
- /package/.next/standalone/.next/static/{nwbyWT06Ud0XdXOrVJyvS → A7czO1PZdTcNzLGa_H1sm}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{nwbyWT06Ud0XdXOrVJyvS → A7czO1PZdTcNzLGa_H1sm}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{nwbyWT06Ud0XdXOrVJyvS → A7czO1PZdTcNzLGa_H1sm}/_ssgManifest.js +0 -0
|
@@ -33,7 +33,6 @@ import { dirname, resolve, sep } from "node:path";
|
|
|
33
33
|
import {
|
|
34
34
|
selectOne,
|
|
35
35
|
multiSelect,
|
|
36
|
-
BACK,
|
|
37
36
|
promptText,
|
|
38
37
|
intro,
|
|
39
38
|
outro,
|
|
@@ -54,8 +53,7 @@ import {
|
|
|
54
53
|
} from "./integrations";
|
|
55
54
|
import { INTEGRATION_TYPES, type IntegrationType, type HookScope } from "./types";
|
|
56
55
|
import { installHooks } from "./manager";
|
|
57
|
-
import { getConfigPathForScope,
|
|
58
|
-
import { POLICY_PRESETS, resolvePreset, resolveEverything, RECOMMENDED_POLICIES } from "./policy-presets";
|
|
56
|
+
import { getConfigPathForScope, readScopedHooksConfig } from "./hooks-config";
|
|
59
57
|
import { discoverPolicyFiles, findSkippedPolicyFiles } from "./custom-hooks-loader";
|
|
60
58
|
import { trackHookEvent } from "./hook-telemetry";
|
|
61
59
|
import { getInstanceId } from "../../lib/telemetry-id";
|
|
@@ -91,7 +89,6 @@ import {
|
|
|
91
89
|
import {
|
|
92
90
|
detectSetupState,
|
|
93
91
|
isConfigured,
|
|
94
|
-
buildTargetChoices,
|
|
95
92
|
scopesFor,
|
|
96
93
|
type SetupTarget,
|
|
97
94
|
} from "./setup-state";
|
|
@@ -226,175 +223,7 @@ export function buildAgentChoices(scope: HookScope, cwd: string): MultiChoice<In
|
|
|
226
223
|
});
|
|
227
224
|
}
|
|
228
225
|
|
|
229
|
-
const EVERYTHING = "__everything__";
|
|
230
226
|
const ALL_CLIS = "__all_clis__";
|
|
231
|
-
/** Sentinel for the locked "Custom" row — informational, never resolves to
|
|
232
|
-
* builtin policy names (custom policies load by convention, not by config). */
|
|
233
|
-
const CUSTOM = "__custom__";
|
|
234
|
-
/**
|
|
235
|
-
* Sentinel for the locked "enabled individually" row.
|
|
236
|
-
*
|
|
237
|
-
* Policies enabled one at a time (`failproofai policies add <name>`) need not map
|
|
238
|
-
* onto any preset, so seeding the preset boxes cannot represent them. The wizard
|
|
239
|
-
* writes with `replace: true`, which makes the ticked set the WHOLE enabled set —
|
|
240
|
-
* so anything this row stands for must be unioned back in, or confirming the
|
|
241
|
-
* wizard would silently drop it. Locked and pre-checked, because it reports a
|
|
242
|
-
* state rather than offering a choice.
|
|
243
|
-
*/
|
|
244
|
-
const INDIVIDUAL = "__individual__";
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Split what is enabled now into the bundles that cover it and the leftovers.
|
|
248
|
-
*
|
|
249
|
-
* A pure function, and the SINGLE definition of that split — `buildPresetChoices`
|
|
250
|
-
* renders it and the wizard writes from it, so the row the user sees and the set
|
|
251
|
-
* that gets written can never disagree. The first version of this derived the
|
|
252
|
-
* leftovers by parsing them back out of the row's hint text, which coupled a
|
|
253
|
-
* display string to enforcement behaviour and would have broken on any policy
|
|
254
|
-
* name containing the separator.
|
|
255
|
-
*/
|
|
256
|
-
export function splitEnabled(currentlyEnabled: readonly string[] = []): {
|
|
257
|
-
/** Preset ids (or `EVERYTHING`) whose policies are all already enabled. */
|
|
258
|
-
presets: string[];
|
|
259
|
-
/** Enabled policies no ticked bundle accounts for. */
|
|
260
|
-
individual: string[];
|
|
261
|
-
} {
|
|
262
|
-
const current = new Set(currentlyEnabled);
|
|
263
|
-
// A bundle is ticked when everything it turns on is already on. Not "any", or
|
|
264
|
-
// one shared policy would tick every bundle containing it and confirming would
|
|
265
|
-
// enable all of them.
|
|
266
|
-
const isOn = (policies: string[]) =>
|
|
267
|
-
policies.length > 0 && policies.every((name) => current.has(name));
|
|
268
|
-
|
|
269
|
-
const everything = resolveEverything();
|
|
270
|
-
const presets = isOn(everything)
|
|
271
|
-
? [EVERYTHING]
|
|
272
|
-
: POLICY_PRESETS.filter((p) => isOn(resolvePreset(p.id))).map((p) => p.id);
|
|
273
|
-
|
|
274
|
-
// Against the TICKED bundles, not all of them: a policy belonging only to a
|
|
275
|
-
// bundle the user has NOT enabled is still enabled, and that is the fact the
|
|
276
|
-
// locked row exists to make visible.
|
|
277
|
-
const accounted = new Set(
|
|
278
|
-
presets.flatMap((id) => (id === EVERYTHING ? everything : resolvePreset(id))),
|
|
279
|
-
);
|
|
280
|
-
const individual = [...current].filter((name) => !accounted.has(name)).sort();
|
|
281
|
-
return { presets, individual };
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/** The themed preset bundles for the wizard's multi-select, plus an "Everything"
|
|
285
|
-
* option that enables the full builtin policy set. */
|
|
286
|
-
export function buildPresetChoices(
|
|
287
|
-
cwd: string = process.cwd(),
|
|
288
|
-
enabled = true,
|
|
289
|
-
/**
|
|
290
|
-
* What is enabled at this scope RIGHT NOW, used to tick the boxes.
|
|
291
|
-
*
|
|
292
|
-
* Without it every row rendered unticked on every run while the wizard wrote
|
|
293
|
-
* with `replace: true` — so re-running setup showed a blank slate and then made
|
|
294
|
-
* that blank slate authoritative, discarding the user's selection with nothing
|
|
295
|
-
* on screen to say it had happened. The comment on the Custom row below has
|
|
296
|
-
* always described the intended behaviour ("shows the current state rather than
|
|
297
|
-
* resetting it every run"); it was implemented for that one row out of eight.
|
|
298
|
-
*
|
|
299
|
-
* Optional so the first-run call sites stay unchanged: an empty set ticks
|
|
300
|
-
* nothing, which is the correct rendering for a machine with no selection.
|
|
301
|
-
*/
|
|
302
|
-
currentlyEnabled: readonly string[] = [],
|
|
303
|
-
) {
|
|
304
|
-
const { presets: onPresets, individual } = splitEnabled(currentlyEnabled);
|
|
305
|
-
const on = new Set(onPresets);
|
|
306
|
-
|
|
307
|
-
const choices: MultiChoice<string>[] = POLICY_PRESETS.map((p) => ({
|
|
308
|
-
label: p.label,
|
|
309
|
-
value: p.id,
|
|
310
|
-
hint: p.description,
|
|
311
|
-
checked: on.has(p.id),
|
|
312
|
-
}));
|
|
313
|
-
choices.push({
|
|
314
|
-
label: "Everything",
|
|
315
|
-
value: EVERYTHING,
|
|
316
|
-
hint: `all ${resolveEverything().length} policies`,
|
|
317
|
-
checked: on.has(EVERYTHING),
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
// The Custom row is ALWAYS present, because it is the only place the feature
|
|
321
|
-
// is discoverable: a user who has never written a policy cannot learn the
|
|
322
|
-
// capability exists, and one who wrote a badly-named file cannot learn why
|
|
323
|
-
// nothing happened.
|
|
324
|
-
//
|
|
325
|
-
// When there are loadable files it is a REAL checkbox — unticking writes
|
|
326
|
-
// `customPoliciesEnabled: false`, which switches convention discovery off
|
|
327
|
-
// without renaming or deleting anything. With nothing to toggle (no files,
|
|
328
|
-
// or only skipped ones) it falls back to a locked status row.
|
|
329
|
-
const custom = describeCustomPolicies(cwd);
|
|
330
|
-
const skipped = custom.warnings.length;
|
|
331
|
-
const plural = (n: number) => `${n} file${n === 1 ? "" : "s"}`;
|
|
332
|
-
const skippedNote = skipped > 0 ? ` · ${skipped} skipped, see next screen` : "";
|
|
333
|
-
|
|
334
|
-
if (custom.fileCount > 0) {
|
|
335
|
-
choices.push({
|
|
336
|
-
label: "Custom",
|
|
337
|
-
value: CUSTOM,
|
|
338
|
-
checked: enabled,
|
|
339
|
-
// Deliberately NOT summaryExclude'd: this one is a real choice, and the
|
|
340
|
-
// step summary is the only place the user sees what they picked. Hiding
|
|
341
|
-
// it meant unticking Custom and every bundle showed "none", giving no
|
|
342
|
-
// way to tell the toggle had registered.
|
|
343
|
-
hint: `${plural(custom.fileCount)} in ${custom.scopes.join(" + ")}${skippedNote}`,
|
|
344
|
-
});
|
|
345
|
-
} else {
|
|
346
|
-
choices.push({
|
|
347
|
-
label: "Custom",
|
|
348
|
-
value: CUSTOM,
|
|
349
|
-
locked: true,
|
|
350
|
-
checked: false,
|
|
351
|
-
summaryExclude: true,
|
|
352
|
-
hint:
|
|
353
|
-
skipped > 0
|
|
354
|
-
? `${plural(skipped)} found but NOT loaded — see next screen`
|
|
355
|
-
: "none yet · drop *-policies.mjs in .failproofai/policies/",
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
if (individual.length > 0) {
|
|
359
|
-
choices.push({
|
|
360
|
-
label: `${individual.length} enabled individually`,
|
|
361
|
-
value: INDIVIDUAL,
|
|
362
|
-
locked: true,
|
|
363
|
-
hint: `kept as-is · ${individual.join(", ")}`,
|
|
364
|
-
// Not one of the bundles being counted, like the Everything and Custom rows.
|
|
365
|
-
summaryExclude: true,
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
return choices;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Resolve the ticked options to a concrete policy set. Presets are additive —
|
|
374
|
-
* the deduped union of every selected preset's policies — while "Everything"
|
|
375
|
-
* enables the full policy set and wins over any presets.
|
|
376
|
-
*/
|
|
377
|
-
export function resolvePresetSelection(
|
|
378
|
-
values: string[],
|
|
379
|
-
/**
|
|
380
|
-
* What the locked "enabled individually" row stands for. Unioned in whenever
|
|
381
|
-
* that row is present, INCLUDING under "Everything": `resolveEverything()`
|
|
382
|
-
* covers the non-beta builtins only, so a beta policy someone enabled by hand
|
|
383
|
-
* would otherwise be dropped by the very branch meant to enable everything.
|
|
384
|
-
*/
|
|
385
|
-
individual: readonly string[] = [],
|
|
386
|
-
): string[] {
|
|
387
|
-
// The Custom row is informational — custom policies are discovered from disk
|
|
388
|
-
// by the loader, never named in the enabled-policies config — so it must not
|
|
389
|
-
// reach resolvePreset(), which only knows builtin bundle ids. Same for the
|
|
390
|
-
// locked individually-enabled row, which carries its policies in `individual`.
|
|
391
|
-
const selected = values.filter((v) => v !== CUSTOM && v !== INDIVIDUAL);
|
|
392
|
-
const carried = values.includes(INDIVIDUAL) ? individual : [];
|
|
393
|
-
if (selected.includes(EVERYTHING)) {
|
|
394
|
-
return [...new Set([...resolveEverything(), ...carried])];
|
|
395
|
-
}
|
|
396
|
-
return [...new Set([...selected.flatMap((id) => resolvePreset(id)), ...carried])];
|
|
397
|
-
}
|
|
398
227
|
|
|
399
228
|
const DIM_NOTE = "(auto-loaded)";
|
|
400
229
|
|
|
@@ -528,15 +357,6 @@ export function buildCompletionSummary(
|
|
|
528
357
|
customEnabled: boolean | undefined,
|
|
529
358
|
daemonInstalled: boolean,
|
|
530
359
|
connected: boolean,
|
|
531
|
-
/**
|
|
532
|
-
* What was ticked on the policy step, so the summary can NAME the bundles.
|
|
533
|
-
*
|
|
534
|
-
* "9 policies" is a number the user cannot check and did not choose — they
|
|
535
|
-
* picked two named bundles two screens earlier, and the line that confirms
|
|
536
|
-
* their setup should say which. Optional so the existing callers and tests
|
|
537
|
-
* that only have a count keep working and keep the old wording.
|
|
538
|
-
*/
|
|
539
|
-
presetValues?: readonly string[],
|
|
540
360
|
): string {
|
|
541
361
|
const extras: string[] = [];
|
|
542
362
|
if (customEnabled === true) extras.push("custom");
|
|
@@ -554,9 +374,7 @@ export function buildCompletionSummary(
|
|
|
554
374
|
// full line will not fit, which is checked rather than guessed at: the extras
|
|
555
375
|
// clause grows too ("custom, daemon, reporting" is 25 characters), so a names
|
|
556
376
|
// budget alone was wrong for exactly the combinations that need it most.
|
|
557
|
-
|
|
558
|
-
if (named.length <= MAX_SUMMARY_COLUMNS) return named;
|
|
559
|
-
return line(describeSelection(policiesCount, undefined));
|
|
377
|
+
return line(describeSelection(policiesCount));
|
|
560
378
|
}
|
|
561
379
|
|
|
562
380
|
/**
|
|
@@ -603,43 +421,13 @@ export function policyNamesLine(names: string[]): string[] {
|
|
|
603
421
|
return [];
|
|
604
422
|
}
|
|
605
423
|
|
|
606
|
-
/**
|
|
607
|
-
*
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
* ellipsis, so an over-long line does not lose its tail, it reads as broken
|
|
611
|
-
* output — the same constraint that stopped this summary naming all twelve CLIs.
|
|
612
|
-
* All four bundle labels joined is 57 characters, which with the prefix, the
|
|
613
|
-
* harness clause and the extras clause runs to about 106. Two names plus a count
|
|
614
|
-
* of the rest stays inside 80 for every combination, and two is also the common
|
|
615
|
-
* case, so most runs see every name.
|
|
616
|
-
*
|
|
617
|
-
* Falls back to the old "N policies" when nothing maps to a bundle — a machine
|
|
618
|
-
* whose policies were all enabled one at a time with `policies add` has no bundle
|
|
619
|
-
* to name, and inventing one would be worse than the count.
|
|
620
|
-
*/
|
|
621
|
-
function describeSelection(policiesCount: number, presetValues?: readonly string[]): string {
|
|
622
|
-
const plural = `${policiesCount} polic${policiesCount === 1 ? "y" : "ies"}`;
|
|
623
|
-
if (!presetValues) return plural;
|
|
624
|
-
|
|
625
|
-
// "Everything" is one name for the whole set, and the count is the useful half
|
|
626
|
-
// of it — "Everything" alone does not say how much that is.
|
|
627
|
-
if (presetValues.includes(EVERYTHING)) return `Everything (${plural})`;
|
|
628
|
-
|
|
629
|
-
const named = POLICY_PRESETS.filter((p) => presetValues.includes(p.id)).map((p) => p.label);
|
|
630
|
-
// The locked "enabled individually" row stands for policies outside every
|
|
631
|
-
// bundle, so it is counted among the unnamed rest rather than named.
|
|
632
|
-
const individual = presetValues.includes(INDIVIDUAL) ? 1 : 0;
|
|
633
|
-
if (named.length === 0) return plural;
|
|
634
|
-
|
|
635
|
-
const shown = named.slice(0, 2);
|
|
636
|
-
const rest = named.length - shown.length + individual;
|
|
637
|
-
// `+N` rather than `+N more`: five characters, and they decide whether the
|
|
638
|
-
// mixed case (two bundles plus a policy added by hand) gets named at all — with
|
|
639
|
-
// "more" the line is 83 and falls back to a bare count.
|
|
640
|
-
return rest > 0 ? `${shown.join(", ")} +${rest}` : shown.join(", ");
|
|
424
|
+
/** The Policies line's value. With policies no longer chosen during setup, this
|
|
425
|
+
* reports what is enabled and nothing about how it got that way. */
|
|
426
|
+
function describeSelection(policiesCount: number): string {
|
|
427
|
+
return `${policiesCount} ${policiesCount === 1 ? "policy" : "policies"}`;
|
|
641
428
|
}
|
|
642
429
|
|
|
430
|
+
|
|
643
431
|
export function reviewLines(state: {
|
|
644
432
|
/** What the scope step resolved to. Expands to one or two real scopes. */
|
|
645
433
|
target: SetupTarget;
|
|
@@ -1104,247 +892,84 @@ export async function runConfigureWizard(io: WizardIO = {}): Promise<WizardResul
|
|
|
1104
892
|
|
|
1105
893
|
// 0 — Recommended, or choose everything yourself?
|
|
1106
894
|
//
|
|
1107
|
-
//
|
|
1108
|
-
// harnesses, cloud — of somebody who has just installed the tool and does not
|
|
1109
|
-
// yet know what any of them mean. Every one of those answers has a defensible
|
|
1110
|
-
// default, so asking for all four up front is making the person least able to
|
|
1111
|
-
// answer do the most work.
|
|
1112
|
-
//
|
|
1113
|
-
// Recommended is not a shortcut past the decisions; it IS a decision, taken
|
|
1114
|
-
// once, here, on their behalf: global scope, the CLIs actually on this
|
|
1115
|
-
// machine, and `RECOMMENDED_POLICIES` — which is written out in
|
|
1116
|
-
// policy-presets.ts with the reasoning for every inclusion and every
|
|
1117
|
-
// omission, because "what does Recommended do" has to be answerable without
|
|
1118
|
-
// reading code.
|
|
895
|
+
// ONE linear flow, and no opening fork.
|
|
1119
896
|
//
|
|
1120
|
-
//
|
|
1121
|
-
// is
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
message: "Set up failproofai",
|
|
1126
|
-
choices: [
|
|
1127
|
-
{
|
|
1128
|
-
label: "Recommended",
|
|
1129
|
-
value: "recommended",
|
|
1130
|
-
hint: recommendedClis.length
|
|
1131
|
-
? `${recommendedClis.length} detected ${recommendedClis.length === 1 ? "CLI" : "CLIs"} · ${RECOMMENDED_POLICIES.length} policies · global`
|
|
1132
|
-
: `${RECOMMENDED_POLICIES.length} policies · global`,
|
|
1133
|
-
},
|
|
1134
|
-
{
|
|
1135
|
-
label: "Customize",
|
|
1136
|
-
value: "customize",
|
|
1137
|
-
hint: "choose scope, policies and harnesses",
|
|
1138
|
-
},
|
|
1139
|
-
],
|
|
1140
|
-
stdin,
|
|
1141
|
-
stdout,
|
|
1142
|
-
});
|
|
1143
|
-
if (mode === null) return cancel();
|
|
1144
|
-
|
|
1145
|
-
// 1 — Where? Inferred from cwd, then confirmed.
|
|
897
|
+
// Setup used to ask "Recommended or Customize?" before anything else, which
|
|
898
|
+
// is a question about the wizard rather than about the machine — you cannot
|
|
899
|
+
// answer it until you know what the alternatives are, and you only learn that
|
|
900
|
+
// by picking one. Recommended then silently took global scope, the detected
|
|
901
|
+
// CLIs, and fifteen policies nobody had seen.
|
|
1146
902
|
//
|
|
1147
|
-
//
|
|
1148
|
-
//
|
|
1149
|
-
//
|
|
1150
|
-
//
|
|
1151
|
-
//
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
if (mode === "recommended") {
|
|
1157
|
-
// Global, always. A project-scoped install guards the one directory the
|
|
1158
|
-
// command happened to be run from and silently leaves every other repo on
|
|
1159
|
-
// the machine unguarded — which is the opposite of what somebody choosing
|
|
1160
|
-
// "Recommended" is asking for.
|
|
1161
|
-
target = "user";
|
|
1162
|
-
} else if (targetChoices.length === 1) {
|
|
1163
|
-
// From a home directory there is no project to configure, so there is no
|
|
1164
|
-
// question to ask. Say what is about to happen rather than silently
|
|
1165
|
-
// deciding it.
|
|
1166
|
-
target = targetChoices[0].value;
|
|
1167
|
-
stdout.write(`Configuring ${targetChoices[0].label.toLowerCase()} — ${targetChoices[0].hint}.\n\n`);
|
|
1168
|
-
} else {
|
|
1169
|
-
const chosen = await selectOne<SetupTarget>({
|
|
1170
|
-
message: "What are we configuring?",
|
|
1171
|
-
choices: targetChoices.map((c) => ({
|
|
1172
|
-
label: c.label,
|
|
1173
|
-
value: c.value,
|
|
1174
|
-
hint: c.hint,
|
|
1175
|
-
})),
|
|
1176
|
-
stdin,
|
|
1177
|
-
stdout,
|
|
1178
|
-
});
|
|
1179
|
-
if (chosen === null) return cancel();
|
|
1180
|
-
target = chosen;
|
|
1181
|
-
}
|
|
903
|
+
// What is left is three questions, in the order the machine needs them:
|
|
904
|
+
// the daemon (already installed above, because it is the only step that needs
|
|
905
|
+
// a password), then which harnesses, then whether to connect. Scope is not
|
|
906
|
+
// among them: it is GLOBAL, always. A project-scoped install guards the one
|
|
907
|
+
// directory the command happened to be run from and silently leaves every
|
|
908
|
+
// other repo on the machine unguarded — `failproofai policies --install
|
|
909
|
+
// --scope project` is still there for someone who genuinely wants that, and
|
|
910
|
+
// knows they do.
|
|
911
|
+
const target: SetupTarget = "user";
|
|
1182
912
|
const scopes = scopesFor(target);
|
|
1183
|
-
// The scope whose CURRENT state seeds the pickers below. With "Both" the
|
|
1184
|
-
// project is the more specific of the two and the one the user is standing
|
|
1185
|
-
// in, so it wins; anything it does not define still falls back to global at
|
|
1186
|
-
// merge time, which is exactly the layering the policy loader already does.
|
|
1187
913
|
const primaryScope: HookScope = scopes.includes("project") ? "project" : "user";
|
|
1188
914
|
|
|
1189
|
-
// 2 — Which
|
|
1190
|
-
// enabled set is the union of every ticked bundle.
|
|
1191
|
-
//
|
|
1192
|
-
// Before the assistants step, because "what do you want guarded" is the
|
|
1193
|
-
// question the user came here to answer; which CLIs to wire it into is
|
|
1194
|
-
// plumbing that follows from it.
|
|
915
|
+
// 2 — Which harnesses?
|
|
1195
916
|
//
|
|
1196
|
-
//
|
|
1197
|
-
//
|
|
917
|
+
// Setup no longer asks which policies to enable, and that is deliberate.
|
|
918
|
+
// failproofai ships no policies of its own any more: they arrive as packs —
|
|
919
|
+
// from inside this package (`pack add core`) or from anyone's GitHub release.
|
|
920
|
+
// A wizard that pre-ticks OUR list makes a product decision on behalf of
|
|
921
|
+
// somebody who has not seen the list yet, and not everyone wants the set we
|
|
922
|
+
// would have chosen. So setup wires the hooks, and choosing what they
|
|
923
|
+
// enforce is a separate act, taken later and on purpose.
|
|
1198
924
|
//
|
|
1199
|
-
//
|
|
1200
|
-
// is called with `replace: true
|
|
1201
|
-
//
|
|
1202
|
-
//
|
|
1203
|
-
// wizard. `readHooksConfig()` stays for the custom flag, which is read the same
|
|
1204
|
-
// merged way everywhere else.
|
|
1205
|
-
const customEnabledBefore = readHooksConfig().customPoliciesEnabled !== false;
|
|
925
|
+
// Whatever is already enabled at THIS scope is read and carried through
|
|
926
|
+
// untouched. `installHooks` is called with `replace: true`, so passing
|
|
927
|
+
// anything less would switch OFF policies the user had turned on — running
|
|
928
|
+
// setup a second time must never reduce protection.
|
|
1206
929
|
const enabledHere = readScopedHooksConfig(primaryScope, cwd).enabledPolicies ?? [];
|
|
1207
|
-
const
|
|
1208
|
-
|
|
1209
|
-
//
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
//
|
|
1216
|
-
//
|
|
1217
|
-
// end to end: `installHooksImpl` documents its explicit-array path as "may be
|
|
1218
|
-
// empty", `replace: true` makes it the full enabled set, and `summarize([])`
|
|
1219
|
-
// renders "none". Hooks still install, so enforcement can be switched on
|
|
1220
|
-
// later without re-running setup.
|
|
930
|
+
const policies = enabledHere;
|
|
931
|
+
|
|
932
|
+
// Left alone, in both modes. This was a checkbox on the policy step, which is
|
|
933
|
+
// gone; with no row to read, the only honest value is "do not touch it".
|
|
934
|
+
// Writing `false` here switched off every convention policy on disk as a side
|
|
935
|
+
// effect of finishing setup.
|
|
936
|
+
const customEnabled: boolean | undefined = undefined;
|
|
937
|
+
|
|
938
|
+
// An "Everything available" row protects every supported CLI (detected +
|
|
939
|
+
// set-up-ahead); when ticked it wins over the individual boxes.
|
|
1221
940
|
//
|
|
1222
|
-
//
|
|
1223
|
-
//
|
|
1224
|
-
//
|
|
1225
|
-
// Steps 2 and 3 are navigable: ← on the harness step returns to the policy
|
|
1226
|
-
// step with the previous answer still selected. Before this, changing an
|
|
1227
|
-
// earlier answer meant abandoning setup and starting over, because a prompt
|
|
1228
|
-
// had exactly one way out and it was `null`.
|
|
941
|
+
// Always asked, never inferred. This is the one decision setup genuinely
|
|
942
|
+
// cannot make for you: which agents on this machine should be guarded. The
|
|
943
|
+
// detected ones are pre-ticked, so the common answer is a single keypress.
|
|
1229
944
|
//
|
|
1230
|
-
//
|
|
1231
|
-
//
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
* `presets` just above works because it is assigned MID-loop and survives to the
|
|
1250
|
-
* next iteration; this mirrors that, filled from the prompt's `onBack`.
|
|
1251
|
-
*/
|
|
1252
|
-
const carried: { clis: string[] | null } = { clis: null };
|
|
1253
|
-
// The recommended path answers both questions here, which is what SKIPS the
|
|
1254
|
-
// loop below — its condition is `clisSel === null` and this fills it in.
|
|
1255
|
-
// Written as a pre-fill rather than as an extra clause on the loop condition
|
|
1256
|
-
// so the invariant the rest of the function depends on ("past this loop, both
|
|
1257
|
-
// are assigned") stays provable by the compiler rather than by argument.
|
|
1258
|
-
if (mode === "recommended") {
|
|
1259
|
-
presets = [];
|
|
1260
|
-
clisSel = recommendedClis;
|
|
1261
|
-
}
|
|
1262
|
-
while (clisSel === null) {
|
|
1263
|
-
// Re-entering after a ← must show what was picked, not a blank slate.
|
|
1264
|
-
// Selection state lives on each choice, so carry it back in.
|
|
1265
|
-
// Loop-carried: narrowed to `null` on the first pass, repopulated on a ←.
|
|
1266
|
-
const priorPresets = presets as string[] | null;
|
|
1267
|
-
presets = await multiSelect<string>({
|
|
1268
|
-
message: "What should we guard against?",
|
|
1269
|
-
choices: priorPresets
|
|
1270
|
-
? presetChoices.map((c) => ({ ...c, checked: priorPresets.includes(c.value) }))
|
|
1271
|
-
: presetChoices,
|
|
1272
|
-
summaryNoun: "bundles",
|
|
1273
|
-
hint: "space toggles · combine presets · ↵ confirm · none is fine",
|
|
1274
|
-
stdin,
|
|
1275
|
-
stdout,
|
|
1276
|
-
});
|
|
1277
|
-
if (presets === null) return cancel();
|
|
1278
|
-
|
|
1279
|
-
// 3 — Which harnesses? An "Everything available" row protects every supported
|
|
1280
|
-
// CLI (detected + set-up-ahead); when ticked it wins over the individual boxes.
|
|
1281
|
-
// Read off a HOLDER OBJECT, not a bare `let`, and not through a cast.
|
|
1282
|
-
//
|
|
1283
|
-
// A `let` assigned only inside a callback is narrowed by control-flow analysis
|
|
1284
|
-
// to its initializer, so `priorClis.includes` will not compile — and the
|
|
1285
|
-
// original defeated that with `clisSel as string[] | null`. That cast is
|
|
1286
|
-
// precisely why the dead code type-checked and nobody noticed: it silenced the
|
|
1287
|
-
// compiler making exactly the point the reviewer later made by hand, that the
|
|
1288
|
-
// value could only ever be null. A property read carries the declared type
|
|
1289
|
-
// without suppressing anything.
|
|
1290
|
-
const priorClis = carried.clis;
|
|
1291
|
-
const picked: string[] | typeof BACK | null = await multiSelect<string>({
|
|
1292
|
-
message: "Which harnesses should it protect?",
|
|
1293
|
-
choices: [
|
|
1294
|
-
{
|
|
1295
|
-
label: "Everything available",
|
|
1296
|
-
value: ALL_CLIS,
|
|
1297
|
-
// Counts only what this scope can actually take — expanding to all 12
|
|
1298
|
-
// under project scope is what crashed the apply on Hermes.
|
|
1299
|
-
hint: `protect all ${clisSupportingScope(primaryScope).length} CLIs configurable here`,
|
|
1300
|
-
// A selector, not a harness. Counting it gave "13 harnesses" for
|
|
1301
|
-
// the 12 supported CLIs, and listed "Everything available" among them.
|
|
1302
|
-
summaryExclude: true,
|
|
1303
|
-
},
|
|
1304
|
-
...buildAgentChoices(primaryScope, cwd),
|
|
1305
|
-
].map((c) => (priorClis ? { ...c, checked: priorClis.includes(c.value) } : c)),
|
|
945
|
+
// No `allowBack`: with the policy step and the scope fork both gone, there is
|
|
946
|
+
// nothing inside setup to return to.
|
|
947
|
+
let clisSel: string[];
|
|
948
|
+
{
|
|
949
|
+
const picked = await multiSelect<string>({
|
|
950
|
+
message: "Which harnesses should it protect?",
|
|
951
|
+
choices: [
|
|
952
|
+
{
|
|
953
|
+
label: "Everything available",
|
|
954
|
+
value: ALL_CLIS,
|
|
955
|
+
// Counts only what this scope can actually take — expanding to all 12
|
|
956
|
+
// under project scope is what crashed the apply on Hermes.
|
|
957
|
+
hint: `protect all ${clisSupportingScope(primaryScope).length} CLIs configurable here`,
|
|
958
|
+
// A selector, not a harness. Counting it gave "13 harnesses" for
|
|
959
|
+
// the 12 supported CLIs, and listed "Everything available" among them.
|
|
960
|
+
summaryExclude: true,
|
|
961
|
+
},
|
|
962
|
+
...buildAgentChoices(primaryScope, cwd),
|
|
963
|
+
],
|
|
1306
964
|
minSelected: 1,
|
|
1307
965
|
summaryNoun: "harnesses",
|
|
1308
|
-
hint: "detected CLIs are pre-selected · space toggles · ctrl+a all ·
|
|
1309
|
-
allowBack: true as const,
|
|
1310
|
-
// `BACK` is a symbol and cannot carry the selection, so the prompt reports
|
|
1311
|
-
// it here instead — otherwise a ← discards what the user had ticked and the
|
|
1312
|
-
// next pass redraws the detected defaults.
|
|
1313
|
-
onBack: (checkedNow) => {
|
|
1314
|
-
carried.clis = checkedNow;
|
|
1315
|
-
},
|
|
966
|
+
hint: "detected CLIs are pre-selected · space toggles · ctrl+a all · ↵ confirm",
|
|
1316
967
|
stdin,
|
|
1317
968
|
stdout,
|
|
1318
969
|
});
|
|
1319
970
|
if (picked === null) return cancel();
|
|
1320
|
-
// ← re-runs the loop, which re-asks the policy step with its answer intact.
|
|
1321
|
-
if (picked === BACK) continue;
|
|
1322
971
|
clisSel = picked;
|
|
1323
972
|
}
|
|
1324
|
-
// Non-null by construction: the loop only exits once both are assigned, and
|
|
1325
|
-
// the recommended path assigned both before it, which is why it never ran.
|
|
1326
|
-
const chosenPresets: string[] = presets ?? [];
|
|
1327
|
-
const policies =
|
|
1328
|
-
mode === "recommended"
|
|
1329
|
-
? // UNION with what is already enabled here, never a replacement for it.
|
|
1330
|
-
// `installHooks` is called with `replace: true`, so writing the bare
|
|
1331
|
-
// recommended list would silently switch OFF anything the user had
|
|
1332
|
-
// added themselves — turning "give me the sensible defaults" into a
|
|
1333
|
-
// reduction in protection, which is the one direction this must never
|
|
1334
|
-
// move. On a fresh machine `enabledHere` is empty and this is exactly
|
|
1335
|
-
// the 15.
|
|
1336
|
-
[...new Set([...RECOMMENDED_POLICIES, ...enabledHere])]
|
|
1337
|
-
: resolvePresetSelection(chosenPresets, carriedIndividual);
|
|
1338
|
-
// Only meaningful when there are files to switch off; with none, the row is
|
|
1339
|
-
// locked-unchecked and must not write a disabling flag.
|
|
1340
|
-
//
|
|
1341
|
-
// `undefined` on the recommended path, which means "leave the flag alone".
|
|
1342
|
-
// The customize expression would read as `false` here — no bundle was ticked,
|
|
1343
|
-
// so `chosenPresets.includes(CUSTOM)` is false — and would write
|
|
1344
|
-
// `customPoliciesEnabled: false`, disabling every convention policy the user
|
|
1345
|
-
// has on disk as a side effect of choosing the default setup.
|
|
1346
|
-
const customEnabled =
|
|
1347
|
-
mode === "recommended" ? undefined : hasCustomFiles ? chosenPresets.includes(CUSTOM) : undefined;
|
|
1348
973
|
// Filter to what the chosen scopes support in BOTH branches: "Everything
|
|
1349
974
|
// available" must not expand to CLIs that cannot take any selected scope,
|
|
1350
975
|
// and a locked row can't be ticked but belt-and-braces keeps the invariant
|
|
@@ -1764,7 +1389,6 @@ export async function runConfigureWizard(io: WizardIO = {}): Promise<WizardResul
|
|
|
1764
1389
|
cli: clis,
|
|
1765
1390
|
cli_count: clis.length,
|
|
1766
1391
|
policy_count: policies.length,
|
|
1767
|
-
source: chosenPresets.join("+"),
|
|
1768
1392
|
connected: connect !== null,
|
|
1769
1393
|
});
|
|
1770
1394
|
|
|
@@ -1867,7 +1491,6 @@ export async function runConfigureWizard(io: WizardIO = {}): Promise<WizardResul
|
|
|
1867
1491
|
customEnabled,
|
|
1868
1492
|
daemonInstalled,
|
|
1869
1493
|
connected,
|
|
1870
|
-
chosenPresets,
|
|
1871
1494
|
),
|
|
1872
1495
|
{ ok: true },
|
|
1873
1496
|
stdout,
|
package/src/hooks/fp-reset.ts
CHANGED
|
@@ -98,8 +98,6 @@ import {
|
|
|
98
98
|
isDaemonSupportedPlatform,
|
|
99
99
|
probeDaemonEndToEnd,
|
|
100
100
|
} from "./daemon-service";
|
|
101
|
-
import { installBundledPack } from "./pack-store";
|
|
102
|
-
import { readHooksConfig } from "./hooks-config";
|
|
103
101
|
|
|
104
102
|
export interface ResetOutcome {
|
|
105
103
|
/** Paths that existed and were removed. */
|
|
@@ -966,24 +964,17 @@ export function resetHome(from: number, to: number = LAYOUT_VERSION): ResetOutco
|
|
|
966
964
|
// straight out of the same file. Both paths end at the same key, and only one
|
|
967
965
|
// of them represents something a person typed.
|
|
968
966
|
if (telemetryOptOut) updateConfig({ telemetry: { enabled: false } });
|
|
969
|
-
//
|
|
970
|
-
// enforcement floor. Restore it from the installed package before declaring
|
|
971
|
-
// the migration complete; third-party packs remain explicitly re-fetchable.
|
|
967
|
+
// No pack is installed here, deliberately.
|
|
972
968
|
//
|
|
973
|
-
//
|
|
974
|
-
//
|
|
975
|
-
//
|
|
976
|
-
//
|
|
977
|
-
//
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
} catch {
|
|
983
|
-
return undefined;
|
|
984
|
-
}
|
|
985
|
-
})();
|
|
986
|
-
installBundledPack(carried);
|
|
969
|
+
// There is no copy in this package to install — our policies are fetched from
|
|
970
|
+
// their GitHub release like anybody else's — and `resetHome` is SYNCHRONOUS
|
|
971
|
+
// and runs inside `failproofai update`. An upgrade that blocks on github.com,
|
|
972
|
+
// and fails when it is unreachable, is a worse upgrade than one that finishes.
|
|
973
|
+
//
|
|
974
|
+
// Nothing needs carrying either: `enabledPolicies` is left exactly where it
|
|
975
|
+
// is, and the no-pack fallback reads it, so a machine mid-upgrade keeps
|
|
976
|
+
// enforcing precisely what it enforced before. `failproofai policies add core`
|
|
977
|
+
// turns those names into a real pack, carrying the same selection into it.
|
|
987
978
|
// The step's OWN target, not LAYOUT_VERSION.
|
|
988
979
|
//
|
|
989
980
|
// Every step used to end stamping the current layout, which was harmless
|