claude-memory-layer 1.0.54 → 1.0.55
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/dist/cli/index.js +417 -139
- package/dist/cli/index.js.map +4 -4
- package/dist/server/api/index.js +239 -34
- package/dist/server/api/index.js.map +4 -4
- package/dist/server/index.js +251 -46
- package/dist/server/index.js.map +4 -4
- package/package.json +4 -2
package/dist/server/index.js
CHANGED
|
@@ -12,8 +12,8 @@ import { getCookie, setCookie, deleteCookie } from "hono/cookie";
|
|
|
12
12
|
import { logger } from "hono/logger";
|
|
13
13
|
import { serve } from "@hono/node-server";
|
|
14
14
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
15
|
-
import * as
|
|
16
|
-
import * as
|
|
15
|
+
import * as path15 from "path";
|
|
16
|
+
import * as fs13 from "fs";
|
|
17
17
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
18
18
|
import { fileURLToPath as fileUrlToPath } from "url";
|
|
19
19
|
|
|
@@ -2255,12 +2255,12 @@ function resolveProjectStoragePath(projectOrHash) {
|
|
|
2255
2255
|
import Database from "better-sqlite3";
|
|
2256
2256
|
import * as fs4 from "fs";
|
|
2257
2257
|
import * as nodePath from "path";
|
|
2258
|
-
function createSQLiteDatabase(
|
|
2259
|
-
const dir = nodePath.dirname(
|
|
2258
|
+
function createSQLiteDatabase(path16, options) {
|
|
2259
|
+
const dir = nodePath.dirname(path16);
|
|
2260
2260
|
if (!fs4.existsSync(dir)) {
|
|
2261
2261
|
fs4.mkdirSync(dir, { recursive: true });
|
|
2262
2262
|
}
|
|
2263
|
-
const db = new Database(
|
|
2263
|
+
const db = new Database(path16, {
|
|
2264
2264
|
readonly: options?.readonly ?? false
|
|
2265
2265
|
});
|
|
2266
2266
|
if (!options?.readonly && (options?.walMode ?? true)) {
|
|
@@ -2723,17 +2723,17 @@ function emptyOutboxRecoveryResult() {
|
|
|
2723
2723
|
function isRecord(value) {
|
|
2724
2724
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2725
2725
|
}
|
|
2726
|
-
function getNestedRecord(root,
|
|
2726
|
+
function getNestedRecord(root, path16) {
|
|
2727
2727
|
let cursor = root;
|
|
2728
|
-
for (const key of
|
|
2728
|
+
for (const key of path16) {
|
|
2729
2729
|
if (!isRecord(cursor)) return void 0;
|
|
2730
2730
|
cursor = cursor[key];
|
|
2731
2731
|
}
|
|
2732
2732
|
return isRecord(cursor) ? cursor : void 0;
|
|
2733
2733
|
}
|
|
2734
|
-
function getNestedString(root,
|
|
2734
|
+
function getNestedString(root, path16) {
|
|
2735
2735
|
let cursor = root;
|
|
2736
|
-
for (const key of
|
|
2736
|
+
for (const key of path16) {
|
|
2737
2737
|
if (!isRecord(cursor)) return void 0;
|
|
2738
2738
|
cursor = cursor[key];
|
|
2739
2739
|
}
|
|
@@ -6696,12 +6696,12 @@ var GraphPathService = class {
|
|
|
6696
6696
|
}
|
|
6697
6697
|
}
|
|
6698
6698
|
}
|
|
6699
|
-
const paths = Array.from(bestByTarget.entries()).map(([key,
|
|
6699
|
+
const paths = Array.from(bestByTarget.entries()).map(([key, path16]) => ({
|
|
6700
6700
|
target: graph.node(nodeFromKey(key)),
|
|
6701
|
-
hops:
|
|
6702
|
-
totalCost:
|
|
6703
|
-
scoreContribution:
|
|
6704
|
-
steps:
|
|
6701
|
+
hops: path16.hops,
|
|
6702
|
+
totalCost: path16.totalCost,
|
|
6703
|
+
scoreContribution: path16.totalCost > 0 ? 1 / path16.totalCost : 0,
|
|
6704
|
+
steps: path16.steps
|
|
6705
6705
|
})).sort((a, b) => b.scoreContribution - a.scoreContribution || a.hops - b.hops || a.target.name.localeCompare(b.target.name)).slice(0, maxResults);
|
|
6706
6706
|
return { startNodes, effectiveMaxHops, paths };
|
|
6707
6707
|
}
|
|
@@ -9474,12 +9474,12 @@ var Retriever = class {
|
|
|
9474
9474
|
direction: "both"
|
|
9475
9475
|
});
|
|
9476
9476
|
const titleByEntityId = new Map(startNodes.map((node) => [node.entityId, node.title]));
|
|
9477
|
-
for (const
|
|
9478
|
-
if (
|
|
9479
|
-
const target = await this.eventStore.getEvent(
|
|
9477
|
+
for (const path16 of expansion.paths) {
|
|
9478
|
+
if (path16.target.type !== "event") continue;
|
|
9479
|
+
const target = await this.eventStore.getEvent(path16.target.id);
|
|
9480
9480
|
if (!target) continue;
|
|
9481
|
-
const graphPath = toRetrievalGraphPathDebug(
|
|
9482
|
-
const score = graphPathScore(
|
|
9481
|
+
const graphPath = toRetrievalGraphPathDebug(path16, titleByEntityId);
|
|
9482
|
+
const score = graphPathScore(path16, opts.hopPenalty);
|
|
9483
9483
|
const existing = byId.get(target.id);
|
|
9484
9484
|
const graphPaths = mergeGraphPaths(existing?.graphPaths ?? [], [graphPath]);
|
|
9485
9485
|
const graphLane = {
|
|
@@ -9488,7 +9488,7 @@ var Retriever = class {
|
|
|
9488
9488
|
score
|
|
9489
9489
|
};
|
|
9490
9490
|
const row = {
|
|
9491
|
-
id: existing?.id ?? `graph-path-${
|
|
9491
|
+
id: existing?.id ?? `graph-path-${path16.hops}-${target.id}`,
|
|
9492
9492
|
eventId: target.id,
|
|
9493
9493
|
content: target.content,
|
|
9494
9494
|
score: Math.max(existing?.score ?? 0, score),
|
|
@@ -9828,8 +9828,8 @@ _Context:_ ${sessionContext}`;
|
|
|
9828
9828
|
}
|
|
9829
9829
|
matchesMetadataScope(metadata, expected) {
|
|
9830
9830
|
if (!metadata) return false;
|
|
9831
|
-
return Object.entries(expected).every(([
|
|
9832
|
-
const actual =
|
|
9831
|
+
return Object.entries(expected).every(([path16, value]) => {
|
|
9832
|
+
const actual = path16.split(".").reduce((acc, key) => {
|
|
9833
9833
|
if (typeof acc !== "object" || acc === null) return void 0;
|
|
9834
9834
|
return acc[key];
|
|
9835
9835
|
}, metadata);
|
|
@@ -9880,22 +9880,22 @@ function uniqueEntityStartNodes(candidates) {
|
|
|
9880
9880
|
}
|
|
9881
9881
|
return nodes;
|
|
9882
9882
|
}
|
|
9883
|
-
function toRetrievalGraphPathDebug(
|
|
9884
|
-
const firstStep =
|
|
9883
|
+
function toRetrievalGraphPathDebug(path16, titleByEntityId) {
|
|
9884
|
+
const firstStep = path16.steps[0];
|
|
9885
9885
|
const startNode = firstStep?.direction === "incoming" ? firstStep.to : firstStep?.from;
|
|
9886
9886
|
const startEntityId = startNode?.type === "entity" ? startNode.id : "";
|
|
9887
9887
|
return {
|
|
9888
9888
|
startEntityId,
|
|
9889
9889
|
startEntityTitle: titleByEntityId.get(startEntityId) ?? startNode?.name,
|
|
9890
|
-
targetId:
|
|
9891
|
-
targetType:
|
|
9892
|
-
hops:
|
|
9893
|
-
relationPath:
|
|
9890
|
+
targetId: path16.target.id,
|
|
9891
|
+
targetType: path16.target.type,
|
|
9892
|
+
hops: path16.hops,
|
|
9893
|
+
relationPath: path16.steps.map((step) => step.relationType)
|
|
9894
9894
|
};
|
|
9895
9895
|
}
|
|
9896
|
-
function graphPathScore(
|
|
9897
|
-
const base = Math.min(0.95, Math.max(0,
|
|
9898
|
-
return Math.max(0.05, base - hopPenalty * Math.max(0,
|
|
9896
|
+
function graphPathScore(path16, hopPenalty) {
|
|
9897
|
+
const base = Math.min(0.95, Math.max(0, path16.scoreContribution));
|
|
9898
|
+
return Math.max(0.05, base - hopPenalty * Math.max(0, path16.hops - 1));
|
|
9899
9899
|
}
|
|
9900
9900
|
function clampGraphHops(maxHops) {
|
|
9901
9901
|
if (!Number.isFinite(maxHops)) return 2;
|
|
@@ -9903,14 +9903,14 @@ function clampGraphHops(maxHops) {
|
|
|
9903
9903
|
}
|
|
9904
9904
|
function mergeGraphPaths(existing, incoming) {
|
|
9905
9905
|
const byKey = /* @__PURE__ */ new Map();
|
|
9906
|
-
for (const
|
|
9907
|
-
const key = [
|
|
9908
|
-
if (!byKey.has(key)) byKey.set(key,
|
|
9906
|
+
for (const path16 of [...existing, ...incoming]) {
|
|
9907
|
+
const key = [path16.startEntityId, path16.targetType, path16.targetId, path16.hops, ...path16.relationPath].join("\0");
|
|
9908
|
+
if (!byKey.has(key)) byKey.set(key, path16);
|
|
9909
9909
|
}
|
|
9910
9910
|
return [...byKey.values()].sort((a, b) => a.hops - b.hops || compareStable(graphPathSignature(a), graphPathSignature(b))).slice(0, 3);
|
|
9911
9911
|
}
|
|
9912
|
-
function graphPathSignature(
|
|
9913
|
-
return [
|
|
9912
|
+
function graphPathSignature(path16) {
|
|
9913
|
+
return [path16.startEntityId, path16.targetType, path16.targetId, path16.hops, ...path16.relationPath].join("|");
|
|
9914
9914
|
}
|
|
9915
9915
|
function compareStable(a, b) {
|
|
9916
9916
|
if (a < b) return -1;
|
|
@@ -15573,6 +15573,150 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
15573
15573
|
// src/apps/server/api/health.ts
|
|
15574
15574
|
import { Hono as Hono9 } from "hono";
|
|
15575
15575
|
import { spawnSync } from "child_process";
|
|
15576
|
+
import * as fs12 from "fs";
|
|
15577
|
+
import * as path14 from "path";
|
|
15578
|
+
|
|
15579
|
+
// src/core/productivity-health-report.ts
|
|
15580
|
+
var PRODUCTIVITY_HEALTH_SCHEMA_VERSION = "agent-productivity-health-v1";
|
|
15581
|
+
var PRODUCTIVITY_HEALTH_PROFILES = ["coder", "reviewer", "pm", "support", "researcher", "team"];
|
|
15582
|
+
var PRODUCTIVITY_HEALTH_MODES = ["observe", "preview", "enforce"];
|
|
15583
|
+
function parseProductivityHealthProfile(value) {
|
|
15584
|
+
const normalized = typeof value === "string" && value.trim().length > 0 ? value.trim() : "coder";
|
|
15585
|
+
if (PRODUCTIVITY_HEALTH_PROFILES.includes(normalized)) {
|
|
15586
|
+
return normalized;
|
|
15587
|
+
}
|
|
15588
|
+
throw new Error(`Invalid --profile: expected one of ${PRODUCTIVITY_HEALTH_PROFILES.join(", ")}`);
|
|
15589
|
+
}
|
|
15590
|
+
function parseProductivityHealthMode(value) {
|
|
15591
|
+
const normalized = typeof value === "string" && value.trim().length > 0 ? value.trim() : "preview";
|
|
15592
|
+
if (PRODUCTIVITY_HEALTH_MODES.includes(normalized)) {
|
|
15593
|
+
return normalized;
|
|
15594
|
+
}
|
|
15595
|
+
throw new Error(`Invalid --mode: expected one of ${PRODUCTIVITY_HEALTH_MODES.join(", ")}`);
|
|
15596
|
+
}
|
|
15597
|
+
function buildProductivityHealthReport(input) {
|
|
15598
|
+
const profile = input.profile ?? "coder";
|
|
15599
|
+
const mode = input.mode ?? "preview";
|
|
15600
|
+
const embedding = normalizeQueue(input.outbox.embedding);
|
|
15601
|
+
const vector = normalizeQueue(input.outbox.vector);
|
|
15602
|
+
const totals = sumQueues(embedding, vector);
|
|
15603
|
+
const storage = {
|
|
15604
|
+
totalEvents: numberOrZero(input.stats.totalEvents),
|
|
15605
|
+
vectorCount: numberOrZero(input.stats.vectorCount),
|
|
15606
|
+
levelStats: normalizeLevelStats(input.stats.levelStats)
|
|
15607
|
+
};
|
|
15608
|
+
const warningReasons = [];
|
|
15609
|
+
const projectGate = buildProjectScopeGate(input.project);
|
|
15610
|
+
const outboxGate = buildOutboxGate(totals);
|
|
15611
|
+
const densityGate = buildMemoryDensityGate(storage.totalEvents);
|
|
15612
|
+
const riskGates = [projectGate, outboxGate, densityGate];
|
|
15613
|
+
if (projectGate.status === "warn") warningReasons.push("project_scope_unknown");
|
|
15614
|
+
if (outboxGate.status === "warn") warningReasons.push("outbox_requires_attention");
|
|
15615
|
+
if (densityGate.status === "warn") warningReasons.push("memory_density_low");
|
|
15616
|
+
const status = riskGates.some((gate) => gate.status === "warn") ? "needs-attention" : "ok";
|
|
15617
|
+
return {
|
|
15618
|
+
schemaVersion: PRODUCTIVITY_HEALTH_SCHEMA_VERSION,
|
|
15619
|
+
generatedAt: (input.generatedAt ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
15620
|
+
status,
|
|
15621
|
+
profile,
|
|
15622
|
+
mode,
|
|
15623
|
+
project: input.project,
|
|
15624
|
+
summary: { warningReasons },
|
|
15625
|
+
signals: {
|
|
15626
|
+
storage,
|
|
15627
|
+
outbox: { embedding, vector, totals }
|
|
15628
|
+
},
|
|
15629
|
+
riskGates,
|
|
15630
|
+
nextBestAction: selectNextBestAction({ projectGate, outboxGate, densityGate })
|
|
15631
|
+
};
|
|
15632
|
+
}
|
|
15633
|
+
function buildProjectScopeGate(project) {
|
|
15634
|
+
if (project.scope === "project") {
|
|
15635
|
+
return { id: "project-scope-known", severity: "blocker", status: "pass" };
|
|
15636
|
+
}
|
|
15637
|
+
return {
|
|
15638
|
+
id: "project-scope-known",
|
|
15639
|
+
severity: "blocker",
|
|
15640
|
+
status: "warn",
|
|
15641
|
+
message: "No project parameter supplied; report uses global memory scope."
|
|
15642
|
+
};
|
|
15643
|
+
}
|
|
15644
|
+
function buildOutboxGate(totals) {
|
|
15645
|
+
if (totals.failed > 0 || totals.stuckProcessing > 0) {
|
|
15646
|
+
return {
|
|
15647
|
+
id: "outbox-healthy",
|
|
15648
|
+
severity: "warning",
|
|
15649
|
+
status: "warn",
|
|
15650
|
+
message: "Outbox has failed or stuck processing items; run recovery before trusting retrieval freshness."
|
|
15651
|
+
};
|
|
15652
|
+
}
|
|
15653
|
+
return { id: "outbox-healthy", severity: "warning", status: "pass" };
|
|
15654
|
+
}
|
|
15655
|
+
function buildMemoryDensityGate(totalEvents) {
|
|
15656
|
+
if (totalEvents <= 0) {
|
|
15657
|
+
return {
|
|
15658
|
+
id: "memory-density",
|
|
15659
|
+
severity: "warning",
|
|
15660
|
+
status: "warn",
|
|
15661
|
+
message: "No memories are available for this scope yet."
|
|
15662
|
+
};
|
|
15663
|
+
}
|
|
15664
|
+
return { id: "memory-density", severity: "warning", status: "pass" };
|
|
15665
|
+
}
|
|
15666
|
+
function selectNextBestAction(input) {
|
|
15667
|
+
if (input.outboxGate.status === "warn") {
|
|
15668
|
+
return "Run claude-memory-layer process --dry-run-recovery, then process pending embeddings.";
|
|
15669
|
+
}
|
|
15670
|
+
if (input.densityGate.status === "warn") {
|
|
15671
|
+
return "Import or capture project context before relying on productivity memory guidance.";
|
|
15672
|
+
}
|
|
15673
|
+
if (input.projectGate.status === "warn") {
|
|
15674
|
+
return "Provide a project path or project hash before relying on project-scoped productivity guidance.";
|
|
15675
|
+
}
|
|
15676
|
+
return "No immediate maintenance action required.";
|
|
15677
|
+
}
|
|
15678
|
+
function normalizeLevelStats(levelStats) {
|
|
15679
|
+
if (!Array.isArray(levelStats)) return [];
|
|
15680
|
+
return levelStats.filter((entry) => typeof entry.level === "string" && entry.level.trim().length > 0).map((entry) => ({ level: entry.level.trim().slice(0, 20), count: numberOrZero(entry.count) }));
|
|
15681
|
+
}
|
|
15682
|
+
function normalizeQueue(queue) {
|
|
15683
|
+
return {
|
|
15684
|
+
pending: numberOrZero(queue.pending),
|
|
15685
|
+
processing: numberOrZero(queue.processing),
|
|
15686
|
+
failed: numberOrZero(queue.failed),
|
|
15687
|
+
retryableFailed: numberOrZero(queue.retryableFailed),
|
|
15688
|
+
quarantinedFailed: numberOrZero(queue.quarantinedFailed),
|
|
15689
|
+
total: numberOrZero(queue.total),
|
|
15690
|
+
stuckProcessing: numberOrZero(queue.stuckProcessing),
|
|
15691
|
+
oldestProcessingAgeMs: normalizeNullableAge(queue.oldestProcessingAgeMs)
|
|
15692
|
+
};
|
|
15693
|
+
}
|
|
15694
|
+
function sumQueues(a, b) {
|
|
15695
|
+
return {
|
|
15696
|
+
pending: a.pending + b.pending,
|
|
15697
|
+
processing: a.processing + b.processing,
|
|
15698
|
+
failed: a.failed + b.failed,
|
|
15699
|
+
retryableFailed: (a.retryableFailed ?? 0) + (b.retryableFailed ?? 0),
|
|
15700
|
+
quarantinedFailed: (a.quarantinedFailed ?? 0) + (b.quarantinedFailed ?? 0),
|
|
15701
|
+
total: a.total + b.total,
|
|
15702
|
+
stuckProcessing: a.stuckProcessing + b.stuckProcessing,
|
|
15703
|
+
oldestProcessingAgeMs: maxNullable(a.oldestProcessingAgeMs, b.oldestProcessingAgeMs)
|
|
15704
|
+
};
|
|
15705
|
+
}
|
|
15706
|
+
function normalizeNullableAge(value) {
|
|
15707
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : null;
|
|
15708
|
+
}
|
|
15709
|
+
function maxNullable(a, b) {
|
|
15710
|
+
if (a === null) return b;
|
|
15711
|
+
if (b === null) return a;
|
|
15712
|
+
return Math.max(a, b);
|
|
15713
|
+
}
|
|
15714
|
+
function numberOrZero(value) {
|
|
15715
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return 0;
|
|
15716
|
+
return Math.floor(value);
|
|
15717
|
+
}
|
|
15718
|
+
|
|
15719
|
+
// src/apps/server/api/health.ts
|
|
15576
15720
|
var healthRouter = new Hono9();
|
|
15577
15721
|
function hasEnvSignal(name) {
|
|
15578
15722
|
const value = process.env[name];
|
|
@@ -15613,6 +15757,33 @@ function aggregateOutbox(outbox) {
|
|
|
15613
15757
|
const stuckProcessing = (outbox.embedding?.stuckProcessing || 0) + (outbox.vector?.stuckProcessing || 0);
|
|
15614
15758
|
return { pending, processing, failed, retryableFailed, quarantinedFailed, stuckProcessing };
|
|
15615
15759
|
}
|
|
15760
|
+
function resolveProductivityProjectIdentity(project) {
|
|
15761
|
+
if (!project || project.trim().length === 0) return { scope: "global", id: "global" };
|
|
15762
|
+
const normalized = project.trim();
|
|
15763
|
+
return { scope: "project", id: /^[a-f0-9]{8}$/.test(normalized) ? normalized : hashProjectPath(normalized) };
|
|
15764
|
+
}
|
|
15765
|
+
function explicitProjectStoreExists(project) {
|
|
15766
|
+
if (!project || project.trim().length === 0) return true;
|
|
15767
|
+
return fs12.existsSync(path14.join(resolveProjectStoragePath(project.trim()), "events.sqlite"));
|
|
15768
|
+
}
|
|
15769
|
+
function emptyProductivityStats() {
|
|
15770
|
+
return { totalEvents: 0, vectorCount: 0, levelStats: [] };
|
|
15771
|
+
}
|
|
15772
|
+
function emptyProductivityQueue() {
|
|
15773
|
+
return {
|
|
15774
|
+
pending: 0,
|
|
15775
|
+
processing: 0,
|
|
15776
|
+
failed: 0,
|
|
15777
|
+
retryableFailed: 0,
|
|
15778
|
+
quarantinedFailed: 0,
|
|
15779
|
+
total: 0,
|
|
15780
|
+
stuckProcessing: 0,
|
|
15781
|
+
oldestProcessingAgeMs: null
|
|
15782
|
+
};
|
|
15783
|
+
}
|
|
15784
|
+
function emptyProductivityOutbox() {
|
|
15785
|
+
return { embedding: emptyProductivityQueue(), vector: emptyProductivityQueue() };
|
|
15786
|
+
}
|
|
15616
15787
|
healthRouter.get("/setup", async (c) => {
|
|
15617
15788
|
const memoryService = getLightweightServiceFromQuery(c);
|
|
15618
15789
|
try {
|
|
@@ -15669,6 +15840,40 @@ healthRouter.get("/setup", async (c) => {
|
|
|
15669
15840
|
await memoryService.shutdown();
|
|
15670
15841
|
}
|
|
15671
15842
|
});
|
|
15843
|
+
healthRouter.get("/productivity", async (c) => {
|
|
15844
|
+
let memoryService;
|
|
15845
|
+
try {
|
|
15846
|
+
const profile = parseProductivityHealthProfile(c.req.query("profile"));
|
|
15847
|
+
const mode = parseProductivityHealthMode(c.req.query("mode"));
|
|
15848
|
+
const projectQuery = c.req.query("project") || c.req.query("projectId");
|
|
15849
|
+
const project = resolveProductivityProjectIdentity(projectQuery);
|
|
15850
|
+
if (!explicitProjectStoreExists(projectQuery)) {
|
|
15851
|
+
return c.json(buildProductivityHealthReport({
|
|
15852
|
+
stats: emptyProductivityStats(),
|
|
15853
|
+
outbox: emptyProductivityOutbox(),
|
|
15854
|
+
project,
|
|
15855
|
+
profile,
|
|
15856
|
+
mode
|
|
15857
|
+
}));
|
|
15858
|
+
}
|
|
15859
|
+
memoryService = getLightweightServiceFromQuery(c);
|
|
15860
|
+
await memoryService.initialize();
|
|
15861
|
+
const [stats, outbox] = await Promise.all([
|
|
15862
|
+
memoryService.getStats(),
|
|
15863
|
+
memoryService.getOutboxStats()
|
|
15864
|
+
]);
|
|
15865
|
+
return c.json(buildProductivityHealthReport({ stats, outbox, project, profile, mode }));
|
|
15866
|
+
} catch (error) {
|
|
15867
|
+
const status = error instanceof Error && error.message.startsWith("Invalid --") ? 400 : 500;
|
|
15868
|
+
return c.json({
|
|
15869
|
+
status: "error",
|
|
15870
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15871
|
+
error: status === 400 ? "Invalid productivity health option" : "Productivity health check failed"
|
|
15872
|
+
}, status);
|
|
15873
|
+
} finally {
|
|
15874
|
+
if (memoryService) await memoryService.shutdown();
|
|
15875
|
+
}
|
|
15876
|
+
});
|
|
15672
15877
|
healthRouter.get("/", async (c) => {
|
|
15673
15878
|
const memoryService = getLightweightServiceFromQuery(c);
|
|
15674
15879
|
try {
|
|
@@ -15990,20 +16195,20 @@ playgroundRouter.post("/dry-run", async (c) => {
|
|
|
15990
16195
|
var apiRouter = new Hono11().route("/sessions", sessionsRouter).route("/events", eventsRouter).route("/search", searchRouter).route("/stats", statsRouter).route("/citations", citationsRouter).route("/turns", turnsRouter).route("/projects", projectsRouter).route("/chat", chatRouter).route("/health", healthRouter).route("/playground", playgroundRouter);
|
|
15991
16196
|
|
|
15992
16197
|
// src/apps/server/index.ts
|
|
15993
|
-
var moduleDir =
|
|
16198
|
+
var moduleDir = path15.dirname(fileUrlToPath(import.meta.url));
|
|
15994
16199
|
var DASHBOARD_SESSION_COOKIE = "cml_dashboard_session";
|
|
15995
16200
|
var DEFAULT_SESSION_MAX_AGE_SECONDS = 60 * 60 * 24 * 30;
|
|
15996
16201
|
function resolveUiPath() {
|
|
15997
16202
|
const candidates = [
|
|
15998
16203
|
// Built server: dist/server/index.js -> dist/ui
|
|
15999
|
-
|
|
16204
|
+
path15.resolve(moduleDir, "../ui"),
|
|
16000
16205
|
// Source/dev server: src/apps/server/index.ts -> src/apps/dashboard
|
|
16001
|
-
|
|
16206
|
+
path15.resolve(moduleDir, "../dashboard"),
|
|
16002
16207
|
// Fallback when running from a repository root.
|
|
16003
|
-
|
|
16004
|
-
|
|
16208
|
+
path15.resolve(process.cwd(), "dist/ui"),
|
|
16209
|
+
path15.resolve(process.cwd(), "src/apps/dashboard")
|
|
16005
16210
|
];
|
|
16006
|
-
return candidates.find((candidate) =>
|
|
16211
|
+
return candidates.find((candidate) => fs13.existsSync(path15.join(candidate, "index.html"))) ?? candidates[0];
|
|
16007
16212
|
}
|
|
16008
16213
|
function normalizeDashboardHost(host = "localhost") {
|
|
16009
16214
|
const normalized = host.trim().toLowerCase();
|
|
@@ -16231,13 +16436,13 @@ function createDashboardApp(options = {}) {
|
|
|
16231
16436
|
}
|
|
16232
16437
|
app2.route("/api", apiRouter);
|
|
16233
16438
|
const uiPath = resolveUiPath();
|
|
16234
|
-
if (
|
|
16439
|
+
if (fs13.existsSync(uiPath)) {
|
|
16235
16440
|
app2.use("/*", serveStatic({ root: uiPath }));
|
|
16236
16441
|
}
|
|
16237
16442
|
app2.get("*", (c) => {
|
|
16238
|
-
const indexPath =
|
|
16239
|
-
if (
|
|
16240
|
-
return c.html(
|
|
16443
|
+
const indexPath = path15.join(uiPath, "index.html");
|
|
16444
|
+
if (fs13.existsSync(indexPath)) {
|
|
16445
|
+
return c.html(fs13.readFileSync(indexPath, "utf-8"));
|
|
16241
16446
|
}
|
|
16242
16447
|
return c.text('UI not built. Run "npm run build:ui" first.', 404);
|
|
16243
16448
|
});
|