total-recall-brain 3.13.0 → 3.13.2
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 +91 -108
- package/bin/antigravity.mjs +81 -52
- package/bin/total-recall.mjs +32 -57
- package/extension/background.js +1 -1
- package/extension/options/options.js +9 -3
- package/extension/sidepanel/sidepanel.js +0 -2
- package/frontend/index.html +4 -3
- package/frontend/package.json +1 -0
- package/frontend/src/App.tsx +109 -152
- package/frontend/src/api.ts +605 -23
- package/frontend/src/components/BrainSelector.tsx +211 -199
- package/frontend/src/components/DocumentEditorModal.tsx +57 -0
- package/frontend/src/components/DocumentTable.tsx +47 -0
- package/frontend/src/components/Graph3D.tsx +120 -71
- package/frontend/src/components/UsageChart.tsx +3 -3
- package/frontend/src/components/brand/BrandMark.tsx +95 -0
- package/frontend/src/index.css +286 -86
- package/frontend/src/pages/ApiKeysPage.tsx +1878 -215
- package/frontend/src/pages/ChatPage.tsx +103 -45
- package/frontend/src/pages/DesignDocsPage.tsx +5 -5
- package/frontend/src/pages/FilesPage.tsx +10 -6
- package/frontend/src/pages/GraphPage.tsx +5 -3
- package/frontend/src/pages/HelpPage.tsx +2 -2
- package/frontend/src/pages/InboxPage.tsx +82 -0
- package/frontend/src/pages/InstructionsPage.tsx +1 -1
- package/frontend/src/pages/IntegrationsPage.tsx +21 -10
- package/frontend/src/pages/LoginPage.tsx +95 -77
- package/frontend/src/pages/MemoryPage.tsx +31 -8
- package/frontend/src/pages/OnboardingPage.tsx +690 -0
- package/frontend/src/pages/OpenWikiPage.tsx +412 -323
- package/frontend/src/pages/SettingsPage.tsx +825 -922
- package/frontend/src/pages/SkillsPage.tsx +272 -147
- package/frontend/src/pages/UsagePage.tsx +23 -31
- package/frontend/src/pages/VaultPage.tsx +223 -0
- package/frontend/src/types.ts +13 -0
- package/package.json +4 -4
- package/scaffold/.agent/skills/total-recall/SKILL.md +43 -4
- package/scaffold/.agent/skills/total-recall/modules/README.md +10 -0
- package/scaffold/.agent/skills/total-recall/modules/agents/README.md +5 -0
- package/scaffold/.agent/skills/total-recall/{skills/tr-cli-agents → modules/agents}/agents.yml +8 -0
- package/scaffold/.agent/skills/total-recall/modules/skill-deploy/README.md +9 -0
- package/scaffold/.agent/skills/total-recall/openwiki/README.md +10 -0
- package/scaffold/.agent/skills/total-recall/openwiki/architecture.md +5 -0
- package/scaffold/.agent/skills/total-recall/openwiki/ide.md +10 -0
- package/scaffold/.agent/skills/total-recall/openwiki/memory.md +11 -0
- package/scaffold/.agent/skills/total-recall/openwiki/secrets.md +7 -0
- package/scaffold/.agent/skills/total-recall/openwiki/skills.md +7 -0
- package/src/cli/brain.mjs +63 -5
- package/src/cli/connect.mjs +11 -19
- package/src/cli/daemon.mjs +5 -4
- package/src/cli/deploy-ui.mjs +8 -3
- package/src/cli/deploy.mjs +84 -27
- package/src/cli/ingest/index.mjs +1 -1
- package/src/cli/init.mjs +63 -34
- package/src/cli/remember.mjs +3 -3
- package/src/cli/secret.mjs +812 -0
- package/src/cli/setup.mjs +34 -7
- package/src/cli/share.mjs +2 -2
- package/src/cli/skill.mjs +396 -35
- package/src/cli/sync.mjs +4 -4
- package/src/cli/task.mjs +287 -0
- package/src/core/clarity-rewriter.mjs +5 -5
- package/src/core/config.mjs +44 -2
- package/src/core/conflict-detector.mjs +5 -5
- package/src/core/crons.mjs +54 -0
- package/src/core/daemon-loop.mjs +87 -235
- package/src/core/dream.mjs +170 -10
- package/src/core/env-import.mjs +456 -0
- package/src/core/fact-seeker.mjs +14 -14
- package/src/core/import-rules.mjs +20 -7
- package/src/core/inference-engine.mjs +3 -3
- package/src/core/migrate.mjs +14 -8
- package/src/core/okf-adapter.mjs +2 -2
- package/src/core/operation-validator.mjs +39 -331
- package/src/core/optimizer.mjs +7 -0
- package/src/core/post-mortem.mjs +2 -2
- package/src/core/project-brain.mjs +349 -0
- package/src/core/provider-catalog.mjs +296 -0
- package/src/core/quick-capture.mjs +1 -1
- package/src/core/remote-vault-sync.mjs +194 -0
- package/src/core/research-queue.mjs +7 -11
- package/src/core/runtime.mjs +33 -9
- package/src/core/scheduler.mjs +59 -5
- package/src/core/schema.mjs +14 -0
- package/src/core/search.mjs +1 -1
- package/src/core/secrets-env-export.mjs +323 -0
- package/src/core/secrets-rotate.mjs +245 -0
- package/src/core/secrets-store.mjs +645 -0
- package/src/core/semantic-index.mjs +3 -3
- package/src/core/skills-registry.mjs +1062 -0
- package/src/core/source-adapters.mjs +4 -1
- package/src/core/ssss-host-extension.mjs +94 -0
- package/src/core/ssss-kernel-bridge.mjs +530 -0
- package/src/core/surface.mjs +14 -0
- package/src/core/task-envelope.mjs +298 -0
- package/src/core/task-executors.mjs +600 -0
- package/src/core/validated-write.mjs +124 -56
- package/src/core/vault.mjs +21 -34
- package/src/core/webauthn-store.mjs +272 -0
- package/src/server/api.mjs +26 -16
- package/src/server/auth.mjs +51 -0
- package/src/server/index.mjs +3 -3
- package/src/server/keys.mjs +30 -4
- package/src/server/rest.mjs +198 -25
- package/src/server/route-manifest.json +156 -0
- package/src/server/routes/_shared.mjs +40 -1
- package/src/server/routes/docs.mjs +260 -0
- package/src/server/routes/memory.mjs +145 -32
- package/src/server/routes/research.mjs +30 -0
- package/src/server/routes/secrets.mjs +470 -0
- package/src/server/routes/share.mjs +2 -2
- package/src/server/routes/skills.mjs +452 -54
- package/src/server/routes/sync.mjs +48 -0
- package/src/server/routes/webauthn.mjs +184 -0
- package/src/server/update-route-manifest.mjs +39 -0
- package/templates/onboarding-interview.md +32 -22
- package/templates/openwiki/README.md +10 -0
- package/templates/openwiki/architecture.md +5 -0
- package/templates/openwiki/ide.md +10 -0
- package/templates/openwiki/memory.md +11 -0
- package/templates/openwiki/secrets.md +7 -0
- package/templates/openwiki/skills.md +7 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/SKILL.md +0 -121
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/antigravity.txt +0 -20
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/claude.txt +0 -58
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/codex.txt +0 -81
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/gemini.txt +0 -44
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/scripts/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/subagents/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-research/SKILL.md +0 -338
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/SKILL.md +0 -282
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/assets/skill-template/SKILL.md +0 -20
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/evals/evals.json +0 -20
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/hooks/on-skill-add.sh +0 -14
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/hooks/on-skill-delete.sh +0 -14
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/learnings/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/references/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/create-skill.sh +0 -65
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/enforce-skill-optimization.mjs +0 -135
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/skill-watcher.mjs +0 -64
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/subagents/audit-all-skills.md +0 -62
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/SKILL.md +0 -298
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/evals/evals.json +0 -17
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/references/CHANGELOG.md +0 -124
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/references/ssss-spec.md +0 -1325
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/scripts/validate-schema.mjs +0 -232
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/subagents/audit-ssss-node.md +0 -9
- /package/scaffold/.agent/skills/total-recall/{skills/tr-cli-agents/evals → modules/skill-deploy/scripts}/.gitkeep +0 -0
- /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/find-skills.mjs +0 -0
- /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/install-skill.mjs +0 -0
- /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/scan-skill.mjs +0 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task envelope — durable daemon work items on the scheduler queue.
|
|
3
|
+
*
|
|
4
|
+
* Agents and users enqueue open-ended work; the daemon executes under
|
|
5
|
+
* capability policy via the executor registry (task-executors.mjs).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import crypto from 'crypto';
|
|
11
|
+
import matter from 'gray-matter';
|
|
12
|
+
import { atomicWrite, safeStringify } from './vault.mjs';
|
|
13
|
+
|
|
14
|
+
/** Soft kinds — labels only; executor resolution uses `executor` then `category` then kind. */
|
|
15
|
+
export const TASK_KINDS = Object.freeze([
|
|
16
|
+
'memory',
|
|
17
|
+
'research',
|
|
18
|
+
'maintenance',
|
|
19
|
+
'system',
|
|
20
|
+
'custom',
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
export const TASK_STATUSES = Object.freeze([
|
|
24
|
+
'pending',
|
|
25
|
+
'in-progress',
|
|
26
|
+
'in_progress',
|
|
27
|
+
'completed',
|
|
28
|
+
'failed',
|
|
29
|
+
'cancelled',
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
/** Default-deny dangerous capabilities. */
|
|
33
|
+
export const FORBIDDEN_CAPABILITIES = Object.freeze([
|
|
34
|
+
'shell',
|
|
35
|
+
'shell:all',
|
|
36
|
+
'shell:any',
|
|
37
|
+
'fs:root',
|
|
38
|
+
'net:post',
|
|
39
|
+
'net:all',
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
export const DEFAULT_CAPABILITIES = Object.freeze(['vault:read']);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Normalize priority: numeric 0–100, or named levels.
|
|
46
|
+
*/
|
|
47
|
+
export function normalizePriority(priority) {
|
|
48
|
+
if (priority == null || priority === '') return 50;
|
|
49
|
+
if (typeof priority === 'number' && Number.isFinite(priority)) {
|
|
50
|
+
return Math.max(0, Math.min(100, priority));
|
|
51
|
+
}
|
|
52
|
+
const map = {
|
|
53
|
+
absolute: 100,
|
|
54
|
+
high: 80,
|
|
55
|
+
normal: 50,
|
|
56
|
+
medium: 50,
|
|
57
|
+
low: 20,
|
|
58
|
+
};
|
|
59
|
+
const key = String(priority).toLowerCase();
|
|
60
|
+
if (map[key] != null) return map[key];
|
|
61
|
+
const n = parseInt(String(priority), 10);
|
|
62
|
+
return Number.isFinite(n) ? Math.max(0, Math.min(100, n)) : 50;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Build a normalized task envelope object (not yet on disk).
|
|
67
|
+
*/
|
|
68
|
+
export function buildTaskEnvelope({
|
|
69
|
+
intent,
|
|
70
|
+
slug,
|
|
71
|
+
kind = 'custom',
|
|
72
|
+
executor = null,
|
|
73
|
+
category = null,
|
|
74
|
+
priority = 50,
|
|
75
|
+
capabilities = DEFAULT_CAPABILITIES,
|
|
76
|
+
payload = {},
|
|
77
|
+
budget = {},
|
|
78
|
+
origin = {},
|
|
79
|
+
result = {},
|
|
80
|
+
system = false,
|
|
81
|
+
body = '',
|
|
82
|
+
target = null,
|
|
83
|
+
status = 'pending',
|
|
84
|
+
} = {}) {
|
|
85
|
+
if (!intent && !body && !target) {
|
|
86
|
+
throw new Error('Task envelope requires intent, body, or target');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const caps = Array.isArray(capabilities)
|
|
90
|
+
? [...capabilities]
|
|
91
|
+
: String(capabilities || '')
|
|
92
|
+
.split(',')
|
|
93
|
+
.map((c) => c.trim())
|
|
94
|
+
.filter(Boolean);
|
|
95
|
+
|
|
96
|
+
const forbidden = caps.filter((c) => FORBIDDEN_CAPABILITIES.includes(c));
|
|
97
|
+
if (forbidden.length) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Capability not allowed by default policy: ${forbidden.join(', ')}. ` +
|
|
100
|
+
'Shell/net-post require explicit host policy (not yet granted via CLI).',
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const now = new Date().toISOString();
|
|
105
|
+
const baseSlug =
|
|
106
|
+
slug ||
|
|
107
|
+
`task-${kind}-${crypto.createHash('sha256').update(`${intent || body || target}-${now}`).digest('hex').slice(0, 10)}`;
|
|
108
|
+
|
|
109
|
+
const resolvedCategory =
|
|
110
|
+
category ||
|
|
111
|
+
(kind === 'research'
|
|
112
|
+
? 'proactive-research'
|
|
113
|
+
: kind === 'maintenance'
|
|
114
|
+
? 'memory-maintenance'
|
|
115
|
+
: kind === 'system'
|
|
116
|
+
? 'memory-maintenance'
|
|
117
|
+
: kind === 'memory'
|
|
118
|
+
? 'memory-maintenance'
|
|
119
|
+
: 'custom');
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
type: 'task',
|
|
123
|
+
schema_version: 1,
|
|
124
|
+
slug: baseSlug,
|
|
125
|
+
status,
|
|
126
|
+
priority: normalizePriority(priority),
|
|
127
|
+
kind: TASK_KINDS.includes(kind) ? kind : 'custom',
|
|
128
|
+
executor: executor || (kind === 'custom' ? 'custom' : null),
|
|
129
|
+
category: resolvedCategory,
|
|
130
|
+
intent: intent || body || target || '',
|
|
131
|
+
target: target || null,
|
|
132
|
+
capabilities: caps.length ? caps : [...DEFAULT_CAPABILITIES],
|
|
133
|
+
payload: payload && typeof payload === 'object' ? payload : {},
|
|
134
|
+
budget: {
|
|
135
|
+
max_wall_ms: budget.max_wall_ms ?? 120000,
|
|
136
|
+
max_tokens: budget.max_tokens ?? 0,
|
|
137
|
+
max_tool_calls: budget.max_tool_calls ?? 20,
|
|
138
|
+
},
|
|
139
|
+
origin: {
|
|
140
|
+
agent: origin.agent || origin.created_by || 'cli',
|
|
141
|
+
session_id: origin.session_id || null,
|
|
142
|
+
created_at: origin.created_at || now,
|
|
143
|
+
created_by: origin.created_by || origin.agent || 'cli',
|
|
144
|
+
},
|
|
145
|
+
result: {
|
|
146
|
+
land: result.land || 'inbox',
|
|
147
|
+
promote_via: result.promote_via || 'draft',
|
|
148
|
+
},
|
|
149
|
+
system: Boolean(system),
|
|
150
|
+
created_by: origin.created_by || origin.agent || 'cli',
|
|
151
|
+
reason: intent || body || '',
|
|
152
|
+
body: body || intent || '',
|
|
153
|
+
created_at: now,
|
|
154
|
+
updated_at: now,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Normalize a disk/legacy task into envelope fields (in-place-ish return).
|
|
160
|
+
*/
|
|
161
|
+
export function normalizeTask(raw) {
|
|
162
|
+
if (!raw || typeof raw !== 'object') return raw;
|
|
163
|
+
const task = { ...raw };
|
|
164
|
+
task.type = task.type || 'task';
|
|
165
|
+
task.status = task.status || 'pending';
|
|
166
|
+
if (task.status === 'in_progress') task.status = 'in-progress';
|
|
167
|
+
task.priority = normalizePriority(task.priority);
|
|
168
|
+
task.kind = task.kind || inferKind(task);
|
|
169
|
+
task.executor = task.executor || null;
|
|
170
|
+
task.capabilities = Array.isArray(task.capabilities)
|
|
171
|
+
? task.capabilities
|
|
172
|
+
: [...DEFAULT_CAPABILITIES];
|
|
173
|
+
task.payload = task.payload && typeof task.payload === 'object' ? task.payload : {};
|
|
174
|
+
task.intent = task.intent || task.reason || task.body || task.target || task.slug || '';
|
|
175
|
+
task.system = Boolean(task.system);
|
|
176
|
+
return task;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function inferKind(task) {
|
|
180
|
+
if (task.system) return 'system';
|
|
181
|
+
if (task._research_id || String(task.category || '').includes('research') || String(task.slug || '').startsWith('research-')) {
|
|
182
|
+
return 'research';
|
|
183
|
+
}
|
|
184
|
+
if (task.category === 'memory-maintenance' || String(task.slug || '').includes('dream')) {
|
|
185
|
+
return 'maintenance';
|
|
186
|
+
}
|
|
187
|
+
if (task.category === 'custom' || task.executor === 'custom') return 'custom';
|
|
188
|
+
return task.kind || 'custom';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Persist envelope to queueDir as `<slug>.md`.
|
|
193
|
+
*/
|
|
194
|
+
export function persistEnvelope(envelope, queueDir) {
|
|
195
|
+
if (!fs.existsSync(queueDir)) {
|
|
196
|
+
fs.mkdirSync(queueDir, { recursive: true });
|
|
197
|
+
}
|
|
198
|
+
const task = normalizeTask(envelope);
|
|
199
|
+
const filepath = path.join(queueDir, `${task.slug}.md`);
|
|
200
|
+
const { body, _filepath, ...frontmatter } = task;
|
|
201
|
+
for (const key of Object.keys(frontmatter)) {
|
|
202
|
+
if (frontmatter[key] === undefined) delete frontmatter[key];
|
|
203
|
+
}
|
|
204
|
+
atomicWrite(filepath, safeStringify(body || task.intent || '', frontmatter));
|
|
205
|
+
task._filepath = filepath;
|
|
206
|
+
return task;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Enqueue a new task (build + persist).
|
|
211
|
+
*/
|
|
212
|
+
export function addTask(opts, queueDir) {
|
|
213
|
+
const envelope = buildTaskEnvelope(opts);
|
|
214
|
+
return persistEnvelope(envelope, queueDir);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* List tasks from disk. statusFilter: pending|completed|failed|cancelled|all
|
|
219
|
+
*/
|
|
220
|
+
export function listTasks(queueDir, { status = 'pending' } = {}) {
|
|
221
|
+
if (!fs.existsSync(queueDir)) return [];
|
|
222
|
+
const files = fs.readdirSync(queueDir).filter((f) => f.endsWith('.md'));
|
|
223
|
+
const tasks = [];
|
|
224
|
+
for (const file of files) {
|
|
225
|
+
try {
|
|
226
|
+
const filepath = path.join(queueDir, file);
|
|
227
|
+
const raw = fs.readFileSync(filepath, 'utf8');
|
|
228
|
+
const { data, content } = matter(raw);
|
|
229
|
+
const task = normalizeTask({
|
|
230
|
+
...data,
|
|
231
|
+
slug: data.slug || path.basename(file, '.md'),
|
|
232
|
+
body: content,
|
|
233
|
+
_filepath: filepath,
|
|
234
|
+
});
|
|
235
|
+
if (status === 'all') {
|
|
236
|
+
tasks.push(task);
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (status === 'pending') {
|
|
240
|
+
if (task.status === 'pending' || task.status === 'in-progress') tasks.push(task);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (task.status === status) tasks.push(task);
|
|
244
|
+
} catch {
|
|
245
|
+
// skip malformed
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return tasks.sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Load one task by slug.
|
|
253
|
+
*/
|
|
254
|
+
export function getTask(queueDir, slug) {
|
|
255
|
+
const filepath = path.join(queueDir, `${slug}.md`);
|
|
256
|
+
if (!fs.existsSync(filepath)) {
|
|
257
|
+
// scan for match
|
|
258
|
+
const all = listTasks(queueDir, { status: 'all' });
|
|
259
|
+
return all.find((t) => t.slug === slug) || null;
|
|
260
|
+
}
|
|
261
|
+
const raw = fs.readFileSync(filepath, 'utf8');
|
|
262
|
+
const { data, content } = matter(raw);
|
|
263
|
+
return normalizeTask({
|
|
264
|
+
...data,
|
|
265
|
+
slug: data.slug || slug,
|
|
266
|
+
body: content,
|
|
267
|
+
_filepath: filepath,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Cancel a pending/in-progress task.
|
|
273
|
+
*/
|
|
274
|
+
export function cancelTask(queueDir, slug) {
|
|
275
|
+
const task = getTask(queueDir, slug);
|
|
276
|
+
if (!task) return { success: false, error: `Task not found: ${slug}` };
|
|
277
|
+
if (task.status === 'completed' || task.status === 'cancelled') {
|
|
278
|
+
return { success: false, error: `Task already ${task.status}: ${slug}` };
|
|
279
|
+
}
|
|
280
|
+
const filepath = task._filepath || path.join(queueDir, `${task.slug}.md`);
|
|
281
|
+
const raw = fs.readFileSync(filepath, 'utf8');
|
|
282
|
+
const { data, content } = matter(raw);
|
|
283
|
+
data.status = 'cancelled';
|
|
284
|
+
data.updated_at = new Date().toISOString();
|
|
285
|
+
data.cancelled_at = data.updated_at;
|
|
286
|
+
for (const key of Object.keys(data)) {
|
|
287
|
+
if (data[key] === undefined) delete data[key];
|
|
288
|
+
}
|
|
289
|
+
atomicWrite(filepath, safeStringify(content, data));
|
|
290
|
+
return { success: true, task: { ...task, status: 'cancelled' } };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Resolve queue directory for a brain.
|
|
295
|
+
*/
|
|
296
|
+
export function resolveQueueDir(brainDir) {
|
|
297
|
+
return path.join(brainDir, 'scheduler', 'queue');
|
|
298
|
+
}
|