@lifeaitools/rdc-skills 0.24.38 → 0.24.41
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/.claude/settings.json +15 -15
- package/.claude-plugin/marketplace.json +21 -21
- package/.claude-plugin/plugin.json +1371 -1371
- package/.github/workflows/publish.yml +34 -34
- package/.github/workflows/self-test.yml +58 -58
- package/CHANGELOG.md +310 -310
- package/LICENSE +21 -21
- package/MANIFEST.md +221 -221
- package/README.md +376 -375
- package/README.sandbox.md +3 -3
- package/RELEASE.md +42 -0
- package/assets/watcher/viewer.html +164 -164
- package/bin/rdc-skills-mcp.mjs +316 -316
- package/commands/build.md +183 -183
- package/commands/collab.md +180 -180
- package/commands/deploy.md +152 -152
- package/commands/design.md +31 -31
- package/commands/edit.md +28 -28
- package/commands/fixit.md +124 -124
- package/commands/handoff.md +173 -173
- package/commands/help.md +95 -95
- package/commands/overnight.md +220 -220
- package/commands/plan.md +158 -158
- package/commands/preplan.md +131 -131
- package/commands/prototype.md +145 -145
- package/commands/release.md +49 -49
- package/commands/report.md +99 -99
- package/commands/review.md +120 -120
- package/commands/self-test.md +113 -113
- package/commands/status.md +86 -86
- package/commands/watch.md +98 -98
- package/commands/workitems.md +137 -137
- package/git-sha.json +1 -1
- package/guides/agent-bootstrap.md +295 -295
- package/guides/agents/backend.md +104 -104
- package/guides/agents/content.md +94 -94
- package/guides/agents/cs2.md +56 -56
- package/guides/agents/data.md +87 -87
- package/guides/agents/design.md +77 -77
- package/guides/agents/frontend.md +92 -92
- package/guides/agents/infrastructure.md +81 -81
- package/guides/agents/setup.md +281 -281
- package/guides/agents/verify.md +151 -151
- package/guides/agents/viz.md +106 -106
- package/guides/backend.md +146 -146
- package/guides/content.md +147 -147
- package/guides/cs2.md +190 -190
- package/guides/data.md +123 -123
- package/guides/design.md +116 -116
- package/guides/engineering-behavior.md +43 -43
- package/guides/escalation-protocol.md +125 -125
- package/guides/frontend.md +151 -151
- package/guides/history-md-spec.md +297 -297
- package/guides/infrastructure.md +179 -179
- package/guides/lessons-learned-spec.md +151 -145
- package/guides/output-contract.md +108 -108
- package/guides/publish-md-spec.md +289 -289
- package/guides/rdc-skills-startup.md +30 -30
- package/guides/verify.md +11 -11
- package/hooks/check-cwd.js +31 -31
- package/hooks/check-rdc-environment.js +164 -164
- package/hooks/check-services.js +6 -6
- package/hooks/check-stale-work-items.js +19 -19
- package/hooks/foreground-process-gate.js +128 -128
- package/hooks/gate-watchdog-selfcheck.js +257 -257
- package/hooks/hook-logger.js +25 -25
- package/hooks/lib/run-evidence-gate.mjs +241 -241
- package/hooks/no-stop-open-epics.js +127 -127
- package/hooks/post-tool-batch-gate.js +203 -203
- package/hooks/post-work-check.js +21 -21
- package/hooks/postcompact-log.js +13 -13
- package/hooks/precompact-log.js +13 -13
- package/hooks/rate-limit-retry.js +46 -46
- package/hooks/rdc-invocation-marker.js +157 -157
- package/hooks/rdc-output-contract-gate.js +94 -94
- package/hooks/require-work-item-on-commit.js +294 -294
- package/hooks/restart-brief.js +19 -19
- package/hooks/run-hidden-hook.ps1 +47 -47
- package/hooks/task-completed-gate.js +274 -274
- package/hooks/work-item-exit-gate.js +944 -944
- package/lib/catalog.mjs +236 -236
- package/lib/cloud-rewrite.mjs +155 -155
- package/package.json +57 -56
- package/rules/work-items-rpc.md +520 -520
- package/scaffold/templates/HISTORY.md.template +39 -39
- package/scaffold/templates/PUBLISH.md.template +21 -21
- package/scaffold/templates/brochure-studio-default.html +70 -70
- package/scripts/acceptance.mjs +502 -502
- package/scripts/fixtures/guides/bad-guide.md +15 -15
- package/scripts/fixtures/guides-clean/good-guide.md +16 -16
- package/scripts/install-rdc-skills.js +1289 -1289
- package/scripts/install.ps1 +202 -202
- package/scripts/install.sh +132 -132
- package/scripts/lib/assertions.mjs +287 -287
- package/scripts/lib/manifest-schema.mjs +754 -754
- package/scripts/lib/runner.mjs +465 -465
- package/scripts/lib/sandbox.mjs +435 -435
- package/scripts/prepack.mjs +32 -32
- package/scripts/rdc-brochure.mjs +482 -464
- package/scripts/rdc-design-cli.mjs +134 -134
- package/scripts/rebuild-mcp.mjs +107 -107
- package/scripts/self-test.mjs +1460 -1460
- package/scripts/stamp-git-sha.mjs +29 -29
- package/scripts/test-guide-validator.mjs +196 -196
- package/scripts/test-rdc-hooks.mjs +145 -145
- package/scripts/uninstall.ps1 +77 -77
- package/scripts/uninstall.sh +69 -69
- package/scripts/update.ps1 +43 -43
- package/scripts/update.sh +43 -43
- package/scripts/validate-place-histories.js +461 -461
- package/scripts/validate-publish-manifests.js +424 -424
- package/scripts/watch-init.mjs +100 -100
- package/skills/brochure/SKILL.md +107 -107
- package/skills/build/SKILL.md +563 -563
- package/skills/channel-formatter/SKILL.md +533 -533
- package/skills/co-develop/SKILL.md +196 -196
- package/skills/collab/SKILL.md +239 -239
- package/skills/convert/SKILL.md +140 -140
- package/skills/deploy/SKILL.md +541 -541
- package/skills/design/SKILL.md +211 -211
- package/skills/design/reference/ownership.md +16 -16
- package/skills/design/reference/rampa.md +92 -92
- package/skills/design/reference/studio-model.md +153 -153
- package/skills/edit/SKILL.md +98 -98
- package/skills/fixit/SKILL.md +165 -165
- package/skills/fs-mcp/SKILL.md +148 -148
- package/skills/handoff/SKILL.md +236 -200
- package/skills/help/SKILL.md +143 -143
- package/skills/housekeeping/SKILL.md +219 -160
- package/skills/lifeai-brochure-author/SKILL.md +340 -340
- package/skills/overnight/SKILL.md +251 -251
- package/skills/plan/SKILL.md +345 -345
- package/skills/preplan/SKILL.md +90 -90
- package/skills/prototype/SKILL.md +150 -150
- package/skills/rdc-brochurify/SKILL.md +245 -245
- package/skills/rdc-extract-verifier-rules/SKILL.md +191 -191
- package/skills/release/SKILL.md +140 -140
- package/skills/report/SKILL.md +100 -100
- package/skills/review/SKILL.md +152 -152
- package/skills/rpms-filemap/SKILL.cloud.md +111 -111
- package/skills/rpms-filemap/SKILL.md +111 -111
- package/skills/self-test/SKILL.md +132 -132
- package/skills/status/SKILL.md +99 -99
- package/skills/terminal-config/SKILL.md +62 -62
- package/skills/tests/MATRIX.md +54 -54
- package/skills/tests/README.md +47 -47
- package/skills/tests/rdc-brochure.test.json +34 -34
- package/skills/tests/rdc-build.test.json +36 -36
- package/skills/tests/rdc-channel-formatter.test.json +45 -45
- package/skills/tests/rdc-co-develop.test.json +29 -29
- package/skills/tests/rdc-collab.test.json +29 -29
- package/skills/tests/rdc-convert.test.json +35 -35
- package/skills/tests/rdc-deploy.test.json +30 -30
- package/skills/tests/rdc-design.test.json +27 -27
- package/skills/tests/rdc-edit.test.json +29 -29
- package/skills/tests/rdc-fixit.test.json +36 -36
- package/skills/tests/rdc-fs-mcp.test.json +36 -36
- package/skills/tests/rdc-handoff.test.json +28 -28
- package/skills/tests/rdc-help.test.json +29 -29
- package/skills/tests/rdc-housekeeping.test.json +32 -28
- package/skills/tests/rdc-lifeai-brochure-author.test.json +35 -35
- package/skills/tests/rdc-overnight.test.json +37 -37
- package/skills/tests/rdc-plan.test.json +27 -27
- package/skills/tests/rdc-preplan.test.json +31 -31
- package/skills/tests/rdc-prototype.test.json +28 -28
- package/skills/tests/rdc-rdc-brochurify.test.json +23 -23
- package/skills/tests/rdc-rdc-extract-verifier-rules.test.json +34 -34
- package/skills/tests/rdc-release.test.json +29 -29
- package/skills/tests/rdc-report.test.json +28 -28
- package/skills/tests/rdc-review.test.json +29 -29
- package/skills/tests/rdc-rpms-filemap.test.json +28 -28
- package/skills/tests/rdc-self-test.test.json +24 -24
- package/skills/tests/rdc-status.test.json +29 -29
- package/skills/tests/rdc-terminal-config.test.json +29 -29
- package/skills/tests/rdc-watch.test.json +24 -24
- package/skills/tests/rdc-workitems.test.json +27 -27
- package/skills/watch/SKILL.md +97 -97
- package/skills/workitems/SKILL.md +151 -151
- package/tests/acceptance.test.mjs +59 -59
- package/tests/channel-formatter.contract.test.mjs +251 -251
- package/tests/curl-surface.test.mjs +289 -289
- package/tests/harness-gates.test.mjs +325 -325
- package/tests/help-surface.test.mjs +61 -61
- package/tests/housekeeping-lessons-triage.test.mjs +49 -0
- package/tests/install-rdc-skills.test.mjs +49 -49
- package/tests/lessons-pipeline-contract.test.mjs +26 -0
- package/tests/manifest-contract-fields.test.mjs +78 -78
- package/tests/mcp.test.mjs +271 -271
- package/tests/rdc-brochure.test.mjs +125 -0
- package/tests/release-contract.test.mjs +16 -0
- package/tests/require-work-item-on-commit.test.mjs +162 -162
- package/tests/run-evidence-gate.test.mjs +82 -82
- package/tests/skill-test-matrix.test.mjs +66 -66
- package/tests/validate-skills.js +27 -27
- package/tests/work-item-exit-gate-l2.test.mjs +368 -368
- package/tests/work-item-exit-gate-l3.test.mjs +197 -197
package/README.sandbox.md
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
# Sandbox
|
|
2
|
-
|
|
3
|
-
Hello world
|
|
1
|
+
# Sandbox
|
|
2
|
+
|
|
3
|
+
Hello world
|
package/RELEASE.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
doc_type: release-runbook
|
|
3
|
+
status: active
|
|
4
|
+
package: "@lifeaitools/rdc-skills"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Release rdc-skills
|
|
8
|
+
|
|
9
|
+
`@lifeaitools/rdc-skills` is released to npm and then installed into the active
|
|
10
|
+
LifeAI skill profile. This runbook is the required release contract for every
|
|
11
|
+
version.
|
|
12
|
+
|
|
13
|
+
## Release checklist
|
|
14
|
+
|
|
15
|
+
1. Reconcile the release branch with its remote and confirm the intended diff.
|
|
16
|
+
2. Run `npm run validate`, focused deterministic tests, and an independent RDC
|
|
17
|
+
review for the candidate changes.
|
|
18
|
+
3. Bump the package with `npm version patch --no-git-tag-version` (or the
|
|
19
|
+
approved minor or major equivalent).
|
|
20
|
+
4. Commit the version files with the associated RDC work-item trailer and push
|
|
21
|
+
the release branch.
|
|
22
|
+
5. Create and push `v<version>`; the publish workflow publishes the tagged
|
|
23
|
+
package to npm.
|
|
24
|
+
6. Wait for the publish workflow to complete, then verify it with
|
|
25
|
+
`npm view @lifeaitools/rdc-skills version` and
|
|
26
|
+
`npm view @lifeaitools/rdc-skills@<version> version`.
|
|
27
|
+
7. Inspect the registry artifact with
|
|
28
|
+
`npm pack @lifeaitools/rdc-skills@<version>` and verify the changed skill
|
|
29
|
+
files are present in the tarball.
|
|
30
|
+
8. Install the published package with
|
|
31
|
+
`npm install -g @lifeaitools/rdc-skills@<version>`.
|
|
32
|
+
9. Reinstall the active profile with `rdc-skills-install --profile core`; on a
|
|
33
|
+
workstation intentionally configured for the LIFEAI project layout and
|
|
34
|
+
services, use `rdc-skills-install --profile lifeai`.
|
|
35
|
+
10. Verify the global package version, the active profile copy, and the skill
|
|
36
|
+
smoke check before closing the release work item.
|
|
37
|
+
|
|
38
|
+
## Deployment boundary
|
|
39
|
+
|
|
40
|
+
The LifeAI profile installation is the development delivery for this library.
|
|
41
|
+
Application production promotion remains separately approval-gated through
|
|
42
|
+
`rdc:deploy promote`; this package runbook never promotes an application.
|
|
@@ -1,164 +1,164 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<title>rdc:watch — session log</title>
|
|
6
|
-
<style>
|
|
7
|
-
:root {
|
|
8
|
-
--bg: #0b0f0b;
|
|
9
|
-
--fg: #c8f0c8;
|
|
10
|
-
--dim: #6a7a6a;
|
|
11
|
-
--bar: #111a11;
|
|
12
|
-
--bar-stall: #3a2f00;
|
|
13
|
-
--dispatch: #6aa9ff;
|
|
14
|
-
--commit: #58d06a;
|
|
15
|
-
--test: #e6c84a;
|
|
16
|
-
--error: #ff6060;
|
|
17
|
-
--note: #8a8a8a;
|
|
18
|
-
--banner: #ffffff;
|
|
19
|
-
}
|
|
20
|
-
* { box-sizing: border-box; }
|
|
21
|
-
html, body { margin: 0; padding: 0; height: 100%; background: var(--bg); color: var(--fg); font-family: ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace; font-size: 13px; }
|
|
22
|
-
body { display: flex; flex-direction: column; }
|
|
23
|
-
header {
|
|
24
|
-
background: var(--bar);
|
|
25
|
-
border-bottom: 1px solid #1d2a1d;
|
|
26
|
-
padding: 8px 14px;
|
|
27
|
-
display: flex;
|
|
28
|
-
gap: 18px;
|
|
29
|
-
flex-wrap: wrap;
|
|
30
|
-
font-size: 12px;
|
|
31
|
-
color: var(--dim);
|
|
32
|
-
}
|
|
33
|
-
header.stalled { background: var(--bar-stall); color: #ffe08a; }
|
|
34
|
-
header b { color: var(--fg); font-weight: 600; }
|
|
35
|
-
header.stalled b { color: #ffe08a; }
|
|
36
|
-
#log {
|
|
37
|
-
flex: 1;
|
|
38
|
-
overflow-y: auto;
|
|
39
|
-
padding: 10px 14px 40px;
|
|
40
|
-
white-space: pre-wrap;
|
|
41
|
-
word-break: break-word;
|
|
42
|
-
}
|
|
43
|
-
.line { padding: 1px 0; line-height: 1.45; }
|
|
44
|
-
.ts { color: var(--dim); margin-right: 8px; }
|
|
45
|
-
.kind {
|
|
46
|
-
display: inline-block;
|
|
47
|
-
min-width: 70px;
|
|
48
|
-
margin-right: 8px;
|
|
49
|
-
text-transform: uppercase;
|
|
50
|
-
font-size: 11px;
|
|
51
|
-
letter-spacing: 0.05em;
|
|
52
|
-
}
|
|
53
|
-
.dispatch .kind, .dispatch .msg { color: var(--dispatch); }
|
|
54
|
-
.commit .kind, .commit .msg { color: var(--commit); }
|
|
55
|
-
.test .kind, .test .msg { color: var(--test); }
|
|
56
|
-
.error .kind, .error .msg { color: var(--error); }
|
|
57
|
-
.note .kind, .note .msg { color: var(--note); }
|
|
58
|
-
.banner .kind, .banner .msg { color: var(--banner); font-weight: 700; }
|
|
59
|
-
</style>
|
|
60
|
-
</head>
|
|
61
|
-
<body>
|
|
62
|
-
<header id="bar">
|
|
63
|
-
<div>session <b id="sid">—</b></div>
|
|
64
|
-
<div>started <b id="started">—</b></div>
|
|
65
|
-
<div>events <b id="count">0</b></div>
|
|
66
|
-
<div>updated <b id="updated">—</b></div>
|
|
67
|
-
<div id="status"></div>
|
|
68
|
-
</header>
|
|
69
|
-
<div id="log"></div>
|
|
70
|
-
<script>
|
|
71
|
-
(function () {
|
|
72
|
-
const LOG_URL = "./current.log";
|
|
73
|
-
const POLL_MS = 2000;
|
|
74
|
-
const logEl = document.getElementById("log");
|
|
75
|
-
const bar = document.getElementById("bar");
|
|
76
|
-
const sidEl = document.getElementById("sid");
|
|
77
|
-
const startedEl = document.getElementById("started");
|
|
78
|
-
const countEl = document.getElementById("count");
|
|
79
|
-
const updatedEl = document.getElementById("updated");
|
|
80
|
-
const statusEl = document.getElementById("status");
|
|
81
|
-
|
|
82
|
-
let lastLen = 0;
|
|
83
|
-
let count = 0;
|
|
84
|
-
let sessionSet = false;
|
|
85
|
-
|
|
86
|
-
const LINE_RE = /^\[([^\]]+)\]\s*\[([^\]]+)\]\s*(.*)$/;
|
|
87
|
-
const KINDS = new Set(["dispatch", "commit", "test", "error", "note", "banner"]);
|
|
88
|
-
|
|
89
|
-
function isAtBottom() {
|
|
90
|
-
return logEl.scrollHeight - logEl.scrollTop - logEl.clientHeight < 40;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function renderLine(raw) {
|
|
94
|
-
const m = raw.match(LINE_RE);
|
|
95
|
-
let ts = "", kind = "note", msg = raw;
|
|
96
|
-
if (m) {
|
|
97
|
-
ts = m[1];
|
|
98
|
-
kind = KINDS.has(m[2].toLowerCase()) ? m[2].toLowerCase() : "note";
|
|
99
|
-
msg = m[3];
|
|
100
|
-
}
|
|
101
|
-
const div = document.createElement("div");
|
|
102
|
-
div.className = "line " + kind;
|
|
103
|
-
const tsSpan = document.createElement("span");
|
|
104
|
-
tsSpan.className = "ts";
|
|
105
|
-
tsSpan.textContent = ts;
|
|
106
|
-
const kSpan = document.createElement("span");
|
|
107
|
-
kSpan.className = "kind";
|
|
108
|
-
kSpan.textContent = kind;
|
|
109
|
-
const mSpan = document.createElement("span");
|
|
110
|
-
mSpan.className = "msg";
|
|
111
|
-
mSpan.textContent = msg;
|
|
112
|
-
div.appendChild(tsSpan);
|
|
113
|
-
div.appendChild(kSpan);
|
|
114
|
-
div.appendChild(mSpan);
|
|
115
|
-
logEl.appendChild(div);
|
|
116
|
-
|
|
117
|
-
if (kind === "banner" && !sessionSet) {
|
|
118
|
-
const idMatch = msg.match(/Session started:\s*(\S+)/);
|
|
119
|
-
if (idMatch) sidEl.textContent = idMatch[1];
|
|
120
|
-
if (ts) startedEl.textContent = ts;
|
|
121
|
-
sessionSet = true;
|
|
122
|
-
}
|
|
123
|
-
count += 1;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async function poll() {
|
|
127
|
-
try {
|
|
128
|
-
const res = await fetch(LOG_URL + "?t=" + Date.now(), { cache: "no-store" });
|
|
129
|
-
if (!res.ok) throw new Error("http " + res.status);
|
|
130
|
-
const text = await res.text();
|
|
131
|
-
if (text.length < lastLen) {
|
|
132
|
-
// File shrank — reset (new session).
|
|
133
|
-
logEl.innerHTML = "";
|
|
134
|
-
lastLen = 0;
|
|
135
|
-
count = 0;
|
|
136
|
-
sessionSet = false;
|
|
137
|
-
}
|
|
138
|
-
if (text.length > lastLen) {
|
|
139
|
-
const stick = isAtBottom();
|
|
140
|
-
const chunk = text.slice(lastLen);
|
|
141
|
-
lastLen = text.length;
|
|
142
|
-
const lines = chunk.split(/\r?\n/);
|
|
143
|
-
for (const line of lines) {
|
|
144
|
-
if (line.trim().length === 0) continue;
|
|
145
|
-
renderLine(line);
|
|
146
|
-
}
|
|
147
|
-
countEl.textContent = String(count);
|
|
148
|
-
updatedEl.textContent = new Date().toLocaleTimeString();
|
|
149
|
-
if (stick) logEl.scrollTop = logEl.scrollHeight;
|
|
150
|
-
}
|
|
151
|
-
bar.classList.remove("stalled");
|
|
152
|
-
statusEl.textContent = "";
|
|
153
|
-
} catch (e) {
|
|
154
|
-
bar.classList.add("stalled");
|
|
155
|
-
statusEl.textContent = "stalled — retrying";
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
poll();
|
|
160
|
-
setInterval(poll, POLL_MS);
|
|
161
|
-
})();
|
|
162
|
-
</script>
|
|
163
|
-
</body>
|
|
164
|
-
</html>
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>rdc:watch — session log</title>
|
|
6
|
+
<style>
|
|
7
|
+
:root {
|
|
8
|
+
--bg: #0b0f0b;
|
|
9
|
+
--fg: #c8f0c8;
|
|
10
|
+
--dim: #6a7a6a;
|
|
11
|
+
--bar: #111a11;
|
|
12
|
+
--bar-stall: #3a2f00;
|
|
13
|
+
--dispatch: #6aa9ff;
|
|
14
|
+
--commit: #58d06a;
|
|
15
|
+
--test: #e6c84a;
|
|
16
|
+
--error: #ff6060;
|
|
17
|
+
--note: #8a8a8a;
|
|
18
|
+
--banner: #ffffff;
|
|
19
|
+
}
|
|
20
|
+
* { box-sizing: border-box; }
|
|
21
|
+
html, body { margin: 0; padding: 0; height: 100%; background: var(--bg); color: var(--fg); font-family: ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace; font-size: 13px; }
|
|
22
|
+
body { display: flex; flex-direction: column; }
|
|
23
|
+
header {
|
|
24
|
+
background: var(--bar);
|
|
25
|
+
border-bottom: 1px solid #1d2a1d;
|
|
26
|
+
padding: 8px 14px;
|
|
27
|
+
display: flex;
|
|
28
|
+
gap: 18px;
|
|
29
|
+
flex-wrap: wrap;
|
|
30
|
+
font-size: 12px;
|
|
31
|
+
color: var(--dim);
|
|
32
|
+
}
|
|
33
|
+
header.stalled { background: var(--bar-stall); color: #ffe08a; }
|
|
34
|
+
header b { color: var(--fg); font-weight: 600; }
|
|
35
|
+
header.stalled b { color: #ffe08a; }
|
|
36
|
+
#log {
|
|
37
|
+
flex: 1;
|
|
38
|
+
overflow-y: auto;
|
|
39
|
+
padding: 10px 14px 40px;
|
|
40
|
+
white-space: pre-wrap;
|
|
41
|
+
word-break: break-word;
|
|
42
|
+
}
|
|
43
|
+
.line { padding: 1px 0; line-height: 1.45; }
|
|
44
|
+
.ts { color: var(--dim); margin-right: 8px; }
|
|
45
|
+
.kind {
|
|
46
|
+
display: inline-block;
|
|
47
|
+
min-width: 70px;
|
|
48
|
+
margin-right: 8px;
|
|
49
|
+
text-transform: uppercase;
|
|
50
|
+
font-size: 11px;
|
|
51
|
+
letter-spacing: 0.05em;
|
|
52
|
+
}
|
|
53
|
+
.dispatch .kind, .dispatch .msg { color: var(--dispatch); }
|
|
54
|
+
.commit .kind, .commit .msg { color: var(--commit); }
|
|
55
|
+
.test .kind, .test .msg { color: var(--test); }
|
|
56
|
+
.error .kind, .error .msg { color: var(--error); }
|
|
57
|
+
.note .kind, .note .msg { color: var(--note); }
|
|
58
|
+
.banner .kind, .banner .msg { color: var(--banner); font-weight: 700; }
|
|
59
|
+
</style>
|
|
60
|
+
</head>
|
|
61
|
+
<body>
|
|
62
|
+
<header id="bar">
|
|
63
|
+
<div>session <b id="sid">—</b></div>
|
|
64
|
+
<div>started <b id="started">—</b></div>
|
|
65
|
+
<div>events <b id="count">0</b></div>
|
|
66
|
+
<div>updated <b id="updated">—</b></div>
|
|
67
|
+
<div id="status"></div>
|
|
68
|
+
</header>
|
|
69
|
+
<div id="log"></div>
|
|
70
|
+
<script>
|
|
71
|
+
(function () {
|
|
72
|
+
const LOG_URL = "./current.log";
|
|
73
|
+
const POLL_MS = 2000;
|
|
74
|
+
const logEl = document.getElementById("log");
|
|
75
|
+
const bar = document.getElementById("bar");
|
|
76
|
+
const sidEl = document.getElementById("sid");
|
|
77
|
+
const startedEl = document.getElementById("started");
|
|
78
|
+
const countEl = document.getElementById("count");
|
|
79
|
+
const updatedEl = document.getElementById("updated");
|
|
80
|
+
const statusEl = document.getElementById("status");
|
|
81
|
+
|
|
82
|
+
let lastLen = 0;
|
|
83
|
+
let count = 0;
|
|
84
|
+
let sessionSet = false;
|
|
85
|
+
|
|
86
|
+
const LINE_RE = /^\[([^\]]+)\]\s*\[([^\]]+)\]\s*(.*)$/;
|
|
87
|
+
const KINDS = new Set(["dispatch", "commit", "test", "error", "note", "banner"]);
|
|
88
|
+
|
|
89
|
+
function isAtBottom() {
|
|
90
|
+
return logEl.scrollHeight - logEl.scrollTop - logEl.clientHeight < 40;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function renderLine(raw) {
|
|
94
|
+
const m = raw.match(LINE_RE);
|
|
95
|
+
let ts = "", kind = "note", msg = raw;
|
|
96
|
+
if (m) {
|
|
97
|
+
ts = m[1];
|
|
98
|
+
kind = KINDS.has(m[2].toLowerCase()) ? m[2].toLowerCase() : "note";
|
|
99
|
+
msg = m[3];
|
|
100
|
+
}
|
|
101
|
+
const div = document.createElement("div");
|
|
102
|
+
div.className = "line " + kind;
|
|
103
|
+
const tsSpan = document.createElement("span");
|
|
104
|
+
tsSpan.className = "ts";
|
|
105
|
+
tsSpan.textContent = ts;
|
|
106
|
+
const kSpan = document.createElement("span");
|
|
107
|
+
kSpan.className = "kind";
|
|
108
|
+
kSpan.textContent = kind;
|
|
109
|
+
const mSpan = document.createElement("span");
|
|
110
|
+
mSpan.className = "msg";
|
|
111
|
+
mSpan.textContent = msg;
|
|
112
|
+
div.appendChild(tsSpan);
|
|
113
|
+
div.appendChild(kSpan);
|
|
114
|
+
div.appendChild(mSpan);
|
|
115
|
+
logEl.appendChild(div);
|
|
116
|
+
|
|
117
|
+
if (kind === "banner" && !sessionSet) {
|
|
118
|
+
const idMatch = msg.match(/Session started:\s*(\S+)/);
|
|
119
|
+
if (idMatch) sidEl.textContent = idMatch[1];
|
|
120
|
+
if (ts) startedEl.textContent = ts;
|
|
121
|
+
sessionSet = true;
|
|
122
|
+
}
|
|
123
|
+
count += 1;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function poll() {
|
|
127
|
+
try {
|
|
128
|
+
const res = await fetch(LOG_URL + "?t=" + Date.now(), { cache: "no-store" });
|
|
129
|
+
if (!res.ok) throw new Error("http " + res.status);
|
|
130
|
+
const text = await res.text();
|
|
131
|
+
if (text.length < lastLen) {
|
|
132
|
+
// File shrank — reset (new session).
|
|
133
|
+
logEl.innerHTML = "";
|
|
134
|
+
lastLen = 0;
|
|
135
|
+
count = 0;
|
|
136
|
+
sessionSet = false;
|
|
137
|
+
}
|
|
138
|
+
if (text.length > lastLen) {
|
|
139
|
+
const stick = isAtBottom();
|
|
140
|
+
const chunk = text.slice(lastLen);
|
|
141
|
+
lastLen = text.length;
|
|
142
|
+
const lines = chunk.split(/\r?\n/);
|
|
143
|
+
for (const line of lines) {
|
|
144
|
+
if (line.trim().length === 0) continue;
|
|
145
|
+
renderLine(line);
|
|
146
|
+
}
|
|
147
|
+
countEl.textContent = String(count);
|
|
148
|
+
updatedEl.textContent = new Date().toLocaleTimeString();
|
|
149
|
+
if (stick) logEl.scrollTop = logEl.scrollHeight;
|
|
150
|
+
}
|
|
151
|
+
bar.classList.remove("stalled");
|
|
152
|
+
statusEl.textContent = "";
|
|
153
|
+
} catch (e) {
|
|
154
|
+
bar.classList.add("stalled");
|
|
155
|
+
statusEl.textContent = "stalled — retrying";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
poll();
|
|
160
|
+
setInterval(poll, POLL_MS);
|
|
161
|
+
})();
|
|
162
|
+
</script>
|
|
163
|
+
</body>
|
|
164
|
+
</html>
|