compounding-loop 0.1.0 → 0.4.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/README.md +40 -6
- package/dist/cli/checkLanes.js +60 -0
- package/dist/cli/checkLanes.js.map +1 -0
- package/dist/cli/claim.js +84 -0
- package/dist/cli/claim.js.map +1 -0
- package/dist/cli/doctor.js +54 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.js +253 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.js +2 -1
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/laneRound.js +96 -0
- package/dist/cli/laneRound.js.map +1 -0
- package/dist/cli/laneStatus.js +54 -0
- package/dist/cli/laneStatus.js.map +1 -0
- package/dist/cli/lanesInit.js +56 -0
- package/dist/cli/lanesInit.js.map +1 -0
- package/dist/cli/lock.js +62 -0
- package/dist/cli/lock.js.map +1 -0
- package/dist/cli/loops.js +30 -3
- package/dist/cli/loops.js.map +1 -1
- package/dist/cli/new.js +5 -5
- package/dist/cli/new.js.map +1 -1
- package/dist/cli/next.js +18 -0
- package/dist/cli/next.js.map +1 -0
- package/dist/cli/pause.js +81 -0
- package/dist/cli/pause.js.map +1 -0
- package/dist/cli/run.js +119 -19
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/runs.js +83 -0
- package/dist/cli/runs.js.map +1 -0
- package/dist/cli/shipCheck.js +59 -0
- package/dist/cli/shipCheck.js.map +1 -0
- package/dist/core/doctor.js +49 -0
- package/dist/core/doctor.js.map +1 -0
- package/dist/core/laneRound.js +63 -0
- package/dist/core/laneRound.js.map +1 -0
- package/dist/core/laneStatus.js +82 -0
- package/dist/core/laneStatus.js.map +1 -0
- package/dist/core/lanes.js +149 -0
- package/dist/core/lanes.js.map +1 -0
- package/dist/core/lanesInit.js +143 -0
- package/dist/core/lanesInit.js.map +1 -0
- package/dist/core/next.js +34 -0
- package/dist/core/next.js.map +1 -0
- package/dist/core/parse/changelog.js +41 -0
- package/dist/core/parse/changelog.js.map +1 -0
- package/dist/core/parse/claim.js +24 -0
- package/dist/core/parse/claim.js.map +1 -0
- package/dist/core/parse/controlRoom.js +38 -0
- package/dist/core/parse/controlRoom.js.map +1 -0
- package/dist/core/parse/index.js +12 -0
- package/dist/core/parse/index.js.map +1 -0
- package/dist/core/parse/markers.js +20 -0
- package/dist/core/parse/markers.js.map +1 -0
- package/dist/core/parse/paused.js +26 -0
- package/dist/core/parse/paused.js.map +1 -0
- package/dist/core/parse/runs.js +132 -0
- package/dist/core/parse/runs.js.map +1 -0
- package/dist/core/parse/task.js +96 -0
- package/dist/core/parse/task.js.map +1 -0
- package/dist/core/repo.js +2 -7
- package/dist/core/repo.js.map +1 -1
- package/dist/core/runPrompt.js +9 -3
- package/dist/core/runPrompt.js.map +1 -1
- package/dist/core/shipCheck.js +45 -0
- package/dist/core/shipCheck.js.map +1 -0
- package/dist/core/table.js +22 -1
- package/dist/core/table.js.map +1 -1
- package/dist/server/checkLabels.js +27 -0
- package/dist/server/checkLabels.js.map +1 -0
- package/dist/server/controls.js +72 -0
- package/dist/server/controls.js.map +1 -0
- package/dist/server/data.js +28 -8
- package/dist/server/data.js.map +1 -1
- package/dist/server/events.js +1 -0
- package/dist/server/events.js.map +1 -1
- package/dist/server/health.js +47 -8
- package/dist/server/health.js.map +1 -1
- package/dist/server/index.js +29 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/laneChecks.js +25 -0
- package/dist/server/laneChecks.js.map +1 -0
- package/dist/server/live/build.js +163 -0
- package/dist/server/live/build.js.map +1 -0
- package/dist/server/live/github.js +54 -0
- package/dist/server/live/github.js.map +1 -0
- package/dist/server/live/index.js +9 -0
- package/dist/server/live/index.js.map +1 -0
- package/dist/server/live/merge.js +16 -0
- package/dist/server/live/merge.js.map +1 -0
- package/dist/server/live/urls.js +59 -0
- package/dist/server/live/urls.js.map +1 -0
- package/dist/server/loops.js +146 -39
- package/dist/server/loops.js.map +1 -1
- package/dist/server/roundState.js +27 -0
- package/dist/server/roundState.js.map +1 -0
- package/dist/server/runs.js +81 -0
- package/dist/server/runs.js.map +1 -0
- package/dist/server/static-export.js +22 -1
- package/dist/server/static-export.js.map +1 -1
- package/dist/server/taskBullets.js +33 -0
- package/dist/server/taskBullets.js.map +1 -0
- package/dist/server/this-repo.json +1574 -0
- package/dist/web/assets/index-BOPytDNU.js +53 -0
- package/dist/web/assets/index-BSHEI7fa.css +2 -0
- package/dist/web/index.html +3 -3
- package/method/COMPOUNDING_LOOP.md +55 -0
- package/method/example/K-0002.md +16 -0
- package/method/example/K-0003.md +16 -0
- package/method/retro.md +7 -0
- package/package.json +6 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/commands/loop-lanes.md +11 -0
- package/plugin/commands/loop-next.md +11 -0
- package/plugin/commands/loop-pause.md +10 -0
- package/runners/github-actions/lanes.yml +156 -0
- package/runners/local/README.md +4 -1
- package/runners/local/compounding-loop.service +20 -0
- package/runners/local/compounding-loop.timer +11 -0
- package/runners/local/launchd.plist +36 -0
- package/runners/routine/README.md +38 -1
- package/runners/routine/control-room-prompt.md +16 -0
- package/runners/routine/lane-prompt.md +12 -0
- package/runners/routine/prompt.md +3 -3
- package/templates/chrome-extension/extension/icons/icon128.png +0 -0
- package/templates/chrome-extension/extension/icons/icon16.png +0 -0
- package/templates/chrome-extension/extension/icons/icon48.png +0 -0
- package/templates/chrome-extension/extension/manifest.json +19 -3
- package/templates/chrome-extension/gitignore +1 -1
- package/templates/lanes/README.md +23 -0
- package/templates/lanes/ci-check-lanes.yml +38 -0
- package/templates/lanes/claude-section.md +20 -0
- package/templates/lanes/control-room.md +3 -0
- package/templates/lanes/outbox.md +5 -0
- package/templates/lanes/task.md +36 -0
- package/templates/static-site/.ai/loop-prompt.md +2 -2
- package/templates/static-site/CLAUDE.md +2 -0
- package/templates/static-site/gitignore +1 -1
- package/dist/web/assets/index-BDdtnG33.js +0 -49
- package/dist/web/assets/index-D3-N8DN4.css +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../../src/server/live/github.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Live GitHub data path for the static dashboard. Everything here is pure and browser-safe (no `node:` imports).
|
|
2
|
+
export { buildLiveLoop } from './build.js';
|
|
3
|
+
export { latestFinishedRun, mapCommits, mapRuns } from './github.js';
|
|
4
|
+
export { mergeCommits, snapshotSince } from './merge.js';
|
|
5
|
+
export { BASE_FILES, COMMITS_PAGE_SIZE, GITHUB_API, GITHUB_RAW, MAX_ROUND_FILES, commitsSinceUrl, countRoundFiles, hasMoreCommits, isRepoSlug, laneFilePaths, liveUrls, rawUrl } from './urls.js';
|
|
6
|
+
export { checkLabel, formatUtc, withLabel } from '../checkLabels.js';
|
|
7
|
+
export { buildRuns, lastRunOf, sessionKey, silentLanesItem } from '../runs.js';
|
|
8
|
+
export { briefFitsUrl, MAX_URL_LENGTH, deleteFileUrl, editFileUrl, latestReleaseRunApiUrl, newFileUrl, nextRoundUrl, pauseUrl, releaseWorkflowUrl, resumeUrl, tagLookupApiUrl, tagsApiUrl, NEXT_ROUND_FILE, RELEASE_WORKFLOW } from '../controls.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/live/index.ts"],"names":[],"mappings":"AAAA,iHAAiH;AACjH,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAErE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAElM,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,sBAAsB,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** The commit date to pass as `since` for the live fetch: the newest commit in the snapshot. */
|
|
2
|
+
export function snapshotSince(snapshot) {
|
|
3
|
+
return snapshot.reduce((newest, c) => (newest === undefined || c.at > newest ? c.at : newest), undefined);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Newest first, one entry per commit. A commit in both lists keeps the live copy (it is the fresher read) and only
|
|
7
|
+
* the fields shown to people.
|
|
8
|
+
*/
|
|
9
|
+
export function mergeCommits(snapshot, live) {
|
|
10
|
+
const bySha = new Map();
|
|
11
|
+
for (const c of [...snapshot, ...live])
|
|
12
|
+
bySha.set(c.sha, { sha: c.sha, at: c.at, message: c.message, ...(c.author && { author: c.author }), ...(c.lane && { lane: c.lane }), ...(c.session && { session: c.session }) });
|
|
13
|
+
// Array.prototype.sort is stable, so equal timestamps keep their insertion order.
|
|
14
|
+
return [...bySha.values()].sort((a, b) => (a.at < b.at ? 1 : a.at > b.at ? -1 : 0));
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../../../src/server/live/merge.ts"],"names":[],"mappings":"AAGA,gGAAgG;AAChG,MAAM,UAAU,aAAa,CAAC,QAA+B;IAC3D,OAAO,QAAQ,CAAC,MAAM,CAAqB,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC;AAChI,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAA+B,EAAE,IAA2B;IACvF,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IACzN,kFAAkF;IAClF,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtF,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Pure (no Node imports): which GitHub URLs the browser must fetch to build a live loop.
|
|
2
|
+
export const GITHUB_API = 'https://api.github.com';
|
|
3
|
+
export const GITHUB_RAW = 'https://raw.githubusercontent.com';
|
|
4
|
+
/** `.ai/done-vN.md` files probed to count finished rounds (a missing file is a 404, which is fine). */
|
|
5
|
+
export const MAX_ROUND_FILES = 9;
|
|
6
|
+
const SLUG = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
7
|
+
export function isRepoSlug(slug) {
|
|
8
|
+
return SLUG.test(slug) && !slug.split('/').some((part) => part === '.' || part === '..');
|
|
9
|
+
}
|
|
10
|
+
/** Repo-relative paths of the files every loop has, laned or not. */
|
|
11
|
+
export const BASE_FILES = [
|
|
12
|
+
'.ai/task.md',
|
|
13
|
+
'.ai/lanes.json',
|
|
14
|
+
'package.json',
|
|
15
|
+
'.ai/PAUSED',
|
|
16
|
+
'.ai/control-room.md',
|
|
17
|
+
'CHANGELOG.md',
|
|
18
|
+
'DONE.md',
|
|
19
|
+
'BLOCKED.md',
|
|
20
|
+
...Array.from({ length: MAX_ROUND_FILES }, (_, i) => `.ai/done-v${i + 1}.md`),
|
|
21
|
+
];
|
|
22
|
+
/** Per-lane files, once `.ai/lanes.json` is known. */
|
|
23
|
+
export function laneFilePaths(lanes) {
|
|
24
|
+
return lanes.flatMap((lane) => ['task.md', 'outbox.md', 'DONE.md', 'BLOCKED.md', 'claim', 'runs.jsonl'].map((f) => `.ai/lanes/${lane}/${f}`));
|
|
25
|
+
}
|
|
26
|
+
export function rawUrl(slug, file, branch = 'main') {
|
|
27
|
+
return `${GITHUB_RAW}/${slug}/${branch}/${file}`;
|
|
28
|
+
}
|
|
29
|
+
/** Everything the browser fetches for `slug`; throws on anything that is not `owner/name`. */
|
|
30
|
+
export function liveUrls(slug, branch = 'main') {
|
|
31
|
+
if (!isRepoSlug(slug))
|
|
32
|
+
throw new Error(`not a GitHub repo slug: ${slug}`);
|
|
33
|
+
return {
|
|
34
|
+
commits: `${GITHUB_API}/repos/${slug}/commits?sha=${encodeURIComponent(branch)}&per_page=100`,
|
|
35
|
+
runs: `${GITHUB_API}/repos/${slug}/actions/runs?branch=${encodeURIComponent(branch)}&per_page=20`,
|
|
36
|
+
files: Object.fromEntries(BASE_FILES.map((file) => [file, rawUrl(slug, file, branch)])),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** GitHub's page size for the commits endpoint; a full page means there may be another. */
|
|
40
|
+
export const COMMITS_PAGE_SIZE = 100;
|
|
41
|
+
/**
|
|
42
|
+
* Commits newer than the snapshot: `since` is the snapshot head's commit date (ISO 8601). GitHub treats it as
|
|
43
|
+
* inclusive, which is fine because `mergeCommits` drops the duplicate. Page 1 is the same call as `liveUrls().commits`
|
|
44
|
+
* plus `since`; keep requesting the next page while `hasMoreCommits` says the last one was full.
|
|
45
|
+
*/
|
|
46
|
+
export function commitsSinceUrl(slug, since, page = 1, branch = 'main') {
|
|
47
|
+
if (!isRepoSlug(slug))
|
|
48
|
+
throw new Error(`not a GitHub repo slug: ${slug}`);
|
|
49
|
+
if (Number.isNaN(Date.parse(since)))
|
|
50
|
+
throw new Error(`not a date: ${since}`);
|
|
51
|
+
const query = `sha=${encodeURIComponent(branch)}&since=${encodeURIComponent(new Date(since).toISOString())}&per_page=${COMMITS_PAGE_SIZE}&page=${page}`;
|
|
52
|
+
return `${GITHUB_API}/repos/${slug}/commits?${query}`;
|
|
53
|
+
}
|
|
54
|
+
export const hasMoreCommits = (pageLength) => pageLength >= COMMITS_PAGE_SIZE;
|
|
55
|
+
/** Number of `.ai/done-vN.md` files present, counted from the fetched files. */
|
|
56
|
+
export function countRoundFiles(files) {
|
|
57
|
+
return Array.from({ length: MAX_ROUND_FILES }, (_, i) => files[`.ai/done-v${i + 1}.md`]).filter((t) => t !== undefined).length;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=urls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"urls.js","sourceRoot":"","sources":["../../../src/server/live/urls.ts"],"names":[],"mappings":"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"}
|
package/dist/server/loops.js
CHANGED
|
@@ -1,84 +1,131 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
|
-
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
2
|
+
import { readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { promisify } from 'node:util';
|
|
5
5
|
import { runAnswer } from '../cli/loops.js';
|
|
6
|
+
import { runDoctor } from '../cli/doctor.js';
|
|
7
|
+
import { runLaneRound } from '../cli/laneRound.js';
|
|
8
|
+
import { runPause, runResume } from '../cli/pause.js';
|
|
9
|
+
import { readLaneStatuses } from '../cli/laneStatus.js';
|
|
10
|
+
import { parseLanesConfig } from '../core/lanes.js';
|
|
6
11
|
import { parseBlocked } from '../core/blocked.js';
|
|
7
12
|
import { readLoopFacts, readRunCounter } from '../core/repo.js';
|
|
8
13
|
import { deriveStatus } from '../core/status.js';
|
|
14
|
+
import { packageVersion, releaseInboxItem, roundState } from './roundState.js';
|
|
15
|
+
import { commitLane, latestControlNotes, parseChangelog, parsePaused } from '../core/parse/index.js';
|
|
16
|
+
import { shipCheck } from '../core/shipCheck.js';
|
|
17
|
+
import { buildRuns, lastRunOf, sessionKey, silentLanesItem } from './runs.js';
|
|
9
18
|
import { checkLoop } from './health.js';
|
|
19
|
+
import { bulletsUnder, contractFromTask, laneNotesFromTask } from './taskBullets.js';
|
|
20
|
+
export { bulletsUnder, contractFromTask };
|
|
10
21
|
import { sortByAttention, toSummary } from './data.js';
|
|
11
22
|
const execFileAsync = promisify(execFile);
|
|
12
23
|
async function readOptional(file) {
|
|
13
24
|
return readFile(file, 'utf8').catch(() => undefined);
|
|
14
25
|
}
|
|
15
|
-
/** Recent commits of a clone, newest first. Empty when the folder is not a repo. */
|
|
16
|
-
export async function readTimeline(dir, limit = 50) {
|
|
26
|
+
/** Recent commits of a clone, newest first. Empty when the folder is not a repo. `laneNames` lets the subject prefix name a lane. */
|
|
27
|
+
export async function readTimeline(dir, limit = 50, laneNames = []) {
|
|
17
28
|
try {
|
|
18
|
-
const { stdout } = await execFileAsync('git', ['log', `-${limit}`, '--format=%h%
|
|
29
|
+
const { stdout } = await execFileAsync('git', ['log', `-${limit}`, '--format=%h%x1f%cI%x1f%an%x1f%s%x1f%b%x1e'], {
|
|
19
30
|
cwd: dir,
|
|
20
31
|
env: { ...process.env, GIT_CEILING_DIRECTORIES: path.dirname(path.resolve(dir)) },
|
|
21
32
|
});
|
|
22
33
|
return stdout
|
|
23
|
-
.split('\
|
|
34
|
+
.split('\x1e')
|
|
35
|
+
.map((record) => record.trim())
|
|
24
36
|
.filter(Boolean)
|
|
25
|
-
.map((
|
|
26
|
-
const [sha = '', at = '',
|
|
27
|
-
|
|
37
|
+
.map((record) => {
|
|
38
|
+
const [sha = '', at = '', name = '', subject = '', body = ''] = record.split('\x1f');
|
|
39
|
+
const author = name.trim();
|
|
40
|
+
const session = /^Claude-Session:\s*(\S+)\s*$/m.exec(body)?.[1];
|
|
41
|
+
return {
|
|
42
|
+
sha,
|
|
43
|
+
at: new Date(at).toISOString(),
|
|
44
|
+
message: subject,
|
|
45
|
+
// A name that is an email address is not a display name.
|
|
46
|
+
...(author && !author.includes('@') && { author }),
|
|
47
|
+
lane: commitLane(`${subject}\n\n${body}`, laneNames),
|
|
48
|
+
...(session && { session: sessionKey(session) }),
|
|
49
|
+
};
|
|
28
50
|
});
|
|
29
51
|
}
|
|
30
52
|
catch {
|
|
31
53
|
return [];
|
|
32
54
|
}
|
|
33
55
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const bullet = /^\s*[-*]\s+(.*\S)\s*$/.exec(line);
|
|
45
|
-
if (bullet?.[1])
|
|
46
|
-
bullets.push(bullet[1]);
|
|
47
|
-
else if (bullets.length > 0 && line.trim() === '')
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
return bullets;
|
|
56
|
+
function pausedInfo(text) {
|
|
57
|
+
const { by, at, reason } = parsePaused(text);
|
|
58
|
+
return { by, ...(at && { at: at.toISOString() }), ...(reason && { reason }) };
|
|
59
|
+
}
|
|
60
|
+
async function readRuns(dir, lanes, lanesJson, timeline, now) {
|
|
61
|
+
const cron = new Map((parseLanesConfig(lanesJson ?? '').config?.lanes ?? []).map((l) => [l.name, l.cron]));
|
|
62
|
+
const pausedText = await readOptional(path.join(dir, '.ai', 'PAUSED'));
|
|
63
|
+
const laneNames = lanes.map((l) => l.name);
|
|
64
|
+
const logs = Object.fromEntries(await Promise.all(laneNames.map(async (n) => [n, await readOptional(path.join(dir, '.ai', 'lanes', n, 'runs.jsonl'))])));
|
|
65
|
+
return buildRuns({ lanes: laneNames.map((name) => ({ name, ...(cron.get(name) && { cron: cron.get(name) }), stopped: ['done', 'blocked'].includes(lanes.find((l) => l.name === name)?.state ?? '') || lanes.find((l) => l.name === name)?.claim !== undefined })), logs, commits: timeline, now, ...(pausedText !== undefined && { paused: parsePaused(pausedText).at ? { at: parsePaused(pausedText).at } : {} }) });
|
|
51
66
|
}
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
|
|
67
|
+
/** Local git facts for the ship-check; a failing git call reads as the safe answer (tag free, tree dirty). */
|
|
68
|
+
async function readShipGit(dir, tag) {
|
|
69
|
+
const git = (args) => execFileAsync('git', args, { cwd: dir, timeout: 15_000 }).then((r) => r.stdout, () => undefined);
|
|
70
|
+
const [tags, status] = await Promise.all([git(['tag', '--list', tag]), git(['status', '--porcelain'])]);
|
|
71
|
+
return { tagExists: (tags ?? '').split('\n').some((t) => t.trim() === tag), treeClean: status !== undefined && status.trim() === '' };
|
|
55
72
|
}
|
|
56
73
|
/** Reads one clone into the API's LoopDetail. `now` is a parameter so tests are deterministic. */
|
|
57
74
|
export async function readLoop(dir, now = new Date()) {
|
|
58
|
-
const
|
|
75
|
+
const lanes = await readLaneStatuses(dir, now);
|
|
76
|
+
const laneNames = lanes?.map((l) => l.name) ?? [];
|
|
77
|
+
const [baseFacts, counter, timeline, taskText, packageText, lanesJson] = await Promise.all([
|
|
59
78
|
readLoopFacts(dir),
|
|
60
79
|
readRunCounter(dir),
|
|
61
|
-
readTimeline(dir),
|
|
80
|
+
readTimeline(dir, 300, laneNames),
|
|
62
81
|
readOptional(path.join(dir, '.ai', 'task.md')),
|
|
82
|
+
readOptional(path.join(dir, 'package.json')),
|
|
83
|
+
readOptional(path.join(dir, '.ai', 'lanes.json')),
|
|
63
84
|
]);
|
|
85
|
+
const [changelogText, controlText, pausedText] = await Promise.all(['CHANGELOG.md', path.join('.ai', 'control-room.md'), path.join('.ai', 'PAUSED')].map((f) => readOptional(path.join(dir, f))));
|
|
86
|
+
const notes = controlText === undefined || !lanes ? [] : Object.entries(latestControlNotes(controlText, laneNames)).map(([lane, n]) => ({ lane, at: n.at, text: n.text }));
|
|
87
|
+
const runsData = lanes && (await readRuns(dir, lanes, lanesJson, timeline, now));
|
|
64
88
|
// Any failing health check turns the loop "failing" with that check's reason (IDEA.md status rule 5).
|
|
65
|
-
const checks = await checkLoop(dir, baseFacts, now);
|
|
89
|
+
const checks = await checkLoop(dir, baseFacts, now, undefined, lanes);
|
|
66
90
|
const facts = { ...baseFacts, healthFailures: checks.filter((c) => !c.ok).map((c) => c.reason) };
|
|
67
91
|
const status = deriveStatus(facts, now);
|
|
68
92
|
const round = facts.roundsDone + 1;
|
|
69
93
|
const task = taskText ?? '';
|
|
94
|
+
const version = packageVersion(packageText);
|
|
95
|
+
const { roundLabel, releaseWaiting } = roundState({ round, hasDone: facts.hasDone, laned: lanes !== undefined, ...(version && { version }) });
|
|
96
|
+
const doneText = lanes && version ? await readOptional(path.join(dir, 'DONE.md')) : undefined;
|
|
97
|
+
const newestTests = runsData?.runs.find((r) => r.tests && r.endedAt);
|
|
98
|
+
const ship = doneText !== undefined && version
|
|
99
|
+
? shipCheck({
|
|
100
|
+
version,
|
|
101
|
+
doneText,
|
|
102
|
+
...(changelogText !== undefined && { changelogText }),
|
|
103
|
+
...(await readShipGit(dir, `v${version}`)),
|
|
104
|
+
...(newestTests?.tests && { tests: { ...newestTests.tests, ...(newestTests.endedAt && { at: newestTests.endedAt }) } }),
|
|
105
|
+
})
|
|
106
|
+
: undefined;
|
|
70
107
|
return {
|
|
71
108
|
id: path.basename(dir),
|
|
72
109
|
name: path.basename(dir),
|
|
73
110
|
state: status.state,
|
|
74
|
-
reason: status.reason,
|
|
111
|
+
reason: releaseWaiting && status.state === 'done' ? `${roundLabel} · release waiting on you` : status.reason,
|
|
112
|
+
roundLabel,
|
|
113
|
+
...(pausedText !== undefined && { paused: pausedInfo(pausedText) }),
|
|
114
|
+
...(notes.length > 0 && { controlNotes: notes }),
|
|
115
|
+
...(changelogText !== undefined && { releases: parseChangelog(changelogText) }),
|
|
116
|
+
...(runsData && lastRunOf(runsData.runs) && { lastRun: lastRunOf(runsData.runs) }),
|
|
117
|
+
...(runsData && { runs: runsData.runs, nextRuns: runsData.nextRuns }),
|
|
118
|
+
...(ship && { ship }),
|
|
119
|
+
...(releaseWaiting && { releaseWaiting }),
|
|
75
120
|
round,
|
|
76
121
|
// The total is unknown for a real loop (the owner can always add a round), so it never reads "5 of 3".
|
|
77
122
|
roundsTotal: round,
|
|
78
123
|
run: counter?.run ?? 0,
|
|
79
124
|
runLimit: counter?.limit ?? 0,
|
|
80
125
|
...(timeline[0] && { lastCommit: timeline[0] }),
|
|
126
|
+
...(lanes && { lanes }),
|
|
81
127
|
timeline,
|
|
128
|
+
...(lanes && { laneNotes: await Promise.all(lanes.map(async ({ name }) => laneNotesFromTask(name, (await readOptional(path.join(dir, '.ai', 'lanes', name, 'task.md'))) ?? '', facts.hasDone || (await readOptional(path.join(dir, '.ai', 'lanes', name, 'DONE.md'))) !== undefined))) }),
|
|
82
129
|
contract: contractFromTask(task, facts.hasDone),
|
|
83
130
|
knowledge: bulletsUnder(task, /^confirmed/i),
|
|
84
131
|
decisions: bulletsUnder(task, /^decisions/i),
|
|
@@ -98,6 +145,21 @@ export async function findLoopDirs(projectsDir) {
|
|
|
98
145
|
}
|
|
99
146
|
return dirs.sort();
|
|
100
147
|
}
|
|
148
|
+
async function readReleaseItem(dir) {
|
|
149
|
+
const file = path.join(dir, 'DONE.md');
|
|
150
|
+
const [info, laned] = await Promise.all([stat(file).catch(() => undefined), stat(path.join(dir, '.ai', 'lanes.json')).catch(() => undefined)]);
|
|
151
|
+
if (!info || !laned)
|
|
152
|
+
return undefined;
|
|
153
|
+
return releaseInboxItem(path.basename(dir), packageVersion(await readOptional(path.join(dir, 'package.json'))), info.mtime.toISOString());
|
|
154
|
+
}
|
|
155
|
+
async function readSilentItem(dir, now) {
|
|
156
|
+
const lanes = await readLaneStatuses(dir, now);
|
|
157
|
+
if (!lanes)
|
|
158
|
+
return undefined;
|
|
159
|
+
const names = lanes.map((l) => l.name);
|
|
160
|
+
const runs = await readRuns(dir, lanes, await readOptional(path.join(dir, '.ai', 'lanes.json')), await readTimeline(dir, 300, names), now);
|
|
161
|
+
return silentLanesItem(path.basename(dir), runs.silentLanes, now.toISOString());
|
|
162
|
+
}
|
|
101
163
|
async function readInboxItem(dir) {
|
|
102
164
|
const file = path.join(dir, 'BLOCKED.md');
|
|
103
165
|
const [text, info] = await Promise.all([readOptional(file), stat(file).catch(() => undefined)]);
|
|
@@ -106,22 +168,39 @@ async function readInboxItem(dir) {
|
|
|
106
168
|
const note = parseBlocked(text);
|
|
107
169
|
return { loopId: path.basename(dir), file: 'BLOCKED.md', question: note.question, bestGuess: note.bestGuess, since: info.mtime.toISOString() };
|
|
108
170
|
}
|
|
171
|
+
/** Best effort: a clone without a remote (or offline) keeps the commit and the loop pulls it later. */
|
|
172
|
+
const pushQuietly = (dir) => execFileAsync('git', ['push'], { cwd: dir, timeout: 30_000, env: { ...process.env, GIT_TERMINAL_PROMPT: '0' } }).then(() => true, () => false);
|
|
109
173
|
/** A DataSource over a folder of loop clones, re-read on every call so the dashboard is never stale. */
|
|
110
174
|
export function projectsSource(projectsDir, now = () => new Date()) {
|
|
111
175
|
const all = async () => Promise.all((await findLoopDirs(projectsDir)).map((dir) => readLoop(dir, now())));
|
|
176
|
+
// Match the id against folder names; never build a path from it.
|
|
177
|
+
// One git-changing action per clone at a time: a double click must not race on index.lock.
|
|
178
|
+
const busy = new Set();
|
|
179
|
+
const exclusive = async (dir, run) => {
|
|
180
|
+
if (busy.has(dir))
|
|
181
|
+
throw new Error('Another action on this loop is still running; try again in a moment.');
|
|
182
|
+
busy.add(dir);
|
|
183
|
+
try {
|
|
184
|
+
return await run();
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
busy.delete(dir);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
const loopDir = async (id) => (await findLoopDirs(projectsDir)).find((d) => path.basename(d) === id);
|
|
112
191
|
return {
|
|
113
192
|
loops: async () => sortByAttention((await all()).map(toSummary)),
|
|
114
|
-
// Match the id against folder names; never build a path from it.
|
|
115
193
|
loop: async (id) => {
|
|
116
|
-
const dir =
|
|
194
|
+
const dir = await loopDir(id);
|
|
117
195
|
return dir === undefined ? undefined : readLoop(dir, now());
|
|
118
196
|
},
|
|
119
197
|
inbox: async () => {
|
|
120
|
-
const
|
|
198
|
+
const dirs = await findLoopDirs(projectsDir);
|
|
199
|
+
const items = [...(await Promise.all(dirs.map(readInboxItem))), ...(await Promise.all(dirs.map(readReleaseItem))), ...(await Promise.all(dirs.map((d) => readSilentItem(d, now()))))];
|
|
121
200
|
return items.filter((i) => i !== undefined).sort((a, b) => a.since.localeCompare(b.since));
|
|
122
201
|
},
|
|
123
202
|
answer: async (id, answer) => {
|
|
124
|
-
const dir =
|
|
203
|
+
const dir = await loopDir(id);
|
|
125
204
|
if (dir === undefined)
|
|
126
205
|
return undefined;
|
|
127
206
|
// runAnswer reads the exact word "accept" as "use the best guess"; the web form sends literal text only.
|
|
@@ -129,12 +208,40 @@ export function projectsSource(projectsDir, now = () => new Date()) {
|
|
|
129
208
|
await runAnswer(dir, text === 'accept' ? 'Accept.' : text, { dryRun: false, now: now() });
|
|
130
209
|
const commit = (await execFileAsync('git', ['rev-parse', '--short', 'HEAD'], { cwd: dir })).stdout.trim();
|
|
131
210
|
// Best effort: a clone without a remote (or offline) keeps the commit and the loop pulls it later.
|
|
132
|
-
|
|
133
|
-
|
|
211
|
+
return { commit, pushed: await pushQuietly(dir) };
|
|
212
|
+
},
|
|
213
|
+
control: {
|
|
214
|
+
pause: async (id, reason) => {
|
|
215
|
+
const dir = await loopDir(id);
|
|
216
|
+
if (dir === undefined)
|
|
217
|
+
return undefined;
|
|
218
|
+
const { changed, by, pushed, pushError } = await exclusive(dir, () => runPause(dir, reason === undefined ? {} : { reason }));
|
|
219
|
+
return { changed, ...(by !== undefined && { by }), ...(pushed !== undefined && { pushed }), ...(pushError !== undefined && { pushError }) };
|
|
220
|
+
},
|
|
221
|
+
resume: async (id) => {
|
|
222
|
+
const dir = await loopDir(id);
|
|
223
|
+
if (dir === undefined)
|
|
224
|
+
return undefined;
|
|
225
|
+
const { changed, pushed, pushError } = await exclusive(dir, () => runResume(dir));
|
|
226
|
+
return { changed, ...(pushed !== undefined && { pushed }), ...(pushError !== undefined && { pushError }) };
|
|
227
|
+
},
|
|
228
|
+
nextRound: async (id, brief) => {
|
|
229
|
+
const dir = await loopDir(id);
|
|
230
|
+
if (dir === undefined)
|
|
231
|
+
return undefined;
|
|
232
|
+
// The brief lands where the web buttons put it; runLaneRound archives and deletes it.
|
|
233
|
+
const briefFile = path.join(dir, '.ai', 'next-round.md');
|
|
234
|
+
await writeFile(briefFile, brief.endsWith('\n') ? brief : `${brief}\n`);
|
|
235
|
+
// A failed round must not leave the brief behind for a lane's `git add -A`.
|
|
236
|
+
const { committed } = await exclusive(dir, () => runLaneRound(dir, { briefFile, dryRun: false, commit: true, now: now() })).finally(() => rm(briefFile, { force: true }));
|
|
237
|
+
const pushed = committed ? await pushQuietly(dir) : undefined;
|
|
238
|
+
return { changed: true, committed, ...(pushed !== undefined && { pushed }) };
|
|
239
|
+
},
|
|
134
240
|
},
|
|
241
|
+
doctor: () => runDoctor(),
|
|
135
242
|
checks: async () => {
|
|
136
243
|
const dirs = await findLoopDirs(projectsDir);
|
|
137
|
-
const perLoop = await Promise.all(dirs.map(async (dir) => checkLoop(dir, await readLoopFacts(dir), now())));
|
|
244
|
+
const perLoop = await Promise.all(dirs.map(async (dir) => checkLoop(dir, await readLoopFacts(dir), now(), undefined, await readLaneStatuses(dir, now()))));
|
|
138
245
|
return perLoop.flat();
|
|
139
246
|
},
|
|
140
247
|
};
|
package/dist/server/loops.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loops.js","sourceRoot":"","sources":["../../src/server/loops.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"loops.js","sourceRoot":"","sources":["../../src/server/loops.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** `version` of a package.json text; undefined when the text is not JSON or has no string version. */
|
|
2
|
+
export function packageVersion(packageJson) {
|
|
3
|
+
if (packageJson === undefined)
|
|
4
|
+
return undefined;
|
|
5
|
+
try {
|
|
6
|
+
const version = JSON.parse(packageJson).version;
|
|
7
|
+
return typeof version === 'string' && version !== '' ? version : undefined;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function roundState({ round, hasDone, laned, version }) {
|
|
14
|
+
return { roundLabel: hasDone ? `Round ${round} done${version ? ` · ${version}` : ''}` : `Round ${round}`, releaseWaiting: hasDone && laned };
|
|
15
|
+
}
|
|
16
|
+
/** The attention-list entry for a waiting release; `kind: 'release'` tells the UI not to offer an answer form. */
|
|
17
|
+
export function releaseInboxItem(loopId, version, since) {
|
|
18
|
+
return {
|
|
19
|
+
loopId,
|
|
20
|
+
file: 'DONE.md',
|
|
21
|
+
kind: 'release',
|
|
22
|
+
question: version ? `Release ${version} is waiting on you` : 'A release is waiting on you',
|
|
23
|
+
bestGuess: 'Check "Ready to ship", then run the Release workflow',
|
|
24
|
+
since,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=roundState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roundState.js","sourceRoot":"","sources":["../../src/server/roundState.ts"],"names":[],"mappings":"AAqBA,sGAAsG;AACtG,MAAM,UAAU,cAAc,CAAC,WAA+B;IAC5D,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAChD,IAAI,CAAC;QACH,MAAM,OAAO,GAAI,IAAI,CAAC,KAAK,CAAC,WAAW,CAA2B,CAAC,OAAO,CAAC;QAC3E,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAc;IACvE,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,KAAK,QAAQ,OAAO,CAAC,CAAC,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,EAAE,EAAE,cAAc,EAAE,OAAO,IAAI,KAAK,EAAE,CAAC;AAC/I,CAAC;AAED,kHAAkH;AAClH,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,OAA2B,EAAE,KAAa;IACzF,OAAO;QACL,MAAM;QACN,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,WAAW,OAAO,oBAAoB,CAAC,CAAC,CAAC,6BAA6B;QAC1F,SAAS,EAAE,sDAAsD;QACjE,KAAK;KACN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Pure (no Node imports): turns the per-lane run logs, the cron slots and the commits into the Runs tab's data.
|
|
2
|
+
import { expectedSlots, matchSlots, missedInARow, parseRunLog } from '../core/parse/index.js';
|
|
3
|
+
/** How far back the Runs tab looks. */
|
|
4
|
+
export const RUNS_WINDOW_HOURS = 48;
|
|
5
|
+
/** A slot this young may still be running (the log line is written at the end), so it is not "missing" yet. */
|
|
6
|
+
export const SLOT_GRACE_MINUTES = 90;
|
|
7
|
+
/** Missing runs in a row that raise the "lanes are not running" attention item. */
|
|
8
|
+
export const SILENT_STREAK = 3;
|
|
9
|
+
const HOUR = 3_600_000;
|
|
10
|
+
const MINUTE = 60_000;
|
|
11
|
+
function laneRuns(lane, input) {
|
|
12
|
+
const from = new Date(input.now.getTime() - RUNS_WINDOW_HOURS * HOUR);
|
|
13
|
+
const everLogged = parseRunLog(input.logs[lane.name] ?? '');
|
|
14
|
+
const entries = everLogged.filter((e) => new Date(e.at) > from);
|
|
15
|
+
const own = input.commits.filter((c) => c.lane === lane.name);
|
|
16
|
+
const slots = lane.cron ? expectedSlots(lane.cron, from, input.now) : [];
|
|
17
|
+
const next = lane.cron ? expectedSlots(lane.cron, input.now, new Date(input.now.getTime() + 7 * 24 * HOUR))[0]?.toISOString() : undefined;
|
|
18
|
+
const results = slots.length > 0 ? matchSlots(slots, entries, own.map((c) => new Date(c.at))) : [];
|
|
19
|
+
// Entries that answered no slot (a manual run, or a lane without cron) are runs too.
|
|
20
|
+
const claimed = new Set(results.flatMap((r) => (r.entry ? [r.entry.at] : [])));
|
|
21
|
+
const manual = entries.filter((e) => !claimed.has(e.at)).map((entry) => ({ slot: new Date(entry.at).toISOString(), entry, outcome: outcomeOf(entry) }));
|
|
22
|
+
const graceEnd = input.now.getTime() - SLOT_GRACE_MINUTES * MINUTE;
|
|
23
|
+
const pausedFrom = input.paused ? (input.paused.at?.getTime() ?? -Infinity) : Infinity;
|
|
24
|
+
const settled = results.filter((r) => r.outcome !== 'no-run-recorded' || (new Date(r.slot).getTime() <= graceEnd && new Date(r.slot).getTime() < pausedFrom));
|
|
25
|
+
const all = [...settled, ...manual].sort((a, b) => a.slot.localeCompare(b.slot));
|
|
26
|
+
const loggedSessions = new Set(entries.flatMap((e) => (e.session ? [sessionKey(e.session)] : [])));
|
|
27
|
+
const runs = all.map((r, i) => {
|
|
28
|
+
const windowStart = new Date(r.slot).getTime();
|
|
29
|
+
const windowEnd = all[i + 1] ? new Date(all[i + 1].slot).getTime() : Infinity;
|
|
30
|
+
const session = r.entry?.session === undefined ? undefined : sessionKey(r.entry.session);
|
|
31
|
+
const inWindow = (c) => at(c) >= windowStart && at(c) < windowEnd;
|
|
32
|
+
// A commit of this run's session belongs to it; a commit of another logged session belongs to that run; the rest go by time.
|
|
33
|
+
const belongs = (c) => (c.session !== undefined && c.session === session) || (inWindow(c) && !(c.session !== undefined && loggedSessions.has(c.session)));
|
|
34
|
+
const commits = own.filter(belongs);
|
|
35
|
+
return {
|
|
36
|
+
lane: lane.name,
|
|
37
|
+
at: new Date(r.slot).toISOString(),
|
|
38
|
+
outcome: r.outcome,
|
|
39
|
+
...(r.entry && { endedAt: new Date(r.entry.at).toISOString() }),
|
|
40
|
+
units: r.entry?.units ?? [],
|
|
41
|
+
...(r.entry?.tests && { tests: r.entry.tests }),
|
|
42
|
+
commits,
|
|
43
|
+
...(r.entry?.note && { note: r.entry.note }),
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
return { runs, ...(next && { next }), // A lane that never logged a run or made a commit is new, not silent.
|
|
47
|
+
missed: !lane.stopped && !input.paused && (everLogged.length > 0 || own.length > 0) ? missedInARow(settled) : 0 };
|
|
48
|
+
}
|
|
49
|
+
const at = (c) => new Date(c.at).getTime();
|
|
50
|
+
const outcomeOf = (e) => (e.tests && e.tests.failed > 0 ? 'tests-failed' : e.pushed ? 'pushed' : 'nothing-pushed');
|
|
51
|
+
/** The last 8 characters of a session id or `Claude-Session:` URL, the key commits and run lines share. */
|
|
52
|
+
export function sessionKey(idOrUrl) {
|
|
53
|
+
return idOrUrl.trim().replace(/\/+$/, '').slice(-8);
|
|
54
|
+
}
|
|
55
|
+
export function buildRuns(input) {
|
|
56
|
+
const perLane = input.lanes.map((lane) => ({ lane, ...laneRuns(lane, input) }));
|
|
57
|
+
return {
|
|
58
|
+
runs: perLane.flatMap((l) => l.runs).sort((a, b) => b.at.localeCompare(a.at)),
|
|
59
|
+
nextRuns: perLane.flatMap((l) => (l.next ? [{ lane: l.lane.name, at: l.next }] : [])),
|
|
60
|
+
silentLanes: perLane.filter((l) => l.missed >= SILENT_STREAK).map((l) => ({ lane: l.lane.name, missed: l.missed })),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** The attention item for lanes that stopped running, or none. */
|
|
64
|
+
export function silentLanesItem(loopId, silent, since) {
|
|
65
|
+
if (silent.length === 0)
|
|
66
|
+
return undefined;
|
|
67
|
+
return {
|
|
68
|
+
loopId,
|
|
69
|
+
file: '.ai/lanes.json',
|
|
70
|
+
kind: 'lanes-not-running',
|
|
71
|
+
question: `Lanes are not running: ${silent.map((s) => `${s.lane} (${s.missed} missed)`).join(', ')}. Check your plan's usage limit.`,
|
|
72
|
+
bestGuess: 'Wait for the limit to reset, or run a lane by hand',
|
|
73
|
+
since,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** The newest run of any lane, for the fleet card. Missing runs count: they are what the owner needs to see. */
|
|
77
|
+
export function lastRunOf(runs) {
|
|
78
|
+
const newest = runs[0];
|
|
79
|
+
return newest && { lane: newest.lane, at: newest.at, outcome: newest.outcome };
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=runs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runs.js","sourceRoot":"","sources":["../../src/server/runs.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { DEFAULT_RUNNER } from './settings.js';
|
|
4
|
+
import { withLabel } from './checkLabels.js';
|
|
3
5
|
import { sortByAttention, toSummary } from './data.js';
|
|
6
|
+
import { CSP } from './static.js';
|
|
7
|
+
/** The only two hosts the static site may fetch from, besides itself: the live data path (src/server/live). */
|
|
8
|
+
export const LIVE_CONNECT_HOSTS = ['https://api.github.com', 'https://raw.githubusercontent.com'];
|
|
9
|
+
/** The local server's CSP with `connect-src` widened to the live hosts. Everything else stays identical. */
|
|
10
|
+
export const STATIC_CSP = CSP.replace("connect-src 'self'", `connect-src 'self' ${LIVE_CONNECT_HOSTS.join(' ')}`);
|
|
11
|
+
export const SELF_LIVE_CONFIG = { repo: 'Minntooos/Compounding-Loop', loop: 'compounding-loop', pages: 'https://minntooos.github.io/Compounding-Loop/' };
|
|
12
|
+
/**
|
|
13
|
+
* GitHub Pages cannot set headers, so the CSP goes into a meta tag as the first child of `<head>`.
|
|
14
|
+
* (`frame-ancestors` is ignored in a meta tag; Pages sends its own framing rules.)
|
|
15
|
+
*/
|
|
16
|
+
export function staticIndexHtml(html) {
|
|
17
|
+
const meta = `<meta http-equiv="Content-Security-Policy" content="${STATIC_CSP.replace(/; frame-ancestors [^;]+/, '')}">`;
|
|
18
|
+
if (!/<head[^>]*>/i.test(html))
|
|
19
|
+
throw new Error('index.html has no <head>');
|
|
20
|
+
return html.replace(/<head[^>]*>/i, (head) => `${head}\n ${meta}`);
|
|
21
|
+
}
|
|
4
22
|
/**
|
|
5
23
|
* The read-only API as plain files, for hosting the demo on GitHub Pages (no server there).
|
|
6
24
|
* Keys are paths relative to the site root. The web app in static mode fetches these relative URLs.
|
|
@@ -12,7 +30,10 @@ export function staticApiFiles(snapshot, version) {
|
|
|
12
30
|
for (const loop of snapshot.loops)
|
|
13
31
|
files.set(`api/loops/${loop.id}.json`, loop);
|
|
14
32
|
files.set('api/inbox.json', snapshot.inbox);
|
|
15
|
-
files.set('api/checks.json', snapshot.checks);
|
|
33
|
+
files.set('api/checks.json', snapshot.checks.map(withLabel));
|
|
34
|
+
// Same body as the demo server's /api/settings; without the file the static site logged a 404 on every visit.
|
|
35
|
+
files.set('api/settings.json', { demo: true, defaultRunner: DEFAULT_RUNNER });
|
|
36
|
+
files.set('live.json', SELF_LIVE_CONFIG);
|
|
16
37
|
return files;
|
|
17
38
|
}
|
|
18
39
|
export async function writeStaticApi(outDir, snapshot, version) {
|