create-egregore 0.21.1 → 0.21.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/assets/egregore-launcher.js +2 -2
- package/lib/renderer-install.js +42 -0
- package/lib/setup.js +15 -14
- package/package.json +2 -2
- package/runtime/claude/.claude/skills/view/SKILL.md +2 -1
- package/runtime/claude/bin/artifact-renderer-install.sh +157 -0
- package/runtime/claude/bin/artifact-renderer.sh +57 -0
- package/runtime/claude/bin/handoff-preview.sh +2 -4
- package/runtime/claude/bin/handoff-run.sh +4 -11
- package/runtime/claude/bin/lib/artifact-renderer/dependencies-lock.json +94 -0
- package/runtime/claude/bin/lib/artifact-renderer/dependencies.json +8 -0
- package/runtime/claude/bin/lib/dashboard-artifact.sh +2 -2
- package/runtime/claude/bin/lib/greeting.sh +2 -13
- package/runtime/claude/bin/post-update.sh +5 -0
- package/runtime/claude/bin/publish-artifact.sh +6 -27
- package/runtime/claude/bin/view-render.sh +2 -13
- package/runtime/claude/egregore_runtime/upgrade.py +51 -0
- package/runtime/claude/manifest.json +4 -0
- package/runtime/codex/bin/artifact-renderer-install.sh +157 -0
- package/runtime/codex/bin/artifact-renderer.sh +57 -0
- package/runtime/codex/bin/handoff-preview.sh +2 -4
- package/runtime/codex/bin/handoff-run.sh +4 -11
- package/runtime/codex/bin/lib/artifact-renderer/dependencies-lock.json +94 -0
- package/runtime/codex/bin/lib/artifact-renderer/dependencies.json +8 -0
- package/runtime/codex/bin/lib/dashboard-artifact.sh +2 -2
- package/runtime/codex/bin/lib/greeting.sh +2 -13
- package/runtime/codex/bin/post-update.sh +5 -0
- package/runtime/codex/bin/publish-artifact.sh +6 -27
- package/runtime/codex/bin/view-render.sh +2 -13
- package/runtime/codex/egregore_runtime/upgrade.py +51 -0
- package/runtime/codex/manifest.json +4 -0
- package/runtime/pi/bin/artifact-renderer-install.sh +157 -0
- package/runtime/pi/bin/artifact-renderer.sh +57 -0
- package/runtime/pi/bin/handoff-preview.sh +2 -4
- package/runtime/pi/bin/handoff-run.sh +4 -11
- package/runtime/pi/bin/lib/artifact-renderer/dependencies-lock.json +94 -0
- package/runtime/pi/bin/lib/artifact-renderer/dependencies.json +8 -0
- package/runtime/pi/bin/lib/dashboard-artifact.sh +2 -2
- package/runtime/pi/bin/lib/greeting.sh +2 -13
- package/runtime/pi/bin/pi-product-workflows.mjs +1 -4
- package/runtime/pi/bin/post-update.sh +5 -0
- package/runtime/pi/bin/publish-artifact.sh +6 -27
- package/runtime/pi/bin/view-render.sh +2 -13
- package/runtime/pi/egregore_runtime/upgrade.py +51 -0
- package/runtime/pi/manifest.json +4 -0
- package/runtime/prime/bin/artifact-renderer-install.sh +157 -0
- package/runtime/prime/bin/artifact-renderer.sh +57 -0
- package/runtime/prime/bin/handoff-preview.sh +2 -4
- package/runtime/prime/bin/handoff-run.sh +4 -11
- package/runtime/prime/bin/lib/artifact-renderer/dependencies-lock.json +94 -0
- package/runtime/prime/bin/lib/artifact-renderer/dependencies.json +8 -0
- package/runtime/prime/bin/lib/dashboard-artifact.sh +2 -2
- package/runtime/prime/bin/lib/greeting.sh +2 -13
- package/runtime/prime/bin/pi-product-workflows.mjs +1 -4
- package/runtime/prime/bin/post-update.sh +5 -0
- package/runtime/prime/bin/publish-artifact.sh +6 -27
- package/runtime/prime/bin/view-render.sh +2 -13
- package/runtime/prime/egregore_runtime/upgrade.py +51 -0
- package/runtime/prime/manifest.json +4 -0
|
@@ -17,6 +17,7 @@ from __future__ import annotations
|
|
|
17
17
|
import json
|
|
18
18
|
import os
|
|
19
19
|
import re
|
|
20
|
+
import signal
|
|
20
21
|
import subprocess
|
|
21
22
|
import sys
|
|
22
23
|
import time
|
|
@@ -27,6 +28,8 @@ from typing import Any, Mapping
|
|
|
27
28
|
from .adapters.qmd import QMD_VERSION, RERANKING_ENABLED, QmdLocalRetriever
|
|
28
29
|
|
|
29
30
|
SCHEMA_VERSION = "egregore-runtime-upgrade/v1"
|
|
31
|
+
_RENDERER_INSTALL_TIMEOUT_SECONDS = 180
|
|
32
|
+
_RENDERER_TERMINATE_GRACE_SECONDS = 5
|
|
30
33
|
|
|
31
34
|
# Stage keys in preparation order. Labels are the user-facing truth shown by
|
|
32
35
|
# Settings; keep them in plain team language.
|
|
@@ -628,6 +631,54 @@ class UpgradeEngine:
|
|
|
628
631
|
if probe.returncode != 0:
|
|
629
632
|
self._fail(state, "node", "the selected Node runtime failed its version probe")
|
|
630
633
|
return False
|
|
634
|
+
# Explicit Runtime preparation owns package provisioning. Preview and
|
|
635
|
+
# session startup only resolve installed renderers. Older candidates
|
|
636
|
+
# predate this helper and retain their original upgrade contract.
|
|
637
|
+
renderer_installer = (
|
|
638
|
+
Path(state.get("installed_path") or "")
|
|
639
|
+
/ "runtime" / APPLY_TREE / "bin" / "artifact-renderer-install.sh"
|
|
640
|
+
)
|
|
641
|
+
if renderer_installer.is_file():
|
|
642
|
+
environment = {**os.environ, **self.retriever.environment}
|
|
643
|
+
environment["PATH"] = str(Path(node).parent) + os.pathsep + environment.get("PATH", "")
|
|
644
|
+
try:
|
|
645
|
+
with subprocess.Popen(
|
|
646
|
+
("bash", str(renderer_installer), "--root", str(self.repository_root)),
|
|
647
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
|
|
648
|
+
cwd=self.repository_root, env=environment, start_new_session=True,
|
|
649
|
+
) as renderer:
|
|
650
|
+
try:
|
|
651
|
+
stdout, stderr = renderer.communicate(timeout=_RENDERER_INSTALL_TIMEOUT_SECONDS)
|
|
652
|
+
except subprocess.TimeoutExpired:
|
|
653
|
+
# npm is a child of the installer shell. Stop the entire
|
|
654
|
+
# group so its EXIT cleanup can run without an orphaned
|
|
655
|
+
# package manager still writing into the staging tree.
|
|
656
|
+
try:
|
|
657
|
+
os.killpg(renderer.pid, signal.SIGTERM)
|
|
658
|
+
except ProcessLookupError:
|
|
659
|
+
pass
|
|
660
|
+
try:
|
|
661
|
+
renderer.communicate(timeout=_RENDERER_TERMINATE_GRACE_SECONDS)
|
|
662
|
+
except subprocess.TimeoutExpired:
|
|
663
|
+
pass
|
|
664
|
+
finally:
|
|
665
|
+
# A child may ignore TERM even after Bash exits.
|
|
666
|
+
try:
|
|
667
|
+
os.killpg(renderer.pid, signal.SIGKILL)
|
|
668
|
+
except ProcessLookupError:
|
|
669
|
+
pass
|
|
670
|
+
renderer.communicate()
|
|
671
|
+
raise
|
|
672
|
+
except subprocess.TimeoutExpired:
|
|
673
|
+
self._fail(state, "node", f"artifact renderer provisioning timed out after {_RENDERER_INSTALL_TIMEOUT_SECONDS} seconds; retry preparation")
|
|
674
|
+
return False
|
|
675
|
+
except OSError as exc:
|
|
676
|
+
self._fail(state, "node", f"artifact renderer provisioning failed: {exc}")
|
|
677
|
+
return False
|
|
678
|
+
if renderer.returncode != 0:
|
|
679
|
+
detail = (stderr or stdout).strip()
|
|
680
|
+
self._fail(state, "node", f"artifact renderer provisioning failed: {detail}")
|
|
681
|
+
return False
|
|
631
682
|
self._mark(state, "node", "done", f"{probe.stdout.strip()} at instance-selected path")
|
|
632
683
|
return True
|
|
633
684
|
|
|
@@ -135,6 +135,8 @@
|
|
|
135
135
|
"bin/agent.sh",
|
|
136
136
|
"bin/api-call.sh",
|
|
137
137
|
"bin/artifact-register.sh",
|
|
138
|
+
"bin/artifact-renderer-install.sh",
|
|
139
|
+
"bin/artifact-renderer.sh",
|
|
138
140
|
"bin/artifact-writeback.sh",
|
|
139
141
|
"bin/artifacts.sh",
|
|
140
142
|
"bin/attendant.sh",
|
|
@@ -182,6 +184,8 @@
|
|
|
182
184
|
"bin/issue.sh",
|
|
183
185
|
"bin/knowledge.sh",
|
|
184
186
|
"bin/lib/artifact-id.sh",
|
|
187
|
+
"bin/lib/artifact-renderer/dependencies-lock.json",
|
|
188
|
+
"bin/lib/artifact-renderer/dependencies.json",
|
|
185
189
|
"bin/lib/config.sh",
|
|
186
190
|
"bin/lib/context.sh",
|
|
187
191
|
"bin/lib/dashboard-artifact.sh",
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Explicit setup/update/repair only. Rendering and startup must never install.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
6
|
+
TARGET_ROOT="$SCRIPT_DIR"
|
|
7
|
+
CHECK_ONLY=0
|
|
8
|
+
LOCK_FD=""
|
|
9
|
+
while [ "$#" -gt 0 ]; do
|
|
10
|
+
case "$1" in
|
|
11
|
+
--root)
|
|
12
|
+
[ "$#" -ge 2 ] && [ -n "$2" ] || { echo 'artifact renderer: --root needs a directory' >&2; exit 2; }
|
|
13
|
+
TARGET_ROOT="$2"; shift 2 ;;
|
|
14
|
+
--check) CHECK_ONLY=1; shift ;;
|
|
15
|
+
--lock-fd) LOCK_FD="${2:?missing lock descriptor}"; shift 2 ;;
|
|
16
|
+
*) echo 'Usage: bash bin/artifact-renderer-install.sh [--root <instance>] [--check]' >&2; exit 2 ;;
|
|
17
|
+
esac
|
|
18
|
+
done
|
|
19
|
+
# npm 11 treats a symlinked --prefix as a linked root package, which makes
|
|
20
|
+
# `ci` reject the lockfile (including macOS's /var -> /private/var alias).
|
|
21
|
+
TARGET_ROOT="$(cd "$TARGET_ROOT" && pwd -P)"
|
|
22
|
+
MANIFEST_DIR="$SCRIPT_DIR/bin/lib/artifact-renderer"
|
|
23
|
+
|
|
24
|
+
for required in dependencies.json dependencies-lock.json; do
|
|
25
|
+
[ -f "$MANIFEST_DIR/$required" ] || {
|
|
26
|
+
echo "artifact renderer: pinned $required is missing; update the framework and retry" >&2
|
|
27
|
+
exit 1
|
|
28
|
+
}
|
|
29
|
+
done
|
|
30
|
+
NODE_BIN="$(bash "$SCRIPT_DIR/bin/node-run.sh" -p 'process.execPath')"
|
|
31
|
+
export PATH="$(dirname "$NODE_BIN"):$PATH"
|
|
32
|
+
MANIFEST_INFO="$(python3 - "$MANIFEST_DIR" <<'PY'
|
|
33
|
+
import hashlib, json, pathlib, re, sys
|
|
34
|
+
source = pathlib.Path(sys.argv[1])
|
|
35
|
+
version = json.loads((source / 'dependencies.json').read_text())['dependencies']['egregore-artifacts']
|
|
36
|
+
if not isinstance(version, str) or not re.fullmatch(r'[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?', version):
|
|
37
|
+
raise SystemExit('artifact renderer: an exact package version is required')
|
|
38
|
+
digest = hashlib.sha256((source / 'dependencies-lock.json').read_bytes()).hexdigest()[:16]
|
|
39
|
+
print(version, version + '-' + digest)
|
|
40
|
+
PY
|
|
41
|
+
)"
|
|
42
|
+
read -r VERSION INSTALL_ID <<< "$MANIFEST_INFO"
|
|
43
|
+
TOOLS_DIR="$TARGET_ROOT/.egregore/tools/artifact-renderer"
|
|
44
|
+
PREFIX="$TOOLS_DIR/$INSTALL_ID"
|
|
45
|
+
|
|
46
|
+
renderer_ready() {
|
|
47
|
+
local candidate="$1"
|
|
48
|
+
cmp -s "$MANIFEST_DIR/dependencies.json" "$candidate/package.json" || return 1
|
|
49
|
+
cmp -s "$MANIFEST_DIR/dependencies-lock.json" "$candidate/package-lock.json" || return 1
|
|
50
|
+
"$NODE_BIN" -e '
|
|
51
|
+
const fs = require("node:fs");
|
|
52
|
+
const path = require("node:path");
|
|
53
|
+
const actual = JSON.parse(fs.readFileSync(path.join(process.argv[1], "node_modules/egregore-artifacts/package.json"), "utf8"));
|
|
54
|
+
if (actual.version !== process.argv[2]) process.exit(1);
|
|
55
|
+
' "$candidate" "$VERSION" >/dev/null 2>&1 || return 1
|
|
56
|
+
"$NODE_BIN" "$candidate/node_modules/egregore-artifacts/bin/cli.js" --help >/dev/null 2>&1
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if renderer_ready "$PREFIX"; then
|
|
60
|
+
printf 'Artifact renderer %s is ready\n' "$VERSION"
|
|
61
|
+
exit 0
|
|
62
|
+
fi
|
|
63
|
+
if [ "$CHECK_ONLY" = 1 ]; then
|
|
64
|
+
printf 'Artifact renderer %s is not ready; run bash bin/artifact-renderer-install.sh\n' "$VERSION" >&2
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
NPM_BIN="$(command -v npm || true)"
|
|
69
|
+
[ -n "$NPM_BIN" ] && "$NPM_BIN" --version >/dev/null 2>&1 || {
|
|
70
|
+
echo 'artifact renderer: npm is unavailable; install Node with npm, then retry' >&2
|
|
71
|
+
exit 1
|
|
72
|
+
}
|
|
73
|
+
mkdir -p "$TOOLS_DIR"
|
|
74
|
+
[ ! -L "$PREFIX" ] || { echo 'artifact renderer: managed install must not be a symbolic link' >&2; exit 1; }
|
|
75
|
+
LOCK="$TOOLS_DIR/.$INSTALL_ID.lock"
|
|
76
|
+
# A kernel lock survives in children while they are still installing and is
|
|
77
|
+
# released after forced interruption. Its persistent file never means "busy".
|
|
78
|
+
# Re-exec under the inherited descriptor so Bash, npm, and cleanup all share
|
|
79
|
+
# ownership; unlike mkdir locks, a killed process cannot leave a stale owner.
|
|
80
|
+
if [ -z "$LOCK_FD" ]; then
|
|
81
|
+
exec python3 - "$LOCK" "$SCRIPT_DIR/bin/artifact-renderer-install.sh" "$TARGET_ROOT" <<'PY'
|
|
82
|
+
import fcntl, os, stat, sys
|
|
83
|
+
lock, script, root = sys.argv[1:]
|
|
84
|
+
fd = os.open(lock, os.O_CREAT | os.O_RDWR | getattr(os, 'O_NOFOLLOW', 0), 0o600)
|
|
85
|
+
if not stat.S_ISREG(os.fstat(fd).st_mode):
|
|
86
|
+
raise SystemExit('artifact renderer: invalid install lock')
|
|
87
|
+
try:
|
|
88
|
+
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
89
|
+
except BlockingIOError:
|
|
90
|
+
raise SystemExit('artifact renderer: another install is in progress; retry after it finishes')
|
|
91
|
+
os.set_inheritable(fd, True)
|
|
92
|
+
os.execvp('bash', ['bash', script, '--root', root, '--lock-fd', str(fd)])
|
|
93
|
+
PY
|
|
94
|
+
fi
|
|
95
|
+
# Validate the internal descriptor instead of accepting an unlocked re-entry.
|
|
96
|
+
python3 - "$LOCK_FD" "$LOCK" <<'PY'
|
|
97
|
+
import fcntl, os, sys
|
|
98
|
+
try:
|
|
99
|
+
fd = int(sys.argv[1])
|
|
100
|
+
if not os.path.samestat(os.fstat(fd), os.stat(sys.argv[2])):
|
|
101
|
+
raise ValueError('wrong lock file')
|
|
102
|
+
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
103
|
+
except (ValueError, OSError):
|
|
104
|
+
raise SystemExit('artifact renderer: install lock is unavailable')
|
|
105
|
+
PY
|
|
106
|
+
STAGE=""
|
|
107
|
+
BACKUP="$TOOLS_DIR/.$INSTALL_ID.previous"
|
|
108
|
+
cleanup() {
|
|
109
|
+
if [ -n "$BACKUP" ] && [ -d "$BACKUP" ] && [ ! -e "$PREFIX" ]; then mv "$BACKUP" "$PREFIX"; fi
|
|
110
|
+
[ -z "$STAGE" ] || rm -rf "$STAGE"
|
|
111
|
+
}
|
|
112
|
+
trap cleanup EXIT
|
|
113
|
+
trap 'exit 130' INT
|
|
114
|
+
trap 'exit 143' TERM
|
|
115
|
+
|
|
116
|
+
# Recover an interrupted swap and discard only this pin's abandoned staging
|
|
117
|
+
# trees. Another Runtime's version/lock and in-progress install stay untouched.
|
|
118
|
+
if [ -d "$BACKUP" ]; then
|
|
119
|
+
if [ ! -e "$PREFIX" ]; then mv "$BACKUP" "$PREFIX"; else rm -rf "$BACKUP"; fi
|
|
120
|
+
fi
|
|
121
|
+
for abandoned in "$TOOLS_DIR/.$INSTALL_ID.stage."*; do
|
|
122
|
+
[ ! -d "$abandoned" ] || rm -rf "$abandoned"
|
|
123
|
+
done
|
|
124
|
+
|
|
125
|
+
# Recheck under the lock in case another explicit installer just completed.
|
|
126
|
+
if renderer_ready "$PREFIX"; then
|
|
127
|
+
printf 'Artifact renderer %s is ready\n' "$VERSION"
|
|
128
|
+
exit 0
|
|
129
|
+
fi
|
|
130
|
+
STAGE="$(mktemp -d "$TOOLS_DIR/.$INSTALL_ID.stage.XXXXXX")"
|
|
131
|
+
cp "$MANIFEST_DIR/dependencies.json" "$STAGE/package.json"
|
|
132
|
+
cp "$MANIFEST_DIR/dependencies-lock.json" "$STAGE/package-lock.json"
|
|
133
|
+
printf 'Installing artifact renderer %s with locked dependencies…\n' "$VERSION"
|
|
134
|
+
if ! "$NPM_BIN" ci --prefix "$STAGE" --ignore-scripts --omit=dev --no-audit --no-fund >"$STAGE/install.log" 2>&1; then
|
|
135
|
+
echo 'artifact renderer: installation failed; the previous install was kept' >&2
|
|
136
|
+
# npm often appends a long usage guide after the actionable error.
|
|
137
|
+
head -12 "$STAGE/install.log" >&2
|
|
138
|
+
if [ "$(wc -l < "$STAGE/install.log")" -gt 20 ]; then
|
|
139
|
+
echo '… npm output abbreviated …' >&2
|
|
140
|
+
tail -8 "$STAGE/install.log" >&2
|
|
141
|
+
fi
|
|
142
|
+
exit 1
|
|
143
|
+
fi
|
|
144
|
+
if ! renderer_ready "$STAGE"; then
|
|
145
|
+
echo 'artifact renderer: the installed package failed its readiness check; the previous install was kept' >&2
|
|
146
|
+
exit 1
|
|
147
|
+
fi
|
|
148
|
+
rm -f "$STAGE/install.log"
|
|
149
|
+
# Both renames are on the same filesystem. No live install is removed until
|
|
150
|
+
# the complete replacement has passed its version/dependency probe.
|
|
151
|
+
if [ -e "$PREFIX" ]; then
|
|
152
|
+
mv "$PREFIX" "$BACKUP"
|
|
153
|
+
fi
|
|
154
|
+
mv "$STAGE" "$PREFIX"
|
|
155
|
+
STAGE=""
|
|
156
|
+
[ ! -d "$BACKUP" ] || rm -rf "$BACKUP"
|
|
157
|
+
printf 'Artifact renderer %s installed\n' "$VERSION"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# One offline resolver for source checkouts and installed Egregore instances.
|
|
5
|
+
# Provisioning is explicit (setup/update/artifact-renderer-install.sh), never
|
|
6
|
+
# a side effect of rendering private content.
|
|
7
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
8
|
+
|
|
9
|
+
resolve_renderer() {
|
|
10
|
+
local source_cli="$ROOT/packages/egregore-artifacts/bin/cli.js"
|
|
11
|
+
local manifest_dir="$ROOT/bin/lib/artifact-renderer"
|
|
12
|
+
local managed="" install_id=""
|
|
13
|
+
local installed=""
|
|
14
|
+
|
|
15
|
+
if [ "${EGREGORE_USE_PUBLISHED:-0}" != "1" ] && \
|
|
16
|
+
[ -f "$source_cli" ] && \
|
|
17
|
+
[ -d "$ROOT/packages/egregore-artifacts/node_modules/react" ]; then
|
|
18
|
+
printf '%s\n' "$source_cli"
|
|
19
|
+
return 0
|
|
20
|
+
fi
|
|
21
|
+
if [ -f "$manifest_dir/dependencies.json" ] && [ -f "$manifest_dir/dependencies-lock.json" ]; then
|
|
22
|
+
install_id="$(python3 - "$manifest_dir" <<'PY'
|
|
23
|
+
import hashlib, json, pathlib, re, sys
|
|
24
|
+
source = pathlib.Path(sys.argv[1])
|
|
25
|
+
version = json.loads((source / 'dependencies.json').read_text())['dependencies']['egregore-artifacts']
|
|
26
|
+
if not isinstance(version, str) or not re.fullmatch(r'[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?', version):
|
|
27
|
+
raise SystemExit('artifact renderer: an exact package version is required')
|
|
28
|
+
digest = hashlib.sha256((source / 'dependencies-lock.json').read_bytes()).hexdigest()[:16]
|
|
29
|
+
print(version + '-' + digest)
|
|
30
|
+
PY
|
|
31
|
+
)" || return 1
|
|
32
|
+
managed="$ROOT/.egregore/tools/artifact-renderer/$install_id"
|
|
33
|
+
if cmp -s "$manifest_dir/dependencies.json" "$managed/package.json" && \
|
|
34
|
+
cmp -s "$manifest_dir/dependencies-lock.json" "$managed/package-lock.json" && \
|
|
35
|
+
[ -f "$managed/node_modules/egregore-artifacts/bin/cli.js" ] && \
|
|
36
|
+
[ -d "$managed/node_modules/react" ]; then
|
|
37
|
+
printf '%s\n' "$managed/node_modules/egregore-artifacts/bin/cli.js"
|
|
38
|
+
return 0
|
|
39
|
+
fi
|
|
40
|
+
fi
|
|
41
|
+
installed="$(command -v egregore-artifacts 2>/dev/null || true)"
|
|
42
|
+
if [ -n "$installed" ] && [ -f "$installed" ]; then
|
|
43
|
+
printf '%s\n' "$installed"
|
|
44
|
+
return 0
|
|
45
|
+
fi
|
|
46
|
+
echo "The artifact renderer is not installed or its dependencies are missing." >&2
|
|
47
|
+
echo "Run bash bin/artifact-renderer-install.sh from this instance, then retry." >&2
|
|
48
|
+
return 1
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
renderer="$(resolve_renderer)" || exit 1
|
|
52
|
+
if [ "${1:-}" = "--resolve" ]; then
|
|
53
|
+
[ "$#" -eq 1 ] || { echo "--resolve takes no arguments" >&2; exit 2; }
|
|
54
|
+
printf '%s\n' "$renderer"
|
|
55
|
+
exit 0
|
|
56
|
+
fi
|
|
57
|
+
exec bash "$ROOT/bin/node-run.sh" "$renderer" "$@"
|
|
@@ -110,13 +110,11 @@ preview() {
|
|
|
110
110
|
|
|
111
111
|
local render_ok=0
|
|
112
112
|
if [ -n "$composed" ]; then
|
|
113
|
-
if bash "$ROOT/bin/
|
|
114
|
-
"$ROOT/packages/egregore-artifacts/bin/cli.js" composed "$spec_file" \
|
|
113
|
+
if bash "$ROOT/bin/artifact-renderer.sh" composed "$spec_file" \
|
|
115
114
|
--source "$content_file" --verify-fidelity; then
|
|
116
115
|
render_ok=1
|
|
117
116
|
fi
|
|
118
|
-
elif bash "$ROOT/bin/
|
|
119
|
-
"$ROOT/packages/egregore-artifacts/bin/cli.js" handoff "$content_file" \
|
|
117
|
+
elif bash "$ROOT/bin/artifact-renderer.sh" handoff "$content_file" \
|
|
120
118
|
--verify-fidelity; then
|
|
121
119
|
render_ok=1
|
|
122
120
|
fi
|
|
@@ -414,21 +414,14 @@ PID_MEMORY=$!
|
|
|
414
414
|
# keep their familiar look.
|
|
415
415
|
RENDERED_HTML="$TMPD/handoff.html"
|
|
416
416
|
RENDER_OK=0
|
|
417
|
-
LOCAL_CLI="$SCRIPT_DIR/packages/egregore-artifacts/bin/cli.js"
|
|
418
417
|
FIDELITY_ARGS=(--verify-fidelity)
|
|
419
418
|
if [ "$RENDER_TYPE" = "composed" ]; then
|
|
420
419
|
FIDELITY_ARGS+=(--source "$ABS_FILE")
|
|
421
420
|
fi
|
|
422
|
-
#
|
|
423
|
-
#
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
if [ -f "$LOCAL_CLI" ] && [ -d "$SCRIPT_DIR/packages/egregore-artifacts/node_modules/react" ]; then
|
|
427
|
-
if bash "$SCRIPT_DIR/bin/node-run.sh" "$LOCAL_CLI" "$RENDER_TYPE" "${RENDER_SRC:-$ABS_FILE}" "${FIDELITY_ARGS[@]}" --output "$RENDERED_HTML" >/dev/null 2>"$TMPD/render-error"; then
|
|
428
|
-
RENDER_OK=1
|
|
429
|
-
fi
|
|
430
|
-
else
|
|
431
|
-
echo "local egregore-artifacts renderer unavailable; sending structured handoff without custom HTML" > "$TMPD/render-error"
|
|
421
|
+
# Resolve exactly as previews do, without a registry fetch. An unavailable
|
|
422
|
+
# renderer still leaves the existing structured-only handoff fallback.
|
|
423
|
+
if bash "$SCRIPT_DIR/bin/artifact-renderer.sh" "$RENDER_TYPE" "${RENDER_SRC:-$ABS_FILE}" "${FIDELITY_ARGS[@]}" --output "$RENDERED_HTML" >/dev/null 2>"$TMPD/render-error"; then
|
|
424
|
+
RENDER_OK=1
|
|
432
425
|
fi
|
|
433
426
|
|
|
434
427
|
# Step 2: build the emissary payload. If rendering succeeded AND the
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "egregore-managed-artifact-renderer",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "egregore-managed-artifact-renderer",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"egregore-artifacts": "0.30.0"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"node_modules/@json-render/core": {
|
|
15
|
+
"version": "0.16.0",
|
|
16
|
+
"resolved": "https://registry.npmjs.org/@json-render/core/-/core-0.16.0.tgz",
|
|
17
|
+
"integrity": "sha512-qQp8BB/3pWYapTGXBDSBMXRCdrC05VJPLL3drXMPX/QbUB3nuvtyXUGmAZFUz8eLUy7JImODvb3GNIq38dGzhQ==",
|
|
18
|
+
"license": "Apache-2.0",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"zod": "^4.3.6"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"zod": "^4.0.0"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"node_modules/@json-render/react": {
|
|
27
|
+
"version": "0.16.0",
|
|
28
|
+
"resolved": "https://registry.npmjs.org/@json-render/react/-/react-0.16.0.tgz",
|
|
29
|
+
"integrity": "sha512-PJQjLftGYZ5LNlA1IFww+yNOAJZRW2feEcH/L10u/8M/b/zImr9wfqQVMDyC87myoEzhyucpkQ6joISeZdKxsw==",
|
|
30
|
+
"license": "Apache-2.0",
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@json-render/core": "0.16.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"react": "^19.2.3"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"node_modules/egregore-artifacts": {
|
|
39
|
+
"version": "0.30.0",
|
|
40
|
+
"resolved": "https://registry.npmjs.org/egregore-artifacts/-/egregore-artifacts-0.30.0.tgz",
|
|
41
|
+
"integrity": "sha512-0mihiLED2+n3CT4/VNBW7driwTdQwNaDztJ0T5/TFZ9qJhtUAJCyeawKfnOXgjuTkwwu+d6dY/KMuZMGONGDvQ==",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@json-render/core": "^0.16.0",
|
|
45
|
+
"@json-render/react": "^0.16.0",
|
|
46
|
+
"react": "^19.2.4",
|
|
47
|
+
"react-dom": "^19.2.4",
|
|
48
|
+
"zod": "^4.3.6"
|
|
49
|
+
},
|
|
50
|
+
"bin": {
|
|
51
|
+
"egregore-artifacts": "bin/cli.js"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"node_modules/react": {
|
|
58
|
+
"version": "19.3.0",
|
|
59
|
+
"resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz",
|
|
60
|
+
"integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==",
|
|
61
|
+
"license": "MIT",
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=0.10.0"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"node_modules/react-dom": {
|
|
67
|
+
"version": "19.3.0",
|
|
68
|
+
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz",
|
|
69
|
+
"integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==",
|
|
70
|
+
"license": "MIT",
|
|
71
|
+
"dependencies": {
|
|
72
|
+
"scheduler": "^0.28.0"
|
|
73
|
+
},
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"react": "^19.3.0"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"node_modules/scheduler": {
|
|
79
|
+
"version": "0.28.0",
|
|
80
|
+
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz",
|
|
81
|
+
"integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==",
|
|
82
|
+
"license": "MIT"
|
|
83
|
+
},
|
|
84
|
+
"node_modules/zod": {
|
|
85
|
+
"version": "4.6.5",
|
|
86
|
+
"resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz",
|
|
87
|
+
"integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==",
|
|
88
|
+
"license": "MIT",
|
|
89
|
+
"funding": {
|
|
90
|
+
"url": "https://github.com/sponsors/colinhacks"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -121,7 +121,7 @@ mkdir -p /tmp
|
|
|
121
121
|
} > "$_DB_MD" 2>/dev/null
|
|
122
122
|
|
|
123
123
|
# --- Publish markdown → HTML → hosted URL ---
|
|
124
|
-
# publish-artifact.sh handles HTML generation
|
|
124
|
+
# publish-artifact.sh handles HTML generation with the provisioned renderer.
|
|
125
125
|
# then uploads. Don't pre-generate HTML — that causes double-wrapping.
|
|
126
126
|
# https:// URLs are universally cmd+clickable across all terminal emulators.
|
|
127
127
|
#
|
|
@@ -233,7 +233,7 @@ else
|
|
|
233
233
|
if [ -z "$DASHBOARD_URL" ]; then
|
|
234
234
|
_DB_HTML="/tmp/egregore-artifacts/dashboard-${EGREGORE_SESSION_ID}.html"
|
|
235
235
|
mkdir -p /tmp/egregore-artifacts
|
|
236
|
-
if
|
|
236
|
+
if bash "$SCRIPT_DIR/bin/artifact-renderer.sh" document "$_DB_MD" --output "$_DB_HTML" --no-open >/dev/null 2>&1 && [ -f "$_DB_HTML" ]; then
|
|
237
237
|
DASHBOARD_URL="file://${_DB_HTML}"
|
|
238
238
|
fi
|
|
239
239
|
fi
|
|
@@ -481,19 +481,8 @@ bash "$SCRIPT_DIR/bin/telemetry.sh" emit "session_start" \
|
|
|
481
481
|
# --- Health check-in (background, non-blocking) ---
|
|
482
482
|
bash "$SCRIPT_DIR/bin/startup-check.sh" >/dev/null 2>&1 &
|
|
483
483
|
|
|
484
|
-
#
|
|
485
|
-
#
|
|
486
|
-
# fresh npx cache pays a 5-10s npm download. Warmup is throttled to
|
|
487
|
-
# once per hour via a marker file so we don't storm npm when a user
|
|
488
|
-
# opens many tabs, and don't start runaway retry loops when offline.
|
|
489
|
-
_WARM_TS_FILE="$HOME/.cache/egregore/npx-warm-ts"
|
|
490
|
-
mkdir -p "$(dirname "$_WARM_TS_FILE")" 2>/dev/null
|
|
491
|
-
_WARM_LAST=$(cat "$_WARM_TS_FILE" 2>/dev/null || echo 0)
|
|
492
|
-
_WARM_NOW=$(date +%s)
|
|
493
|
-
if [ $(( _WARM_NOW - _WARM_LAST )) -gt 3600 ]; then
|
|
494
|
-
(npx -y egregore-artifacts@latest --help >/dev/null 2>&1 \
|
|
495
|
-
&& echo "$_WARM_NOW" > "$_WARM_TS_FILE" &) >/dev/null 2>&1
|
|
496
|
-
fi
|
|
484
|
+
# Artifact dependencies are provisioned during explicit setup/update. Startup
|
|
485
|
+
# must not warm a floating npx cache that the offline renderer cannot resolve.
|
|
497
486
|
|
|
498
487
|
# Clean up temp files
|
|
499
488
|
rm -rf "$CTX_DIR"
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
|
|
11
11
|
set -euo pipefail
|
|
12
12
|
|
|
13
|
+
# This migration entry point is invoked by explicit /update, never startup.
|
|
14
|
+
# The helper preserves a previous working install if provisioning fails.
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
16
|
+
bash "$SCRIPT_DIR/bin/artifact-renderer-install.sh"
|
|
17
|
+
|
|
13
18
|
MIGRATED=0
|
|
14
19
|
|
|
15
20
|
# --- Migration: commands → skills (CC 2.0+) ---
|
|
@@ -242,34 +242,13 @@ else
|
|
|
242
242
|
fi
|
|
243
243
|
fi
|
|
244
244
|
|
|
245
|
-
#
|
|
246
|
-
#
|
|
247
|
-
# are exercised without waiting for an npm release. Fall back to the
|
|
248
|
-
# published `npx egregore-artifacts` otherwise.
|
|
249
|
-
# Set EGREGORE_USE_PUBLISHED=1 to force the npm-published renderer even
|
|
250
|
-
# when a local install is present — useful in the upstream dev repo to
|
|
251
|
-
# dogfood what teammates and OSS users actually run.
|
|
252
|
-
LOCAL_CLI="$SCRIPT_DIR/packages/egregore-artifacts/bin/cli.js"
|
|
245
|
+
# Use the same provisioned renderer as previews and /view. Rendering never
|
|
246
|
+
# downloads a package; EGREGORE_USE_PUBLISHED skips the source checkout.
|
|
253
247
|
RENDER_ERROR="/tmp/egregore-artifacts/render-error-$$.log"
|
|
254
|
-
if
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
exit 5
|
|
259
|
-
fi
|
|
260
|
-
else
|
|
261
|
-
# Installed published renderer only — never npx/@latest: a hidden
|
|
262
|
-
# registry fetch of a floating version is not an acceptable renderer.
|
|
263
|
-
PUBLISHED_CLI="$(command -v egregore-artifacts || true)"
|
|
264
|
-
if [ -z "$PUBLISHED_CLI" ]; then
|
|
265
|
-
echo "no artifact renderer available: the checked-out packages/egregore-artifacts is unusable and no installed egregore-artifacts was found" >&2
|
|
266
|
-
exit 5
|
|
267
|
-
fi
|
|
268
|
-
if ! bash "$SCRIPT_DIR/bin/node-run.sh" "$PUBLISHED_CLI" "${RENDER_ARGS[@]}" >/dev/null 2>"$RENDER_ERROR"; then
|
|
269
|
-
sed -n '1,4p' "$RENDER_ERROR" >&2
|
|
270
|
-
rm -f "$RENDER_ERROR"
|
|
271
|
-
exit 5
|
|
272
|
-
fi
|
|
248
|
+
if ! bash "$SCRIPT_DIR/bin/artifact-renderer.sh" "${RENDER_ARGS[@]}" >/dev/null 2>"$RENDER_ERROR"; then
|
|
249
|
+
sed -n '1,4p' "$RENDER_ERROR" >&2
|
|
250
|
+
rm -f "$RENDER_ERROR"
|
|
251
|
+
exit 5
|
|
273
252
|
fi
|
|
274
253
|
rm -f "$RENDER_ERROR"
|
|
275
254
|
fi
|
|
@@ -49,19 +49,8 @@ _open() {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
# --- Resolve the packaged deterministic renderer (never fetch, never npx) ---
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
RENDER_ID="$SCRIPT_DIR/packages/egregore-artifacts/bin/cli.js"
|
|
55
|
-
elif [ -f "$SCRIPT_DIR/packages/egregore-artifacts/bin/cli.js" ]; then
|
|
56
|
-
echo "The checked-out artifact renderer is missing its local dependencies." >&2
|
|
57
|
-
exit 1
|
|
58
|
-
elif command -v egregore-artifacts >/dev/null 2>&1; then
|
|
59
|
-
RENDER=(bash "$SCRIPT_DIR/bin/node-run.sh" "$(command -v egregore-artifacts)")
|
|
60
|
-
RENDER_ID="$(command -v egregore-artifacts)"
|
|
61
|
-
else
|
|
62
|
-
echo "The local artifact renderer is not installed for this instance." >&2
|
|
63
|
-
exit 1
|
|
64
|
-
fi
|
|
52
|
+
RENDER_ID="$(bash "$SCRIPT_DIR/bin/artifact-renderer.sh" --resolve)" || exit 1
|
|
53
|
+
RENDER=(bash "$SCRIPT_DIR/bin/node-run.sh" "$RENDER_ID")
|
|
65
54
|
|
|
66
55
|
# --- Live surfaces render fresh every time (their data is the present) ------
|
|
67
56
|
case "$TYPE" in
|
|
@@ -17,6 +17,7 @@ from __future__ import annotations
|
|
|
17
17
|
import json
|
|
18
18
|
import os
|
|
19
19
|
import re
|
|
20
|
+
import signal
|
|
20
21
|
import subprocess
|
|
21
22
|
import sys
|
|
22
23
|
import time
|
|
@@ -27,6 +28,8 @@ from typing import Any, Mapping
|
|
|
27
28
|
from .adapters.qmd import QMD_VERSION, RERANKING_ENABLED, QmdLocalRetriever
|
|
28
29
|
|
|
29
30
|
SCHEMA_VERSION = "egregore-runtime-upgrade/v1"
|
|
31
|
+
_RENDERER_INSTALL_TIMEOUT_SECONDS = 180
|
|
32
|
+
_RENDERER_TERMINATE_GRACE_SECONDS = 5
|
|
30
33
|
|
|
31
34
|
# Stage keys in preparation order. Labels are the user-facing truth shown by
|
|
32
35
|
# Settings; keep them in plain team language.
|
|
@@ -628,6 +631,54 @@ class UpgradeEngine:
|
|
|
628
631
|
if probe.returncode != 0:
|
|
629
632
|
self._fail(state, "node", "the selected Node runtime failed its version probe")
|
|
630
633
|
return False
|
|
634
|
+
# Explicit Runtime preparation owns package provisioning. Preview and
|
|
635
|
+
# session startup only resolve installed renderers. Older candidates
|
|
636
|
+
# predate this helper and retain their original upgrade contract.
|
|
637
|
+
renderer_installer = (
|
|
638
|
+
Path(state.get("installed_path") or "")
|
|
639
|
+
/ "runtime" / APPLY_TREE / "bin" / "artifact-renderer-install.sh"
|
|
640
|
+
)
|
|
641
|
+
if renderer_installer.is_file():
|
|
642
|
+
environment = {**os.environ, **self.retriever.environment}
|
|
643
|
+
environment["PATH"] = str(Path(node).parent) + os.pathsep + environment.get("PATH", "")
|
|
644
|
+
try:
|
|
645
|
+
with subprocess.Popen(
|
|
646
|
+
("bash", str(renderer_installer), "--root", str(self.repository_root)),
|
|
647
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
|
|
648
|
+
cwd=self.repository_root, env=environment, start_new_session=True,
|
|
649
|
+
) as renderer:
|
|
650
|
+
try:
|
|
651
|
+
stdout, stderr = renderer.communicate(timeout=_RENDERER_INSTALL_TIMEOUT_SECONDS)
|
|
652
|
+
except subprocess.TimeoutExpired:
|
|
653
|
+
# npm is a child of the installer shell. Stop the entire
|
|
654
|
+
# group so its EXIT cleanup can run without an orphaned
|
|
655
|
+
# package manager still writing into the staging tree.
|
|
656
|
+
try:
|
|
657
|
+
os.killpg(renderer.pid, signal.SIGTERM)
|
|
658
|
+
except ProcessLookupError:
|
|
659
|
+
pass
|
|
660
|
+
try:
|
|
661
|
+
renderer.communicate(timeout=_RENDERER_TERMINATE_GRACE_SECONDS)
|
|
662
|
+
except subprocess.TimeoutExpired:
|
|
663
|
+
pass
|
|
664
|
+
finally:
|
|
665
|
+
# A child may ignore TERM even after Bash exits.
|
|
666
|
+
try:
|
|
667
|
+
os.killpg(renderer.pid, signal.SIGKILL)
|
|
668
|
+
except ProcessLookupError:
|
|
669
|
+
pass
|
|
670
|
+
renderer.communicate()
|
|
671
|
+
raise
|
|
672
|
+
except subprocess.TimeoutExpired:
|
|
673
|
+
self._fail(state, "node", f"artifact renderer provisioning timed out after {_RENDERER_INSTALL_TIMEOUT_SECONDS} seconds; retry preparation")
|
|
674
|
+
return False
|
|
675
|
+
except OSError as exc:
|
|
676
|
+
self._fail(state, "node", f"artifact renderer provisioning failed: {exc}")
|
|
677
|
+
return False
|
|
678
|
+
if renderer.returncode != 0:
|
|
679
|
+
detail = (stderr or stdout).strip()
|
|
680
|
+
self._fail(state, "node", f"artifact renderer provisioning failed: {detail}")
|
|
681
|
+
return False
|
|
631
682
|
self._mark(state, "node", "done", f"{probe.stdout.strip()} at instance-selected path")
|
|
632
683
|
return True
|
|
633
684
|
|