failproofai 0.0.14-beta.1 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.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/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +9 -9
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +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/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/reminder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.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 +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +9 -9
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0--lkk6._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-219ec._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__04_h-a5._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06cuf1y._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__08w4wmd._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g6bbuw._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jnjf2t._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q0qzx1._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q2nbsl._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rv7m0k._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__16le-kd._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ffkmds._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1legmza._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1mrihkj._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qb590j._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qxztj-._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1shcqgr._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1xuedea._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +4 -4
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.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]__015_i4t._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__030k0c6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__03lv-pe._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__05r_17v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e2l3c1._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hci7t3._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hdsupo._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p84xee._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qeuy9c._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s89hoe._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ynf7tx._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0brjczq._.js → [root-of-the-server]__0zf8xlq._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__170799-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17ai7sy._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1nq7ivq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0wp9w6i._.js → [root-of-the-server]__1s2b6h0._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tkjqka._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_05whahf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1kje4fm._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
- 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/lib_0xkhw_v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_12rjfea.js → node_modules_html-to-image_es_index_0gkzjaa.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +10 -10
- package/.next/standalone/.next/static/chunks/06z_ex1ts3n4s.js +1 -0
- package/.next/standalone/.next/static/chunks/0o0ut0ugd0_-s.js +1 -0
- package/.next/standalone/.next/static/chunks/0xl97x-874k-u.js +1 -0
- package/.next/standalone/.next/static/chunks/19d7hqx8udopz.js +1 -0
- package/.next/standalone/.next/static/chunks/{0emg30_6svycu.js → 29o4j32z643_a.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1359o5785rmve.js → 2uh_4k4m296z5.js} +2 -2
- package/.next/standalone/.next/static/chunks/370sj2lgkw5ey.js +1 -0
- package/.next/standalone/.next/static/chunks/{0d5lypu1zs21r.js → 3s8aaig91937v.js} +1 -1
- package/.next/standalone/.next/static/chunks/3w7g0n0iwwnbs.js +1 -0
- package/.next/standalone/.next/static/chunks/{0o9wpnze1ey8y.js → 42oeva1-w0z2d.js} +1 -1
- package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +11 -2
- package/.next/standalone/app/components/copy-button.tsx +22 -6
- package/.next/standalone/app/components/log-viewer/entry-row.tsx +8 -3
- package/.next/standalone/app/components/raw-log-viewer.tsx +33 -15
- package/.next/standalone/app/policies/hooks-client.tsx +1 -1
- package/.next/standalone/{ci/cli-integration → integration-suite}/README.md +25 -7
- package/.next/standalone/integration-suite/ci-entrypoint.sh +164 -0
- package/.next/standalone/{ci/cli-integration → integration-suite}/run.sh +4 -4
- package/.next/standalone/lib/entry-keys.ts +36 -0
- package/.next/standalone/lib/format-duration.ts +23 -9
- package/.next/standalone/package.json +5 -4
- package/.next/standalone/server.js +1 -1
- package/dist/cli.mjs +342 -106
- package/lib/entry-keys.ts +36 -0
- package/lib/format-duration.ts +23 -9
- package/package.json +5 -4
- package/scripts/translate-docs/cli.ts +6 -0
- package/scripts/translate-docs/mdx-translator.ts +30 -19
- package/scripts/translate-docs/readme-translator.ts +31 -21
- package/scripts/translate-docs/translator.ts +142 -7
- package/scripts/translate-docs/types.ts +6 -0
- package/scripts/translate-docs/validate-translation.ts +132 -0
- package/scripts/validate-mdx.ts +80 -11
- package/src/audit/cli.ts +12 -6
- package/src/auth/cli.ts +14 -5
- package/src/hooks/builtin-policies.ts +168 -50
- package/src/hooks/configure-wizard.ts +238 -22
- package/src/hooks/custom-hooks-loader.ts +70 -4
- package/src/hooks/handler.ts +4 -1
- package/src/hooks/policy-types.ts +8 -0
- package/src/hooks/tui.ts +95 -28
- package/.next/standalone/.next/static/chunks/20tuctw38jbgw.js +0 -1
- package/.next/standalone/.next/static/chunks/2ldhnaweu9ew3.js +0 -1
- package/.next/standalone/.next/static/chunks/2vav_x0_336ds.js +0 -1
- package/.next/standalone/.next/static/chunks/3cg7x0joi2b98.js +0 -1
- package/.next/standalone/.next/static/chunks/3k87p-tijud-4.js +0 -1
- package/.next/standalone/.next/static/chunks/3kmaqcp7rldad.js +0 -1
- /package/.next/standalone/.next/static/{LkN-JjmhKGmL-WklH7WVi → WQRKgSfFekLcC_pvyzp4S}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{LkN-JjmhKGmL-WklH7WVi → WQRKgSfFekLcC_pvyzp4S}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{LkN-JjmhKGmL-WklH7WVi → WQRKgSfFekLcC_pvyzp4S}/_ssgManifest.js +0 -0
- /package/.next/standalone/{ci/cli-integration → integration-suite}/Dockerfile +0 -0
- /package/.next/standalone/{ci/cli-integration → integration-suite}/canary-policies.mjs +0 -0
- /package/.next/standalone/{ci/cli-integration → integration-suite}/capture-tokens.sh +0 -0
- /package/.next/standalone/{ci/cli-integration → integration-suite}/inject-tokens.sh +0 -0
- /package/.next/standalone/{ci/cli-integration → integration-suite}/install-clis.sh +0 -0
- /package/.next/standalone/{ci/cli-integration → integration-suite}/probe-cli.sh +0 -0
- /package/.next/standalone/{ci/cli-integration → integration-suite}/report.js +0 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
3
|
+
# failproofai integration suite — CI entrypoint (runs on the GitHub Actions runner).
|
|
4
|
+
#
|
|
5
|
+
# This is the suite's single front door for CI. The workflow
|
|
6
|
+
# (.github/workflows/integration-suite.yml) is a thin trigger: it checks out,
|
|
7
|
+
# installs bun, maps secrets into the environment, and calls this script. Every
|
|
8
|
+
# step that actually *does* something lives here, so the harness can be read —
|
|
9
|
+
# and run — without opening the YAML.
|
|
10
|
+
#
|
|
11
|
+
# Responsibilities, in order:
|
|
12
|
+
# 1. build failproofai under test (dist/index.js + dist/cli.mjs)
|
|
13
|
+
# 2. decode the base64 OAuth-token secrets into $TOKENS_DIR
|
|
14
|
+
# 3. build the sandbox image and create the per-run HOME volume
|
|
15
|
+
# 4. install all 12 CLIs @latest into that volume, then inject the tokens
|
|
16
|
+
# 5. assemble the gateway env-file
|
|
17
|
+
# 6. hand off to run.sh, which does the version-gating, probing and reporting
|
|
18
|
+
#
|
|
19
|
+
# Cleanup (env-file + docker volume) runs via trap, so it happens on failure and
|
|
20
|
+
# on early exit too — not just on the happy path.
|
|
21
|
+
#
|
|
22
|
+
# Inputs are all environment variables, so this is runnable outside GH Actions:
|
|
23
|
+
#
|
|
24
|
+
# GITHUB_WORKSPACE repo checkout (default: this script's parent dir)
|
|
25
|
+
# CANARY_LLM_API_KEY gateway credentials -> written into the env-file
|
|
26
|
+
# CANARY_LLM_BASE_URL (default https://models.aikin.club)
|
|
27
|
+
# CANARY_LLM_MODEL (default deepseek-v4-pro)
|
|
28
|
+
# CANARY_CLAUDE_MODEL (default claude-haiku-4-5)
|
|
29
|
+
# CANARY_PI_MODEL (default claude-haiku-4-5)
|
|
30
|
+
# COPILOT_GITHUB_TOKEN copilot PAT
|
|
31
|
+
# CURSOR_TOKEN_TGZ_B64 } base64 gzip-tars of each OAuth credential tree,
|
|
32
|
+
# DEVIN_TOKEN_TGZ_B64 } rooted at $HOME (see capture-tokens.sh)
|
|
33
|
+
# ANTIGRAVITY_TOKEN_TGZ_B64
|
|
34
|
+
# CANARY_STATE version-gate state file (restored/saved by the workflow cache)
|
|
35
|
+
# CANARY_SLACK_WEBHOOK optional; report is POSTed here when set
|
|
36
|
+
# CANARY_FP_SHA failproofai HEAD, the second version-gate dimension
|
|
37
|
+
# CANARY_VERSION_GATED "all" (default) | comma-sep list | "none" to force-probe
|
|
38
|
+
# CANARY_CLIS space-separated CLI subset (empty = all 12)
|
|
39
|
+
# CANARY_SKIP_BUILD set to 1 to reuse an existing dist/ (local iteration)
|
|
40
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
41
|
+
set -u
|
|
42
|
+
|
|
43
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
44
|
+
REPO="${GITHUB_WORKSPACE:-$(dirname "$HERE")}"
|
|
45
|
+
|
|
46
|
+
VOL="${CANARY_VOL:-integration-suite}"
|
|
47
|
+
IMAGE="${CANARY_IMAGE:-failproofai-integration-suite:base}"
|
|
48
|
+
ENVFILE="${CANARY_ENVFILE:-$REPO/canary.env}"
|
|
49
|
+
TOKENS_DIR="${CANARY_TOKENS_DIR:-$REPO/tokens}"
|
|
50
|
+
STATE="${CANARY_STATE:-$REPO/integration-suite-state.json}"
|
|
51
|
+
|
|
52
|
+
step() { echo "── $* ──" >&2; }
|
|
53
|
+
|
|
54
|
+
cleanup() {
|
|
55
|
+
local rc=$?
|
|
56
|
+
step "cleanup"
|
|
57
|
+
rm -f "$ENVFILE"
|
|
58
|
+
rm -rf "$TOKENS_DIR"
|
|
59
|
+
docker volume rm "$VOL" -f >/dev/null 2>&1 || true
|
|
60
|
+
exit "$rc"
|
|
61
|
+
}
|
|
62
|
+
trap cleanup EXIT
|
|
63
|
+
|
|
64
|
+
# ── 0. adopt legacy version-gate state (rename transition) ──────────────────
|
|
65
|
+
# This harness was renamed from `cli-integration`, which took the Actions cache
|
|
66
|
+
# key with it. The workflow restores the legacy cache alongside the new one; if
|
|
67
|
+
# only the legacy file is present, adopt it so the version-gate keeps its
|
|
68
|
+
# baseline instead of re-probing all 12 CLIs and reporting every one as changed.
|
|
69
|
+
# Safe to delete (with the workflow's legacy restore step) once a run has saved
|
|
70
|
+
# state under the new key.
|
|
71
|
+
LEGACY_STATE="${CANARY_LEGACY_STATE:-$REPO/cli-integration-state.json}"
|
|
72
|
+
if [ ! -s "$STATE" ] && [ -s "$LEGACY_STATE" ]; then
|
|
73
|
+
step "adopting legacy version-gate state from $(basename "$LEGACY_STATE")"
|
|
74
|
+
cp "$LEGACY_STATE" "$STATE" || echo " WARN: copy failed — gate starts empty" >&2
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# ── 1. build failproofai under test ─────────────────────────────────────────
|
|
78
|
+
if [ "${CANARY_SKIP_BUILD:-0}" = 1 ]; then
|
|
79
|
+
step "skipping build (CANARY_SKIP_BUILD=1)"
|
|
80
|
+
else
|
|
81
|
+
step "building failproofai under test (dist/index.js + dist/cli.mjs — no dashboard)"
|
|
82
|
+
(
|
|
83
|
+
cd "$REPO" || exit 1
|
|
84
|
+
bun install --frozen-lockfile || exit 1
|
|
85
|
+
bun build --target=node --format=cjs --outfile=dist/index.js src/index.ts || exit 1
|
|
86
|
+
bun run build:cli || exit 1
|
|
87
|
+
) || { echo "✗ build failed" >&2; exit 1; }
|
|
88
|
+
fi
|
|
89
|
+
if [ ! -s "$REPO/dist/index.js" ] || [ ! -s "$REPO/dist/cli.mjs" ]; then
|
|
90
|
+
echo "✗ dist/index.js and dist/cli.mjs must both be non-empty" >&2
|
|
91
|
+
exit 1
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# ── 2. decode OAuth token secrets ───────────────────────────────────────────
|
|
95
|
+
# Each is a base64 gzip-tar rooted at $HOME. A missing secret is NOT fatal: that
|
|
96
|
+
# CLI simply reports ERROR (can't auth) rather than taking the whole run down.
|
|
97
|
+
step "decoding OAuth token secrets"
|
|
98
|
+
mkdir -p "$TOKENS_DIR"
|
|
99
|
+
chmod 700 "$TOKENS_DIR"
|
|
100
|
+
decode_token() {
|
|
101
|
+
local name="$1" b64="$2"
|
|
102
|
+
if [ -z "$b64" ]; then
|
|
103
|
+
echo " ✗ $name: no secret set (that CLI will report ERROR)"
|
|
104
|
+
return 0
|
|
105
|
+
fi
|
|
106
|
+
if printf '%s' "$b64" | base64 -d > "$TOKENS_DIR/$name.tgz" 2>/dev/null; then
|
|
107
|
+
echo " ✓ $name: $(wc -c < "$TOKENS_DIR/$name.tgz") bytes"
|
|
108
|
+
else
|
|
109
|
+
echo " ✗ $name: base64 decode FAILED (that CLI will report ERROR)"
|
|
110
|
+
rm -f "$TOKENS_DIR/$name.tgz"
|
|
111
|
+
fi
|
|
112
|
+
}
|
|
113
|
+
decode_token cursor "${CURSOR_TOKEN_TGZ_B64:-}"
|
|
114
|
+
decode_token devin "${DEVIN_TOKEN_TGZ_B64:-}"
|
|
115
|
+
decode_token antigravity "${ANTIGRAVITY_TOKEN_TGZ_B64:-}"
|
|
116
|
+
|
|
117
|
+
# ── 3. sandbox image + per-run volume ───────────────────────────────────────
|
|
118
|
+
step "building sandbox image ($IMAGE)"
|
|
119
|
+
docker build -t "$IMAGE" "$HERE/" || { echo "✗ sandbox image build failed" >&2; exit 1; }
|
|
120
|
+
|
|
121
|
+
step "creating per-run HOME volume ($VOL)"
|
|
122
|
+
docker volume rm "$VOL" -f >/dev/null 2>&1 || true
|
|
123
|
+
docker volume create "$VOL" >/dev/null || { echo "✗ volume create failed" >&2; exit 1; }
|
|
124
|
+
|
|
125
|
+
# ── 4. install CLIs @latest, then inject tokens ─────────────────────────────
|
|
126
|
+
step "installing 12 CLIs @latest into the fresh volume"
|
|
127
|
+
docker run --rm -v "$VOL:/home/canary" -v "$HERE:/opt/canary:ro" \
|
|
128
|
+
"$IMAGE" bash /opt/canary/install-clis.sh \
|
|
129
|
+
|| { echo "✗ CLI install failed" >&2; exit 1; }
|
|
130
|
+
|
|
131
|
+
step "injecting OAuth credential files"
|
|
132
|
+
docker run --rm -v "$VOL:/home/canary" \
|
|
133
|
+
-v "$HERE:/opt/canary:ro" -v "$TOKENS_DIR:/opt/tokens:ro" \
|
|
134
|
+
"$IMAGE" bash /opt/canary/inject-tokens.sh \
|
|
135
|
+
|| { echo "✗ token injection failed" >&2; exit 1; }
|
|
136
|
+
|
|
137
|
+
# ── 5. gateway env-file ─────────────────────────────────────────────────────
|
|
138
|
+
# umask 077 so the file with the gateway key is never group/world readable.
|
|
139
|
+
step "assembling gateway env-file"
|
|
140
|
+
(
|
|
141
|
+
umask 077
|
|
142
|
+
{
|
|
143
|
+
echo "CANARY_LLM_API_KEY=${CANARY_LLM_API_KEY:-}"
|
|
144
|
+
echo "CANARY_LLM_BASE_URL=${CANARY_LLM_BASE_URL:-https://models.aikin.club}"
|
|
145
|
+
echo "CANARY_LLM_MODEL=${CANARY_LLM_MODEL:-deepseek-v4-pro}"
|
|
146
|
+
echo "CANARY_CLAUDE_MODEL=${CANARY_CLAUDE_MODEL:-claude-haiku-4-5}"
|
|
147
|
+
echo "CANARY_PI_MODEL=${CANARY_PI_MODEL:-claude-haiku-4-5}"
|
|
148
|
+
echo "COPILOT_GITHUB_TOKEN=${COPILOT_GITHUB_TOKEN:-}"
|
|
149
|
+
} > "$ENVFILE"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# ── 6. probe + report ───────────────────────────────────────────────────────
|
|
153
|
+
# run.sh word-splits CANARY_CLIS into args; unquoted on purpose. It owns the
|
|
154
|
+
# exit code (non-zero iff a hard FAIL verdict is present), which the trap
|
|
155
|
+
# preserves.
|
|
156
|
+
step "running integration probes + report"
|
|
157
|
+
# shellcheck disable=SC2086
|
|
158
|
+
CANARY_REPO="$REPO" \
|
|
159
|
+
CANARY_SANDBOX="$HERE" \
|
|
160
|
+
CANARY_VOL="$VOL" \
|
|
161
|
+
CANARY_IMAGE="$IMAGE" \
|
|
162
|
+
CANARY_STATE="$STATE" \
|
|
163
|
+
CANARY_ENVFILE="$ENVFILE" \
|
|
164
|
+
bash "$HERE/run.sh" ${CANARY_CLIS:-}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
3
|
-
# failproofai
|
|
3
|
+
# failproofai integration suite — orchestrator (runs on the GitHub Actions runner).
|
|
4
4
|
# ./run.sh [cli ...] default: all 12
|
|
5
5
|
#
|
|
6
6
|
# Mirrors the box's run-canary.sh, but every path is env-parameterised (the
|
|
7
7
|
# runner is ephemeral) and the report is POSTed to a Slack Incoming Webhook
|
|
8
|
-
# instead of delivered via Hermes. Assumes
|
|
8
|
+
# instead of delivered via Hermes. Assumes ci-entrypoint.sh has already:
|
|
9
9
|
# • checked out + built failproofai (dist/index.js + dist/cli.mjs) at $CANARY_REPO
|
|
10
10
|
# • built the sandbox image ($CANARY_IMAGE)
|
|
11
11
|
# • created the per-run volume ($CANARY_VOL) and INSTALLED the CLIs into it
|
|
@@ -23,8 +23,8 @@ HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
|
23
23
|
|
|
24
24
|
REPO="${CANARY_REPO:?CANARY_REPO (built failproofai checkout) required}"
|
|
25
25
|
SANDBOX="${CANARY_SANDBOX:-$HERE}"
|
|
26
|
-
VOL="${CANARY_VOL:-
|
|
27
|
-
IMAGE="${CANARY_IMAGE:-failproofai-
|
|
26
|
+
VOL="${CANARY_VOL:-integration-suite}"
|
|
27
|
+
IMAGE="${CANARY_IMAGE:-failproofai-integration-suite:base}"
|
|
28
28
|
STATE="${CANARY_STATE:-$HERE/state.json}"
|
|
29
29
|
ENVFILE="${CANARY_ENVFILE:?CANARY_ENVFILE (docker --env-file with gateway creds) required}"
|
|
30
30
|
MODEL="${CANARY_LLM_MODEL:-deepseek-v4-pro}"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable per-entry identity for the log viewer.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately standalone and dependency-free: `lib/log-entries.ts` reaches
|
|
5
|
+
* fs/promises through its project-resolution imports, so a *value* import of
|
|
6
|
+
* it from a client component drags node:fs into the browser bundle and the
|
|
7
|
+
* session page 500s. Client code may only import types from there.
|
|
8
|
+
*/
|
|
9
|
+
import type { LogEntry } from "./log-entries";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Assigns every entry a unique, render-stable identity.
|
|
13
|
+
*
|
|
14
|
+
* Not every CLI writes a per-record `uuid` — Codex, Copilot, Cursor and Pi
|
|
15
|
+
* transcripts have none, so `baseEntry` leaves it "". Keying rows off
|
|
16
|
+
* `uuid || timestamp` therefore collapses to the timestamp, which those CLIs
|
|
17
|
+
* reuse freely (one 771-record Codex session: 93 timestamps shared by 2-5
|
|
18
|
+
* records each). Duplicate React keys break reconciliation, and in the
|
|
19
|
+
* virtualized log list that strands orphaned DOM nodes stacked on top of live
|
|
20
|
+
* rows — they keep their old transform and never recover (#292 follow-up).
|
|
21
|
+
*
|
|
22
|
+
* Collisions are disambiguated by occurrence order, so keys stay stable for as
|
|
23
|
+
* long as the parsed entry list is — which is what React and the virtualizer's
|
|
24
|
+
* measurement cache both require.
|
|
25
|
+
*/
|
|
26
|
+
export function buildEntryKeys(entries: LogEntry[]): Map<LogEntry, string> {
|
|
27
|
+
const keys = new Map<LogEntry, string>();
|
|
28
|
+
const seen = new Map<string, number>();
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
const base = entry.uuid || `${entry._source}:${entry.timestamp}`;
|
|
31
|
+
const n = seen.get(base) ?? 0;
|
|
32
|
+
seen.set(base, n + 1);
|
|
33
|
+
keys.set(entry, n === 0 ? base : `${base}#${n}`);
|
|
34
|
+
}
|
|
35
|
+
return keys;
|
|
36
|
+
}
|
|
@@ -16,14 +16,28 @@ export function formatRelativeTime(ts: number): string {
|
|
|
16
16
|
|
|
17
17
|
export function formatDuration(ms: number): string {
|
|
18
18
|
if (ms < 1000) return `${ms}ms`;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return `${
|
|
19
|
+
|
|
20
|
+
// Round to the precision the output will actually use, then bucket.
|
|
21
|
+
// Seconds are shown with one decimal place, so round to the nearest 0.1 s
|
|
22
|
+
// before deciding whether we have crossed into the minute range.
|
|
23
|
+
const deciseconds = Math.round(ms / 100);
|
|
24
|
+
if (deciseconds < 600) {
|
|
25
|
+
return `${(deciseconds / 10).toFixed(1)}s`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Minutes are shown with whole seconds, so round to the nearest second
|
|
29
|
+
// before splitting into minutes and seconds.
|
|
30
|
+
const totalSeconds = Math.round(ms / 1000);
|
|
31
|
+
if (totalSeconds < 3600) {
|
|
32
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
33
|
+
const seconds = totalSeconds % 60;
|
|
34
|
+
return `${minutes}m ${seconds}s`;
|
|
26
35
|
}
|
|
27
|
-
|
|
28
|
-
|
|
36
|
+
|
|
37
|
+
// Hours are shown with whole minutes, so round to the nearest minute
|
|
38
|
+
// before splitting into hours and minutes.
|
|
39
|
+
const totalMinutes = Math.round(ms / 60000);
|
|
40
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
41
|
+
const minutes = totalMinutes % 60;
|
|
42
|
+
return `${hours}h ${minutes}m`;
|
|
29
43
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "failproofai",
|
|
3
|
-
"version": "0.0.14
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously — for Claude Code & the Agents SDK",
|
|
5
5
|
"bin": {
|
|
6
6
|
"failproofai": "./dist/cli.mjs"
|
|
@@ -71,11 +71,11 @@
|
|
|
71
71
|
"access": "public"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@anthropic-ai/sdk": "^0.
|
|
74
|
+
"@anthropic-ai/sdk": "^0.112.4",
|
|
75
75
|
"@mdx-js/mdx": "^3.1.1",
|
|
76
76
|
"@tailwindcss/postcss": "^4.3.1",
|
|
77
77
|
"@tanstack/react-virtual": "^3.14.3",
|
|
78
|
-
"@testing-library/jest-dom": "^
|
|
78
|
+
"@testing-library/jest-dom": "^7.0.0",
|
|
79
79
|
"@testing-library/react": "^16.3.2",
|
|
80
80
|
"@testing-library/user-event": "^14.6.1",
|
|
81
81
|
"@types/node": "26.1.1",
|
|
@@ -106,6 +106,7 @@
|
|
|
106
106
|
"postcss": "8.5.14",
|
|
107
107
|
"eslint-plugin-react-hooks": "7.0.1",
|
|
108
108
|
"vite": "8.0.16",
|
|
109
|
-
"undici": "7.28.0"
|
|
109
|
+
"undici": "7.28.0",
|
|
110
|
+
"brace-expansion": "5.0.7"
|
|
110
111
|
}
|
|
111
112
|
}
|
|
@@ -9,7 +9,7 @@ const currentPort = parseInt(process.env.PORT, 10) || 3000
|
|
|
9
9
|
const hostname = process.env.HOSTNAME || '0.0.0.0'
|
|
10
10
|
|
|
11
11
|
let keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10)
|
|
12
|
-
const nextConfig = {"env":{"NEXT_PUBLIC_APP_VERSION":"0.0.14
|
|
12
|
+
const nextConfig = {"env":{"NEXT_PUBLIC_APP_VERSION":"0.0.14"},"typescript":{"ignoreBuildErrors":false},"typedRoutes":false,"distDir":"./.next","cleanDistDir":true,"assetPrefix":"","cacheMaxMemorySize":52428800,"configOrigin":"next.config.ts","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"poweredByHeader":true,"compress":true,"images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":14400,"formats":["image/webp"],"maximumRedirects":3,"maximumResponseBody":50000000,"dangerouslyAllowLocalIP":false,"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"attachment","localPatterns":[{"pathname":"**","search":""}],"remotePatterns":[],"qualities":[75],"unoptimized":true,"customCacheHandler":false},"devIndicators":{"position":"bottom-left"},"onDemandEntries":{"maxInactiveAge":60000,"pagesBufferLength":5},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"excludeDefaultMomentLocales":true,"reactProductionProfiling":false,"reactStrictMode":null,"reactMaxHeadersLength":6000,"httpAgentOptions":{"keepAlive":true},"logging":{"serverFunctions":true,"browserToTerminal":"warn"},"compiler":{},"expireTime":31536000,"staticPageGenerationTimeout":60,"output":"standalone","modularizeImports":{"@mui/icons-material":{"transform":"@mui/icons-material/{{member}}"},"lodash":{"transform":"lodash/{{member}}"}},"outputFileTracingRoot":"/home/runner/work/failproofai/failproofai","cacheComponents":false,"cacheLife":{"default":{"stale":300,"revalidate":900,"expire":4294967294},"seconds":{"stale":30,"revalidate":1,"expire":60},"minutes":{"stale":300,"revalidate":60,"expire":3600},"hours":{"stale":300,"revalidate":3600,"expire":86400},"days":{"stale":300,"revalidate":86400,"expire":604800},"weeks":{"stale":300,"revalidate":604800,"expire":2592000},"max":{"stale":300,"revalidate":2592000,"expire":31536000}},"cacheHandlers":{},"experimental":{"appNewScrollHandler":false,"useSkewCookie":false,"cssChunking":true,"multiZoneDraftMode":false,"appNavFailHandling":false,"prerenderEarlyExit":true,"serverMinification":true,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"cachedNavigations":false,"partialFallbacks":false,"dynamicOnHover":false,"varyParams":false,"prefetchInlining":false,"preloadEntriesOnStart":true,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","proxyPrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":3,"memoryBasedWorkersCount":false,"imgOptConcurrency":null,"imgOptTimeoutInSeconds":7,"imgOptMaxInputPixels":268402689,"imgOptSequentialRead":null,"imgOptSkipMetadata":null,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"swcTraceProfiling":false,"forceSwcTransforms":false,"largePageDataBytes":128000,"typedEnv":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"authInterrupts":false,"webpackMemoryOptimizations":false,"optimizeServerReact":true,"strictRouteTypes":false,"viewTransition":false,"removeUncaughtErrorAndRejectionListeners":false,"validateRSCRequestHeaders":false,"staleTimes":{"dynamic":0,"static":300},"reactDebugChannel":true,"serverComponentsHmrCache":true,"staticGenerationMaxConcurrency":8,"staticGenerationMinPagesPerWorker":25,"transitionIndicator":false,"gestureTransition":false,"inlineCss":false,"useCache":false,"globalNotFound":false,"browserDebugInfoInTerminal":"warn","lockDistDir":true,"proxyClientMaxBodySize":10485760,"hideLogsAfterAbort":false,"mcpServer":true,"turbopackFileSystemCacheForDev":true,"turbopackFileSystemCacheForBuild":false,"turbopackInferModuleSideEffects":true,"turbopackPluginRuntimeStrategy":"childProcesses","optimizePackageImports":["lucide-react","date-fns","lodash-es","ramda","antd","react-bootstrap","ahooks","@ant-design/icons","@headlessui/react","@headlessui-float/react","@heroicons/react/20/solid","@heroicons/react/24/solid","@heroicons/react/24/outline","@visx/visx","@tremor/react","rxjs","@mui/material","@mui/icons-material","recharts","react-use","effect","@effect/schema","@effect/platform","@effect/platform-node","@effect/platform-browser","@effect/platform-bun","@effect/sql","@effect/sql-mssql","@effect/sql-mysql2","@effect/sql-pg","@effect/sql-sqlite-node","@effect/sql-sqlite-bun","@effect/sql-sqlite-wasm","@effect/sql-sqlite-react-native","@effect/rpc","@effect/rpc-http","@effect/typeclass","@effect/experimental","@effect/opentelemetry","@material-ui/core","@material-ui/icons","@tabler/icons-react","mui-core","react-icons/ai","react-icons/bi","react-icons/bs","react-icons/cg","react-icons/ci","react-icons/di","react-icons/fa","react-icons/fa6","react-icons/fc","react-icons/fi","react-icons/gi","react-icons/go","react-icons/gr","react-icons/hi","react-icons/hi2","react-icons/im","react-icons/io","react-icons/io5","react-icons/lia","react-icons/lib","react-icons/lu","react-icons/md","react-icons/pi","react-icons/ri","react-icons/rx","react-icons/si","react-icons/sl","react-icons/tb","react-icons/tfi","react-icons/ti","react-icons/vsc","react-icons/wi"],"trustHostHeader":false,"isExperimentalCompile":false},"htmlLimitedBots":"[\\w-]+-Google|Google-[\\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight","bundlePagesRouterDependencies":false,"configFileName":"next.config.ts","outputFileTracingExcludes":{"*":["node_modules/@img/**","node_modules/sharp/**"]},"turbopack":{"root":"/home/runner/work/failproofai/failproofai"},"distDirRoot":".next"}
|
|
13
13
|
|
|
14
14
|
process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig)
|
|
15
15
|
|