claude-memory-layer 1.0.17 → 1.0.19
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/config/kpi-thresholds.json +7 -0
- package/dist/cli/index.js +372 -74
- package/dist/cli/index.js.map +3 -3
- package/dist/hooks/post-tool-use.js +6 -0
- package/dist/hooks/post-tool-use.js.map +2 -2
- package/dist/hooks/session-end.js +6 -0
- package/dist/hooks/session-end.js.map +2 -2
- package/dist/hooks/session-start.js +29 -13
- package/dist/hooks/session-start.js.map +2 -2
- package/dist/hooks/stop.js +6 -0
- package/dist/hooks/stop.js.map +2 -2
- package/dist/hooks/user-prompt-submit.js +245 -31
- package/dist/hooks/user-prompt-submit.js.map +3 -3
- package/dist/server/api/index.js +329 -31
- package/dist/server/api/index.js.map +3 -3
- package/dist/server/index.js +336 -38
- package/dist/server/index.js.map +3 -3
- package/dist/services/memory-service.js +6 -0
- package/dist/services/memory-service.js.map +2 -2
- package/dist/ui/app.js +236 -4
- package/dist/ui/index.html +51 -0
- package/dist/ui/style.css +34 -0
- package/memory/_index.md +4 -0
- package/memory/agent_response/uncategorized/2026-02-26.md +151 -1
- package/memory/agent_response/uncategorized/2026-03-03.md +14 -0
- package/memory/session_summary/uncategorized/2026-02-26.md +13 -0
- package/memory/session_summary/uncategorized/2026-03-03.md +5 -0
- package/memory/tool_observation/uncategorized/2026-02-26.md +9 -1
- package/memory/tool_observation/uncategorized/2026-03-03.md +21 -0
- package/memory/user_prompt/uncategorized/2026-02-26.md +9 -0
- package/package.json +3 -2
- package/scripts/delete-unknown-projects.js +154 -0
- package/src/hooks/session-start.ts +9 -3
- package/src/hooks/user-prompt-submit.ts +225 -29
- package/src/server/api/events.ts +1 -0
- package/src/server/api/stats.ts +346 -0
- package/src/services/memory-service.ts +7 -0
- package/src/ui/app.js +236 -4
- package/src/ui/index.html +51 -0
- package/src/ui/style.css +34 -0
|
@@ -8,6 +8,9 @@ const __dirname = dirname(__filename);
|
|
|
8
8
|
|
|
9
9
|
// src/hooks/user-prompt-submit.ts
|
|
10
10
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
11
|
+
import * as fs6 from "fs";
|
|
12
|
+
import * as path5 from "path";
|
|
13
|
+
import * as os3 from "os";
|
|
11
14
|
|
|
12
15
|
// src/services/memory-service.ts
|
|
13
16
|
import * as path3 from "path";
|
|
@@ -70,11 +73,11 @@ function toDate(value) {
|
|
|
70
73
|
return new Date(value);
|
|
71
74
|
return new Date(String(value));
|
|
72
75
|
}
|
|
73
|
-
function createDatabase(
|
|
76
|
+
function createDatabase(path6, options) {
|
|
74
77
|
if (options?.readOnly) {
|
|
75
|
-
return new duckdb.Database(
|
|
78
|
+
return new duckdb.Database(path6, { access_mode: "READ_ONLY" });
|
|
76
79
|
}
|
|
77
|
-
return new duckdb.Database(
|
|
80
|
+
return new duckdb.Database(path6);
|
|
78
81
|
}
|
|
79
82
|
function dbRun(db, sql, params = []) {
|
|
80
83
|
return new Promise((resolve2, reject) => {
|
|
@@ -758,12 +761,12 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
758
761
|
import Database from "better-sqlite3";
|
|
759
762
|
import * as fs from "fs";
|
|
760
763
|
import * as nodePath from "path";
|
|
761
|
-
function createSQLiteDatabase(
|
|
762
|
-
const dir = nodePath.dirname(
|
|
764
|
+
function createSQLiteDatabase(path6, options) {
|
|
765
|
+
const dir = nodePath.dirname(path6);
|
|
763
766
|
if (!fs.existsSync(dir)) {
|
|
764
767
|
fs.mkdirSync(dir, { recursive: true });
|
|
765
768
|
}
|
|
766
|
-
const db = new Database(
|
|
769
|
+
const db = new Database(path6, {
|
|
767
770
|
readonly: options?.readonly ?? false
|
|
768
771
|
});
|
|
769
772
|
if (!options?.readonly && (options?.walMode ?? true)) {
|
|
@@ -3439,8 +3442,8 @@ _Context:_ ${sessionContext}`;
|
|
|
3439
3442
|
matchesMetadataScope(metadata, expected) {
|
|
3440
3443
|
if (!metadata)
|
|
3441
3444
|
return false;
|
|
3442
|
-
return Object.entries(expected).every(([
|
|
3443
|
-
const actual =
|
|
3445
|
+
return Object.entries(expected).every(([path6, value]) => {
|
|
3446
|
+
const actual = path6.split(".").reduce((acc, key) => {
|
|
3444
3447
|
if (typeof acc !== "object" || acc === null)
|
|
3445
3448
|
return void 0;
|
|
3446
3449
|
return acc[key];
|
|
@@ -6881,6 +6884,12 @@ var MemoryService = class {
|
|
|
6881
6884
|
recordMemoryAccess(eventId, sessionId, confidence = 1) {
|
|
6882
6885
|
this.graduation.recordAccess(eventId, sessionId, confidence);
|
|
6883
6886
|
}
|
|
6887
|
+
/**
|
|
6888
|
+
* Backward-compatible alias used by some hooks
|
|
6889
|
+
*/
|
|
6890
|
+
async close() {
|
|
6891
|
+
await this.shutdown();
|
|
6892
|
+
}
|
|
6884
6893
|
/**
|
|
6885
6894
|
* Shutdown service
|
|
6886
6895
|
*/
|
|
@@ -6916,6 +6925,42 @@ var MemoryService = class {
|
|
|
6916
6925
|
}
|
|
6917
6926
|
};
|
|
6918
6927
|
var serviceCache = /* @__PURE__ */ new Map();
|
|
6928
|
+
var GLOBAL_KEY = "__global__";
|
|
6929
|
+
function getDefaultMemoryService() {
|
|
6930
|
+
if (!serviceCache.has(GLOBAL_KEY)) {
|
|
6931
|
+
serviceCache.set(GLOBAL_KEY, new MemoryService({
|
|
6932
|
+
storagePath: "~/.claude-code/memory",
|
|
6933
|
+
analyticsEnabled: false,
|
|
6934
|
+
// Hooks don't need DuckDB
|
|
6935
|
+
sharedStoreConfig: { enabled: false }
|
|
6936
|
+
// Shared store uses DuckDB too
|
|
6937
|
+
}));
|
|
6938
|
+
}
|
|
6939
|
+
return serviceCache.get(GLOBAL_KEY);
|
|
6940
|
+
}
|
|
6941
|
+
function getMemoryServiceForProject(projectPath, sharedStoreConfig) {
|
|
6942
|
+
const hash = hashProjectPath(projectPath);
|
|
6943
|
+
if (!serviceCache.has(hash)) {
|
|
6944
|
+
const storagePath = getProjectStoragePath(projectPath);
|
|
6945
|
+
serviceCache.set(hash, new MemoryService({
|
|
6946
|
+
storagePath,
|
|
6947
|
+
projectHash: hash,
|
|
6948
|
+
projectPath,
|
|
6949
|
+
// Override shared store config - hooks don't need DuckDB
|
|
6950
|
+
sharedStoreConfig: sharedStoreConfig ?? { enabled: false },
|
|
6951
|
+
analyticsEnabled: false
|
|
6952
|
+
// Hooks don't need DuckDB
|
|
6953
|
+
}));
|
|
6954
|
+
}
|
|
6955
|
+
return serviceCache.get(hash);
|
|
6956
|
+
}
|
|
6957
|
+
function getMemoryServiceForSession(sessionId) {
|
|
6958
|
+
const projectInfo = getSessionProject(sessionId);
|
|
6959
|
+
if (projectInfo) {
|
|
6960
|
+
return getMemoryServiceForProject(projectInfo.projectPath);
|
|
6961
|
+
}
|
|
6962
|
+
return getDefaultMemoryService();
|
|
6963
|
+
}
|
|
6919
6964
|
function getLightweightMemoryService(sessionId) {
|
|
6920
6965
|
const projectInfo = getSessionProject(sessionId);
|
|
6921
6966
|
const key = projectInfo ? `lightweight_${projectInfo.projectHash}` : "lightweight_global";
|
|
@@ -6968,6 +7013,10 @@ var MAX_MEMORIES = parseInt(process.env.CLAUDE_MEMORY_MAX_COUNT || "5");
|
|
|
6968
7013
|
var BASE_MIN_SCORE = parseFloat(process.env.CLAUDE_MEMORY_MIN_SCORE || "0.4");
|
|
6969
7014
|
var FALLBACK_MIN_SCORE = parseFloat(process.env.CLAUDE_MEMORY_FALLBACK_MIN_SCORE || "0.3");
|
|
6970
7015
|
var ENABLE_SEARCH = process.env.CLAUDE_MEMORY_SEARCH !== "false";
|
|
7016
|
+
var RETRIEVAL_MODE = process.env.CLAUDE_MEMORY_RETRIEVAL_MODE || "hybrid";
|
|
7017
|
+
var SEMANTIC_TIMEOUT_MS = parseInt(process.env.CLAUDE_MEMORY_SEMANTIC_TIMEOUT_MS || "1200");
|
|
7018
|
+
var ADHERENCE_INTERVAL_TURNS = parseInt(process.env.CLAUDE_MEMORY_ADHERENCE_INTERVAL_TURNS || "3");
|
|
7019
|
+
var ADHERENCE_STATE_DIR = path5.join(os3.homedir(), ".claude-code", "memory");
|
|
6971
7020
|
function shouldStorePrompt(prompt) {
|
|
6972
7021
|
const trimmed = prompt.trim();
|
|
6973
7022
|
if (trimmed.startsWith("/"))
|
|
@@ -6986,6 +7035,113 @@ function getDynamicMinScore(prompt) {
|
|
|
6986
7035
|
return Math.max(0.3, BASE_MIN_SCORE - 0.05);
|
|
6987
7036
|
return BASE_MIN_SCORE;
|
|
6988
7037
|
}
|
|
7038
|
+
function withTimeout(promise, timeoutMs) {
|
|
7039
|
+
return new Promise((resolve2, reject) => {
|
|
7040
|
+
const timer = setTimeout(() => reject(new Error(`semantic retrieval timeout (${timeoutMs}ms)`)), timeoutMs);
|
|
7041
|
+
promise.then((result) => {
|
|
7042
|
+
clearTimeout(timer);
|
|
7043
|
+
resolve2(result);
|
|
7044
|
+
}).catch((error) => {
|
|
7045
|
+
clearTimeout(timer);
|
|
7046
|
+
reject(error);
|
|
7047
|
+
});
|
|
7048
|
+
});
|
|
7049
|
+
}
|
|
7050
|
+
function formatMemoryContext(items) {
|
|
7051
|
+
if (items.length === 0)
|
|
7052
|
+
return "";
|
|
7053
|
+
const lines = items.map((m) => {
|
|
7054
|
+
const preview = m.content.length > 300 ? m.content.substring(0, 300) + "..." : m.content;
|
|
7055
|
+
return `- [${m.type}] ${preview}`;
|
|
7056
|
+
});
|
|
7057
|
+
return `\u{1F4A1} **Related memories found:**
|
|
7058
|
+
|
|
7059
|
+
${lines.join("\n\n")}`;
|
|
7060
|
+
}
|
|
7061
|
+
function getAdherenceStatePath(sessionId) {
|
|
7062
|
+
return path5.join(ADHERENCE_STATE_DIR, `.adherence-state-${sessionId}.json`);
|
|
7063
|
+
}
|
|
7064
|
+
function readAdherenceState(sessionId) {
|
|
7065
|
+
try {
|
|
7066
|
+
const filePath = getAdherenceStatePath(sessionId);
|
|
7067
|
+
if (!fs6.existsSync(filePath)) {
|
|
7068
|
+
return {
|
|
7069
|
+
sessionId,
|
|
7070
|
+
turnCount: 0,
|
|
7071
|
+
lastCheckedTurn: 0,
|
|
7072
|
+
lastPrompt: "",
|
|
7073
|
+
lastReason: "init",
|
|
7074
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7075
|
+
};
|
|
7076
|
+
}
|
|
7077
|
+
const data = fs6.readFileSync(filePath, "utf8");
|
|
7078
|
+
const parsed = JSON.parse(data);
|
|
7079
|
+
if (parsed.sessionId !== sessionId)
|
|
7080
|
+
throw new Error("session mismatch");
|
|
7081
|
+
return parsed;
|
|
7082
|
+
} catch {
|
|
7083
|
+
return {
|
|
7084
|
+
sessionId,
|
|
7085
|
+
turnCount: 0,
|
|
7086
|
+
lastCheckedTurn: 0,
|
|
7087
|
+
lastPrompt: "",
|
|
7088
|
+
lastReason: "init",
|
|
7089
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7090
|
+
};
|
|
7091
|
+
}
|
|
7092
|
+
}
|
|
7093
|
+
function writeAdherenceState(state) {
|
|
7094
|
+
try {
|
|
7095
|
+
if (!fs6.existsSync(ADHERENCE_STATE_DIR)) {
|
|
7096
|
+
fs6.mkdirSync(ADHERENCE_STATE_DIR, { recursive: true });
|
|
7097
|
+
}
|
|
7098
|
+
const filePath = getAdherenceStatePath(state.sessionId);
|
|
7099
|
+
const tempPath = filePath + ".tmp";
|
|
7100
|
+
fs6.writeFileSync(tempPath, JSON.stringify(state));
|
|
7101
|
+
fs6.renameSync(tempPath, filePath);
|
|
7102
|
+
} catch {
|
|
7103
|
+
}
|
|
7104
|
+
}
|
|
7105
|
+
function hasWriteIntent(prompt) {
|
|
7106
|
+
return /(fix|refactor|implement|change|modify|edit|update|rewrite|patch|create|add|remove|delete|버그|수정|리팩터|구현|추가|삭제|개선)/i.test(prompt);
|
|
7107
|
+
}
|
|
7108
|
+
function tokenize(text) {
|
|
7109
|
+
const stopwords = /* @__PURE__ */ new Set(["the", "and", "for", "with", "that", "this", "from", "have", "what", "when", "where", "how", "why", "\uADF8\uB9AC\uACE0", "\uADF8\uB9AC\uACE0\uC694", "\uC774\uAC70", "\uADF8\uAC70", "\uD574\uC8FC\uC138\uC694", "\uD574\uC918", "\uC880", "\uC5D0\uC11C", "\uC73C\uB85C", "\uD558\uB294", "\uD574"]);
|
|
7110
|
+
return text.toLowerCase().replace(/[^a-z0-9가-힣\s]/g, " ").split(/\s+/).filter((w) => w.length >= 2 && !stopwords.has(w));
|
|
7111
|
+
}
|
|
7112
|
+
function isTopicShift(currentPrompt, lastPrompt) {
|
|
7113
|
+
if (!lastPrompt || lastPrompt.length < 10)
|
|
7114
|
+
return false;
|
|
7115
|
+
const a = new Set(tokenize(currentPrompt));
|
|
7116
|
+
const b = new Set(tokenize(lastPrompt));
|
|
7117
|
+
if (a.size === 0 || b.size === 0)
|
|
7118
|
+
return false;
|
|
7119
|
+
let intersection = 0;
|
|
7120
|
+
for (const token of a) {
|
|
7121
|
+
if (b.has(token))
|
|
7122
|
+
intersection++;
|
|
7123
|
+
}
|
|
7124
|
+
const union = a.size + b.size - intersection;
|
|
7125
|
+
const similarity = union > 0 ? intersection / union : 0;
|
|
7126
|
+
return similarity < 0.2;
|
|
7127
|
+
}
|
|
7128
|
+
function shouldRunAdherenceCheck(turnCount, prompt, state) {
|
|
7129
|
+
if (turnCount === 1)
|
|
7130
|
+
return { run: true, reason: "first-turn" };
|
|
7131
|
+
if (hasWriteIntent(prompt))
|
|
7132
|
+
return { run: true, reason: "write-intent" };
|
|
7133
|
+
if (isTopicShift(prompt, state.lastPrompt))
|
|
7134
|
+
return { run: true, reason: "topic-shift" };
|
|
7135
|
+
if (turnCount - state.lastCheckedTurn >= ADHERENCE_INTERVAL_TURNS)
|
|
7136
|
+
return { run: true, reason: "interval" };
|
|
7137
|
+
return { run: false, reason: "skip" };
|
|
7138
|
+
}
|
|
7139
|
+
function logAdherenceDecision(sessionId, turn, run, reason) {
|
|
7140
|
+
if (!process.env.CLAUDE_MEMORY_DEBUG)
|
|
7141
|
+
return;
|
|
7142
|
+
const mode = run ? "enforced" : "skipped";
|
|
7143
|
+
console.error(`[adherence] session=${sessionId} turn=${turn} mode=${mode} reason=${reason}`);
|
|
7144
|
+
}
|
|
6989
7145
|
async function main() {
|
|
6990
7146
|
const inputData = await readStdin();
|
|
6991
7147
|
const input = JSON.parse(inputData);
|
|
@@ -6993,49 +7149,107 @@ async function main() {
|
|
|
6993
7149
|
writeTurnState(input.session_id, turnId);
|
|
6994
7150
|
const memoryService = getLightweightMemoryService(input.session_id);
|
|
6995
7151
|
try {
|
|
7152
|
+
let context = "";
|
|
7153
|
+
const adherenceState = readAdherenceState(input.session_id);
|
|
7154
|
+
const currentTurn = adherenceState.turnCount + 1;
|
|
7155
|
+
const adherenceDecision = shouldRunAdherenceCheck(currentTurn, input.prompt, adherenceState);
|
|
7156
|
+
logAdherenceDecision(input.session_id, currentTurn, adherenceDecision.run, adherenceDecision.reason);
|
|
6996
7157
|
if (shouldStorePrompt(input.prompt)) {
|
|
6997
7158
|
await memoryService.storeUserPrompt(
|
|
6998
7159
|
input.session_id,
|
|
6999
7160
|
input.prompt,
|
|
7000
|
-
{
|
|
7161
|
+
{
|
|
7162
|
+
turnId,
|
|
7163
|
+
adherence: {
|
|
7164
|
+
checked: adherenceDecision.run,
|
|
7165
|
+
reason: adherenceDecision.reason,
|
|
7166
|
+
turn: currentTurn
|
|
7167
|
+
}
|
|
7168
|
+
}
|
|
7001
7169
|
);
|
|
7002
7170
|
}
|
|
7003
|
-
|
|
7004
|
-
if (ENABLE_SEARCH && input.prompt.length > 10) {
|
|
7171
|
+
if (ENABLE_SEARCH && input.prompt.length > 10 && adherenceDecision.run) {
|
|
7005
7172
|
const minScore = getDynamicMinScore(input.prompt);
|
|
7006
|
-
let
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7173
|
+
let mergedMemories = [];
|
|
7174
|
+
const canUseSemantic = RETRIEVAL_MODE === "semantic" || RETRIEVAL_MODE === "hybrid";
|
|
7175
|
+
if (canUseSemantic) {
|
|
7176
|
+
try {
|
|
7177
|
+
const semanticService = getMemoryServiceForSession(input.session_id);
|
|
7178
|
+
const semantic = await withTimeout(
|
|
7179
|
+
semanticService.retrieveMemories(input.prompt, {
|
|
7180
|
+
topK: MAX_MEMORIES,
|
|
7181
|
+
minScore,
|
|
7182
|
+
sessionId: input.session_id,
|
|
7183
|
+
intentRewrite: true,
|
|
7184
|
+
adaptiveRerank: true,
|
|
7185
|
+
projectScopeMode: "strict"
|
|
7186
|
+
}),
|
|
7187
|
+
SEMANTIC_TIMEOUT_MS
|
|
7188
|
+
);
|
|
7189
|
+
mergedMemories = semantic.memories.map((m) => ({
|
|
7190
|
+
type: m.event.eventType,
|
|
7191
|
+
content: m.event.content,
|
|
7192
|
+
id: m.event.id,
|
|
7193
|
+
score: m.score
|
|
7194
|
+
}));
|
|
7195
|
+
} catch {
|
|
7196
|
+
}
|
|
7197
|
+
}
|
|
7198
|
+
const shouldUseKeywordFallback = RETRIEVAL_MODE === "keyword" || RETRIEVAL_MODE === "hybrid" || mergedMemories.length === 0;
|
|
7199
|
+
if (shouldUseKeywordFallback && mergedMemories.length < MAX_MEMORIES) {
|
|
7200
|
+
let results = await memoryService.keywordSearch(input.prompt, {
|
|
7012
7201
|
topK: MAX_MEMORIES,
|
|
7013
|
-
minScore
|
|
7202
|
+
minScore
|
|
7014
7203
|
});
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7204
|
+
if (results.length === 0 && FALLBACK_MIN_SCORE < minScore) {
|
|
7205
|
+
results = await memoryService.keywordSearch(input.prompt, {
|
|
7206
|
+
topK: MAX_MEMORIES,
|
|
7207
|
+
minScore: FALLBACK_MIN_SCORE
|
|
7208
|
+
});
|
|
7209
|
+
}
|
|
7210
|
+
const existingIds = new Set(mergedMemories.map((m) => m.id).filter(Boolean));
|
|
7019
7211
|
for (const r of results) {
|
|
7212
|
+
if (existingIds.has(r.event.id))
|
|
7213
|
+
continue;
|
|
7214
|
+
mergedMemories.push({
|
|
7215
|
+
type: r.event.eventType,
|
|
7216
|
+
content: r.event.content,
|
|
7217
|
+
id: r.event.id,
|
|
7218
|
+
score: r.score
|
|
7219
|
+
});
|
|
7220
|
+
if (mergedMemories.length >= MAX_MEMORIES)
|
|
7221
|
+
break;
|
|
7222
|
+
}
|
|
7223
|
+
}
|
|
7224
|
+
if (mergedMemories.length > 0) {
|
|
7225
|
+
const eventIds = mergedMemories.map((m) => m.id).filter((v) => Boolean(v));
|
|
7226
|
+
if (eventIds.length > 0) {
|
|
7227
|
+
await memoryService.incrementMemoryAccess(eventIds);
|
|
7228
|
+
}
|
|
7229
|
+
for (const m of mergedMemories) {
|
|
7230
|
+
if (!m.id)
|
|
7231
|
+
continue;
|
|
7020
7232
|
try {
|
|
7021
7233
|
await memoryService.recordRetrieval(
|
|
7022
|
-
|
|
7234
|
+
m.id,
|
|
7023
7235
|
input.session_id,
|
|
7024
|
-
|
|
7236
|
+
m.score ?? minScore,
|
|
7025
7237
|
input.prompt
|
|
7026
7238
|
);
|
|
7027
7239
|
} catch {
|
|
7028
7240
|
}
|
|
7029
7241
|
}
|
|
7030
|
-
|
|
7031
|
-
const preview = r.event.content.length > 300 ? r.event.content.substring(0, 300) + "..." : r.event.content;
|
|
7032
|
-
return `- [${r.event.eventType}] ${preview}`;
|
|
7033
|
-
});
|
|
7034
|
-
context = `\u{1F4A1} **Related memories found:**
|
|
7035
|
-
|
|
7036
|
-
${memories.join("\n\n")}`;
|
|
7242
|
+
context = formatMemoryContext(mergedMemories);
|
|
7037
7243
|
}
|
|
7038
7244
|
}
|
|
7245
|
+
writeAdherenceState({
|
|
7246
|
+
sessionId: input.session_id,
|
|
7247
|
+
turnCount: currentTurn,
|
|
7248
|
+
lastCheckedTurn: adherenceDecision.run ? currentTurn : adherenceState.lastCheckedTurn,
|
|
7249
|
+
lastPrompt: input.prompt,
|
|
7250
|
+
lastReason: adherenceDecision.reason,
|
|
7251
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7252
|
+
});
|
|
7039
7253
|
const output = { context };
|
|
7040
7254
|
console.log(JSON.stringify(output));
|
|
7041
7255
|
} catch (error) {
|