@karmaniverous/jeeves-meta 0.20.0-1 → 0.20.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 +2 -2
- package/dist/cli/jeeves-meta/index.js +67 -36
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ jeeves-meta service install --config /path/to/jeeves-meta/config.json
|
|
|
56
56
|
|
|
57
57
|
| Method | Path | Description |
|
|
58
58
|
| --- | --- | --- |
|
|
59
|
-
| GET | `/status` | Service health, queue state, dependency checks, phase-state summary |
|
|
59
|
+
| GET | `/status` | Service health, queue state, dependency checks, phase-state summary. Meta counts come from the last meta list (at most 60 s old, refreshed in the background), so the check never waits on a corpus walk. |
|
|
60
60
|
| GET | `/metas` | List metas with summary stats and per-meta projection. Response includes `phaseState` and `owedPhase` per meta. |
|
|
61
61
|
| GET | `/metas/:path` | Full detail for a single meta, with optional archive history. Response includes `phaseState`, `owedPhase` and `crossRefs`. |
|
|
62
62
|
| GET | `/preview` | Dry-run: preview inputs for next synthesis |
|
|
@@ -71,7 +71,7 @@ jeeves-meta service install --config /path/to/jeeves-meta/config.json
|
|
|
71
71
|
| PATCH | `/metas/:path` | Update user-settable reserved properties (`_steer`, `_emphasis`, `_depth`, `_crossRefs`, `_disabled`) |
|
|
72
72
|
| GET | `/openapi.json` | The OpenAPI 3.1 document of every route above |
|
|
73
73
|
|
|
74
|
-
Every route is defined in `META_CATALOG` (`@karmaniverous/jeeves-meta-core`) and validated against it: a malformed request is answered 400 `FST_ERR_VALIDATION`.
|
|
74
|
+
Every route is defined in `META_CATALOG` (`@karmaniverous/jeeves-meta-core`) and validated against it: a malformed request is answered 400 `FST_ERR_VALIDATION`. While the service starts, `GET /status` answers 200 with `serviceState: "starting"` (no meta counts until the first meta list loads), `/config`, `/config/apply` and `/openapi.json` are served, and every other route answers 503 `META_NOT_READY`. Use [`@karmaniverous/jeeves-meta-sdk`](../sdk/README.md) for a typed client.
|
|
75
75
|
|
|
76
76
|
### Errors
|
|
77
77
|
|
|
@@ -657,7 +657,8 @@ function getDeltaFiles(generatedAt, scopeFiles) {
|
|
|
657
657
|
}
|
|
658
658
|
|
|
659
659
|
/**
|
|
660
|
-
* In-memory cache for listMetas results with TTL
|
|
660
|
+
* In-memory cache for listMetas results with TTL, concurrent refresh guard
|
|
661
|
+
* and a non-blocking read (`peek`) for health checks.
|
|
661
662
|
*
|
|
662
663
|
* @module cache
|
|
663
664
|
*/
|
|
@@ -677,6 +678,22 @@ class MetaCache {
|
|
|
677
678
|
}
|
|
678
679
|
return this.refresh(config, watcher);
|
|
679
680
|
}
|
|
681
|
+
/**
|
|
682
|
+
* The cached result without waiting on a stale one: when it has expired,
|
|
683
|
+
* return it anyway and refresh in the background. Only the first load,
|
|
684
|
+
* with nothing cached yet, is awaited. For callers that must answer fast
|
|
685
|
+
* (`GET /status`), where a 60 s-old list is good enough.
|
|
686
|
+
*/
|
|
687
|
+
async peek(config, watcher) {
|
|
688
|
+
if (!this.result)
|
|
689
|
+
return this.refresh(config, watcher);
|
|
690
|
+
if (Date.now() - this.updatedAt >= TTL_MS) {
|
|
691
|
+
// A failed background refresh keeps the old result; the next caller
|
|
692
|
+
// retries it.
|
|
693
|
+
this.refresh(config, watcher).catch(() => undefined);
|
|
694
|
+
}
|
|
695
|
+
return this.result;
|
|
696
|
+
}
|
|
680
697
|
/** Force-expire the cache so next get() triggers a refresh. */
|
|
681
698
|
invalidate() {
|
|
682
699
|
this.updatedAt = 0;
|
|
@@ -31854,6 +31871,8 @@ async function checkWatcher(url) {
|
|
|
31854
31871
|
function deriveServiceState(deps) {
|
|
31855
31872
|
if (deps.shuttingDown)
|
|
31856
31873
|
return 'stopping';
|
|
31874
|
+
if (!deps.ready)
|
|
31875
|
+
return 'starting';
|
|
31857
31876
|
if (deps.queue.currentPhase)
|
|
31858
31877
|
return 'synthesizing';
|
|
31859
31878
|
if (deps.queue.depth > 0)
|
|
@@ -31888,41 +31907,47 @@ function createStatusHandlers(deps) {
|
|
|
31888
31907
|
};
|
|
31889
31908
|
let nextPhase = null;
|
|
31890
31909
|
let metaCounts = null;
|
|
31891
|
-
|
|
31892
|
-
|
|
31893
|
-
|
|
31894
|
-
|
|
31895
|
-
|
|
31896
|
-
|
|
31897
|
-
|
|
31910
|
+
// While starting, the first meta list is still loading: report the
|
|
31911
|
+
// state, dependencies and stats without it (Decision 199).
|
|
31912
|
+
if (deps.ready) {
|
|
31913
|
+
try {
|
|
31914
|
+
// Never wait on a refresh of the whole corpus: a health check answers
|
|
31915
|
+
// from the last list, refreshed in the background.
|
|
31916
|
+
const metaResult = await cache.peek(config, watcher);
|
|
31917
|
+
// Count raw phase states (before retry) for display
|
|
31918
|
+
for (const entry of metaResult.entries) {
|
|
31919
|
+
const ps = derivePhaseState(entry.meta);
|
|
31920
|
+
for (const phase of ['architect', 'builder', 'critic']) {
|
|
31921
|
+
phaseStateSummary[phase][ps[phase]]++;
|
|
31922
|
+
}
|
|
31898
31923
|
}
|
|
31899
|
-
|
|
31900
|
-
|
|
31901
|
-
|
|
31902
|
-
|
|
31903
|
-
|
|
31904
|
-
|
|
31905
|
-
|
|
31906
|
-
|
|
31907
|
-
|
|
31908
|
-
|
|
31909
|
-
|
|
31924
|
+
// Build candidates (with auto-retry) for scheduling
|
|
31925
|
+
const candidates = buildPhaseCandidates(metaResult.entries, config.architectEvery);
|
|
31926
|
+
// Find next phase candidate
|
|
31927
|
+
const winner = selectPhaseCandidate(candidates, config.depthWeight);
|
|
31928
|
+
if (winner) {
|
|
31929
|
+
nextPhase = {
|
|
31930
|
+
path: winner.node.metaPath,
|
|
31931
|
+
phase: winner.owedPhase,
|
|
31932
|
+
band: winner.band,
|
|
31933
|
+
staleness: finiteOrNull(winner.effectiveStaleness),
|
|
31934
|
+
};
|
|
31935
|
+
}
|
|
31936
|
+
// Meta counts summary
|
|
31937
|
+
const summary = computeSummary(metaResult.entries, config.depthWeight);
|
|
31938
|
+
metaCounts = {
|
|
31939
|
+
total: summary.total,
|
|
31940
|
+
enabled: summary.total - summary.disabled,
|
|
31941
|
+
disabled: summary.disabled,
|
|
31942
|
+
neverSynthesized: summary.neverSynthesized,
|
|
31943
|
+
stale: summary.stale,
|
|
31944
|
+
errors: summary.errors,
|
|
31945
|
+
locked: summary.locked,
|
|
31910
31946
|
};
|
|
31911
31947
|
}
|
|
31912
|
-
|
|
31913
|
-
|
|
31914
|
-
|
|
31915
|
-
total: summary.total,
|
|
31916
|
-
enabled: summary.total - summary.disabled,
|
|
31917
|
-
disabled: summary.disabled,
|
|
31918
|
-
neverSynthesized: summary.neverSynthesized,
|
|
31919
|
-
stale: summary.stale,
|
|
31920
|
-
errors: summary.errors,
|
|
31921
|
-
locked: summary.locked,
|
|
31922
|
-
};
|
|
31923
|
-
}
|
|
31924
|
-
catch {
|
|
31925
|
-
// Watcher unreachable — phase summary unavailable
|
|
31948
|
+
catch {
|
|
31949
|
+
// Watcher unreachable — phase summary unavailable
|
|
31950
|
+
}
|
|
31926
31951
|
}
|
|
31927
31952
|
return {
|
|
31928
31953
|
serviceState: deriveServiceState(deps),
|
|
@@ -32169,7 +32194,12 @@ function registerRoutes(app, deps) {
|
|
|
32169
32194
|
* @module server
|
|
32170
32195
|
*/
|
|
32171
32196
|
/** Routes served while the service is starting (matched route URLs). */
|
|
32172
|
-
const UNGATED_ROUTES = new Set([
|
|
32197
|
+
const UNGATED_ROUTES = new Set([
|
|
32198
|
+
'/status',
|
|
32199
|
+
'/config',
|
|
32200
|
+
'/config/apply',
|
|
32201
|
+
'/openapi.json',
|
|
32202
|
+
]);
|
|
32173
32203
|
/**
|
|
32174
32204
|
* Create and configure the Fastify server.
|
|
32175
32205
|
*
|
|
@@ -32400,9 +32430,10 @@ async function startService(config, configPath, configApply) {
|
|
|
32400
32430
|
queue.onEnqueue(() => {
|
|
32401
32431
|
void queue.processQueue(synthesizeFn);
|
|
32402
32432
|
});
|
|
32403
|
-
// Startup: clean stale locks (gap #16)
|
|
32433
|
+
// Startup: clean stale locks (gap #16). Through the cache, so the first
|
|
32434
|
+
// GET /status after start-up answers without a second walk.
|
|
32404
32435
|
try {
|
|
32405
|
-
const metaResult = await
|
|
32436
|
+
const metaResult = await cache.get(config, watcher);
|
|
32406
32437
|
const metaPaths = metaResult.entries.map((e) => e.node.metaPath);
|
|
32407
32438
|
cleanupStaleLocks(metaPaths, logger);
|
|
32408
32439
|
}
|
package/package.json
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@karmaniverous/jeeves": "0.11.0",
|
|
11
|
-
"@karmaniverous/jeeves-meta-core": "^0.4.0
|
|
12
|
-
"@karmaniverous/jeeves-meta-sdk": "^0.
|
|
11
|
+
"@karmaniverous/jeeves-meta-core": "^0.4.0",
|
|
12
|
+
"@karmaniverous/jeeves-meta-sdk": "^0.1.0",
|
|
13
13
|
"@karmaniverous/jeeves-watcher-sdk": "^0.4.1",
|
|
14
14
|
"commander": "^15",
|
|
15
15
|
"croner": "^10",
|
|
@@ -112,5 +112,5 @@
|
|
|
112
112
|
"wire:record": "vitest run --config vitest.wire-record.config.ts"
|
|
113
113
|
},
|
|
114
114
|
"type": "module",
|
|
115
|
-
"version": "0.20.0
|
|
115
|
+
"version": "0.20.0"
|
|
116
116
|
}
|