@oneie/claude 0.7.0 → 0.9.0
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/agents/abm-strategist.md +89 -0
- package/agents/ads-meta.md +97 -0
- package/agents/analyst.md +173 -0
- package/agents/architect.md +280 -0
- package/agents/brand-guardian.md +88 -0
- package/agents/brand-strategist.md +92 -0
- package/agents/campaign-content.md +90 -0
- package/agents/campaign-email.md +88 -0
- package/agents/campaign-sms.md +87 -0
- package/agents/campaign-social.md +87 -0
- package/agents/cco.md +99 -0
- package/agents/ceo.md +106 -0
- package/agents/chairman.md +120 -0
- package/agents/cmo.md +101 -0
- package/agents/community-greeter.md +89 -0
- package/agents/community-moderator.md +92 -0
- package/agents/compliance.md +92 -0
- package/agents/copywriter.md +100 -0
- package/agents/creative-strategist.md +95 -0
- package/agents/cro.md +98 -0
- package/agents/cto.md +267 -0
- package/agents/customer-interviewer.md +93 -0
- package/agents/customer-researcher.md +89 -0
- package/agents/customer-success-manager.md +90 -0
- package/agents/customer-trainer.md +90 -0
- package/agents/cxo.md +100 -0
- package/agents/demand-creator.md +89 -0
- package/agents/demo-mover.md +83 -0
- package/agents/demo-specialist.md +89 -0
- package/agents/demo-thai-family-law.md +87 -0
- package/agents/designer.md +112 -0
- package/agents/discovery-caller.md +98 -0
- package/agents/doctor.md +139 -0
- package/agents/educate-coach.md +100 -0
- package/agents/elevate-tutor.md +83 -0
- package/agents/email-lifecycle-marketer.md +91 -0
- package/agents/engage-specialist.md +99 -0
- package/agents/events-coordinator.md +90 -0
- package/agents/foundation-builder.md +94 -0
- package/agents/funnel-architect.md +89 -0
- package/agents/gift-creator.md +100 -0
- package/agents/google-ads.md +103 -0
- package/agents/guide.md +292 -0
- package/agents/helpdesk-dispatcher.md +88 -0
- package/agents/hook-specialist.md +91 -0
- package/agents/identify-optimizer.md +101 -0
- package/agents/implementer.md +305 -0
- package/agents/incident-commander.md +120 -0
- package/agents/insights-lead.md +93 -0
- package/agents/journey-runner.md +113 -0
- package/agents/linkedin-ads.md +123 -0
- package/agents/live-sales-chat.md +90 -0
- package/agents/market-researcher.md +88 -0
- package/agents/media-buyer.md +96 -0
- package/agents/movers-customer-researcher.md +95 -0
- package/agents/movers-foundation-builder.md +96 -0
- package/agents/movers-market-researcher.md +97 -0
- package/agents/movers-pricing-strategist.md +171 -0
- package/agents/nurture-architect.md +99 -0
- package/agents/offer-architect.md +92 -0
- package/agents/onboarder.md +222 -0
- package/agents/onboarding-specialist.md +92 -0
- package/agents/operations-dashboard.md +98 -0
- package/agents/perf-engineer.md +348 -0
- package/agents/playbook-writer.md +71 -0
- package/agents/plg-strategist.md +91 -0
- package/agents/positioning-architect.md +88 -0
- package/agents/press-officer.md +89 -0
- package/agents/pricing-strategist.md +94 -0
- package/agents/privacy-officer.md +92 -0
- package/agents/referral-manager.md +91 -0
- package/agents/refine-analyst.md +102 -0
- package/agents/release-manager.md +261 -0
- package/agents/renewals-upsell-rep.md +90 -0
- package/agents/review-engineer.md +327 -0
- package/agents/rewards-steward.md +82 -0
- package/agents/sales-call-coach.md +94 -0
- package/agents/sales-closer.md +98 -0
- package/agents/security-auditor.md +313 -0
- package/agents/sell-closer.md +101 -0
- package/agents/share-amplifier.md +99 -0
- package/agents/social-media-manager.md +92 -0
- package/agents/storyteller.md +250 -0
- package/agents/strategist.md +83 -0
- package/agents/strategy-aligner.md +94 -0
- package/agents/support-agent.md +90 -0
- package/agents/tagger.md +245 -0
- package/agents/tech-writer.md +216 -0
- package/agents/test-engineer.md +243 -0
- package/agents/tiktok-ads.md +105 -0
- package/agents/tracking-engineer.md +92 -0
- package/agents/upsell-strategist.md +101 -0
- package/agents/voice.md +85 -0
- package/agents/w4-tools.md +153 -0
- package/agents/w4-verify.md +20 -138
- package/agents/workflow-optimiser.md +84 -0
- package/commands/close.md +814 -160
- package/commands/create.md +2 -2
- package/commands/deploy.md +554 -175
- package/commands/do.md +119 -109
- package/commands/fast.md +67 -0
- package/commands/improve.md +2 -2
- package/commands/one.md +418 -0
- package/commands/see.md +1 -1
- package/commands/sync.md +1 -1
- package/commands/tasks.md +222 -0
- package/commands/vespio.md +110 -0
- package/commands/vespio.remote.md +227 -0
- package/hooks/hooks.json +25 -79
- package/hooks/lib/governor-escape-match.sh +88 -0
- package/hooks/lib/hook.sh +4 -13
- package/hooks/lib/signal.sh +9 -2
- package/hooks/scripts/branch-pin.sh +6 -0
- package/hooks/scripts/config-protect.sh +6 -0
- package/hooks/scripts/do-outcome-gate.sh +19 -2
- package/hooks/scripts/git-add-guard.sh +43 -2
- package/hooks/scripts/governor-escape.sh +96 -0
- package/hooks/scripts/load-guard.sh +127 -0
- package/hooks/scripts/post-edit-check.sh +36 -29
- package/hooks/scripts/session-start.sh +34 -80
- package/hooks/scripts/task-complete-verify.sh +46 -40
- package/package.json +1 -1
- package/rules/documentation.md +9 -0
- package/scripts/ad-copy-lint.sh +656 -0
- package/scripts/agentverse-audit.sh +256 -0
- package/scripts/asi-walk.sh +435 -0
- package/scripts/astro-build-cached.sh +143 -0
- package/scripts/blocks-manifest-cached.sh +100 -0
- package/scripts/blocks-manifest.mjs +497 -0
- package/scripts/blocks-ratchet.sh +116 -0
- package/scripts/blocks-render-probe.mjs +529 -0
- package/scripts/blocks-usage.mjs +334 -0
- package/scripts/chat-context-check.sh +89 -0
- package/scripts/close-metrics.sh +558 -0
- package/scripts/close-owner.sh +268 -0
- package/scripts/db-sync-lock-check.sh +116 -0
- package/scripts/deploy-dev.sh +35 -0
- package/scripts/deploy-emit.sh +298 -0
- package/scripts/deploy-gate-check.sh +128 -0
- package/scripts/deploy-ready.sh +78 -0
- package/scripts/deploy-record.sh +605 -0
- package/scripts/deploy.sh +1273 -0
- package/scripts/do-auto.sh +598 -48
- package/scripts/do-brief.sh +113 -0
- package/scripts/do-close.sh +1137 -0
- package/scripts/do-commit.sh +75 -0
- package/scripts/do-consumer-sweep.sh +18 -1
- package/scripts/do-cycle-shape-check.sh +160 -0
- package/scripts/do-decide.sh +476 -0
- package/scripts/do-derive-check.sh +436 -0
- package/scripts/do-fleet.sh +106 -28
- package/scripts/do-folder.sh +10 -1
- package/scripts/do-next.sh +106 -0
- package/scripts/do-orchestrate.sh +17 -5
- package/scripts/do-plan-json.mjs +201 -0
- package/scripts/do-plan-json.sh +8 -0
- package/scripts/do-preflight.sh +117 -0
- package/scripts/do-project.sh +157 -0
- package/scripts/do-prove-selftest.sh +108 -0
- package/scripts/do-prove.sh +295 -23
- package/scripts/do-rank.py +31 -1
- package/scripts/do-recon-cache.sh +7 -1
- package/scripts/do-recon-pack.sh +196 -0
- package/scripts/do-reconcile.sh +121 -4
- package/scripts/do-signal.sh +280 -23
- package/scripts/do-smoke.sh +18 -1
- package/scripts/do-test-gate.sh +80 -0
- package/scripts/do-tick.sh +102 -0
- package/scripts/do-tier.sh +6 -0
- package/scripts/do-triage.sh +182 -0
- package/scripts/do-ui-gate.sh +1 -1
- package/scripts/do-w4-gates.sh +451 -0
- package/scripts/do-walk.sh +12 -1
- package/scripts/env-sync.sh +173 -0
- package/scripts/factory-brief-check.sh +330 -0
- package/scripts/factory-check.sh +68 -14
- package/scripts/factory-close-check.sh +257 -0
- package/scripts/factory-emit.sh +211 -0
- package/scripts/factory-executor-check.mjs +353 -0
- package/scripts/factory-peak.sh +301 -0
- package/scripts/factory-repo.sh +118 -3
- package/scripts/factory-review-check.mjs +61 -0
- package/scripts/factory-ship.sh +61 -0
- package/scripts/factory-tasks-check.sh +18 -1
- package/scripts/factory-turn.sh +326 -0
- package/scripts/factory-walk.sh +396 -0
- package/scripts/factory-width.sh +57 -0
- package/scripts/fade-toxic.sh +4 -3
- package/scripts/fixtures/factory-brief-real.md +44 -0
- package/scripts/fixtures/triage-dupe.md +5 -0
- package/scripts/fleet-manifest.mjs +108 -0
- package/scripts/fleet-status.sh +110 -0
- package/scripts/full-suite-paths-check.sh +144 -0
- package/scripts/gate-reaper-check.sh +98 -0
- package/scripts/gate-reaper.sh +125 -0
- package/scripts/gate-run.sh +73 -0
- package/scripts/gc-content-check.sh +140 -0
- package/scripts/gen-dev-config.py +20 -0
- package/scripts/govern-bound-check.sh +60 -0
- package/scripts/govern-claims-check.sh +233 -0
- package/scripts/govern-mem-check.sh +290 -0
- package/scripts/governor-doors-check.sh +362 -0
- package/scripts/governor-escape-check.sh +171 -0
- package/scripts/health.sh +413 -0
- package/scripts/id-inventory.mjs +418 -0
- package/scripts/land.sh +551 -0
- package/scripts/lib/gc-finished.sh +77 -0
- package/scripts/lib/govern.sh +361 -0
- package/scripts/lib/govern.ts +756 -0
- package/scripts/lighthouse-run.sh +187 -0
- package/scripts/livekit-live-check.sh +61 -0
- package/scripts/livekit-ratchet.sh +46 -0
- package/scripts/load-guard-check.sh +49 -0
- package/scripts/machine-check.sh +102 -0
- package/scripts/machine-watch.sh +177 -0
- package/scripts/one-agents.mjs +415 -0
- package/scripts/one-resume.sh +78 -0
- package/scripts/orphan-baseline.json +182 -0
- package/scripts/orphan-modules.mjs +179 -0
- package/scripts/pr-body.sh +335 -0
- package/scripts/preview-fd-check.sh +289 -0
- package/scripts/promise-manifest.mjs +24 -1
- package/scripts/release.sh +322 -0
- package/scripts/roles-check.sh +946 -0
- package/scripts/sdk-build-cached.sh +64 -0
- package/scripts/signal-watch.sh +241 -0
- package/scripts/skills-publish.sh +94 -0
- package/scripts/speed-cache-check.sh +173 -0
- package/scripts/speed-check.mjs +907 -0
- package/scripts/speed-parity-check.sh +648 -0
- package/scripts/speed-waterfall-check.sh +355 -0
- package/scripts/substrate-env-parity.mjs +156 -0
- package/scripts/tasks-claim-race.mjs +108 -0
- package/scripts/tasks-loop.sh +185 -0
- package/scripts/test-cached.sh +255 -0
- package/scripts/test-full.sh +87 -0
- package/scripts/test-honesty.mjs +137 -0
- package/scripts/test-lanes.sh +166 -0
- package/scripts/test-speed.sh +94 -0
- package/scripts/triage-shape-check.sh +149 -0
- package/scripts/tsc-cached.sh +179 -0
- package/scripts/typedb-flake-check.sh +197 -0
- package/scripts/urls-lint.sh +15 -0
- package/scripts/verify-fast.sh +445 -0
- package/scripts/vespio-sync.sh +149 -0
- package/scripts/wf-check.mjs +104 -1
- package/scripts/worktree-preview.sh +879 -0
- package/scripts/worktree-up.sh +208 -0
- package/skills/livekit-agents/SKILL.md +285 -0
- package/skills/livekit-agents/references/freshness-rules.md +168 -0
- package/skills/shadcn/SKILL.md +1 -1
- package/skills/signal/SKILL.md +0 -1
- package/skills/voice/SKILL.md +94 -6
- package/skills/voice/corpus-check.sh +87 -0
- package/hooks/scripts/compact-hint.sh +0 -35
- package/hooks/scripts/gate-guard.sh +0 -83
- package/hooks/scripts/read-tracker.sh +0 -26
- package/hooks/scripts/session-end-verify.sh +0 -51
- package/hooks/scripts/stop-reflect.sh +0 -140
- package/hooks/scripts/sync-priority-todo.sh +0 -57
- package/hooks/scripts/sync-todo-docs.sh +0 -46
- package/hooks/scripts/tool-signal.sh +0 -48
- package/scripts/do-tasks-bridge.py +0 -366
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# asi-walk.sh — the deterministic walk that brings an agent along the ASI
|
|
3
|
+
# (Fetch.ai / Agentverse) lifecycle. Zero LLM. Numeric receipts. Closed loop.
|
|
4
|
+
#
|
|
5
|
+
# The lifecycle has two halves and they are NOT the same kind of check:
|
|
6
|
+
#
|
|
7
|
+
# LOCAL (default, no credentials) authored → parses → compiles →
|
|
8
|
+
# imports resolve → protocols emitted → stamped
|
|
9
|
+
# REMOTE (--remote, needs a key) create → upload → secrets → start →
|
|
10
|
+
# poll → reachable
|
|
11
|
+
#
|
|
12
|
+
# The local half is the half that goes RED TODAY if a compiler regression
|
|
13
|
+
# lands. It is the default for that reason. The remote half touches a third
|
|
14
|
+
# party, so it is opt-in by flag AND gated on the key being present — it is
|
|
15
|
+
# never attempted by default and never by --self-test.
|
|
16
|
+
#
|
|
17
|
+
# ── The three exit states ────────────────────────────────────────────────
|
|
18
|
+
# A station that could not run is NOT a station that passed. Precedent:
|
|
19
|
+
# pay/backend/src/chains/lightning.ts reports "unconfigured" rather than
|
|
20
|
+
# fabricating a pass when LIGHTNING_API_KEY is unset. Same rule here.
|
|
21
|
+
#
|
|
22
|
+
# 0 every station that ran, passed
|
|
23
|
+
# 1 a station RAN and FAILED
|
|
24
|
+
# 3 a station COULD NOT RUN (no key, no network) — only with --strict,
|
|
25
|
+
# or always for --remote, because an unrun remote station aggregating
|
|
26
|
+
# into green is the exact lie this script exists to prevent.
|
|
27
|
+
#
|
|
28
|
+
# Usage:
|
|
29
|
+
# asi-walk.sh <agent.md|agent-dir> # local stations
|
|
30
|
+
# asi-walk.sh <agent.md> --skills <dir> # resolve skills from dir
|
|
31
|
+
# asi-walk.sh <agent.md> --remote # + remote READ stations (needs key)
|
|
32
|
+
# asi-walk.sh <agent.md> --publish # actually DEPLOY to Agentverse
|
|
33
|
+
#
|
|
34
|
+
# --remote is read-only: it proves the key is accepted and the API answers.
|
|
35
|
+
# --publish is the only flag that mutates a third party, and it drives the real
|
|
36
|
+
# `oneie agent publish --target agentverse` path rather than a reimplementation.
|
|
37
|
+
#
|
|
38
|
+
# AGENTVERSE_API_KEY is NOT the ASI:One key. Measured 2026-08-24: an ASI:One key
|
|
39
|
+
# (api.asi1.ai) gets 401 "Could not validate credentials" from
|
|
40
|
+
# agentverse.ai/v1/hosting. Two products, two credentials. Get an Agentverse key
|
|
41
|
+
# from agentverse.ai → Profile → API Keys.
|
|
42
|
+
# asi-walk.sh <agent.md> --strict # unrun local station ⇒ exit 3
|
|
43
|
+
# asi-walk.sh <agent.md> --json # machine-readable receipt
|
|
44
|
+
# asi-walk.sh --list # print stations, run nothing
|
|
45
|
+
# asi-walk.sh --self-test # prove each station can go RED
|
|
46
|
+
#
|
|
47
|
+
# Env:
|
|
48
|
+
# AGENTVERSE_API_KEY required by --remote. Absent ⇒ every remote station
|
|
49
|
+
# reports unconfigured (exit 3), never a pass.
|
|
50
|
+
# ASI_WALK_BASE Agentverse API base (default https://agentverse.ai/v1)
|
|
51
|
+
# ONE_API_KEY substrate key injected into a published agent
|
|
52
|
+
# (legacy ONEIE_API_KEY also read, per resolveKey order)
|
|
53
|
+
# ANTHROPIC_API_KEY the deployed agent's model credential. Absent is
|
|
54
|
+
# reported, not invented — a published agent without it
|
|
55
|
+
# dies on its first handled message.
|
|
56
|
+
set -uo pipefail
|
|
57
|
+
|
|
58
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
59
|
+
ASI_WALK_BASE="${ASI_WALK_BASE:-https://agentverse.ai/v1}"
|
|
60
|
+
|
|
61
|
+
AGENT_PATH=""; SKILLS_DIR=""; DO_REMOTE=0; DO_PUBLISH=0; STRICT=0; JSON=0; LIST=0; SELFTEST=0
|
|
62
|
+
while [[ $# -gt 0 ]]; do
|
|
63
|
+
case "$1" in
|
|
64
|
+
--skills) SKILLS_DIR="${2:-}"; shift 2 ;;
|
|
65
|
+
--remote) DO_REMOTE=1; shift ;;
|
|
66
|
+
--publish) DO_REMOTE=1; DO_PUBLISH=1; shift ;;
|
|
67
|
+
--strict) STRICT=1; shift ;;
|
|
68
|
+
--json) JSON=1; shift ;;
|
|
69
|
+
--list) LIST=1; shift ;;
|
|
70
|
+
--self-test) SELFTEST=1; shift ;;
|
|
71
|
+
-h|--help) sed -n '2,45p' "${BASH_SOURCE[0]}"; exit 0 ;;
|
|
72
|
+
-*) echo "asi-walk: unknown flag $1" >&2; exit 2 ;;
|
|
73
|
+
*) AGENT_PATH="$1"; shift ;;
|
|
74
|
+
esac
|
|
75
|
+
done
|
|
76
|
+
|
|
77
|
+
PASS=0; FAIL=0; UNRUN=0
|
|
78
|
+
declare -a RECEIPTS=()
|
|
79
|
+
|
|
80
|
+
# station <id> <title> — then the body runs; use ok/bad/unrun to close it.
|
|
81
|
+
_station_id=""; _station_title=""
|
|
82
|
+
station() { _station_id="$1"; _station_title="$2"; }
|
|
83
|
+
ok() { PASS=$((PASS+1)); RECEIPTS+=("$_station_id|pass|$_station_title|${1:-}"); printf ' \033[32mPASS\033[0m %-22s %s\n' "$_station_id" "${1:-$_station_title}"; }
|
|
84
|
+
bad() { FAIL=$((FAIL+1)); RECEIPTS+=("$_station_id|fail|$_station_title|${1:-}"); printf ' \033[31mFAIL\033[0m %-22s %s\n' "$_station_id" "${1:-$_station_title}"; }
|
|
85
|
+
unrun() { UNRUN=$((UNRUN+1)); RECEIPTS+=("$_station_id|unrun|$_station_title|${1:-}"); printf ' \033[33mUNRUN\033[0m %-22s %s\n' "$_station_id" "${1:-$_station_title}"; }
|
|
86
|
+
|
|
87
|
+
STATIONS_LOCAL=(
|
|
88
|
+
"parses|agent.md exists and frontmatter parses"
|
|
89
|
+
"name|frontmatter declares a name"
|
|
90
|
+
"compiles|compileAgent(target=uagents) emits Python"
|
|
91
|
+
"import-root|Model imported from the uagents ROOT, not uagents.models"
|
|
92
|
+
"agent-ctor|emitted Python constructs Agent(...)"
|
|
93
|
+
"protocols|skills resolve to Protocol/on_message handlers"
|
|
94
|
+
"stamp|Generated by ONE stamp present"
|
|
95
|
+
"model-cred|ANTHROPIC_API_KEY available for the deployed agent"
|
|
96
|
+
"substrate-cred|ONE/ONEIE key available to inject"
|
|
97
|
+
)
|
|
98
|
+
STATIONS_REMOTE=(
|
|
99
|
+
"av-key|AGENTVERSE_API_KEY present"
|
|
100
|
+
"av-reach|Agentverse API reachable AND the key is accepted"
|
|
101
|
+
"av-publish|oneie agent publish --target agentverse (needs --publish)"
|
|
102
|
+
"av-poll|GET /hosting/agents/{addr} reports compiled"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
if [[ $LIST -eq 1 ]]; then
|
|
106
|
+
echo "LOCAL stations (default):"
|
|
107
|
+
for s in "${STATIONS_LOCAL[@]}"; do printf ' %-16s %s\n' "${s%%|*}" "${s#*|}"; done
|
|
108
|
+
echo "REMOTE stations (--remote, needs AGENTVERSE_API_KEY):"
|
|
109
|
+
for s in "${STATIONS_REMOTE[@]}"; do printf ' %-16s %s\n' "${s%%|*}" "${s#*|}"; done
|
|
110
|
+
exit 0
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# ── resolve the agent file ───────────────────────────────────────────────
|
|
114
|
+
resolve_agent() {
|
|
115
|
+
local p="$1"
|
|
116
|
+
[[ -z "$p" ]] && return 1
|
|
117
|
+
if [[ -d "$p" ]]; then
|
|
118
|
+
for cand in "$p/agent.md" "$p/$(basename "$p").md"; do
|
|
119
|
+
[[ -f "$cand" ]] && { echo "$cand"; return 0; }
|
|
120
|
+
done
|
|
121
|
+
local first; first="$(find "$p" -maxdepth 1 -name '*.md' | head -1)"
|
|
122
|
+
[[ -n "$first" ]] && { echo "$first"; return 0; }
|
|
123
|
+
return 1
|
|
124
|
+
fi
|
|
125
|
+
[[ -f "$p" ]] && { echo "$p"; return 0; }
|
|
126
|
+
return 1
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# compile_out <agent.md> <skills-dir-or-empty> — prints emitted Python, or
|
|
130
|
+
# prints nothing and returns non-zero. Uses the REAL SDK compiler, never a
|
|
131
|
+
# reimplementation: this station must break when the compiler breaks.
|
|
132
|
+
#
|
|
133
|
+
# ── why this retries, and why stderr is kept ─────────────────────────────
|
|
134
|
+
# This swallowed stderr with 2>/dev/null. A transient compiler failure (cold
|
|
135
|
+
# module cache, a saturated box) then looked BYTE-IDENTICAL to "this agent has
|
|
136
|
+
# nothing to compile": empty stdout either way. Measured: the first run of an
|
|
137
|
+
# agent reported pass=6 unrun=3 where five consecutive warm runs reported
|
|
138
|
+
# pass=7 unrun=2 — the same agent, two different verdicts, cache warmth the only
|
|
139
|
+
# variable. A walk whose answer depends on cache warmth is not deterministic,
|
|
140
|
+
# which is the one property this script exists to have.
|
|
141
|
+
# Retry once on empty output, and keep stderr so a real failure can be NAMED
|
|
142
|
+
# instead of silently degrading into an unrun station.
|
|
143
|
+
COMPILE_ERR=""
|
|
144
|
+
compile_out() {
|
|
145
|
+
local md="$1" skills="$2" out="" err_file
|
|
146
|
+
md="$(cd "$(dirname "$md")" && pwd)/$(basename "$md")"
|
|
147
|
+
[[ -n "$skills" && -d "$skills" ]] && skills="$(cd "$skills" && pwd)"
|
|
148
|
+
err_file="$(mktemp)"
|
|
149
|
+
for attempt in 1 2; do
|
|
150
|
+
out="$(_compile_once "$md" "$skills" 2>"$err_file")"
|
|
151
|
+
[[ -n "$out" ]] && break
|
|
152
|
+
done
|
|
153
|
+
COMPILE_ERR="$(head -c 400 "$err_file" | tr '\n' ' ')"
|
|
154
|
+
rm -f "$err_file"
|
|
155
|
+
printf '%s' "$out"
|
|
156
|
+
[[ -n "$out" ]]
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
_compile_once() {
|
|
160
|
+
local md="$1" skills="$2"
|
|
161
|
+
# ABSOLUTE paths: `bun --cwd` moves the working directory to packages/sdk, so a
|
|
162
|
+
# relative agent path resolves against the wrong root and reads ENOENT — which
|
|
163
|
+
# the caller would otherwise report as the compiler emitting nothing.
|
|
164
|
+
ONE_ASI_MD="$md" ONE_ASI_SKILLS="$skills" bun --cwd "$REPO_ROOT/packages/sdk" - <<'TS'
|
|
165
|
+
import { readFileSync, existsSync, readdirSync } from 'node:fs'
|
|
166
|
+
import { join, basename } from 'node:path'
|
|
167
|
+
import { compileAgent } from './src/compile'
|
|
168
|
+
const md = readFileSync(process.env.ONE_ASI_MD!, 'utf8')
|
|
169
|
+
const dir = process.env.ONE_ASI_SKILLS || ''
|
|
170
|
+
const skills: Record<string, string> = {}
|
|
171
|
+
// Two shapes in this tree: a flat `<dir>/<name>.md`, and the shared pool's
|
|
172
|
+
// `<dir>/<name>/SKILL.md` (one.ie/ai/skills and .claude/skills both use the
|
|
173
|
+
// second). Reading only the flat form silently resolves ZERO skills against the
|
|
174
|
+
// real pool, which then reads as "skills never reached the compiler".
|
|
175
|
+
// Never branch on the dirent TYPE. `withFileTypes` reports a symlinked
|
|
176
|
+
// directory as isSymbolicLink(), not isDirectory(), so an --skills dir built
|
|
177
|
+
// from symlinks (the only way to span both pools) resolved ZERO skills and every
|
|
178
|
+
// agent read as "skills never reached the compiler". Probe the PATHS instead —
|
|
179
|
+
// existsSync follows links, so all three shapes resolve the same way.
|
|
180
|
+
if (dir && existsSync(dir)) {
|
|
181
|
+
for (const name of readdirSync(dir)) {
|
|
182
|
+
if (name.endsWith('.md')) {
|
|
183
|
+
const flat = join(dir, name)
|
|
184
|
+
if (existsSync(flat)) skills[basename(name, '.md')] = readFileSync(flat, 'utf8')
|
|
185
|
+
continue
|
|
186
|
+
}
|
|
187
|
+
for (const inner of ['SKILL.md', `${name}.md`]) {
|
|
188
|
+
const p = join(dir, name, inner)
|
|
189
|
+
if (existsSync(p)) { skills[name] = readFileSync(p, 'utf8'); break }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
process.stdout.write(compileAgent(md, { target: 'uagents', skills }))
|
|
194
|
+
TS
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
run_local() {
|
|
198
|
+
local md="$1" skills="$2"
|
|
199
|
+
|
|
200
|
+
station parses "agent.md exists and frontmatter parses"
|
|
201
|
+
if [[ ! -f "$md" ]]; then bad "no such file: $md"; return; fi
|
|
202
|
+
if ! head -1 "$md" | grep -q '^---'; then bad "no YAML frontmatter fence at line 1"; else ok "$md"; fi
|
|
203
|
+
|
|
204
|
+
station name "frontmatter declares a name"
|
|
205
|
+
local nm; nm="$(sed -n '2,40p' "$md" | grep -m1 '^name:' | sed 's/^name:[[:space:]]*//')"
|
|
206
|
+
if [[ -n "$nm" ]]; then ok "name=$nm"; else bad "no name: in frontmatter"; fi
|
|
207
|
+
|
|
208
|
+
station compiles "compileAgent(target=uagents) emits Python"
|
|
209
|
+
local out; out="$(compile_out "$md" "$skills")"
|
|
210
|
+
if [[ -z "$out" ]]; then
|
|
211
|
+
# Name the cause. "produced nothing" with the reason hidden is what let a
|
|
212
|
+
# transient failure masquerade as an agent with nothing to compile.
|
|
213
|
+
bad "compiler produced nothing after 2 attempts${COMPILE_ERR:+ — stderr: $COMPILE_ERR}";
|
|
214
|
+
for s in import-root agent-ctor protocols stamp; do
|
|
215
|
+
station "$s" "skipped — nothing compiled"; unrun "no compiler output to assert on"
|
|
216
|
+
done
|
|
217
|
+
else
|
|
218
|
+
ok "$(wc -l <<<"$out" | tr -d ' ') lines of Python"
|
|
219
|
+
|
|
220
|
+
# The D10 regression guard. uagents/models.py does not exist upstream
|
|
221
|
+
# (404); Model is re-exported from the package root. A wrong path here is
|
|
222
|
+
# an ImportError at agent startup, which no local typecheck can see.
|
|
223
|
+
station import-root "Model imported from the uagents ROOT"
|
|
224
|
+
if grep -q 'from uagents\.models import' <<<"$out"; then
|
|
225
|
+
bad "emits 'from uagents.models import' — that module does not exist upstream"
|
|
226
|
+
elif grep -qE '^from uagents import .*\bModel\b' <<<"$out"; then
|
|
227
|
+
ok "from uagents import ... Model"
|
|
228
|
+
# Match Model as a PYTHON SYMBOL only — `class X(Model)` / `: Model`. The
|
|
229
|
+
# emitted file embeds the agent's markdown prompt as a string, and that prose
|
|
230
|
+
# says things like "## Model · Effort dial"; a bare \bModel\b grep matches the
|
|
231
|
+
# documentation and reports a NameError that does not exist.
|
|
232
|
+
elif grep -qE '\(Model\)|:[[:space:]]*Model\b' <<<"$out"; then
|
|
233
|
+
bad "uses Model as a symbol but never imports it from the root (NameError at runtime)"
|
|
234
|
+
else
|
|
235
|
+
unrun "this agent emits no Model symbol (no skills, no endpoints)"
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
station agent-ctor "emitted Python constructs Agent(...)"
|
|
239
|
+
if grep -qE '(^|[^A-Za-z_])Agent\(' <<<"$out"; then ok; else bad "no Agent( constructor emitted"; fi
|
|
240
|
+
|
|
241
|
+
# D8 guard: with no skills map, hasSkills is false and NO protocol handlers
|
|
242
|
+
# are emitted — every deploy shipped a shell.
|
|
243
|
+
#
|
|
244
|
+
# Read the AGENT'S OWN declaration first. An agent that declares no skills
|
|
245
|
+
# (absent, or `skills: []` — 28 of 116 in this tree) correctly emits no
|
|
246
|
+
# handler, and calling that a failure blamed 28 healthy agents for a
|
|
247
|
+
# property they never claimed. Only a DECLARED-but-unresolved skill is a
|
|
248
|
+
# defect: that is a dangling ref, and it publishes a silent shell.
|
|
249
|
+
station protocols "declared skills resolve to Protocol/on_message handlers"
|
|
250
|
+
local decl; decl="$(sed -n '2,40p' "$md" | grep -m1 '^skills:' | sed 's/^skills:[[:space:]]*//')"
|
|
251
|
+
local n_handlers; n_handlers="$(grep -c 'on_message' <<<"$out")"
|
|
252
|
+
if [[ -z "$decl" || "$decl" == "[]" ]]; then
|
|
253
|
+
unrun "agent declares no skills — no protocol expected"
|
|
254
|
+
elif [[ -z "$skills" ]]; then
|
|
255
|
+
unrun "declares skills ($decl) but no --skills dir given; cannot resolve"
|
|
256
|
+
elif [[ "$n_handlers" -gt 0 ]]; then
|
|
257
|
+
ok "$n_handlers on_message handler(s) from: $decl"
|
|
258
|
+
else
|
|
259
|
+
bad "declares skills ($decl) but NONE resolved — dangling ref, would publish a handler-less agent"
|
|
260
|
+
fi
|
|
261
|
+
|
|
262
|
+
station stamp "Generated by ONE stamp present"
|
|
263
|
+
if grep -q 'Generated by ONE' <<<"$out"; then ok; else bad "no ONE attribution in emitted code"; fi
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
# Credentials the PUBLISHED agent needs. Reported, never invented. An agent
|
|
267
|
+
# that deploys without a model credential dies on its first handled message.
|
|
268
|
+
station model-cred "ANTHROPIC_API_KEY available for the deployed agent"
|
|
269
|
+
if [[ -n "${ANTHROPIC_API_KEY:-}" ]]; then ok "present in env (value never printed)"
|
|
270
|
+
else unrun "unset — a published agent would fail on its first message"; fi
|
|
271
|
+
|
|
272
|
+
station substrate-cred "ONE/ONEIE key available to inject"
|
|
273
|
+
if [[ -n "${ONE_API_KEY:-}" ]]; then ok "ONE_API_KEY present"
|
|
274
|
+
elif [[ -n "${ONEIE_API_KEY:-}" ]]; then ok "ONEIE_API_KEY (legacy) present"
|
|
275
|
+
else unrun "neither ONE_API_KEY nor ONEIE_API_KEY set"; fi
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
run_remote() {
|
|
279
|
+
station av-key "AGENTVERSE_API_KEY present"
|
|
280
|
+
if [[ -z "${AGENTVERSE_API_KEY:-}" ]]; then
|
|
281
|
+
unrun "unset — refusing to guess; remote stations cannot run. NOTE: this is NOT the ASI:One key; get one from agentverse.ai → Profile → API Keys"
|
|
282
|
+
for s in av-reach av-publish av-poll; do
|
|
283
|
+
station "$s" "gated on AGENTVERSE_API_KEY"; unrun "no credential"
|
|
284
|
+
done
|
|
285
|
+
return
|
|
286
|
+
fi
|
|
287
|
+
ok "present (value never printed)"
|
|
288
|
+
|
|
289
|
+
station av-reach "Agentverse API reachable"
|
|
290
|
+
local code
|
|
291
|
+
code="$(curl -s -o /dev/null -w '%{http_code}' -m 15 \
|
|
292
|
+
-H "Authorization: Bearer ${AGENTVERSE_API_KEY}" \
|
|
293
|
+
"$ASI_WALK_BASE/hosting/agents" 2>/dev/null)"
|
|
294
|
+
if [[ "$code" == "200" ]]; then ok "GET /hosting/agents → 200"
|
|
295
|
+
elif [[ -z "$code" || "$code" == "000" ]]; then
|
|
296
|
+
unrun "no network / no response from $ASI_WALK_BASE"
|
|
297
|
+
for s in av-publish av-poll; do
|
|
298
|
+
station "$s" "unreachable"; unrun "API did not respond"
|
|
299
|
+
done
|
|
300
|
+
return
|
|
301
|
+
elif [[ "$code" == "401" || "$code" == "403" ]]; then
|
|
302
|
+
# A key that is PRESENT but REJECTED is the failure the av-key station
|
|
303
|
+
# cannot see. Measured 2026-08-24: the ASI:One key (api.asi1.ai) returns
|
|
304
|
+
# 401 "Could not validate credentials" here — Agentverse hosting is a
|
|
305
|
+
# SEPARATE credential from the ASI:One LLM key. Distinguish the two 401s:
|
|
306
|
+
# no credential answers "Not authenticated"; a rejected Bearer answers
|
|
307
|
+
# "Could not validate credentials".
|
|
308
|
+
bad "GET /hosting/agents → $code (key present but REJECTED — an ASI:One key is not an Agentverse key)"
|
|
309
|
+
for s in av-publish av-poll; do station "$s" "gated on a valid credential"; unrun "credential rejected"; done
|
|
310
|
+
return
|
|
311
|
+
else bad "GET /hosting/agents → $code"; fi
|
|
312
|
+
|
|
313
|
+
# ── the write half ──────────────────────────────────────────────────────
|
|
314
|
+
# This CREATES a live hosted agent on a third party, so it needs its own
|
|
315
|
+
# opt-in: --remote alone stays read-only (key + reachability). --publish arms
|
|
316
|
+
# it. Read-only at every level by default.
|
|
317
|
+
#
|
|
318
|
+
# It invokes the REAL CLI publish path, never a reimplementation. A walk that
|
|
319
|
+
# POSTs its own create/upload/start sequence would prove a code path nobody
|
|
320
|
+
# ships; `oneie agent publish --target agentverse` is the door production
|
|
321
|
+
# uses, so that is the door this station opens.
|
|
322
|
+
station av-publish "oneie agent publish --target agentverse"
|
|
323
|
+
if [[ $DO_PUBLISH -ne 1 ]]; then
|
|
324
|
+
unrun "not armed — pass --publish to actually deploy (it creates a live agent)"
|
|
325
|
+
station av-poll "GET /hosting/agents/{addr} reports compiled"; unrun "nothing published"
|
|
326
|
+
return
|
|
327
|
+
fi
|
|
328
|
+
|
|
329
|
+
# Run the LOCAL CLI source, never `bunx oneie` — bunx resolves the PUBLISHED
|
|
330
|
+
# npm package, which lags this tree. Measured: it errored `unknown option
|
|
331
|
+
# '--skills'` because that flag exists only locally, so the walk was testing a
|
|
332
|
+
# different CLI than the one being developed. `bun run <src>` pins it to HEAD.
|
|
333
|
+
local pub rc addr cli="$REPO_ROOT/packages/cli/src/index.ts"
|
|
334
|
+
if [[ ! -f "$cli" ]]; then
|
|
335
|
+
bad "local CLI source not found at $cli"
|
|
336
|
+
station av-poll "GET /hosting/agents/{addr} reports compiled"; unrun "no CLI to run"
|
|
337
|
+
return
|
|
338
|
+
fi
|
|
339
|
+
pub="$(cd "$REPO_ROOT" && AGENTVERSE_API_KEY="$AGENTVERSE_API_KEY" \
|
|
340
|
+
bun "$cli" agent publish "$MD" --target agentverse \
|
|
341
|
+
${SKILLS_DIR:+--skills "$SKILLS_DIR"} 2>&1)"; rc=$?
|
|
342
|
+
addr="$(grep -oE 'agent1[a-z0-9]{20,}' <<<"$pub" | head -1)"
|
|
343
|
+
if [[ $rc -ne 0 ]]; then
|
|
344
|
+
bad "publish exited $rc: $(head -c 300 <<<"$pub" | tr '\n' ' ')"
|
|
345
|
+
station av-poll "GET /hosting/agents/{addr} reports compiled"; unrun "publish failed"
|
|
346
|
+
return
|
|
347
|
+
fi
|
|
348
|
+
if [[ -z "$addr" ]]; then
|
|
349
|
+
bad "publish reported ok but returned no agent address: $(head -c 200 <<<"$pub" | tr '\n' ' ')"
|
|
350
|
+
station av-poll "GET /hosting/agents/{addr} reports compiled"; unrun "no address to poll"
|
|
351
|
+
return
|
|
352
|
+
fi
|
|
353
|
+
ok "published → $addr"
|
|
354
|
+
|
|
355
|
+
station av-poll "GET /hosting/agents/{addr} reports compiled"
|
|
356
|
+
local body compiled
|
|
357
|
+
body="$(curl -s -m 20 -H "Authorization: Bearer ${AGENTVERSE_API_KEY}" \
|
|
358
|
+
"$ASI_WALK_BASE/hosting/agents/$addr" 2>/dev/null)"
|
|
359
|
+
compiled="$(grep -o '"compiled":[a-z]*' <<<"$body" | cut -d: -f2)"
|
|
360
|
+
if [[ "$compiled" == "true" ]]; then ok "compiled=true, running on Agentverse"
|
|
361
|
+
elif [[ "$compiled" == "false" ]]; then bad "compiled=false — deployed but will not run"
|
|
362
|
+
else unrun "could not read compiled from the status body"; fi
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
self_test() {
|
|
366
|
+
echo "asi-walk --self-test: proving each local station can go RED"
|
|
367
|
+
local tmp; tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' RETURN
|
|
368
|
+
local rc red=0 total=0
|
|
369
|
+
|
|
370
|
+
# 1. missing file must fail
|
|
371
|
+
total=$((total+1))
|
|
372
|
+
"${BASH_SOURCE[0]}" "$tmp/nope.md" >/dev/null 2>&1; rc=$?
|
|
373
|
+
if [[ $rc -ne 0 ]]; then red=$((red+1)); echo " ok missing-file reds (exit $rc)"; else echo " BAD missing-file passed"; fi
|
|
374
|
+
|
|
375
|
+
# 2. no frontmatter must fail
|
|
376
|
+
total=$((total+1))
|
|
377
|
+
printf 'no frontmatter here\n' > "$tmp/bare.md"
|
|
378
|
+
"${BASH_SOURCE[0]}" "$tmp/bare.md" >/dev/null 2>&1; rc=$?
|
|
379
|
+
if [[ $rc -ne 0 ]]; then red=$((red+1)); echo " ok no-frontmatter reds (exit $rc)"; else echo " BAD no-frontmatter passed"; fi
|
|
380
|
+
|
|
381
|
+
# 3. frontmatter without a name must fail
|
|
382
|
+
total=$((total+1))
|
|
383
|
+
printf -- '---\ndescription: x\n---\nbody\n' > "$tmp/noname.md"
|
|
384
|
+
"${BASH_SOURCE[0]}" "$tmp/noname.md" >/dev/null 2>&1; rc=$?
|
|
385
|
+
if [[ $rc -ne 0 ]]; then red=$((red+1)); echo " ok no-name reds (exit $rc)"; else echo " BAD no-name passed"; fi
|
|
386
|
+
|
|
387
|
+
# 4. --remote with no key must NOT report green (exit 3, never 0)
|
|
388
|
+
total=$((total+1))
|
|
389
|
+
printf -- '---\nname: t\n---\nbody\n' > "$tmp/ok.md"
|
|
390
|
+
( unset AGENTVERSE_API_KEY; "${BASH_SOURCE[0]}" "$tmp/ok.md" --remote >/dev/null 2>&1 ); rc=$?
|
|
391
|
+
if [[ $rc -eq 3 ]]; then red=$((red+1)); echo " ok --remote with no key exits 3 (unconfigured, not green)"
|
|
392
|
+
else echo " BAD --remote with no key exited $rc (expected 3)"; fi
|
|
393
|
+
|
|
394
|
+
echo "self-test: $red/$total station guards proved falsifiable"
|
|
395
|
+
[[ $red -eq $total ]] || return 1
|
|
396
|
+
return 0
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
# ── main ─────────────────────────────────────────────────────────────────
|
|
400
|
+
if [[ $SELFTEST -eq 1 ]]; then self_test; exit $?; fi
|
|
401
|
+
|
|
402
|
+
MD="$(resolve_agent "$AGENT_PATH")" || { echo "asi-walk: no agent .md at '${AGENT_PATH:-<missing>}'" >&2; exit 2; }
|
|
403
|
+
|
|
404
|
+
# A sibling skills/ dir next to the agent is the convention resolveSkillMds
|
|
405
|
+
# uses; adopt it so the protocols station has something real to assert on.
|
|
406
|
+
if [[ -z "$SKILLS_DIR" && -d "$(dirname "$MD")/skills" ]]; then SKILLS_DIR="$(dirname "$MD")/skills"; fi
|
|
407
|
+
|
|
408
|
+
echo "asi-walk — ASI (Fetch.ai/Agentverse) lifecycle"
|
|
409
|
+
echo " agent: $MD"
|
|
410
|
+
echo " skills: ${SKILLS_DIR:-<none>}"
|
|
411
|
+
echo " remote: $([[ $DO_REMOTE -eq 1 ]] && echo yes || echo 'no (local stations only)')"
|
|
412
|
+
echo
|
|
413
|
+
echo "LOCAL"
|
|
414
|
+
run_local "$MD" "$SKILLS_DIR"
|
|
415
|
+
if [[ $DO_REMOTE -eq 1 ]]; then echo; echo "REMOTE"; run_remote; fi
|
|
416
|
+
|
|
417
|
+
echo
|
|
418
|
+
echo "asi-walk: pass=$PASS fail=$FAIL unrun=$UNRUN"
|
|
419
|
+
|
|
420
|
+
if [[ $JSON -eq 1 ]]; then
|
|
421
|
+
printf '{"pass":%d,"fail":%d,"unrun":%d,"stations":[' "$PASS" "$FAIL" "$UNRUN"
|
|
422
|
+
for i in "${!RECEIPTS[@]}"; do
|
|
423
|
+
IFS='|' read -r id st title note <<<"${RECEIPTS[$i]}"
|
|
424
|
+
[[ $i -gt 0 ]] && printf ','
|
|
425
|
+
printf '{"id":"%s","state":"%s","title":"%s","note":"%s"}' "$id" "$st" "$title" "${note//\"/\\\"}"
|
|
426
|
+
done
|
|
427
|
+
printf ']}\n'
|
|
428
|
+
fi
|
|
429
|
+
|
|
430
|
+
# A failure always wins. An unrun station is exit 3 under --strict, and ALWAYS
|
|
431
|
+
# exit 3 when --remote was asked for: "I could not check" must never be read as
|
|
432
|
+
# "it works".
|
|
433
|
+
[[ $FAIL -gt 0 ]] && exit 1
|
|
434
|
+
if [[ $UNRUN -gt 0 ]] && { [[ $STRICT -eq 1 ]] || [[ $DO_REMOTE -eq 1 ]]; }; then exit 3; fi
|
|
435
|
+
exit 0
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# astro-build-cached.sh — build one.ie/web, or skip when nothing it reads has moved.
|
|
3
|
+
#
|
|
4
|
+
# manifest: needs-env
|
|
5
|
+
#
|
|
6
|
+
# WHY. The production astro build is one of the two heavy deploy gates: ~174s with
|
|
7
|
+
# --max-old-space-size=8192. It is a pure function of its sources, so re-running it
|
|
8
|
+
# over a tree that has not moved since the last successful build reproduces bytes
|
|
9
|
+
# that are already on disk. Same idiom as sdk-build-cached.sh, widened to cover
|
|
10
|
+
# everything the site build actually reads.
|
|
11
|
+
#
|
|
12
|
+
# SCOPE IS THE WHOLE SAFETY ARGUMENT. Under-scope the fingerprint and this ships a
|
|
13
|
+
# STALE dist -- unproven code in production, the one failure the deploy gate exists
|
|
14
|
+
# to prevent. So the key covers one.ie/web in full (src, public, every config) AND
|
|
15
|
+
# packages/, because the site imports @oneie/sdk, @oneie/design, @oneie/frontend and
|
|
16
|
+
# ten plugins by path. It is deliberately over-broad: an unnecessary rebuild costs
|
|
17
|
+
# three minutes, a missed one costs a bad deploy.
|
|
18
|
+
#
|
|
19
|
+
# The key is CONTENT, never mtimes and never `git status --porcelain` (which names
|
|
20
|
+
# files, so an edit that keeps a file "modified" does not move it -- the trap that
|
|
21
|
+
# was caught red-handed in test-cached.sh).
|
|
22
|
+
#
|
|
23
|
+
# SAFETY. The stamp lives INSIDE dist/. Delete, clean, or never build dist and the
|
|
24
|
+
# stamp goes with it, so a missing dist can never read as cached. The stamp is
|
|
25
|
+
# written only AFTER the build exits 0 and dist/server/ exists -- a failed or
|
|
26
|
+
# half-written build leaves no stamp and the next run rebuilds.
|
|
27
|
+
#
|
|
28
|
+
# ASTRO_BUILD_FORCE=1 rebuilds unconditionally. --self-test runs the fixtures.
|
|
29
|
+
set -uo pipefail
|
|
30
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
31
|
+
WEB="$ROOT/one.ie/web"
|
|
32
|
+
STAMP="$WEB/dist/.astro-build-fingerprint"
|
|
33
|
+
|
|
34
|
+
# Files the build reads that git CANNOT see. `.env`/`.env.local` are gitignored,
|
|
35
|
+
# so neither ls-files nor diff nor even ls-files --others (which honours
|
|
36
|
+
# .gitignore) covers them -- and astro inlines PUBLIC_* values from them into the
|
|
37
|
+
# output. A key blind to them would hand back a cache hit for a build that would
|
|
38
|
+
# now produce different bytes. Lockfiles are listed explicitly for the same
|
|
39
|
+
# reason: a dependency bump can move only a lockfile.
|
|
40
|
+
# The credential file is part of the key (BETTER_AUTH_SECRET et al. are read at
|
|
41
|
+
# build time), and it honours ONE_ENV_FILE / DO_ENV_FILE like every credential
|
|
42
|
+
# read in the harness — factory-repo.sh --check-env-indirection is the gate.
|
|
43
|
+
_ENV_FILE="${ONE_ENV_FILE:-${DO_ENV_FILE:-one.ie/web/.env}}"
|
|
44
|
+
_UNTRACKED_INPUTS="$_ENV_FILE one.ie/web/.env.local one.ie/web/bun.lock one.ie/web/package-lock.json packages/bun.lock bun.lock"
|
|
45
|
+
|
|
46
|
+
_fp() {
|
|
47
|
+
{ git -C "$ROOT" ls-files -s one.ie/web packages 2>/dev/null
|
|
48
|
+
git -C "$ROOT" diff -- one.ie/web packages 2>/dev/null
|
|
49
|
+
git -C "$ROOT" ls-files --others --exclude-standard one.ie/web packages 2>/dev/null \
|
|
50
|
+
| grep -v '^one.ie/web/dist/' \
|
|
51
|
+
| while IFS= read -r u; do shasum -a 256 "$ROOT/$u" 2>/dev/null; done
|
|
52
|
+
for f in $_UNTRACKED_INPUTS; do
|
|
53
|
+
# An input that is absent must hash as "absent", not as nothing -- otherwise
|
|
54
|
+
# deleting .env would leave the key unmoved.
|
|
55
|
+
if [ -f "$ROOT/$f" ]; then shasum -a 256 "$ROOT/$f" 2>/dev/null
|
|
56
|
+
else echo "ABSENT $f"; fi
|
|
57
|
+
done
|
|
58
|
+
} | shasum -a 256 | cut -d' ' -f1
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if [ "${1:-}" = "--self-test" ]; then
|
|
62
|
+
fails=0
|
|
63
|
+
if [ ! -f "$STAMP" ]; then
|
|
64
|
+
echo "n/a: no prior build stamp — run a real build first, then re-run --self-test" >&2
|
|
65
|
+
echo " (an absent stamp is 'not measured', never 'ok')" >&2
|
|
66
|
+
exit 2
|
|
67
|
+
fi
|
|
68
|
+
# 1. an unchanged tree is cached
|
|
69
|
+
o1=$(bash "$0" --check-only)
|
|
70
|
+
case "$o1" in *cached*) echo "ok: unchanged tree reads as cached";;
|
|
71
|
+
*) echo "FAIL: unchanged tree did not read as cached: $o1"; fails=$((fails+1));; esac
|
|
72
|
+
# RED PROOF 1 — a source edit must invalidate
|
|
73
|
+
probe="$WEB/src/.__build_cache_probe.ts"; echo 'export const __p = 1' > "$probe"
|
|
74
|
+
o2=$(bash "$0" --check-only)
|
|
75
|
+
case "$o2" in *cached*) echo "FAIL: an src edit did NOT invalidate the build cache"; fails=$((fails+1));;
|
|
76
|
+
*) echo "ok: an src edit invalidates";; esac
|
|
77
|
+
rm -f "$probe"
|
|
78
|
+
# RED PROOF 2 — an edit in packages/ must invalidate (the cross-package trap)
|
|
79
|
+
probe2="$ROOT/packages/design/.__build_cache_probe.ts"; echo 'export const __p = 1' > "$probe2"
|
|
80
|
+
o3=$(bash "$0" --check-only)
|
|
81
|
+
case "$o3" in *cached*) echo "FAIL: a packages/ edit did NOT invalidate"; fails=$((fails+1));;
|
|
82
|
+
*) echo "ok: a packages/ edit invalidates";; esac
|
|
83
|
+
rm -f "$probe2"
|
|
84
|
+
# RED PROOF 3 — a gitignored .env edit must invalidate. git cannot see this file
|
|
85
|
+
# at all, so a key built only from ls-files/diff stays put while the build's
|
|
86
|
+
# inlined PUBLIC_* values change underneath it.
|
|
87
|
+
envf="$WEB/.env"
|
|
88
|
+
if [ -f "$envf" ]; then
|
|
89
|
+
cp "$envf" "$envf.__probe.bak"
|
|
90
|
+
echo '# __build_cache_probe' >> "$envf"
|
|
91
|
+
o3b=$(bash "$0" --check-only)
|
|
92
|
+
case "$o3b" in *cached*) echo "FAIL: a .env edit did NOT invalidate (git cannot see it)"; fails=$((fails+1));;
|
|
93
|
+
*) echo "ok: a gitignored .env edit invalidates";; esac
|
|
94
|
+
mv "$envf.__probe.bak" "$envf"
|
|
95
|
+
else
|
|
96
|
+
echo "n/a: no $_ENV_FILE present, cannot run the gitignored-input proof" >&2
|
|
97
|
+
fi
|
|
98
|
+
# RED PROOF 4 — a missing stamp must never read as cached
|
|
99
|
+
mv "$STAMP" "$STAMP.bak"
|
|
100
|
+
o4=$(bash "$0" --check-only)
|
|
101
|
+
case "$o4" in *cached*) echo "FAIL: missing stamp read as cached"; fails=$((fails+1));;
|
|
102
|
+
*) echo "ok: a missing stamp forces a rebuild";; esac
|
|
103
|
+
mv "$STAMP.bak" "$STAMP"
|
|
104
|
+
# RED PROOF 5 — a stamp whose dist/server is gone must never read as cached
|
|
105
|
+
if [ -d "$WEB/dist/server" ]; then
|
|
106
|
+
mv "$WEB/dist/server" "$WEB/dist/.server.bak"
|
|
107
|
+
o5=$(bash "$0" --check-only)
|
|
108
|
+
case "$o5" in *cached*) echo "FAIL: a stamp with no dist/server read as cached"; fails=$((fails+1));;
|
|
109
|
+
*) echo "ok: a missing dist/server forces a rebuild";; esac
|
|
110
|
+
mv "$WEB/dist/.server.bak" "$WEB/dist/server"
|
|
111
|
+
else
|
|
112
|
+
echo "n/a: dist/server absent, cannot run the missing-output proof" >&2
|
|
113
|
+
fi
|
|
114
|
+
[ "$fails" -eq 0 ] && { echo "astro-build-cached: self-test PASS"; exit 0; }
|
|
115
|
+
echo "astro-build-cached: self-test FAILED ($fails)"; exit 1
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
CHECK_ONLY=0
|
|
119
|
+
[ "${1:-}" = "--check-only" ] && CHECK_ONLY=1
|
|
120
|
+
|
|
121
|
+
want="$(_fp)"
|
|
122
|
+
# THREE conditions, all required. dist/server is checked as well as the stamp
|
|
123
|
+
# because the stamp records "these sources built cleanly", not "the output is
|
|
124
|
+
# still here" -- a `rm -rf dist/server` with the stamp left behind must rebuild.
|
|
125
|
+
if [ "${ASTRO_BUILD_FORCE:-0}" != "1" ] \
|
|
126
|
+
&& [ -f "$STAMP" ] && [ "$(cat "$STAMP" 2>/dev/null)" = "$want" ] \
|
|
127
|
+
&& [ -d "$WEB/dist/server" ]; then
|
|
128
|
+
echo "[astro] cached — one.ie/web + packages unchanged (${want:0:12})"
|
|
129
|
+
exit 0
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
if (( CHECK_ONLY )); then
|
|
133
|
+
echo "[astro] would REBUILD (${want:0:12})"
|
|
134
|
+
exit 0
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
( cd "$WEB" && NODE_ENV=production bun run build ) || { echo "[astro] BUILD FAILED" >&2; exit 1; }
|
|
138
|
+
# Never stamp an output that is not there. A build that exits 0 without producing
|
|
139
|
+
# dist/server has not proven anything, and stamping it would hand the next deploy
|
|
140
|
+
# a cache hit over a missing artifact.
|
|
141
|
+
[ -d "$WEB/dist/server" ] || { echo "[astro] build exited 0 but dist/server is missing — NOT cached" >&2; exit 1; }
|
|
142
|
+
printf '%s' "$want" > "$STAMP"
|
|
143
|
+
echo "[astro] built (${want:0:12})"
|