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
package/src/hooks/pack-store.ts
CHANGED
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
* Asset URLs are CONSTRUCTED from an owner, repo and concrete tag. When the
|
|
19
19
|
* user omits a tag, one `releases/latest` redirect resolves it before any asset
|
|
20
20
|
* is fetched or written. There is no API lookup or rate-limit dependency, and
|
|
21
|
-
* the installed record always names the pinned result
|
|
21
|
+
* the installed record always names the pinned result — which has to AGREE with
|
|
22
|
+
* the version that pack's manifest declares, or nothing is installed at all
|
|
23
|
+
* (`packTagMatchesVersion`).
|
|
22
24
|
*
|
|
23
25
|
* ## Never on the hook path
|
|
24
26
|
*
|
|
@@ -157,6 +159,14 @@ function validated(owner: string, repo: string, tag: string | null): PackSpec {
|
|
|
157
159
|
* unauthenticated rate limit, and stays pointed at whatever
|
|
158
160
|
* `FAILPROOFAI_PACK_BASE_URL` names — which is how a mirror, and the tests,
|
|
159
161
|
* work at all.
|
|
162
|
+
*
|
|
163
|
+
* The price of that, and it is a real one: the redirect is issued only for a
|
|
164
|
+
* PUBLISHED, non-prerelease release. A repository whose newest release is a
|
|
165
|
+
* prerelease or a draft therefore either redirects to an OLDER stable tag —
|
|
166
|
+
* pinning something the publisher has already superseded — or issues no
|
|
167
|
+
* redirect at all. Only the second is detectable from here, so the error names
|
|
168
|
+
* the prerelease case explicitly; the first is why naming a tag is the answer
|
|
169
|
+
* whenever a publisher's newest release is not their newest stable one.
|
|
160
170
|
*/
|
|
161
171
|
export async function resolveLatestTag(spec: PackSpec): Promise<string> {
|
|
162
172
|
const url = `${baseUrl()}/${spec.owner}/${spec.repo}/releases/latest`;
|
|
@@ -168,7 +178,9 @@ export async function resolveLatestTag(spec: PackSpec): Promise<string> {
|
|
|
168
178
|
if (!location) {
|
|
169
179
|
throw new Error(
|
|
170
180
|
`could not resolve the newest release of ${spec.owner}/${spec.repo} ` +
|
|
171
|
-
`(GET ${url} returned ${response.status} with no redirect).
|
|
181
|
+
`(GET ${url} returned ${response.status} with no redirect). That redirect is issued only ` +
|
|
182
|
+
`for a published, non-prerelease release, so either the repository has no releases at all ` +
|
|
183
|
+
`or every release it has is a prerelease or a draft. Name a tag explicitly.`,
|
|
172
184
|
);
|
|
173
185
|
}
|
|
174
186
|
const match = location.match(/\/releases\/tag\/(.+)$/);
|
|
@@ -193,6 +205,33 @@ export function packAssetUrl(spec: PinnedPackSpec, asset: string): string {
|
|
|
193
205
|
return `${baseUrl()}/${spec.owner}/${spec.repo}/releases/download/${spec.tag}/${asset}`;
|
|
194
206
|
}
|
|
195
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Does a release tag describe the version the pack's own manifest declares?
|
|
210
|
+
*
|
|
211
|
+
* These are two independently-typed strings that everything downstream treats as
|
|
212
|
+
* one fact: the TAG builds every asset URL and is what `installed.json` records
|
|
213
|
+
* as the source, while the VERSION is read out of the manifest and is what
|
|
214
|
+
* `pack list` prints and `pack:<id>@<version>:<name>` keys are built from. Left
|
|
215
|
+
* uncompared, a pack tagged `v2` whose manifest still said `1.0.0` installed
|
|
216
|
+
* cleanly and recorded a version no release of that repository answers to.
|
|
217
|
+
*
|
|
218
|
+
* A leading `v` is accepted because both spellings are in live use for the same
|
|
219
|
+
* release — `pack build` tells publishers to tag `<version>` while this repo's
|
|
220
|
+
* own releases are tagged `v<version>` — and refusing that would break packs
|
|
221
|
+
* that are perfectly coherent.
|
|
222
|
+
*
|
|
223
|
+
* Compared on the tag's LAST `/`-delimited segment, so the monorepo shape
|
|
224
|
+
* `parsePackSpec` deliberately supports (`release/2.1`) still installs:
|
|
225
|
+
* `PACK_VERSION_RE` forbids `/`, so such a tag could never equal a version and a
|
|
226
|
+
* whole-string comparison would make the shape uninstallable rather than merely
|
|
227
|
+
* unusual. Everything else is a genuine disagreement — the release was tagged
|
|
228
|
+
* without rebuilding the manifest, or the wrong tag was typed.
|
|
229
|
+
*/
|
|
230
|
+
export function packTagMatchesVersion(tag: string, version: string): boolean {
|
|
231
|
+
const tail = tag.slice(tag.lastIndexOf("/") + 1);
|
|
232
|
+
return tail === version || tail === `v${version}`;
|
|
233
|
+
}
|
|
234
|
+
|
|
196
235
|
async function fetchBytes(url: string): Promise<Buffer> {
|
|
197
236
|
const response = await fetch(url, {
|
|
198
237
|
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
@@ -277,6 +316,16 @@ interface FetchedPack {
|
|
|
277
316
|
* and has to resolve it the same way — `core` worked in the terminal and failed
|
|
278
317
|
* in the browser for exactly as long as this list sat in `pack-cli.ts`.
|
|
279
318
|
*/
|
|
319
|
+
/**
|
|
320
|
+
* Where our own policies actually live.
|
|
321
|
+
*
|
|
322
|
+
* They are a GitHub release like anybody else's, and the package carries no
|
|
323
|
+
* copy of them. That is the point: a pack that ships inside the binary is a
|
|
324
|
+
* policy set we chose for you and put on your disk before you asked, and it
|
|
325
|
+
* gives our own policies a delivery path nobody else's pack can use. One lane,
|
|
326
|
+
* the same for everyone — ours are just the pack whose short name we spell.
|
|
327
|
+
*/
|
|
328
|
+
export const CORE_SOURCE = "FailproofAI/policies";
|
|
280
329
|
export const CORE_ALIASES = new Set(["core", "failproofai", "official"]);
|
|
281
330
|
|
|
282
331
|
export interface PackAddOptions {
|
|
@@ -307,18 +356,12 @@ export async function addPackFromSource(
|
|
|
307
356
|
source: string,
|
|
308
357
|
opts: PackAddOptions = {},
|
|
309
358
|
): Promise<AddedPack> {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
enabled: result.enabled ?? result.available ?? [],
|
|
317
|
-
available: result.available ?? [],
|
|
318
|
-
bundled: true,
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
const result = await addPack(source, opts);
|
|
359
|
+
// `core` is a spelling of CORE_SOURCE, not a second delivery path. Resolved
|
|
360
|
+
// HERE rather than in the CLI so the dashboard and the CLI cannot disagree
|
|
361
|
+
// about what the short name means — they did once, and a name the CLI could
|
|
362
|
+
// resolve failed in the browser.
|
|
363
|
+
const resolved = CORE_ALIASES.has(source.trim().toLowerCase()) ? CORE_SOURCE : source;
|
|
364
|
+
const result = await addPack(resolved, opts);
|
|
322
365
|
return {
|
|
323
366
|
id: result.id,
|
|
324
367
|
version: result.version,
|
|
@@ -607,6 +650,27 @@ export async function addPack(
|
|
|
607
650
|
: { ...parsed, tag: await resolveLatestTag(parsed) };
|
|
608
651
|
const resolvedFromLatest = parsed.tag === null;
|
|
609
652
|
const fetched = await fetchPack(spec);
|
|
653
|
+
// Checked here, before a byte is written and before the artifact is imported,
|
|
654
|
+
// so a refusal leaves the machine exactly as it was — and on the RESOLVED tag
|
|
655
|
+
// as well as a typed one, because a tagless add pins whatever
|
|
656
|
+
// `releases/latest` pointed at and lands the same incoherent pair in
|
|
657
|
+
// `installed.json` with nobody having typed it. Deliberately NOT in
|
|
658
|
+
// `fetchPackPreview`: looking at a pack has to stay possible precisely so a
|
|
659
|
+
// publisher can SEE the two values disagree.
|
|
660
|
+
if (!packTagMatchesVersion(spec.tag, fetched.version)) {
|
|
661
|
+
const fromTag = spec.tag.slice(spec.tag.lastIndexOf("/") + 1).replace(/^v/, "");
|
|
662
|
+
const fix =
|
|
663
|
+
`re-tag the release ${fetched.version} (a leading v is fine), or rebuild the pack with ` +
|
|
664
|
+
`--version ${fromTag}`;
|
|
665
|
+
throw new Error(
|
|
666
|
+
`${fetched.id} was not installed: release tag ${spec.tag} does not match the version its ` +
|
|
667
|
+
`manifest declares (${fetched.version}). ` +
|
|
668
|
+
(resolvedFromLatest
|
|
669
|
+
? `That is the newest release of ${spec.owner}/${spec.repo}, so the release and the pack ` +
|
|
670
|
+
`disagree at the source — ask the publisher to ${fix}.`
|
|
671
|
+
: `Name the tag whose manifest says ${fetched.version}, or fix the pack: ${fix}.`),
|
|
672
|
+
);
|
|
673
|
+
}
|
|
610
674
|
const available = fetched.policies.map((p) => p.name);
|
|
611
675
|
|
|
612
676
|
const prior = priorRecordFor(fetched.id, fetched.artifactDigest);
|
|
@@ -686,25 +750,21 @@ export async function addPack(
|
|
|
686
750
|
}
|
|
687
751
|
|
|
688
752
|
/**
|
|
689
|
-
*
|
|
753
|
+
* The vendored pack directory, when a working tree happens to have one.
|
|
690
754
|
*
|
|
691
|
-
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
755
|
+
* The published package does NOT carry `policy-pack/` and there is no install
|
|
756
|
+
* path through here. Our policies are fetched from their GitHub release like
|
|
757
|
+
* anybody else's — a pack that ships inside the binary is a policy set chosen
|
|
758
|
+
* for the user and written to their disk before they asked, and it gave our own
|
|
759
|
+
* policies a delivery route no third-party pack could use.
|
|
760
|
+
*
|
|
761
|
+
* The one remaining caller is `audit/replay.ts`, which prefers the pack's own
|
|
762
|
+
* functions when a dev tree has run `build:pack` and falls back to the compiled
|
|
763
|
+
* implementations when it returns null. Both produce identical text, so the two
|
|
764
|
+
* paths score the same; the fallback is the normal case now.
|
|
696
765
|
*
|
|
697
766
|
* Anchored at `FAILPROOFAI_PACKAGE_ROOT` for the reason `npmPlatformBinaryPath`
|
|
698
767
|
* documents: `import.meta.url` does not survive the CJS bundle.
|
|
699
|
-
*
|
|
700
|
-
* The bytes are COPIED into `packsDir()` rather than loaded where they lie. A
|
|
701
|
-
* `sudo npm i -g` package directory is root-owned, and the loader writes its
|
|
702
|
-
* rewritten module tree BESIDE the source it loads — so loading in place gives a
|
|
703
|
-
* non-root hook EACCES, the pack never loads, and the hook exits 0. Copying is
|
|
704
|
-
* what avoids that, and it is the same trap CHANGELOG #694 fixed for the shim.
|
|
705
|
-
*
|
|
706
|
-
* Idempotent, and never throws: a package without the directory returns null,
|
|
707
|
-
* which is a normal state for a dev checkout that has not run `build:pack`.
|
|
708
768
|
*/
|
|
709
769
|
export function bundledPackDir(): string | null {
|
|
710
770
|
const root = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
@@ -713,105 +773,6 @@ export function bundledPackDir(): string | null {
|
|
|
713
773
|
return existsSync(resolve(dir, PACK_MANIFEST_ASSET)) ? dir : null;
|
|
714
774
|
}
|
|
715
775
|
|
|
716
|
-
export interface BundledPackResult {
|
|
717
|
-
installed: boolean;
|
|
718
|
-
id?: string;
|
|
719
|
-
version?: string;
|
|
720
|
-
enabled?: string[];
|
|
721
|
-
available?: string[];
|
|
722
|
-
/** Ids this install absorbed — the same pack, previously under another name. */
|
|
723
|
-
replaced?: string[];
|
|
724
|
-
reason?: string;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
export function installBundledPack(opts?: { only?: string[]; categories?: string[]; all?: boolean }): BundledPackResult {
|
|
728
|
-
const dir = bundledPackDir();
|
|
729
|
-
if (!dir) return { installed: false, reason: "this build ships no bundled pack" };
|
|
730
|
-
|
|
731
|
-
try {
|
|
732
|
-
const checksums = readFileSync(resolve(dir, PACK_CHECKSUMS_ASSET), "utf8");
|
|
733
|
-
const manifestBytes = readFileSync(resolve(dir, PACK_MANIFEST_ASSET));
|
|
734
|
-
const artifact = readFileSync(resolve(dir, PACK_ENTRY_ASSET));
|
|
735
|
-
|
|
736
|
-
// Verified even though it never crossed a network. The digest is what the
|
|
737
|
-
// hook path re-checks before every import, so it has to describe the bytes
|
|
738
|
-
// actually installed — and a tarball can be corrupted on disk like anything
|
|
739
|
-
// else.
|
|
740
|
-
const manifestDigest = digestFor(checksums, PACK_MANIFEST_ASSET);
|
|
741
|
-
const entryDigest = digestFor(checksums, PACK_ENTRY_ASSET);
|
|
742
|
-
const artifactDigest = sha256(artifact);
|
|
743
|
-
if (!manifestDigest || sha256(manifestBytes) !== manifestDigest) {
|
|
744
|
-
return { installed: false, reason: `${PACK_MANIFEST_ASSET} failed integrity verification` };
|
|
745
|
-
}
|
|
746
|
-
if (!entryDigest || artifactDigest !== entryDigest) {
|
|
747
|
-
return { installed: false, reason: `${PACK_ENTRY_ASSET} failed integrity verification` };
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
const parsed = JSON.parse(manifestBytes.toString("utf8")) as {
|
|
751
|
-
id?: unknown; version?: unknown; policies?: unknown; effect?: unknown;
|
|
752
|
-
};
|
|
753
|
-
if (!Array.isArray(parsed.policies)) {
|
|
754
|
-
return { installed: false, reason: "bundled pack manifest is malformed" };
|
|
755
|
-
}
|
|
756
|
-
const identity = parsePackIdentity(parsed);
|
|
757
|
-
// Same rules the loader applies, so a bundled pack that could never load
|
|
758
|
-
// fails the install rather than looking fine until the next tool call.
|
|
759
|
-
const policies = parsed.policies.map((pol, i) => parsePackPolicy(identity.id, pol, i));
|
|
760
|
-
|
|
761
|
-
const root = packsRoot();
|
|
762
|
-
const artifactRel = `artifacts/${artifactDigest}.mjs`;
|
|
763
|
-
const artifactAbs = resolve(root, artifactRel);
|
|
764
|
-
if (!existsSync(artifactAbs)) writeAtomic(artifactAbs, artifact);
|
|
765
|
-
|
|
766
|
-
const prior = priorRecordFor(identity.id, artifactDigest);
|
|
767
|
-
const available = policies.map((pol) => pol.name);
|
|
768
|
-
const { enabled } = resolveSelection(
|
|
769
|
-
policies,
|
|
770
|
-
opts,
|
|
771
|
-
prior?.enabled ? prior.enabled.filter((n) => available.includes(n)) : prior?.enabled,
|
|
772
|
-
Boolean(prior),
|
|
773
|
-
);
|
|
774
|
-
|
|
775
|
-
const absorbed = upsertInstalled({
|
|
776
|
-
id: identity.id,
|
|
777
|
-
version: identity.version,
|
|
778
|
-
// Recorded as `bundled:` rather than a github: source, because it did not
|
|
779
|
-
// come from one — and `pack add` on this id would otherwise look like a
|
|
780
|
-
// re-fetch of something that was never fetched.
|
|
781
|
-
source: `bundled:${identity.id}@${identity.version}`,
|
|
782
|
-
entry: artifactRel,
|
|
783
|
-
sha256: artifactDigest,
|
|
784
|
-
policies,
|
|
785
|
-
...(parsed.effect !== undefined ? { effect: identity.effect } : {}),
|
|
786
|
-
...(enabled ? { enabled } : {}),
|
|
787
|
-
});
|
|
788
|
-
|
|
789
|
-
return {
|
|
790
|
-
installed: true,
|
|
791
|
-
id: identity.id,
|
|
792
|
-
version: identity.version,
|
|
793
|
-
enabled: enabled ?? available,
|
|
794
|
-
available,
|
|
795
|
-
...(absorbed.length > 0 ? { replaced: absorbed } : {}),
|
|
796
|
-
};
|
|
797
|
-
} catch (err) {
|
|
798
|
-
return { installed: false, reason: errText(err) };
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
/** Remove a pack from the activation pointer. Returns false if it was not installed. */
|
|
803
|
-
/**
|
|
804
|
-
* Turn ONE policy of an installed pack on or off.
|
|
805
|
-
*
|
|
806
|
-
* The selection in `installed.json` is the lever, not a `disabledCustomPolicies`
|
|
807
|
-
* entry: those are keyed by `pack:<id>@<version>:<name>`, so an upgrade stops
|
|
808
|
-
* matching them and silently switches back on everything the user had turned
|
|
809
|
-
* off. The selection is version-stable and is carried forward by `pack add`.
|
|
810
|
-
*
|
|
811
|
-
* Enabling also clears any matching disabled key, or a policy switched off from
|
|
812
|
-
* the dashboard could not be switched back on from the CLI — the two mechanisms
|
|
813
|
-
* would disagree and the more obscure one would win.
|
|
814
|
-
*/
|
|
815
776
|
export function setPackPolicyEnabled(
|
|
816
777
|
packId: string,
|
|
817
778
|
name: string,
|
package/src/hooks/tui.ts
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
* repaint) and, on resolve, collapses that region to a one-line summary that
|
|
15
15
|
* stays on screen — so the next prompt simply appends below, building the log.
|
|
16
16
|
* No external dependencies. Honors NO_COLOR and non-TTY (returns the default
|
|
17
|
-
* without drawing), and
|
|
18
|
-
*
|
|
17
|
+
* without drawing), and paints at the deepest tier the terminal admits to —
|
|
18
|
+
* 24-bit, the xterm-256 cube, or the 16 basic ANSI hues.
|
|
19
19
|
*/
|
|
20
20
|
import * as readline from "node:readline";
|
|
21
21
|
|
|
@@ -108,7 +108,7 @@ const RADIO_OFF = "○";
|
|
|
108
108
|
export const CHECK_ON = "◼";
|
|
109
109
|
export const CHECK_OFF = "◻";
|
|
110
110
|
export const CARET = "❯";
|
|
111
|
-
const
|
|
111
|
+
const MARK = "\u25AE\u25AE"; // ▮▮ — the brand mark, per the design system
|
|
112
112
|
|
|
113
113
|
// ── color ─────────────────────────────────────────────────────────────────
|
|
114
114
|
// The single source of truth for the brand palette — exported (via `paint`)
|
|
@@ -116,13 +116,32 @@ const FLOWER = "❋";
|
|
|
116
116
|
// re-deriving their own copies.
|
|
117
117
|
interface Hue {
|
|
118
118
|
rgb: [number, number, number];
|
|
119
|
+
/**
|
|
120
|
+
* xterm-256 index for the middle tier, verified as the nearest cube entry by
|
|
121
|
+
* Euclidean distance rather than eyeballed.
|
|
122
|
+
*
|
|
123
|
+
* Optional because `dim` has no honest answer: its `basic` is SGR 2, an
|
|
124
|
+
* ATTRIBUTE that steps down whatever foreground the user's theme is already
|
|
125
|
+
* using, and the cube's nearest grey (243, #767676) is a fixed colour that
|
|
126
|
+
* fights every theme that is not ours. So dim keeps the attribute at all
|
|
127
|
+
* three tiers and this stays undefined.
|
|
128
|
+
*/
|
|
129
|
+
c256?: number;
|
|
119
130
|
basic: string;
|
|
120
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* TWO accent hues and no third — the brand system states that as a rule, and
|
|
134
|
+
* this table used to break it: `pink` was #ff2e88, a hot pink that appears in
|
|
135
|
+
* no brand token, sitting beside `logoPink` #e4587d which was the real one. The
|
|
136
|
+
* mark and the prompts were therefore two different pinks, and neither surface
|
|
137
|
+
* could be recoloured without the other drifting. There is now one pink.
|
|
138
|
+
* Everything past the two accents is a state — amber for warn, an attribute for
|
|
139
|
+
* dim — never identity.
|
|
140
|
+
*/
|
|
121
141
|
const HUES = {
|
|
122
|
-
guide: { rgb: [102, 209, 181], basic: "36" }, //
|
|
123
|
-
pink: { rgb: [
|
|
124
|
-
|
|
125
|
-
warn: { rgb: [227, 179, 65], basic: "33" },
|
|
142
|
+
guide: { rgb: [102, 209, 181], c256: 79, basic: "36" }, // #66d1b5 mint — the policy flower / step spine
|
|
143
|
+
pink: { rgb: [228, 88, 125], c256: 168, basic: "95" }, // #e4587d — selection, enabled, the mark, the brand
|
|
144
|
+
warn: { rgb: [227, 179, 65], c256: 179, basic: "33" },
|
|
126
145
|
dim: { rgb: [107, 118, 132], basic: "2" },
|
|
127
146
|
} satisfies Record<string, Hue>;
|
|
128
147
|
|
|
@@ -137,32 +156,68 @@ export function colorsEnabled(out: TTYOut): boolean {
|
|
|
137
156
|
* blue that appear nowhere in the brand — so `audit` looked like a different
|
|
138
157
|
* product from `config`. Routing it here
|
|
139
158
|
* keeps HUES the single source of truth: change a hue once and every surface
|
|
140
|
-
* follows.
|
|
141
|
-
*
|
|
159
|
+
* follows. Paints at the deepest tier the terminal admits to; the caller still
|
|
160
|
+
* decides *whether* to colour at all.
|
|
142
161
|
*/
|
|
143
162
|
export function brandAnsi(role: keyof typeof HUES): string {
|
|
144
|
-
|
|
145
|
-
const code = truecolorEnabled() ? `38;2;${h.rgb.join(";")}` : h.basic;
|
|
146
|
-
return `${ESC}[${code}m`;
|
|
163
|
+
return `${ESC}[${fg(HUES[role], colorTier())}m`;
|
|
147
164
|
}
|
|
148
165
|
|
|
149
166
|
export const ANSI_RESET = `${ESC}[0m`;
|
|
150
167
|
export const ANSI_BOLD = `${ESC}[1m`;
|
|
151
168
|
export const ANSI_DIM = `${ESC}[2m`;
|
|
152
|
-
function truecolorEnabled(): boolean {
|
|
153
|
-
return /truecolor|24bit/i.test(process.env.COLORTERM || "");
|
|
154
|
-
}
|
|
155
169
|
|
|
156
|
-
/**
|
|
157
|
-
|
|
170
|
+
/** How much colour this terminal will actually render. */
|
|
171
|
+
type ColorTier = "truecolor" | "ansi256" | "basic";
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Three tiers, because the two-way gate skipped the one most terminals are in.
|
|
175
|
+
*
|
|
176
|
+
* COLORTERM-or-nothing sent every terminal that renders 256 colours but does
|
|
177
|
+
* not advertise 24-bit — tmux and screen, ssh into a stock xterm, most CI
|
|
178
|
+
* runners — all the way down to the 16 basic hues, where the brand pink lands
|
|
179
|
+
* on generic bright magenta and the mint on generic cyan. TERM naming its own
|
|
180
|
+
* depth is the signal those terminals do set, and it costs one regex.
|
|
181
|
+
*
|
|
182
|
+
* Deliberately no `FORCE_COLOR` / `-256color`-less allowlist: over-claiming a
|
|
183
|
+
* depth prints raw escape bytes into the user's scrollback, which is worse than
|
|
184
|
+
* an approximate hue. Under-claiming only costs fidelity.
|
|
185
|
+
*/
|
|
186
|
+
function colorTier(): ColorTier {
|
|
187
|
+
if (/truecolor|24bit/i.test(process.env.COLORTERM || "")) return "truecolor";
|
|
188
|
+
if (/256color/i.test(process.env.TERM || "")) return "ansi256";
|
|
189
|
+
return "basic";
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** SGR foreground parameters for a hue at a tier. The ONE place a hue turns
|
|
193
|
+
* into bytes, so a new tier is added here and nowhere else. */
|
|
194
|
+
function fg(h: Hue, tier: ColorTier): string {
|
|
195
|
+
if (tier === "truecolor") return `38;2;${h.rgb[0]};${h.rgb[1]};${h.rgb[2]}`;
|
|
196
|
+
if (tier === "ansi256" && h.c256 !== undefined) return `38;5;${h.c256}`;
|
|
197
|
+
return h.basic;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** The same as a background — only the logomark needs one, for a half-block
|
|
201
|
+
* cell whose two pixels are different hues. The current grid has no such cell
|
|
202
|
+
* (the flower and the cross never share a column), so this is here for the
|
|
203
|
+
* next grid edit, which the rules above LOGO_GRID actively invite. Never
|
|
204
|
+
* called at the basic tier: the mark draws monochrome there rather than
|
|
205
|
+
* approximate two brand hues with ANSI 5 and 6. */
|
|
206
|
+
function bg(h: Hue, tier: ColorTier): string {
|
|
207
|
+
return tier === "ansi256" && h.c256 !== undefined
|
|
208
|
+
? `48;5;${h.c256}`
|
|
209
|
+
: `48;2;${h.rgb[0]};${h.rgb[1]};${h.rgb[2]}`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Brand painter: role-named color functions at the terminal's best tier,
|
|
213
|
+
* identity when `on` is false. */
|
|
158
214
|
export function paint(on: boolean) {
|
|
159
|
-
const
|
|
215
|
+
const tier: ColorTier = on ? colorTier() : "basic";
|
|
160
216
|
const mk =
|
|
161
217
|
(h: Hue, bold = false) =>
|
|
162
218
|
(s: string): string => {
|
|
163
219
|
if (!on) return s;
|
|
164
|
-
|
|
165
|
-
return `${ESC}[${bold ? "1;" : ""}${code}m${s}${ESC}[0m`;
|
|
220
|
+
return `${ESC}[${bold ? "1;" : ""}${fg(h, tier)}m${s}${ESC}[0m`;
|
|
166
221
|
};
|
|
167
222
|
return {
|
|
168
223
|
bold: (s: string) => (on ? `${ESC}[1m${s}${ESC}[0m` : s),
|
|
@@ -170,7 +225,11 @@ export function paint(on: boolean) {
|
|
|
170
225
|
guide: mk(HUES.guide),
|
|
171
226
|
pink: mk(HUES.pink),
|
|
172
227
|
pinkBold: mk(HUES.pink, true),
|
|
173
|
-
|
|
228
|
+
// An ALIAS of `pink`, not a hue. The logomark's softer artwork tint
|
|
229
|
+
// collapsed into the one brand pink, so this name survives only for its
|
|
230
|
+
// single caller — install-prompt.ts's "beta" pill — and should be renamed
|
|
231
|
+
// to `pink` there, at which point it is deleted.
|
|
232
|
+
softPink: mk(HUES.pink),
|
|
174
233
|
warn: mk(HUES.warn),
|
|
175
234
|
};
|
|
176
235
|
}
|
|
@@ -224,37 +283,40 @@ const LOGO_GRID = [
|
|
|
224
283
|
"..ppppppppppp",
|
|
225
284
|
"..ppppppppppp",
|
|
226
285
|
];
|
|
227
|
-
// Derived from the shared HUES table so a palette tweak needs one edit.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
286
|
+
// Derived from the shared HUES table so a palette tweak needs one edit. The
|
|
287
|
+
// mark is painted from the SAME two accents as the prompts — it used to carry
|
|
288
|
+
// its own pink, which is how the two drifted apart.
|
|
289
|
+
const LOGO_TEAL: Hue = HUES.guide;
|
|
290
|
+
const LOGO_PINK: Hue = HUES.pink;
|
|
291
|
+
|
|
292
|
+
/** Render the logomark as half-block art. At the `basic` tier (16 colours, or
|
|
293
|
+
* NO_COLOR) the shape still prints, just monochrome — approximating two brand
|
|
294
|
+
* hues with generic magenta and cyan reads as a different mark, and the shape
|
|
295
|
+
* alone already carries it. */
|
|
296
|
+
function renderLogo(tier: ColorTier): string[] {
|
|
234
297
|
const pad = ".".repeat(LOGO_GRID[0]?.length ?? 0);
|
|
235
|
-
const
|
|
236
|
-
ch === "t" ? LOGO_TEAL : ch === "p" ? LOGO_PINK : null;
|
|
298
|
+
const hue = (ch: string): Hue | null => (ch === "t" ? LOGO_TEAL : ch === "p" ? LOGO_PINK : null);
|
|
237
299
|
const lines: string[] = [];
|
|
238
300
|
for (let r = 0; r < LOGO_GRID.length; r += 2) {
|
|
239
301
|
const top = LOGO_GRID[r];
|
|
240
302
|
const bot = LOGO_GRID[r + 1] ?? pad;
|
|
241
303
|
let line = "";
|
|
242
304
|
for (let x = 0; x < top.length; x++) {
|
|
243
|
-
const t =
|
|
244
|
-
const b =
|
|
305
|
+
const t = hue(top[x]);
|
|
306
|
+
const b = hue(bot[x]);
|
|
245
307
|
if (!t && !b) {
|
|
246
308
|
line += " ";
|
|
247
|
-
} else if (
|
|
309
|
+
} else if (tier === "basic") {
|
|
248
310
|
line += t && b ? "█" : t ? "▀" : "▄";
|
|
249
311
|
} else if (t && b) {
|
|
250
312
|
line +=
|
|
251
313
|
t === b
|
|
252
|
-
? `${ESC}[
|
|
253
|
-
: `${ESC}[
|
|
314
|
+
? `${ESC}[${fg(t, tier)}m█${ESC}[0m`
|
|
315
|
+
: `${ESC}[${fg(t, tier)};${bg(b, tier)}m▀${ESC}[0m`;
|
|
254
316
|
} else if (t) {
|
|
255
|
-
line += `${ESC}[
|
|
317
|
+
line += `${ESC}[${fg(t, tier)}m▀${ESC}[0m`;
|
|
256
318
|
} else {
|
|
257
|
-
line += `${ESC}[
|
|
319
|
+
line += `${ESC}[${fg(b!, tier)}m▄${ESC}[0m`;
|
|
258
320
|
}
|
|
259
321
|
}
|
|
260
322
|
lines.push(line);
|
|
@@ -318,10 +380,12 @@ export function renderBrandLogo(stdout: TTYOut = process.stdout): string[] {
|
|
|
318
380
|
const c = paint(colorsEnabled(stdout));
|
|
319
381
|
const cols = stdout.columns || 80;
|
|
320
382
|
if (cols < LOGO_MIN_COLS) {
|
|
321
|
-
return [`${c.
|
|
383
|
+
return [`${c.pink(MARK)} fa${c.pink("il")}proof ai ${c.dim("· " + TAGLINE)}`];
|
|
322
384
|
}
|
|
323
|
-
|
|
324
|
-
|
|
385
|
+
// NO_COLOR / non-TTY forces `basic`, which renderLogo draws monochrome — so
|
|
386
|
+
// the mark never emits an escape byte on a stream that asked for none.
|
|
387
|
+
const tier: ColorTier = colorsEnabled(stdout) ? colorTier() : "basic";
|
|
388
|
+
const lines = renderLogo(tier).map((l) => ` ${l}`);
|
|
325
389
|
lines.push("");
|
|
326
390
|
lines.push(` fa${c.pink("il")}proof ai`);
|
|
327
391
|
lines.push(` ${c.dim(TAGLINE)}`);
|
|
@@ -427,9 +491,25 @@ type Region = { lastCount: number };
|
|
|
427
491
|
|
|
428
492
|
const WINDOW = 8; // visible rows before the checklist scrolls
|
|
429
493
|
|
|
494
|
+
/**
|
|
495
|
+
* Redraw the region as ONE atomic frame.
|
|
496
|
+
*
|
|
497
|
+
* Two things make it atomic, and both are needed. The cursor-up-and-clear used
|
|
498
|
+
* to be its own `write()`, so a terminal could render the CLEARED state before
|
|
499
|
+
* the new lines arrived — a blank flash on every keystroke, invisible locally
|
|
500
|
+
* and obvious over SSH or inside tmux, where the two writes cross a network or
|
|
501
|
+
* a multiplexer between frames. They are one string now.
|
|
502
|
+
*
|
|
503
|
+
* And that string is wrapped in synchronized output (DECSET 2026), which tells
|
|
504
|
+
* the terminal to hold what it has until the reset rather than painting each
|
|
505
|
+
* chunk as it lands. Terminals that do not implement it ignore an unknown
|
|
506
|
+
* private mode, so it costs nothing where it does not help.
|
|
507
|
+
*/
|
|
430
508
|
function repaint(out: TTYOut, region: Region, lines: string[]): void {
|
|
431
|
-
|
|
432
|
-
|
|
509
|
+
const cols = out.columns || 80;
|
|
510
|
+
const body = lines.map((l) => (l === "" ? l : truncate(l, cols))).join("\n") + "\n";
|
|
511
|
+
const clear = region.lastCount > 0 ? `${ESC}[${region.lastCount}A${ESC}[J` : "";
|
|
512
|
+
out.write(`${ESC}[?2026h${clear}${body}${ESC}[?2026l`);
|
|
433
513
|
region.lastCount = lines.length;
|
|
434
514
|
}
|
|
435
515
|
|
|
@@ -445,6 +525,24 @@ function nameWidth(labels: string[]): number {
|
|
|
445
525
|
return Math.min(24, Math.max(6, ...labels.map((l) => l.length)));
|
|
446
526
|
}
|
|
447
527
|
|
|
528
|
+
/**
|
|
529
|
+
* The hint budget for one row, after a label that outgrew its column.
|
|
530
|
+
*
|
|
531
|
+
* `nameWidth` caps the name column at 24 so that one long name cannot squeeze
|
|
532
|
+
* every description on screen — but `padEnd` pads, it does not TRUNCATE, so a
|
|
533
|
+
* longer name renders at its true width while the description was sized against
|
|
534
|
+
* the cap. The row then overruns the terminal by the difference and the
|
|
535
|
+
* description is cut by the terminal instead of by `ellipsize`, with no `…` to
|
|
536
|
+
* show it happened.
|
|
537
|
+
*
|
|
538
|
+
* Names are deliberately not truncated: a policy name is the thing you type
|
|
539
|
+
* next, and half of one is useless. The description gives up the space, because
|
|
540
|
+
* it is prose and shortening prose costs nothing.
|
|
541
|
+
*/
|
|
542
|
+
function hintBudget(label: string, nameCol: number, budget: number): number {
|
|
543
|
+
return Math.max(6, budget - Math.max(0, label.length - nameCol));
|
|
544
|
+
}
|
|
545
|
+
|
|
448
546
|
type DisplayRow = { kind: "header"; text: string } | { kind: "item"; index: number };
|
|
449
547
|
|
|
450
548
|
/** Flatten choices into section-header + item display rows. */
|
|
@@ -651,7 +749,9 @@ export function selectOne<T>(opts: SelectOneOptions<T>): Promise<T | Back | null
|
|
|
651
749
|
const dot = active ? c.pink(RADIO_ON) : c.dim(RADIO_OFF);
|
|
652
750
|
const rawLabel = choice.label.padEnd(nameCol);
|
|
653
751
|
const label = active ? c.pinkBold(rawLabel) : rawLabel;
|
|
654
|
-
const hint = choice.hint
|
|
752
|
+
const hint = choice.hint
|
|
753
|
+
? ` ${c.dim(ellipsize(choice.hint, hintBudget(choice.label, nameCol, budget)))}`
|
|
754
|
+
: "";
|
|
655
755
|
return `${dot} ${label}${hint}`;
|
|
656
756
|
},
|
|
657
757
|
allowBack: opts.allowBack,
|
|
@@ -709,7 +809,9 @@ export function multiSelect<T>(opts: MultiSelectOptions<T>): Promise<T[] | null
|
|
|
709
809
|
: c.dim(CHECK_OFF);
|
|
710
810
|
const rawLabel = choice.label.padEnd(nameCol);
|
|
711
811
|
const label = active ? c.pinkBold(rawLabel) : checked[index] ? rawLabel : c.dim(rawLabel);
|
|
712
|
-
const hint = choice.hint
|
|
812
|
+
const hint = choice.hint
|
|
813
|
+
? ` ${c.dim(ellipsize(choice.hint, hintBudget(choice.label, nameCol, budget)))}`
|
|
814
|
+
: "";
|
|
713
815
|
return `${caret} ${box} ${label}${hint}`;
|
|
714
816
|
},
|
|
715
817
|
warnLine: () => (warn ? c.warn(`Select at least ${minSelected}.`) : null),
|
|
@@ -1152,9 +1254,9 @@ export function rule(label?: string, opts?: RenderOpts): string[] {
|
|
|
1152
1254
|
const { cols, c } = ctx(opts);
|
|
1153
1255
|
const width = Math.max(4, cols - INDENT.length * 2);
|
|
1154
1256
|
if (!label) return [`${INDENT}${c.dim("─".repeat(width))}`];
|
|
1155
|
-
const head =
|
|
1257
|
+
const head = `━━ ${label} `;
|
|
1156
1258
|
const tail = Math.max(3, width - head.length);
|
|
1157
|
-
return [`${INDENT}${c.dim(head + "
|
|
1259
|
+
return [`${INDENT}${c.dim(head + "━".repeat(tail))}`];
|
|
1158
1260
|
}
|
|
1159
1261
|
|
|
1160
1262
|
export interface Row {
|
|
@@ -1329,7 +1431,7 @@ const CHIP_LABELS: Record<ChipState, string> = {
|
|
|
1329
1431
|
locked: "✓ LOCK",
|
|
1330
1432
|
cloud: "✓ CLOUD",
|
|
1331
1433
|
pack: "✓ PACK",
|
|
1332
|
-
failed: "
|
|
1434
|
+
failed: "\u25B2 FAIL",
|
|
1333
1435
|
observe: "◉ OBS",
|
|
1334
1436
|
};
|
|
1335
1437
|
|
|
@@ -1400,7 +1502,7 @@ function gutterBlock(symbol: string, lines: string[], opts?: RenderOpts): string
|
|
|
1400
1502
|
* scopes or six about the daemon. */
|
|
1401
1503
|
export function warning(lines: string[], opts?: RenderOpts): string[] {
|
|
1402
1504
|
const { c } = ctx(opts);
|
|
1403
|
-
return gutterBlock(c.warn("
|
|
1505
|
+
return gutterBlock(c.warn("\u25B2"), lines, opts);
|
|
1404
1506
|
}
|
|
1405
1507
|
|
|
1406
1508
|
/** The same, for the ones that destroy something. */
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[18622,(a,b,c)=>{b.exports=a.x("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js",()=>require("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js"))},20635,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/action-async-storage.external.js",()=>require("next/dist/server/app-render/action-async-storage.external.js"))},24725,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/after-task-async-storage.external.js",()=>require("next/dist/server/app-render/after-task-async-storage.external.js"))},43285,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/dynamic-access-async-storage.external.js",()=>require("next/dist/server/app-render/dynamic-access-async-storage.external.js"))},56704,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/work-async-storage.external.js",()=>require("next/dist/server/app-render/work-async-storage.external.js"))},32319,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/work-unit-async-storage.external.js",()=>require("next/dist/server/app-render/work-unit-async-storage.external.js"))},59043,(a,b,c)=>{b.exports=a.x("next/dist/server/runtime-reacts.external.js",()=>require("next/dist/server/runtime-reacts.external.js"))},77580,a=>{"use strict";var b=a.i(5050);let c=(0,b.createServerReference)("00b801c035726aaa9d297e34f35258369a37177149",b.callServer,void 0,b.findSourceMapURL,"getTelemetryConfig");a.s(["getTelemetryConfig",0,c])},3171,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(77580),e=a.i(66036);a.s(["default",0,function({error:a,reset:f}){return(0,c.useEffect)(()=>{(0,d.getTelemetryConfig)().then(b=>{(0,e.setClientTelemetryConfig)(b),(0,e.captureClientEvent)("client_error",{error_message:a.message,error_name:a.name,error_digest:a.digest,boundary:"global"})}).catch(()=>{})},[a]),(0,b.jsx)("html",{children:(0,b.jsx)("body",{children:(0,b.jsx)("main",{style:{minHeight:"100vh",display:"flex",alignItems:"center",justifyContent:"center",background:"#031035",color:"#f8fafc",fontFamily:"system-ui, sans-serif"},children:(0,b.jsxs)("div",{style:{textAlign:"center",padding:"2rem",border:"1px solid rgba(239,68,68,0.4)",borderRadius:"0.5rem",maxWidth:"500px"},children:[(0,b.jsx)("h2",{style:{color:"#ef4444",marginBottom:"0.5rem",fontSize:"1.25rem"},children:"Something went wrong"}),(0,b.jsx)("p",{style:{color:"#94a3b8",marginBottom:"1.5rem"},children:a.message||"An unexpected error occurred."}),(0,b.jsx)("button",{onClick:f,style:{padding:"0.5rem 1.25rem",background:"#3b82f6",color:"white",border:"none",borderRadius:"0.375rem",cursor:"pointer",fontSize:"0.875rem"},children:"Try again"})]})})})})}])},66036,a=>{"use strict";a.s(["captureClientEvent",0,function(a,b){},"setClientTelemetryConfig",0,function(a){}])},46058,(a,b,c)=>{"use strict";function d(a){if("function"!=typeof WeakMap)return null;var b=new WeakMap,c=new WeakMap;return(d=function(a){return a?c:b})(a)}c._=function(a,b){if(!b&&a&&a.__esModule)return a;if(null===a||"object"!=typeof a&&"function"!=typeof a)return{default:a};var c=d(b);if(c&&c.has(a))return c.get(a);var e={__proto__:null},f=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var g in a)if("default"!==g&&Object.prototype.hasOwnProperty.call(a,g)){var h=f?Object.getOwnPropertyDescriptor(a,g):null;h&&(h.get||h.set)?Object.defineProperty(e,g,h):e[g]=a[g]}return e.default=a,c&&c.set(a,e),e}},5050,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={callServer:function(){return f.callServer},createServerReference:function(){return h.createServerReference},findSourceMapURL:function(){return g.findSourceMapURL}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(20611),g=a.r(1722),h=a.r(38783)},20611,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"callServer",{enumerable:!0,get:function(){return g}});let d=a.r(72131),e=a.r(88347),f=a.r(90841);async function g(a,b){return new Promise((c,g)=>{(0,d.startTransition)(()=>{(0,f.dispatchAppRouterAction)({type:e.ACTION_SERVER_ACTION,actionId:a,actionArgs:b,resolve:c,reject:g})})})}("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},1722,(a,b,c)=>{"use strict";let d;Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"findSourceMapURL",{enumerable:!0,get:function(){return d}});("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},88347,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d,e,f={ACTION_HMR_REFRESH:function(){return l},ACTION_NAVIGATE:function(){return i},ACTION_REFRESH:function(){return h},ACTION_RESTORE:function(){return j},ACTION_SERVER_ACTION:function(){return m},ACTION_SERVER_PATCH:function(){return k},PrefetchKind:function(){return n},ScrollBehavior:function(){return o}};for(var g in f)Object.defineProperty(c,g,{enumerable:!0,get:f[g]});let h="refresh",i="navigate",j="restore",k="server-patch",l="hmr-refresh",m="server-action";var n=((d={}).AUTO="auto",d.FULL="full",d),o=((e={})[e.Default=0]="Default",e[e.NoScroll=1]="NoScroll",e);("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},90841,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={dispatchAppRouterAction:function(){return i},dispatchGestureState:function(){return j},refreshOnInstantNavigationUnlock:function(){return h},useActionQueue:function(){return k}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(46058)._(a.r(72131)),g=a.r(67009);a.r(88347);function h(){}function i(a){!0;throw Object.defineProperty(Error("Internal Next.js error: Router action dispatched before initialization."),"__NEXT_ERROR_CODE",{value:"E668",enumerable:!1,configurable:!0})}function j(a){!0;throw Object.defineProperty(Error("Internal Next.js error: Router action dispatched before initialization."),"__NEXT_ERROR_CODE",{value:"E668",enumerable:!1,configurable:!0})}function k(a){let[b,c]=f.default.useState(a.state),[d,e]=(0,f.useOptimistic)(b),h=(0,f.useMemo)(()=>d,[d]);return(0,g.isThenable)(h)?(0,f.use)(h):h}("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},42602,(a,b,c)=>{"use strict";b.exports=a.r(18622)},9270,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.AppRouterContext},36313,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.HooksClientContext},18341,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.ServerInsertedHtml},35112,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactDOM},87924,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactJsxRuntime},38783,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactServerDOMTurbopackClient},72131,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].React},67009,(a,b,c)=>{"use strict";function d(a){return null!==a&&"object"==typeof a&&"then"in a&&"function"==typeof a.then}Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"isThenable",{enumerable:!0,get:function(){return d}})}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=%5Broot-of-the-server%5D__0zig5sv._.js.map
|