@joshuaswarren/openclaw-engram 8.3.35 → 8.3.36
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/index.js +586 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16350,8 +16350,8 @@ mistakes: ${res.mistakesCount} patterns`
|
|
|
16350
16350
|
}
|
|
16351
16351
|
|
|
16352
16352
|
// src/cli.ts
|
|
16353
|
-
import
|
|
16354
|
-
import { access as access2, readFile as
|
|
16353
|
+
import path39 from "path";
|
|
16354
|
+
import { access as access2, readFile as readFile28, readdir as readdir17, unlink as unlink5 } from "fs/promises";
|
|
16355
16355
|
|
|
16356
16356
|
// src/transfer/export-json.ts
|
|
16357
16357
|
import path26 from "path";
|
|
@@ -17230,8 +17230,8 @@ function gatherCandidates(input, warnings) {
|
|
|
17230
17230
|
const record = rec;
|
|
17231
17231
|
const content = typeof record.content === "string" ? record.content : null;
|
|
17232
17232
|
if (!content) continue;
|
|
17233
|
-
const
|
|
17234
|
-
if (!
|
|
17233
|
+
const path41 = typeof record.path === "string" ? record.path : "";
|
|
17234
|
+
if (!path41.startsWith("transcripts/") && !path41.includes("/transcripts/")) continue;
|
|
17235
17235
|
rows.push(...parseJsonl(content, warnings));
|
|
17236
17236
|
}
|
|
17237
17237
|
return rows;
|
|
@@ -17699,6 +17699,351 @@ async function migrateObservations(options) {
|
|
|
17699
17699
|
};
|
|
17700
17700
|
}
|
|
17701
17701
|
|
|
17702
|
+
// src/work/storage.ts
|
|
17703
|
+
import path38 from "path";
|
|
17704
|
+
import { randomUUID } from "crypto";
|
|
17705
|
+
import { mkdir as mkdir27, readdir as readdir16, readFile as readFile27, rm as rm3, writeFile as writeFile24 } from "fs/promises";
|
|
17706
|
+
var TASK_TRANSITIONS = {
|
|
17707
|
+
todo: /* @__PURE__ */ new Set(["in_progress", "blocked", "cancelled"]),
|
|
17708
|
+
in_progress: /* @__PURE__ */ new Set(["todo", "blocked", "done", "cancelled"]),
|
|
17709
|
+
blocked: /* @__PURE__ */ new Set(["todo", "in_progress", "cancelled"]),
|
|
17710
|
+
done: /* @__PURE__ */ new Set(),
|
|
17711
|
+
cancelled: /* @__PURE__ */ new Set()
|
|
17712
|
+
};
|
|
17713
|
+
function serializeFrontmatter2(values) {
|
|
17714
|
+
const lines = Object.entries(values).map(([k, v]) => `${k}: ${JSON.stringify(v)}`);
|
|
17715
|
+
return `---
|
|
17716
|
+
${lines.join("\n")}
|
|
17717
|
+
---`;
|
|
17718
|
+
}
|
|
17719
|
+
function parseFrontmatter3(raw) {
|
|
17720
|
+
const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
17721
|
+
if (!match) return null;
|
|
17722
|
+
const fm = match[1] ?? "";
|
|
17723
|
+
const body = match[2] ?? "";
|
|
17724
|
+
const data = {};
|
|
17725
|
+
for (const line of fm.split("\n")) {
|
|
17726
|
+
if (!line.trim()) continue;
|
|
17727
|
+
const idx = line.indexOf(":");
|
|
17728
|
+
if (idx <= 0) continue;
|
|
17729
|
+
const key = line.slice(0, idx).trim();
|
|
17730
|
+
const rawValue = line.slice(idx + 1).trim();
|
|
17731
|
+
try {
|
|
17732
|
+
data[key] = JSON.parse(rawValue);
|
|
17733
|
+
} catch {
|
|
17734
|
+
data[key] = rawValue;
|
|
17735
|
+
}
|
|
17736
|
+
}
|
|
17737
|
+
return { data, body };
|
|
17738
|
+
}
|
|
17739
|
+
function toSafeSlug(value) {
|
|
17740
|
+
return value.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+/, "").replace(/-+$/, "").slice(0, 80);
|
|
17741
|
+
}
|
|
17742
|
+
var WORK_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9-]{0,127}$/;
|
|
17743
|
+
function makeId(prefix, titleOrName, now) {
|
|
17744
|
+
const slug = toSafeSlug(titleOrName) || "item";
|
|
17745
|
+
const nonce = randomUUID().slice(0, 8);
|
|
17746
|
+
return `${prefix}-${now.getTime()}-${slug}-${nonce}`;
|
|
17747
|
+
}
|
|
17748
|
+
function assertValidWorkId(id, kind) {
|
|
17749
|
+
if (!WORK_ID_PATTERN.test(id)) {
|
|
17750
|
+
throw new Error(`invalid ${kind} id: ${id}`);
|
|
17751
|
+
}
|
|
17752
|
+
}
|
|
17753
|
+
function ensureString(value, fallback = "") {
|
|
17754
|
+
return typeof value === "string" ? value : fallback;
|
|
17755
|
+
}
|
|
17756
|
+
function ensureStringArray(value) {
|
|
17757
|
+
if (!Array.isArray(value)) return [];
|
|
17758
|
+
return value.filter((entry) => typeof entry === "string");
|
|
17759
|
+
}
|
|
17760
|
+
function ensureNullableString(value) {
|
|
17761
|
+
return typeof value === "string" ? value : null;
|
|
17762
|
+
}
|
|
17763
|
+
function ensureTaskStatus(value) {
|
|
17764
|
+
if (value === "todo" || value === "in_progress" || value === "blocked" || value === "done" || value === "cancelled") {
|
|
17765
|
+
return value;
|
|
17766
|
+
}
|
|
17767
|
+
return "todo";
|
|
17768
|
+
}
|
|
17769
|
+
function ensureTaskPriority(value) {
|
|
17770
|
+
if (value === "low" || value === "medium" || value === "high") return value;
|
|
17771
|
+
return "medium";
|
|
17772
|
+
}
|
|
17773
|
+
function ensureProjectStatus(value) {
|
|
17774
|
+
if (value === "active" || value === "on_hold" || value === "completed" || value === "archived") return value;
|
|
17775
|
+
return "active";
|
|
17776
|
+
}
|
|
17777
|
+
var WorkStorage = class {
|
|
17778
|
+
constructor(memoryDir) {
|
|
17779
|
+
this.memoryDir = memoryDir;
|
|
17780
|
+
this.tasksDir = path38.join(memoryDir, "work", "tasks");
|
|
17781
|
+
this.projectsDir = path38.join(memoryDir, "work", "projects");
|
|
17782
|
+
}
|
|
17783
|
+
tasksDir;
|
|
17784
|
+
projectsDir;
|
|
17785
|
+
async ensureDirectories() {
|
|
17786
|
+
await mkdir27(this.tasksDir, { recursive: true });
|
|
17787
|
+
await mkdir27(this.projectsDir, { recursive: true });
|
|
17788
|
+
}
|
|
17789
|
+
taskPath(id) {
|
|
17790
|
+
assertValidWorkId(id, "task");
|
|
17791
|
+
return path38.join(this.tasksDir, `${id}.md`);
|
|
17792
|
+
}
|
|
17793
|
+
projectPath(id) {
|
|
17794
|
+
assertValidWorkId(id, "project");
|
|
17795
|
+
return path38.join(this.projectsDir, `${id}.md`);
|
|
17796
|
+
}
|
|
17797
|
+
serializeTask(task) {
|
|
17798
|
+
return `${serializeFrontmatter2(task)}
|
|
17799
|
+
|
|
17800
|
+
${task.description}
|
|
17801
|
+
`;
|
|
17802
|
+
}
|
|
17803
|
+
serializeProject(project) {
|
|
17804
|
+
return `${serializeFrontmatter2(project)}
|
|
17805
|
+
|
|
17806
|
+
${project.description}
|
|
17807
|
+
`;
|
|
17808
|
+
}
|
|
17809
|
+
parseTask(raw) {
|
|
17810
|
+
const parsed = parseFrontmatter3(raw);
|
|
17811
|
+
if (!parsed) return null;
|
|
17812
|
+
const d = parsed.data;
|
|
17813
|
+
return {
|
|
17814
|
+
id: ensureString(d.id),
|
|
17815
|
+
title: ensureString(d.title),
|
|
17816
|
+
description: ensureString(d.description, parsed.body.trim()),
|
|
17817
|
+
status: ensureTaskStatus(d.status),
|
|
17818
|
+
priority: ensureTaskPriority(d.priority),
|
|
17819
|
+
owner: ensureNullableString(d.owner),
|
|
17820
|
+
assignee: ensureNullableString(d.assignee),
|
|
17821
|
+
projectId: ensureNullableString(d.projectId),
|
|
17822
|
+
tags: ensureStringArray(d.tags),
|
|
17823
|
+
dueAt: ensureNullableString(d.dueAt),
|
|
17824
|
+
createdAt: ensureString(d.createdAt),
|
|
17825
|
+
updatedAt: ensureString(d.updatedAt)
|
|
17826
|
+
};
|
|
17827
|
+
}
|
|
17828
|
+
parseProject(raw) {
|
|
17829
|
+
const parsed = parseFrontmatter3(raw);
|
|
17830
|
+
if (!parsed) return null;
|
|
17831
|
+
const d = parsed.data;
|
|
17832
|
+
return {
|
|
17833
|
+
id: ensureString(d.id),
|
|
17834
|
+
name: ensureString(d.name),
|
|
17835
|
+
description: ensureString(d.description, parsed.body.trim()),
|
|
17836
|
+
status: ensureProjectStatus(d.status),
|
|
17837
|
+
owner: ensureNullableString(d.owner),
|
|
17838
|
+
tags: ensureStringArray(d.tags),
|
|
17839
|
+
taskIds: ensureStringArray(d.taskIds),
|
|
17840
|
+
createdAt: ensureString(d.createdAt),
|
|
17841
|
+
updatedAt: ensureString(d.updatedAt)
|
|
17842
|
+
};
|
|
17843
|
+
}
|
|
17844
|
+
async createTask(input, now = /* @__PURE__ */ new Date()) {
|
|
17845
|
+
await this.ensureDirectories();
|
|
17846
|
+
const timestamp = now.toISOString();
|
|
17847
|
+
const task = {
|
|
17848
|
+
id: input.id ?? makeId("task", input.title, now),
|
|
17849
|
+
title: input.title,
|
|
17850
|
+
description: input.description ?? "",
|
|
17851
|
+
status: input.status ?? "todo",
|
|
17852
|
+
priority: input.priority ?? "medium",
|
|
17853
|
+
owner: input.owner ?? null,
|
|
17854
|
+
assignee: input.assignee ?? null,
|
|
17855
|
+
projectId: input.projectId ?? null,
|
|
17856
|
+
tags: input.tags ?? [],
|
|
17857
|
+
dueAt: input.dueAt ?? null,
|
|
17858
|
+
createdAt: timestamp,
|
|
17859
|
+
updatedAt: timestamp
|
|
17860
|
+
};
|
|
17861
|
+
if (task.projectId) {
|
|
17862
|
+
const project = await this.getProject(task.projectId);
|
|
17863
|
+
if (!project) {
|
|
17864
|
+
throw new Error(`project not found: ${task.projectId}`);
|
|
17865
|
+
}
|
|
17866
|
+
}
|
|
17867
|
+
await writeFile24(this.taskPath(task.id), this.serializeTask(task), "utf-8");
|
|
17868
|
+
if (task.projectId) {
|
|
17869
|
+
await this.addTaskIdToProject(task.projectId, task.id, now);
|
|
17870
|
+
}
|
|
17871
|
+
return task;
|
|
17872
|
+
}
|
|
17873
|
+
async getTask(id) {
|
|
17874
|
+
try {
|
|
17875
|
+
const raw = await readFile27(this.taskPath(id), "utf-8");
|
|
17876
|
+
return this.parseTask(raw);
|
|
17877
|
+
} catch {
|
|
17878
|
+
return null;
|
|
17879
|
+
}
|
|
17880
|
+
}
|
|
17881
|
+
async listTasks(filter) {
|
|
17882
|
+
await this.ensureDirectories();
|
|
17883
|
+
const entries = await readdir16(this.tasksDir, { withFileTypes: true });
|
|
17884
|
+
const out = [];
|
|
17885
|
+
for (const entry of entries) {
|
|
17886
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
17887
|
+
const raw = await readFile27(path38.join(this.tasksDir, entry.name), "utf-8");
|
|
17888
|
+
const task = this.parseTask(raw);
|
|
17889
|
+
if (!task) continue;
|
|
17890
|
+
if (filter?.status && task.status !== filter.status) continue;
|
|
17891
|
+
if (filter?.owner && task.owner !== filter.owner) continue;
|
|
17892
|
+
if (filter?.assignee && task.assignee !== filter.assignee) continue;
|
|
17893
|
+
if (filter?.projectId && task.projectId !== filter.projectId) continue;
|
|
17894
|
+
out.push(task);
|
|
17895
|
+
}
|
|
17896
|
+
out.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
17897
|
+
return out;
|
|
17898
|
+
}
|
|
17899
|
+
async updateTask(id, patch, now = /* @__PURE__ */ new Date()) {
|
|
17900
|
+
const existing = await this.getTask(id);
|
|
17901
|
+
if (!existing) return null;
|
|
17902
|
+
const projectIdPatched = Object.prototype.hasOwnProperty.call(patch, "projectId");
|
|
17903
|
+
const statusPatched = Object.prototype.hasOwnProperty.call(patch, "status");
|
|
17904
|
+
const nextProjectId = projectIdPatched ? patch.projectId ?? null : existing.projectId;
|
|
17905
|
+
if (statusPatched && patch.status && existing.status !== patch.status && !TASK_TRANSITIONS[existing.status].has(patch.status)) {
|
|
17906
|
+
throw new Error(`invalid task status transition: ${existing.status} -> ${patch.status}`);
|
|
17907
|
+
}
|
|
17908
|
+
if (projectIdPatched && nextProjectId) {
|
|
17909
|
+
const nextProject = await this.getProject(nextProjectId);
|
|
17910
|
+
if (!nextProject) {
|
|
17911
|
+
throw new Error(`project not found: ${nextProjectId}`);
|
|
17912
|
+
}
|
|
17913
|
+
}
|
|
17914
|
+
if (projectIdPatched && existing.projectId !== nextProjectId) {
|
|
17915
|
+
if (existing.projectId) {
|
|
17916
|
+
await this.removeTaskIdFromProject(existing.projectId, id, now);
|
|
17917
|
+
}
|
|
17918
|
+
if (nextProjectId) {
|
|
17919
|
+
await this.addTaskIdToProject(nextProjectId, id, now);
|
|
17920
|
+
}
|
|
17921
|
+
}
|
|
17922
|
+
const next = {
|
|
17923
|
+
...existing,
|
|
17924
|
+
...patch,
|
|
17925
|
+
projectId: nextProjectId,
|
|
17926
|
+
tags: patch.tags ?? existing.tags,
|
|
17927
|
+
updatedAt: now.toISOString()
|
|
17928
|
+
};
|
|
17929
|
+
await writeFile24(this.taskPath(id), this.serializeTask(next), "utf-8");
|
|
17930
|
+
return next;
|
|
17931
|
+
}
|
|
17932
|
+
async transitionTask(id, nextStatus, now = /* @__PURE__ */ new Date()) {
|
|
17933
|
+
const existing = await this.getTask(id);
|
|
17934
|
+
if (!existing) throw new Error(`task not found: ${id}`);
|
|
17935
|
+
if (existing.status === nextStatus) return existing;
|
|
17936
|
+
if (!TASK_TRANSITIONS[existing.status].has(nextStatus)) {
|
|
17937
|
+
throw new Error(`invalid task status transition: ${existing.status} -> ${nextStatus}`);
|
|
17938
|
+
}
|
|
17939
|
+
const updated = await this.updateTask(id, { status: nextStatus }, now);
|
|
17940
|
+
if (!updated) throw new Error(`task not found after update: ${id}`);
|
|
17941
|
+
return updated;
|
|
17942
|
+
}
|
|
17943
|
+
async deleteTask(id) {
|
|
17944
|
+
try {
|
|
17945
|
+
const existing = await this.getTask(id);
|
|
17946
|
+
await rm3(this.taskPath(id));
|
|
17947
|
+
if (existing?.projectId) {
|
|
17948
|
+
await this.removeTaskIdFromProject(existing.projectId, id);
|
|
17949
|
+
}
|
|
17950
|
+
return true;
|
|
17951
|
+
} catch {
|
|
17952
|
+
return false;
|
|
17953
|
+
}
|
|
17954
|
+
}
|
|
17955
|
+
async createProject(input, now = /* @__PURE__ */ new Date()) {
|
|
17956
|
+
await this.ensureDirectories();
|
|
17957
|
+
const timestamp = now.toISOString();
|
|
17958
|
+
const project = {
|
|
17959
|
+
id: input.id ?? makeId("project", input.name, now),
|
|
17960
|
+
name: input.name,
|
|
17961
|
+
description: input.description ?? "",
|
|
17962
|
+
status: input.status ?? "active",
|
|
17963
|
+
owner: input.owner ?? null,
|
|
17964
|
+
tags: input.tags ?? [],
|
|
17965
|
+
taskIds: [],
|
|
17966
|
+
createdAt: timestamp,
|
|
17967
|
+
updatedAt: timestamp
|
|
17968
|
+
};
|
|
17969
|
+
await writeFile24(this.projectPath(project.id), this.serializeProject(project), "utf-8");
|
|
17970
|
+
return project;
|
|
17971
|
+
}
|
|
17972
|
+
async getProject(id) {
|
|
17973
|
+
try {
|
|
17974
|
+
const raw = await readFile27(this.projectPath(id), "utf-8");
|
|
17975
|
+
return this.parseProject(raw);
|
|
17976
|
+
} catch {
|
|
17977
|
+
return null;
|
|
17978
|
+
}
|
|
17979
|
+
}
|
|
17980
|
+
async listProjects() {
|
|
17981
|
+
await this.ensureDirectories();
|
|
17982
|
+
const entries = await readdir16(this.projectsDir, { withFileTypes: true });
|
|
17983
|
+
const out = [];
|
|
17984
|
+
for (const entry of entries) {
|
|
17985
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
17986
|
+
const raw = await readFile27(path38.join(this.projectsDir, entry.name), "utf-8");
|
|
17987
|
+
const project = this.parseProject(raw);
|
|
17988
|
+
if (project) out.push(project);
|
|
17989
|
+
}
|
|
17990
|
+
out.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
17991
|
+
return out;
|
|
17992
|
+
}
|
|
17993
|
+
async updateProject(id, patch, now = /* @__PURE__ */ new Date()) {
|
|
17994
|
+
const existing = await this.getProject(id);
|
|
17995
|
+
if (!existing) return null;
|
|
17996
|
+
const next = {
|
|
17997
|
+
...existing,
|
|
17998
|
+
...patch,
|
|
17999
|
+
tags: patch.tags ?? existing.tags,
|
|
18000
|
+
taskIds: patch.taskIds ? [...patch.taskIds].sort() : existing.taskIds,
|
|
18001
|
+
updatedAt: now.toISOString()
|
|
18002
|
+
};
|
|
18003
|
+
await writeFile24(this.projectPath(id), this.serializeProject(next), "utf-8");
|
|
18004
|
+
return next;
|
|
18005
|
+
}
|
|
18006
|
+
async deleteProject(id) {
|
|
18007
|
+
try {
|
|
18008
|
+
const existing = await this.getProject(id);
|
|
18009
|
+
if (existing) {
|
|
18010
|
+
for (const taskId of existing.taskIds) {
|
|
18011
|
+
await this.updateTask(taskId, { projectId: null });
|
|
18012
|
+
}
|
|
18013
|
+
}
|
|
18014
|
+
await rm3(this.projectPath(id));
|
|
18015
|
+
return true;
|
|
18016
|
+
} catch {
|
|
18017
|
+
return false;
|
|
18018
|
+
}
|
|
18019
|
+
}
|
|
18020
|
+
async linkTaskToProject(taskId, projectId, now = /* @__PURE__ */ new Date()) {
|
|
18021
|
+
const task = await this.getTask(taskId);
|
|
18022
|
+
if (!task) throw new Error(`task not found: ${taskId}`);
|
|
18023
|
+
const project = await this.getProject(projectId);
|
|
18024
|
+
if (!project) throw new Error(`project not found: ${projectId}`);
|
|
18025
|
+
const updatedTask = await this.updateTask(taskId, { projectId }, now);
|
|
18026
|
+
if (!updatedTask) throw new Error(`task not found after update: ${taskId}`);
|
|
18027
|
+
const updatedProject = await this.getProject(projectId);
|
|
18028
|
+
if (!updatedProject) throw new Error(`project not found after update: ${projectId}`);
|
|
18029
|
+
return { task: updatedTask, project: updatedProject };
|
|
18030
|
+
}
|
|
18031
|
+
async removeTaskIdFromProject(projectId, taskId, now = /* @__PURE__ */ new Date()) {
|
|
18032
|
+
const project = await this.getProject(projectId);
|
|
18033
|
+
if (!project) return;
|
|
18034
|
+
const filtered = project.taskIds.filter((id) => id !== taskId);
|
|
18035
|
+
if (filtered.length === project.taskIds.length) return;
|
|
18036
|
+
await this.updateProject(projectId, { taskIds: filtered }, now);
|
|
18037
|
+
}
|
|
18038
|
+
async addTaskIdToProject(projectId, taskId, now = /* @__PURE__ */ new Date()) {
|
|
18039
|
+
const project = await this.getProject(projectId);
|
|
18040
|
+
if (!project) return;
|
|
18041
|
+
const taskIds = new Set(project.taskIds);
|
|
18042
|
+
taskIds.add(taskId);
|
|
18043
|
+
await this.updateProject(projectId, { taskIds: Array.from(taskIds) }, now);
|
|
18044
|
+
}
|
|
18045
|
+
};
|
|
18046
|
+
|
|
17702
18047
|
// src/cli.ts
|
|
17703
18048
|
function rankCandidateForKeep(a, b) {
|
|
17704
18049
|
const aConfidence = typeof a.frontmatter.confidence === "number" ? a.frontmatter.confidence : 0;
|
|
@@ -17748,6 +18093,29 @@ function planExactDuplicateDeletions(memories) {
|
|
|
17748
18093
|
function planAggressiveDuplicateDeletions(memories) {
|
|
17749
18094
|
return buildDedupePlan(memories, (memory) => normalizeAggressiveBody(memory.content));
|
|
17750
18095
|
}
|
|
18096
|
+
function normalizeNullableCliValue(value) {
|
|
18097
|
+
if (value === void 0) return void 0;
|
|
18098
|
+
const trimmed = value.trim();
|
|
18099
|
+
if (trimmed.length === 0 || trimmed.toLowerCase() === "null") return null;
|
|
18100
|
+
return trimmed;
|
|
18101
|
+
}
|
|
18102
|
+
function parseTagsCsv(raw, preserveEmpty = false) {
|
|
18103
|
+
if (raw === void 0) return void 0;
|
|
18104
|
+
const tags = raw.split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0);
|
|
18105
|
+
if (tags.length === 0) {
|
|
18106
|
+
return preserveEmpty ? [] : void 0;
|
|
18107
|
+
}
|
|
18108
|
+
return tags;
|
|
18109
|
+
}
|
|
18110
|
+
function isWorkTaskStatus(value) {
|
|
18111
|
+
return value === "todo" || value === "in_progress" || value === "blocked" || value === "done" || value === "cancelled";
|
|
18112
|
+
}
|
|
18113
|
+
function isWorkTaskPriority(value) {
|
|
18114
|
+
return value === "low" || value === "medium" || value === "high";
|
|
18115
|
+
}
|
|
18116
|
+
function isWorkProjectStatus(value) {
|
|
18117
|
+
return value === "active" || value === "on_hold" || value === "completed" || value === "archived";
|
|
18118
|
+
}
|
|
17751
18119
|
async function runArchiveObservationsCliCommand(options) {
|
|
17752
18120
|
return archiveObservations({
|
|
17753
18121
|
memoryDir: options.memoryDir,
|
|
@@ -17770,6 +18138,128 @@ async function runMigrateObservationsCliCommand(options) {
|
|
|
17770
18138
|
now: options.now
|
|
17771
18139
|
});
|
|
17772
18140
|
}
|
|
18141
|
+
async function runWorkTaskCliCommand(options) {
|
|
18142
|
+
const storage = new WorkStorage(options.memoryDir);
|
|
18143
|
+
if (options.action === "create") {
|
|
18144
|
+
if (!options.title || options.title.trim().length === 0) throw new Error("missing title");
|
|
18145
|
+
if (options.status !== void 0 && !isWorkTaskStatus(options.status)) throw new Error(`invalid task status: ${options.status}`);
|
|
18146
|
+
if (options.priority !== void 0 && !isWorkTaskPriority(options.priority)) {
|
|
18147
|
+
throw new Error(`invalid task priority: ${options.priority}`);
|
|
18148
|
+
}
|
|
18149
|
+
const explicitId = options.id?.trim();
|
|
18150
|
+
if (explicitId && explicitId.length > 0) {
|
|
18151
|
+
const existing = await storage.getTask(explicitId);
|
|
18152
|
+
if (existing) throw new Error(`task already exists: ${explicitId}`);
|
|
18153
|
+
}
|
|
18154
|
+
return storage.createTask({
|
|
18155
|
+
id: explicitId && explicitId.length > 0 ? explicitId : void 0,
|
|
18156
|
+
title: options.title.trim(),
|
|
18157
|
+
description: options.description?.trim(),
|
|
18158
|
+
status: options.status,
|
|
18159
|
+
priority: options.priority,
|
|
18160
|
+
owner: normalizeNullableCliValue(options.owner),
|
|
18161
|
+
assignee: normalizeNullableCliValue(options.assignee),
|
|
18162
|
+
projectId: normalizeNullableCliValue(options.projectId),
|
|
18163
|
+
tags: options.tags,
|
|
18164
|
+
dueAt: normalizeNullableCliValue(options.dueAt)
|
|
18165
|
+
});
|
|
18166
|
+
}
|
|
18167
|
+
if (options.action === "get") {
|
|
18168
|
+
if (!options.id || options.id.trim().length === 0) throw new Error("missing id");
|
|
18169
|
+
return storage.getTask(options.id.trim());
|
|
18170
|
+
}
|
|
18171
|
+
if (options.action === "list") {
|
|
18172
|
+
if (options.status !== void 0 && !isWorkTaskStatus(options.status)) throw new Error(`invalid task status: ${options.status}`);
|
|
18173
|
+
return storage.listTasks({
|
|
18174
|
+
status: options.status,
|
|
18175
|
+
owner: options.owner?.trim() || void 0,
|
|
18176
|
+
assignee: options.assignee?.trim() || void 0,
|
|
18177
|
+
projectId: options.projectId?.trim() || void 0
|
|
18178
|
+
});
|
|
18179
|
+
}
|
|
18180
|
+
if (options.action === "update") {
|
|
18181
|
+
if (!options.id || options.id.trim().length === 0) throw new Error("missing id");
|
|
18182
|
+
const patch = options.patch ?? {};
|
|
18183
|
+
if (patch.status !== void 0 && !isWorkTaskStatus(patch.status)) throw new Error(`invalid task status: ${patch.status}`);
|
|
18184
|
+
if (patch.priority !== void 0 && !isWorkTaskPriority(patch.priority)) {
|
|
18185
|
+
throw new Error(`invalid task priority: ${patch.priority}`);
|
|
18186
|
+
}
|
|
18187
|
+
const sparsePatch = {};
|
|
18188
|
+
if (Object.prototype.hasOwnProperty.call(patch, "title")) sparsePatch.title = patch.title;
|
|
18189
|
+
if (Object.prototype.hasOwnProperty.call(patch, "description")) sparsePatch.description = patch.description;
|
|
18190
|
+
if (Object.prototype.hasOwnProperty.call(patch, "status")) sparsePatch.status = patch.status;
|
|
18191
|
+
if (Object.prototype.hasOwnProperty.call(patch, "priority")) sparsePatch.priority = patch.priority;
|
|
18192
|
+
if (Object.prototype.hasOwnProperty.call(patch, "owner")) sparsePatch.owner = patch.owner;
|
|
18193
|
+
if (Object.prototype.hasOwnProperty.call(patch, "assignee")) sparsePatch.assignee = patch.assignee;
|
|
18194
|
+
if (Object.prototype.hasOwnProperty.call(patch, "projectId")) sparsePatch.projectId = patch.projectId;
|
|
18195
|
+
if (Object.prototype.hasOwnProperty.call(patch, "tags")) sparsePatch.tags = patch.tags;
|
|
18196
|
+
if (Object.prototype.hasOwnProperty.call(patch, "dueAt")) sparsePatch.dueAt = patch.dueAt;
|
|
18197
|
+
return storage.updateTask(options.id.trim(), sparsePatch);
|
|
18198
|
+
}
|
|
18199
|
+
if (options.action === "transition") {
|
|
18200
|
+
if (!options.id || options.id.trim().length === 0) throw new Error("missing id");
|
|
18201
|
+
if (!options.status || !isWorkTaskStatus(options.status)) throw new Error(`invalid task status: ${options.status}`);
|
|
18202
|
+
return storage.transitionTask(options.id.trim(), options.status);
|
|
18203
|
+
}
|
|
18204
|
+
if (options.action === "delete") {
|
|
18205
|
+
if (!options.id || options.id.trim().length === 0) throw new Error("missing id");
|
|
18206
|
+
return storage.deleteTask(options.id.trim());
|
|
18207
|
+
}
|
|
18208
|
+
if (options.action === "link") {
|
|
18209
|
+
if (!options.id || options.id.trim().length === 0) throw new Error("missing id");
|
|
18210
|
+
if (!options.projectId || options.projectId.trim().length === 0) throw new Error("missing projectId");
|
|
18211
|
+
return storage.linkTaskToProject(options.id.trim(), options.projectId.trim());
|
|
18212
|
+
}
|
|
18213
|
+
throw new Error(`unsupported task action: ${options.action}`);
|
|
18214
|
+
}
|
|
18215
|
+
async function runWorkProjectCliCommand(options) {
|
|
18216
|
+
const storage = new WorkStorage(options.memoryDir);
|
|
18217
|
+
if (options.action === "create") {
|
|
18218
|
+
if (!options.name || options.name.trim().length === 0) throw new Error("missing name");
|
|
18219
|
+
if (options.status !== void 0 && !isWorkProjectStatus(options.status)) {
|
|
18220
|
+
throw new Error(`invalid project status: ${options.status}`);
|
|
18221
|
+
}
|
|
18222
|
+
const explicitId = options.id?.trim();
|
|
18223
|
+
if (explicitId && explicitId.length > 0) {
|
|
18224
|
+
const existing = await storage.getProject(explicitId);
|
|
18225
|
+
if (existing) throw new Error(`project already exists: ${explicitId}`);
|
|
18226
|
+
}
|
|
18227
|
+
return storage.createProject({
|
|
18228
|
+
id: explicitId && explicitId.length > 0 ? explicitId : void 0,
|
|
18229
|
+
name: options.name.trim(),
|
|
18230
|
+
description: options.description?.trim(),
|
|
18231
|
+
status: options.status,
|
|
18232
|
+
owner: normalizeNullableCliValue(options.owner),
|
|
18233
|
+
tags: options.tags
|
|
18234
|
+
});
|
|
18235
|
+
}
|
|
18236
|
+
if (options.action === "get") {
|
|
18237
|
+
if (!options.id || options.id.trim().length === 0) throw new Error("missing id");
|
|
18238
|
+
return storage.getProject(options.id.trim());
|
|
18239
|
+
}
|
|
18240
|
+
if (options.action === "list") {
|
|
18241
|
+
return storage.listProjects();
|
|
18242
|
+
}
|
|
18243
|
+
if (options.action === "update") {
|
|
18244
|
+
if (!options.id || options.id.trim().length === 0) throw new Error("missing id");
|
|
18245
|
+
const patch = options.patch ?? {};
|
|
18246
|
+
if (patch.status !== void 0 && !isWorkProjectStatus(patch.status)) {
|
|
18247
|
+
throw new Error(`invalid project status: ${patch.status}`);
|
|
18248
|
+
}
|
|
18249
|
+
const sparsePatch = {};
|
|
18250
|
+
if (Object.prototype.hasOwnProperty.call(patch, "name")) sparsePatch.name = patch.name;
|
|
18251
|
+
if (Object.prototype.hasOwnProperty.call(patch, "description")) sparsePatch.description = patch.description;
|
|
18252
|
+
if (Object.prototype.hasOwnProperty.call(patch, "status")) sparsePatch.status = patch.status;
|
|
18253
|
+
if (Object.prototype.hasOwnProperty.call(patch, "owner")) sparsePatch.owner = patch.owner;
|
|
18254
|
+
if (Object.prototype.hasOwnProperty.call(patch, "tags")) sparsePatch.tags = patch.tags;
|
|
18255
|
+
return storage.updateProject(options.id.trim(), sparsePatch);
|
|
18256
|
+
}
|
|
18257
|
+
if (options.action === "delete") {
|
|
18258
|
+
if (!options.id || options.id.trim().length === 0) throw new Error("missing id");
|
|
18259
|
+
return storage.deleteProject(options.id.trim());
|
|
18260
|
+
}
|
|
18261
|
+
throw new Error(`unsupported project action: ${options.action}`);
|
|
18262
|
+
}
|
|
17773
18263
|
async function withTimeout(promise, timeoutMs, timeoutMessage) {
|
|
17774
18264
|
let timer;
|
|
17775
18265
|
try {
|
|
@@ -17785,7 +18275,7 @@ async function withTimeout(promise, timeoutMs, timeoutMessage) {
|
|
|
17785
18275
|
}
|
|
17786
18276
|
async function runReplayCliCommand(orchestrator, options) {
|
|
17787
18277
|
const extractionIdleTimeoutMs = Number.isFinite(options.extractionIdleTimeoutMs) ? Math.max(1e3, Math.floor(options.extractionIdleTimeoutMs)) : 15 * 6e4;
|
|
17788
|
-
const inputRaw = await
|
|
18278
|
+
const inputRaw = await readFile28(options.inputPath, "utf-8");
|
|
17789
18279
|
const registry = buildReplayNormalizerRegistry([
|
|
17790
18280
|
openclawReplayNormalizer,
|
|
17791
18281
|
claudeReplayNormalizer,
|
|
@@ -17850,7 +18340,7 @@ async function runReplayCliCommand(orchestrator, options) {
|
|
|
17850
18340
|
async function getPluginVersion() {
|
|
17851
18341
|
try {
|
|
17852
18342
|
const pkgPath = new URL("../package.json", import.meta.url);
|
|
17853
|
-
const raw = await
|
|
18343
|
+
const raw = await readFile28(pkgPath, "utf-8");
|
|
17854
18344
|
const parsed = JSON.parse(raw);
|
|
17855
18345
|
return parsed.version ?? "unknown";
|
|
17856
18346
|
} catch {
|
|
@@ -17869,32 +18359,32 @@ async function resolveMemoryDirForNamespace(orchestrator, namespace) {
|
|
|
17869
18359
|
const ns = (namespace ?? "").trim();
|
|
17870
18360
|
if (!ns) return orchestrator.config.memoryDir;
|
|
17871
18361
|
if (!orchestrator.config.namespacesEnabled) return orchestrator.config.memoryDir;
|
|
17872
|
-
const candidate =
|
|
18362
|
+
const candidate = path39.join(orchestrator.config.memoryDir, "namespaces", ns);
|
|
17873
18363
|
if (ns === orchestrator.config.defaultNamespace) {
|
|
17874
18364
|
return await exists2(candidate) ? candidate : orchestrator.config.memoryDir;
|
|
17875
18365
|
}
|
|
17876
18366
|
return candidate;
|
|
17877
18367
|
}
|
|
17878
18368
|
async function readAllMemoryFiles(memoryDir) {
|
|
17879
|
-
const roots = [
|
|
18369
|
+
const roots = [path39.join(memoryDir, "facts"), path39.join(memoryDir, "corrections")];
|
|
17880
18370
|
const out = [];
|
|
17881
18371
|
const walk = async (dir) => {
|
|
17882
18372
|
let entries;
|
|
17883
18373
|
try {
|
|
17884
|
-
entries = await
|
|
18374
|
+
entries = await readdir17(dir, { withFileTypes: true });
|
|
17885
18375
|
} catch {
|
|
17886
18376
|
return;
|
|
17887
18377
|
}
|
|
17888
18378
|
for (const entry of entries) {
|
|
17889
18379
|
const entryName = typeof entry.name === "string" ? entry.name : entry.name.toString("utf-8");
|
|
17890
|
-
const fullPath =
|
|
18380
|
+
const fullPath = path39.join(dir, entryName);
|
|
17891
18381
|
if (entry.isDirectory()) {
|
|
17892
18382
|
await walk(fullPath);
|
|
17893
18383
|
continue;
|
|
17894
18384
|
}
|
|
17895
18385
|
if (!entry.isFile() || !entryName.endsWith(".md")) continue;
|
|
17896
18386
|
try {
|
|
17897
|
-
const raw = await
|
|
18387
|
+
const raw = await readFile28(fullPath, "utf-8");
|
|
17898
18388
|
const parsed = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
17899
18389
|
if (!parsed) continue;
|
|
17900
18390
|
const fmRaw = parsed[1];
|
|
@@ -18191,6 +18681,82 @@ function registerCli(api, orchestrator) {
|
|
|
18191
18681
|
if (result.backupPath) console.log(`Backup path: ${result.backupPath}`);
|
|
18192
18682
|
console.log("OK");
|
|
18193
18683
|
});
|
|
18684
|
+
cmd.command("task").description("Manage work tasks").argument("<action>", "create|get|list|update|transition|delete|link").option("--id <id>", "Task ID").option("--title <title>", "Task title").option("--description <description>", "Task description").option("--status <status>", "Task status").option("--priority <priority>", "Task priority").option("--owner <owner>", "Task owner").option("--assignee <assignee>", "Task assignee").option("--project-id <projectId>", "Project ID").option("--tags <csv>", "Comma-separated tags").option("--due-at <iso>", "Due timestamp (ISO)").action(async (...args) => {
|
|
18685
|
+
const actionRaw = typeof args[0] === "string" ? args[0].trim().toLowerCase() : "";
|
|
18686
|
+
const options = args[1] ?? {};
|
|
18687
|
+
const statusOptRaw = typeof options.status === "string" ? options.status.trim().toLowerCase() : void 0;
|
|
18688
|
+
const priorityOptRaw = typeof options.priority === "string" ? options.priority.trim().toLowerCase() : void 0;
|
|
18689
|
+
if (statusOptRaw !== void 0 && !isWorkTaskStatus(statusOptRaw)) {
|
|
18690
|
+
throw new Error(`invalid task status: ${statusOptRaw}`);
|
|
18691
|
+
}
|
|
18692
|
+
if (priorityOptRaw !== void 0 && !isWorkTaskPriority(priorityOptRaw)) {
|
|
18693
|
+
throw new Error(`invalid task priority: ${priorityOptRaw}`);
|
|
18694
|
+
}
|
|
18695
|
+
const patch = {};
|
|
18696
|
+
if (typeof options.title === "string") patch.title = options.title.trim();
|
|
18697
|
+
if (typeof options.description === "string") patch.description = options.description.trim();
|
|
18698
|
+
if (statusOptRaw !== void 0) patch.status = statusOptRaw;
|
|
18699
|
+
if (priorityOptRaw !== void 0) patch.priority = priorityOptRaw;
|
|
18700
|
+
if (typeof options.owner === "string") patch.owner = normalizeNullableCliValue(options.owner) ?? null;
|
|
18701
|
+
if (typeof options.assignee === "string") patch.assignee = normalizeNullableCliValue(options.assignee) ?? null;
|
|
18702
|
+
if (typeof options.projectId === "string") patch.projectId = normalizeNullableCliValue(options.projectId) ?? null;
|
|
18703
|
+
if (typeof options.tags === "string") {
|
|
18704
|
+
patch.tags = parseTagsCsv(options.tags, true);
|
|
18705
|
+
}
|
|
18706
|
+
if (typeof options.dueAt === "string") patch.dueAt = normalizeNullableCliValue(options.dueAt) ?? null;
|
|
18707
|
+
const result = await runWorkTaskCliCommand({
|
|
18708
|
+
memoryDir: orchestrator.config.memoryDir,
|
|
18709
|
+
action: actionRaw,
|
|
18710
|
+
id: typeof options.id === "string" ? options.id : void 0,
|
|
18711
|
+
title: typeof options.title === "string" ? options.title : void 0,
|
|
18712
|
+
description: typeof options.description === "string" ? options.description : void 0,
|
|
18713
|
+
status: statusOptRaw,
|
|
18714
|
+
priority: priorityOptRaw,
|
|
18715
|
+
owner: typeof options.owner === "string" ? options.owner : void 0,
|
|
18716
|
+
assignee: typeof options.assignee === "string" ? options.assignee : void 0,
|
|
18717
|
+
projectId: typeof options.projectId === "string" ? options.projectId : void 0,
|
|
18718
|
+
tags: typeof options.tags === "string" ? parseTagsCsv(options.tags, true) : void 0,
|
|
18719
|
+
dueAt: typeof options.dueAt === "string" ? options.dueAt : void 0,
|
|
18720
|
+
patch
|
|
18721
|
+
});
|
|
18722
|
+
if (Array.isArray(result)) {
|
|
18723
|
+
console.log(`Count: ${result.length}`);
|
|
18724
|
+
}
|
|
18725
|
+
console.log(JSON.stringify(result, null, 2));
|
|
18726
|
+
console.log("OK");
|
|
18727
|
+
});
|
|
18728
|
+
cmd.command("project").description("Manage work projects").argument("<action>", "create|get|list|update|delete").option("--id <id>", "Project ID").option("--name <name>", "Project name").option("--description <description>", "Project description").option("--status <status>", "Project status").option("--owner <owner>", "Project owner").option("--tags <csv>", "Comma-separated tags").action(async (...args) => {
|
|
18729
|
+
const actionRaw = typeof args[0] === "string" ? args[0].trim().toLowerCase() : "";
|
|
18730
|
+
const options = args[1] ?? {};
|
|
18731
|
+
const statusOptRaw = typeof options.status === "string" ? options.status.trim().toLowerCase() : void 0;
|
|
18732
|
+
if (statusOptRaw !== void 0 && !isWorkProjectStatus(statusOptRaw)) {
|
|
18733
|
+
throw new Error(`invalid project status: ${statusOptRaw}`);
|
|
18734
|
+
}
|
|
18735
|
+
const patch = {};
|
|
18736
|
+
if (typeof options.name === "string") patch.name = options.name.trim();
|
|
18737
|
+
if (typeof options.description === "string") patch.description = options.description.trim();
|
|
18738
|
+
if (statusOptRaw !== void 0) patch.status = statusOptRaw;
|
|
18739
|
+
if (typeof options.owner === "string") patch.owner = normalizeNullableCliValue(options.owner) ?? null;
|
|
18740
|
+
if (typeof options.tags === "string") {
|
|
18741
|
+
patch.tags = parseTagsCsv(options.tags, true);
|
|
18742
|
+
}
|
|
18743
|
+
const result = await runWorkProjectCliCommand({
|
|
18744
|
+
memoryDir: orchestrator.config.memoryDir,
|
|
18745
|
+
action: actionRaw,
|
|
18746
|
+
id: typeof options.id === "string" ? options.id : void 0,
|
|
18747
|
+
name: typeof options.name === "string" ? options.name : void 0,
|
|
18748
|
+
description: typeof options.description === "string" ? options.description : void 0,
|
|
18749
|
+
status: statusOptRaw,
|
|
18750
|
+
owner: typeof options.owner === "string" ? options.owner : void 0,
|
|
18751
|
+
tags: typeof options.tags === "string" ? parseTagsCsv(options.tags, true) : void 0,
|
|
18752
|
+
patch
|
|
18753
|
+
});
|
|
18754
|
+
if (Array.isArray(result)) {
|
|
18755
|
+
console.log(`Count: ${result.length}`);
|
|
18756
|
+
}
|
|
18757
|
+
console.log(JSON.stringify(result, null, 2));
|
|
18758
|
+
console.log("OK");
|
|
18759
|
+
});
|
|
18194
18760
|
cmd.command("dedupe-exact").description("Delete exact duplicate memory entries (same body text), keeping highest-confidence/newest copy").option("--dry-run", "Show what would be deleted without deleting files").option("--namespace <ns>", "Namespace to dedupe (v3.0+, default: config defaultNamespace)", "").option("--qmd-sync", "Run QMD update/embed after deletions (default: off)").action(async (...args) => {
|
|
18195
18761
|
const options = args[0] ?? {};
|
|
18196
18762
|
const dryRun = options.dryRun === true;
|
|
@@ -18431,7 +18997,7 @@ function registerCli(api, orchestrator) {
|
|
|
18431
18997
|
}
|
|
18432
18998
|
});
|
|
18433
18999
|
cmd.command("identity").description("Show agent identity reflections").action(async () => {
|
|
18434
|
-
const workspaceDir =
|
|
19000
|
+
const workspaceDir = path39.join(process.env.HOME ?? "~", ".openclaw", "workspace");
|
|
18435
19001
|
const identity = await orchestrator.storage.readIdentity(workspaceDir);
|
|
18436
19002
|
if (!identity) {
|
|
18437
19003
|
console.log("No identity file found.");
|
|
@@ -18654,8 +19220,8 @@ function registerCli(api, orchestrator) {
|
|
|
18654
19220
|
const options = args[0] ?? {};
|
|
18655
19221
|
const threadId = options.thread;
|
|
18656
19222
|
const top = parseInt(options.top ?? "10", 10);
|
|
18657
|
-
const memoryDir =
|
|
18658
|
-
const threading = new ThreadingManager(
|
|
19223
|
+
const memoryDir = path39.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
|
|
19224
|
+
const threading = new ThreadingManager(path39.join(memoryDir, "threads"));
|
|
18659
19225
|
if (threadId) {
|
|
18660
19226
|
const thread = await threading.loadThread(threadId);
|
|
18661
19227
|
if (!thread) {
|
|
@@ -18828,16 +19394,16 @@ function parseDuration(duration) {
|
|
|
18828
19394
|
}
|
|
18829
19395
|
|
|
18830
19396
|
// src/index.ts
|
|
18831
|
-
import { readFile as
|
|
19397
|
+
import { readFile as readFile29, writeFile as writeFile25 } from "fs/promises";
|
|
18832
19398
|
import { readFileSync as readFileSync4 } from "fs";
|
|
18833
|
-
import
|
|
19399
|
+
import path40 from "path";
|
|
18834
19400
|
import os5 from "os";
|
|
18835
19401
|
var ENGRAM_REGISTERED_GUARD = "__openclawEngramRegistered";
|
|
18836
19402
|
function loadPluginConfigFromFile() {
|
|
18837
19403
|
try {
|
|
18838
19404
|
const explicitConfigPath = process.env.OPENCLAW_ENGRAM_CONFIG_PATH || process.env.OPENCLAW_CONFIG_PATH;
|
|
18839
19405
|
const homeDir = process.env.HOME ?? os5.homedir();
|
|
18840
|
-
const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath :
|
|
19406
|
+
const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path40.join(homeDir, ".openclaw", "openclaw.json");
|
|
18841
19407
|
const content = readFileSync4(configPath, "utf-8");
|
|
18842
19408
|
const config = JSON.parse(content);
|
|
18843
19409
|
const pluginEntry = config?.plugins?.entries?.["openclaw-engram"];
|
|
@@ -19045,11 +19611,11 @@ Use this context naturally when relevant. Never quote or expose this memory cont
|
|
|
19045
19611
|
);
|
|
19046
19612
|
async function ensureHourlySummaryCron(api2) {
|
|
19047
19613
|
const jobId = "engram-hourly-summary";
|
|
19048
|
-
const cronFilePath =
|
|
19614
|
+
const cronFilePath = path40.join(os5.homedir(), ".openclaw", "cron", "jobs.json");
|
|
19049
19615
|
try {
|
|
19050
19616
|
let jobsData = { version: 1, jobs: [] };
|
|
19051
19617
|
try {
|
|
19052
|
-
const content = await
|
|
19618
|
+
const content = await readFile29(cronFilePath, "utf-8");
|
|
19053
19619
|
jobsData = JSON.parse(content);
|
|
19054
19620
|
} catch {
|
|
19055
19621
|
}
|
|
@@ -19086,7 +19652,7 @@ Use this context naturally when relevant. Never quote or expose this memory cont
|
|
|
19086
19652
|
state: {}
|
|
19087
19653
|
};
|
|
19088
19654
|
jobsData.jobs.push(newJob);
|
|
19089
|
-
await
|
|
19655
|
+
await writeFile25(cronFilePath, JSON.stringify(jobsData, null, 2), "utf-8");
|
|
19090
19656
|
log.info("auto-registered hourly summary cron job");
|
|
19091
19657
|
} catch (err) {
|
|
19092
19658
|
log.error("failed to auto-register hourly summary cron job:", err);
|