mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-07 05:56:21 -10:00
* fix(package): install without the unpublished @openrig/daemon (#66) The CLI package declared the private @openrig/daemon in dependencies and shipped it as a bundled dependency. npm used the bundle, but package managers that resolve every dependency from the registry, such as Bun, failed with a 404. The package already ships the daemon under daemon/. The package build now rewrites the compiled CLI and TUI imports of @openrig/daemon/<subpath> to that copy, using the daemon's exports map, and fails on any unmapped or unstaged subpath or any daemon import left afterwards. The dependency, the bundled copy and their lockfile entries are removed. Source imports and development resolution are unchanged. Reported by @drewpayment. * fix(package): rewrite daemon imports from the syntax tree, not text Parse each staged file with the TypeScript compiler and rewrite only real import/export specifiers and literal import()/require() arguments. Text in comments, strings and templates is no longer changed, and imports that carry comments or use template literals are no longer missed. Unparseable files and non-literal module arguments naming the package now fail the build. * docs: note Bun installation and its postinstall limit Bun can install the CLI, but OpenRig still runs on Node.js and Bun may block the package's postinstall check. * fix(package): run the daemon import rewrite from any checkout path The direct-run check compared import.meta.url with a hand-built file:// URL, which never matched when the path needed percent-encoding (spaces, #) or went through a symlink. The script then exited 0 without rewriting anything. Compare the decoded module path with the real path of argv[1] instead. --------- Co-authored-by: OpenRig contributors <[email protected]>
170 lines
7.6 KiB
Bash
Executable File
170 lines
7.6 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
CLI_DIR="$REPO_ROOT/packages/cli"
|
|
DAEMON_DIR="$REPO_ROOT/packages/daemon"
|
|
UI_DIR="$REPO_ROOT/packages/ui"
|
|
TUI_DIR="$REPO_ROOT/packages/tui"
|
|
SUBSTANCE_SURFACE_ROOTS=()
|
|
|
|
echo "=== OpenRig Package Build ==="
|
|
echo "Repo root: $REPO_ROOT"
|
|
echo ""
|
|
|
|
# 1. Clean previous build artifacts
|
|
echo "[1/6] Cleaning previous artifacts..."
|
|
rm -rf "$CLI_DIR/daemon" "$CLI_DIR/ui" "$CLI_DIR/tui"
|
|
|
|
# 2. Build daemon
|
|
echo "[2/6] Building daemon..."
|
|
(cd "$DAEMON_DIR" && npm run build)
|
|
|
|
# 3. Build UI
|
|
echo "[3/6] Building UI..."
|
|
(cd "$UI_DIR" && npm run build)
|
|
|
|
# 4. Build TUI
|
|
echo "[4/6] Building TUI..."
|
|
(cd "$TUI_DIR" && npm run build)
|
|
|
|
# 5. Build CLI
|
|
echo "[5/6] Building CLI..."
|
|
(cd "$CLI_DIR" && npm run build)
|
|
|
|
# 6. Assemble: copy daemon + UI + TUI into CLI package
|
|
echo "[6/6] Assembling package..."
|
|
|
|
# OPR.0.4.4.11 FR-6 — deploy-identity stamp. Computed ONCE here (the runtime
|
|
# worktree is at the SHA — available at build), then written as the compiled
|
|
# build-info module into EACH package's dist (per-package generated modules;
|
|
# the committed src/build-info.ts files stay the honest dev stubs). No
|
|
# runtime cross-package import.
|
|
STAMP_SEMVER="$(node -p "require('$CLI_DIR/package.json').version")"
|
|
STAMP_COMMIT="$(git -C "$REPO_ROOT" rev-parse HEAD)"
|
|
# DIRTY = the BUILD INPUTS differ from the commit, not "the working dir has scratch in it".
|
|
# Bare `git status --porcelain` counts UNTRACKED files repo-wide, so ordinary scratch
|
|
# (.mcp.json, workspace/, scratch dirs) stamped every build on a working machine as
|
|
# dirty=true — and a dirty stamp is unidentifiable, which defeats the whole point of
|
|
# pinning a runtime to a commit. Untracked files are NOT blanket-ignored (a new
|
|
# untracked .ts under packages/*/src really would compile in), so the check is SCOPED
|
|
# to the build inputs: anything under packages/ or scripts/, tracked or not, counts
|
|
# (scripts/ included because build-package.sh IS the builder — if it differs from the
|
|
# commit, the artifact is not reproducible from that commit either).
|
|
if [ -n "$(git -C "$REPO_ROOT" status --porcelain -- packages scripts)" ]; then STAMP_DIRTY=true; else STAMP_DIRTY=false; fi
|
|
STAMP_BUILT_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
echo "Build identity: ${STAMP_SEMVER} (${STAMP_COMMIT}) dirty=${STAMP_DIRTY} at ${STAMP_BUILT_AT}"
|
|
|
|
write_build_info() {
|
|
cat > "$1" <<EOF
|
|
// GENERATED by scripts/build-package.sh — the packaged build-identity stamp.
|
|
export const BUILD_INFO = {
|
|
semver: "${STAMP_SEMVER}",
|
|
commit: "${STAMP_COMMIT}",
|
|
dirty: ${STAMP_DIRTY},
|
|
builtAt: "${STAMP_BUILT_AT}",
|
|
};
|
|
export function stampFields(info = BUILD_INFO) {
|
|
if (!info.commit) return {};
|
|
return { semver: info.semver, commit: info.commit, dirty: info.dirty, builtAt: info.builtAt };
|
|
}
|
|
EOF
|
|
}
|
|
write_build_info "$DAEMON_DIR/dist/build-info.js"
|
|
write_build_info "$CLI_DIR/dist/build-info.js"
|
|
|
|
# OPR.0.5.3.7 R2 — generate the context-pack projection of canonical skills at
|
|
# PACKAGE TIME (into $DAEMON_DIR/context-packs; never committed, regenerated every
|
|
# build). Validated through the DAEMON's own manifest parser — a malformed
|
|
# projection FAILS THE BUILD here, never at serve time. Pinned to the build semver
|
|
# so served packs carry the binary's version.
|
|
echo "Generating shipped context-packs..."
|
|
(cd "$REPO_ROOT" && node scripts/generate-context-packs.mjs --version="$STAMP_SEMVER")
|
|
|
|
# Daemon: dist + assets + specs
|
|
mkdir -p "$CLI_DIR/daemon/dist"
|
|
cp -r "$DAEMON_DIR/dist/"* "$CLI_DIR/daemon/dist/"
|
|
|
|
if [ -d "$DAEMON_DIR/assets" ]; then
|
|
cp -r "$DAEMON_DIR/assets" "$CLI_DIR/daemon/assets"
|
|
SUBSTANCE_SURFACE_ROOTS+=("daemon/assets")
|
|
fi
|
|
|
|
if [ -d "$DAEMON_DIR/specs" ]; then
|
|
# OPR.0.5.6.12 LP-7 — the specs tree is copied wholesale into the npm
|
|
# artifact, so scan the actual filesystem input (including untracked build
|
|
# inputs) before the copy. Path-safety and manifest validation solve other
|
|
# problems; this is the public-substance refusal floor.
|
|
node "$REPO_ROOT/scripts/check-internal-leak-guard.mjs" \
|
|
--repo "$REPO_ROOT" \
|
|
--rules "$REPO_ROOT/scripts/internal-tokens.generated.json" \
|
|
--mode tree \
|
|
--tree "$DAEMON_DIR/specs"
|
|
cp -r "$DAEMON_DIR/specs" "$CLI_DIR/daemon/specs"
|
|
SUBSTANCE_SURFACE_ROOTS+=("daemon/specs")
|
|
fi
|
|
|
|
# OPR.0.5.3.7 R2 — the shipped context-pack projection (generated above). The daemon
|
|
# registers this as a `builtin` discovery root resolved RELATIVE TO THE BINARY
|
|
# (startup.ts: import.meta.dirname/../context-packs -> <cli>/daemon/context-packs),
|
|
# so `rig context get` serves bytes that match `rig --version` by construction.
|
|
if [ -d "$DAEMON_DIR/context-packs" ]; then
|
|
cp -r "$DAEMON_DIR/context-packs" "$CLI_DIR/daemon/context-packs"
|
|
SUBSTANCE_SURFACE_ROOTS+=("daemon/context-packs")
|
|
fi
|
|
|
|
# The named substance gate reviews the roots the packager ACTUALLY staged. Keep
|
|
# that inventory beside the artifact instead of duplicating a source-root list
|
|
# in the gate; npm's own dry-run file list supplies the complete scan surface.
|
|
node -e '
|
|
const fs = require("fs");
|
|
const [out, ...roots] = process.argv.slice(1);
|
|
if (roots.length === 0) throw new Error("no shippable substance roots were staged");
|
|
fs.writeFileSync(out, JSON.stringify({ schemaVersion: 1, roots: [...new Set(roots)].sort() }, null, 2) + "\n");
|
|
' "$CLI_DIR/daemon/substance-surfaces.json" "${SUBSTANCE_SURFACE_ROOTS[@]}"
|
|
|
|
# Built-in policies (OPR.0.4.8.3): canonical source packages/daemon/policies/builtin/
|
|
# ships with the daemon; startup materializes read-only inspection copies at
|
|
# $OPENRIG_HOME/reference/policies/builtin/ (same chain as reference docs below).
|
|
if [ -d "$DAEMON_DIR/policies" ]; then
|
|
cp -r "$DAEMON_DIR/policies" "$CLI_DIR/daemon/policies"
|
|
fi
|
|
|
|
# Reference docs: copy from repo root docs/reference/ into the assembled package
|
|
if [ -d "$REPO_ROOT/docs/reference" ]; then
|
|
mkdir -p "$CLI_DIR/daemon/docs/reference"
|
|
cp -r "$REPO_ROOT/docs/reference/"* "$CLI_DIR/daemon/docs/reference/"
|
|
fi
|
|
|
|
# Earlier builds assembled a bundled copy of the unpublished @openrig/daemon here. The package now
|
|
# imports the shipped daemon/dist directly (rewrite step below), so remove any stale copy: it would
|
|
# otherwise shadow the development workspace link for code under packages/cli.
|
|
rm -rf "$CLI_DIR/node_modules/@openrig/daemon"
|
|
|
|
# UI: dist
|
|
mkdir -p "$CLI_DIR/ui/dist"
|
|
cp -r "$UI_DIR/dist/"* "$CLI_DIR/ui/dist/"
|
|
|
|
# TUI: dist (public openrig-tui bin declared by the CLI package)
|
|
mkdir -p "$CLI_DIR/tui/dist"
|
|
cp -r "$TUI_DIR/dist/"* "$CLI_DIR/tui/dist/"
|
|
|
|
# The CLI and TUI import daemon code as @openrig/daemon/<subpath>. That package is not published, so
|
|
# rewrite those specifiers in the staged JS to the daemon/dist copy shipped above (#66). Fails the
|
|
# build on an unmapped or unstaged subpath and on any daemon import left afterwards.
|
|
node "$REPO_ROOT/scripts/rewrite-daemon-imports.mjs"
|
|
|
|
# Report
|
|
echo ""
|
|
echo "=== Package Assembled ==="
|
|
echo "CLI dist: $(find "$CLI_DIR/dist" -name '*.js' | wc -l | tr -d ' ') JS files"
|
|
echo "Daemon dist: $(find "$CLI_DIR/daemon/dist" -name '*.js' | wc -l | tr -d ' ') JS files"
|
|
echo "Daemon assets: $(find "$CLI_DIR/daemon/assets" -type f 2>/dev/null | wc -l | tr -d ' ') files"
|
|
echo "Daemon specs: $(find "$CLI_DIR/daemon/specs" -type f 2>/dev/null | wc -l | tr -d ' ') files"
|
|
echo "Daemon policies: $(find "$CLI_DIR/daemon/policies" -type f 2>/dev/null | wc -l | tr -d ' ') files"
|
|
echo "Daemon docs: $(find "$CLI_DIR/daemon/docs" -type f 2>/dev/null | wc -l | tr -d ' ') files"
|
|
echo "UI dist: $(find "$CLI_DIR/ui/dist" -type f | wc -l | tr -d ' ') files"
|
|
echo "TUI dist: $(find "$CLI_DIR/tui/dist" -type f | wc -l | tr -d ' ') files"
|
|
echo ""
|
|
echo "Ready to publish: cd packages/cli && npm publish --access public"
|