@graphmemory/server 1.3.2 → 1.3.3
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/api/rest/graph.js +1 -1
- package/dist/api/rest/index.js +0 -1
- package/dist/api/rest/validation.js +17 -17
- package/dist/api/tools/code/get-file-symbols.js +2 -2
- package/dist/api/tools/code/get-symbol.js +2 -2
- package/dist/api/tools/code/list-files.js +2 -2
- package/dist/api/tools/code/search-code.js +2 -1
- package/dist/api/tools/code/search-files.js +2 -1
- package/dist/api/tools/docs/cross-references.js +2 -1
- package/dist/api/tools/docs/explain-symbol.js +2 -1
- package/dist/api/tools/docs/find-examples.js +2 -1
- package/dist/api/tools/docs/get-node.js +2 -2
- package/dist/api/tools/docs/get-toc.js +2 -2
- package/dist/api/tools/docs/list-snippets.js +4 -4
- package/dist/api/tools/docs/list-topics.js +2 -2
- package/dist/api/tools/docs/search-files.js +2 -1
- package/dist/api/tools/docs/search-snippets.js +3 -2
- package/dist/api/tools/docs/search.js +2 -1
- package/dist/api/tools/file-index/get-file-info.js +2 -2
- package/dist/api/tools/file-index/list-all-files.js +5 -5
- package/dist/api/tools/file-index/search-all-files.js +2 -1
- package/dist/api/tools/knowledge/add-attachment.js +26 -4
- package/dist/api/tools/knowledge/create-note.js +4 -3
- package/dist/api/tools/knowledge/create-relation.js +5 -4
- package/dist/api/tools/knowledge/delete-note.js +2 -2
- package/dist/api/tools/knowledge/delete-relation.js +4 -3
- package/dist/api/tools/knowledge/find-linked-notes.js +4 -3
- package/dist/api/tools/knowledge/get-note.js +2 -2
- package/dist/api/tools/knowledge/list-notes.js +4 -3
- package/dist/api/tools/knowledge/list-relations.js +1 -1
- package/dist/api/tools/knowledge/remove-attachment.js +1 -1
- package/dist/api/tools/knowledge/search-notes.js +2 -1
- package/dist/api/tools/knowledge/update-note.js +6 -5
- package/dist/api/tools/skills/add-attachment.js +26 -4
- package/dist/api/tools/skills/bump-usage.js +2 -2
- package/dist/api/tools/skills/create-skill-link.js +6 -5
- package/dist/api/tools/skills/create-skill.js +8 -7
- package/dist/api/tools/skills/delete-skill-link.js +4 -3
- package/dist/api/tools/skills/delete-skill.js +2 -2
- package/dist/api/tools/skills/find-linked-skills.js +4 -3
- package/dist/api/tools/skills/get-skill.js +2 -2
- package/dist/api/tools/skills/link-skill.js +2 -2
- package/dist/api/tools/skills/list-skills.js +4 -3
- package/dist/api/tools/skills/recall-skills.js +2 -1
- package/dist/api/tools/skills/remove-attachment.js +1 -1
- package/dist/api/tools/skills/search-skills.js +2 -1
- package/dist/api/tools/skills/update-skill.js +10 -9
- package/dist/api/tools/tasks/add-attachment.js +26 -4
- package/dist/api/tools/tasks/create-task-link.js +6 -5
- package/dist/api/tools/tasks/create-task.js +5 -4
- package/dist/api/tools/tasks/delete-task-link.js +4 -3
- package/dist/api/tools/tasks/delete-task.js +2 -2
- package/dist/api/tools/tasks/find-linked-tasks.js +4 -3
- package/dist/api/tools/tasks/get-task.js +2 -2
- package/dist/api/tools/tasks/link-task.js +2 -2
- package/dist/api/tools/tasks/list-tasks.js +5 -4
- package/dist/api/tools/tasks/move-task.js +2 -2
- package/dist/api/tools/tasks/remove-attachment.js +1 -1
- package/dist/api/tools/tasks/search-tasks.js +2 -1
- package/dist/api/tools/tasks/update-task.js +7 -6
- package/dist/cli/indexer.js +2 -0
- package/dist/graphs/code.js +4 -0
- package/dist/graphs/docs.js +4 -0
- package/dist/graphs/file-index.js +4 -0
- package/dist/graphs/knowledge.js +5 -0
- package/dist/graphs/skill.js +5 -0
- package/dist/graphs/task.js +5 -0
- package/dist/lib/defaults.js +1 -1
- package/dist/lib/file-import.js +8 -2
- package/dist/lib/file-mirror.js +77 -21
- package/dist/lib/graph-persistence.js +42 -0
- package/dist/lib/jwt.js +4 -3
- package/dist/lib/promise-queue.js +20 -2
- package/dist/ui/assets/{NoteForm-SQ0b93i0.js → NoteForm-D0lOYBQq.js} +1 -1
- package/dist/ui/assets/{SkillForm-BVsGrNPb.js → SkillForm-CfDWe0Nx.js} +1 -1
- package/dist/ui/assets/{TaskForm-DgPVeiI9.js → TaskForm-ltmMCEAE.js} +1 -1
- package/dist/ui/assets/{_articleId_-FqdaSeYS.js → _articleId_-mEqH7YfV.js} +1 -1
- package/dist/ui/assets/{_docId_-Q0Wmjtp6.js → _docId_-wAt8n8p4.js} +1 -1
- package/dist/ui/assets/{_filePath_-BR0gOT_z.js → _filePath_-DQMFMLQh.js} +1 -1
- package/dist/ui/assets/{_noteId_-BMWd415J.js → _noteId_-Cqxl6H5q.js} +1 -1
- package/dist/ui/assets/{_skillId_-CsHgildJ.js → _skillId_-BlJOfwm_.js} +1 -1
- package/dist/ui/assets/{_taskId_-xDHTfbQw.js → _taskId_-Cs8LaIe4.js} +1 -1
- package/dist/ui/assets/{_toolName_-BSa2uNSu.js → _toolName_-3CHUDagf.js} +1 -1
- package/dist/ui/assets/{attachments-NSvN5_0A.js → attachments-CMDVqPm_.js} +1 -1
- package/dist/ui/assets/{docs-iUK8E40J.js → docs-BuFjplSR.js} +1 -1
- package/dist/ui/assets/{edit-BzIJy_Oo.js → edit-7NV817UE.js} +1 -1
- package/dist/ui/assets/{edit-Dnc067B2.js → edit-Bflx3-cK.js} +1 -1
- package/dist/ui/assets/{edit-U_UEI361.js → edit-CdmIaFUI.js} +1 -1
- package/dist/ui/assets/{esm-BWiKNcBW.js → esm-CqydI1a6.js} +1 -1
- package/dist/ui/assets/{files-B4svJUZh.js → files-BWNbyH1X.js} +1 -1
- package/dist/ui/assets/{graph-CcNP1ckP.js → graph-B9nFxoXm.js} +1 -1
- package/dist/ui/assets/{help-BJZZtKAR.js → help-CqK0hEmf.js} +1 -1
- package/dist/ui/assets/index-80sqSHwS.js +2 -0
- package/dist/ui/assets/{knowledge-CV99ToEV.js → knowledge-g4C4l6uL.js} +1 -1
- package/dist/ui/assets/{new-Dcx8wlp4.js → new-Bqup97cu.js} +1 -1
- package/dist/ui/assets/{new-Sq3NY2oa.js → new-DC3lRvxF.js} +1 -1
- package/dist/ui/assets/{new-BypesKiP.js → new-DbsKrGJ4.js} +1 -1
- package/dist/ui/assets/{prompts-DbsIe3Pm.js → prompts-DyltFLqJ.js} +1 -1
- package/dist/ui/assets/{search-D87r7lIL.js → search-DtRoWsqW.js} +1 -1
- package/dist/ui/assets/{skill-BltAsz7M.js → skill-demt31s6.js} +1 -1
- package/dist/ui/assets/{skills-Dtmg2kDA.js → skills-DRjYPbZM.js} +1 -1
- package/dist/ui/assets/{tasks-BRqIwKCG.js → tasks-CgsSFz6X.js} +1 -1
- package/dist/ui/assets/{tools-CM3gQ4TK.js → tools-BDszA6Kh.js} +1 -1
- package/dist/ui/assets/{vendor-markdown-CT8ZVEPu.js → vendor-markdown-DngssFHR.js} +27 -27
- package/dist/ui/assets/{vendor-md-editor-DmWafJvr.js → vendor-md-editor-DC6xr_29.js} +10 -10
- package/dist/ui/assets/{vendor-mui-BPj7d3Sw.js → vendor-mui-DXUYJbRC.js} +1 -1
- package/dist/ui/assets/{vendor-mui-icons-B196sG3f.js → vendor-mui-icons-YtgP6dg2.js} +1 -1
- package/dist/ui/assets/{vendor-react-CHUjhoxh.js → vendor-react-DxfYAwYK.js} +1 -1
- package/dist/ui/index.html +6 -6
- package/package.json +1 -1
- package/dist/ui/assets/index-CEweXD9O.js +0 -2
|
@@ -10,7 +10,7 @@ function register(server, mgr) {
|
|
|
10
10
|
'Returns the updated task summary. ' +
|
|
11
11
|
'Pass expectedVersion to enable optimistic locking.',
|
|
12
12
|
inputSchema: {
|
|
13
|
-
taskId: zod_1.z.string().describe('Task ID to move'),
|
|
13
|
+
taskId: zod_1.z.string().max(500).describe('Task ID to move'),
|
|
14
14
|
status: zod_1.z.enum(['backlog', 'todo', 'in_progress', 'review', 'done', 'cancelled'])
|
|
15
15
|
.describe('New status'),
|
|
16
16
|
expectedVersion: zod_1.z.number().int().positive().optional().describe('Current version for optimistic locking — request fails with version_conflict if the task has been updated since'),
|
|
@@ -19,7 +19,7 @@ function register(server, mgr) {
|
|
|
19
19
|
try {
|
|
20
20
|
const moved = mgr.moveTask(taskId, status, expectedVersion);
|
|
21
21
|
if (!moved) {
|
|
22
|
-
return { content: [{ type: 'text', text:
|
|
22
|
+
return { content: [{ type: 'text', text: 'Task not found' }], isError: true };
|
|
23
23
|
}
|
|
24
24
|
const task = mgr.getTask(taskId);
|
|
25
25
|
return { content: [{ type: 'text', text: JSON.stringify({
|
|
@@ -6,7 +6,7 @@ function register(server, mgr) {
|
|
|
6
6
|
server.registerTool('remove_task_attachment', {
|
|
7
7
|
description: 'Remove an attachment from a task. The file is deleted from disk.',
|
|
8
8
|
inputSchema: {
|
|
9
|
-
taskId: zod_1.z.string().describe('ID of the task'),
|
|
9
|
+
taskId: zod_1.z.string().max(500).describe('ID of the task'),
|
|
10
10
|
filename: zod_1.z.string().min(1).max(255)
|
|
11
11
|
.refine(s => !/[/\\]/.test(s), 'Filename must not contain path separators')
|
|
12
12
|
.refine(s => !s.includes('..'), 'Filename must not contain ..')
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.register = register;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
+
const defaults_1 = require("../../../lib/defaults");
|
|
5
6
|
function register(server, mgr) {
|
|
6
7
|
server.registerTool('search_tasks', {
|
|
7
8
|
description: 'Semantic search over the task graph. ' +
|
|
@@ -11,7 +12,7 @@ function register(server, mgr) {
|
|
|
11
12
|
'Returns an array sorted by relevance score (0–1), each with: ' +
|
|
12
13
|
'id, title, description, status, priority, tags, score.',
|
|
13
14
|
inputSchema: {
|
|
14
|
-
query: zod_1.z.string().describe('Natural language search query'),
|
|
15
|
+
query: zod_1.z.string().max(defaults_1.MAX_SEARCH_QUERY_LEN).describe('Natural language search query'),
|
|
15
16
|
topK: zod_1.z.number().min(1).max(500).optional().describe('How many top similar tasks to use as seeds (default 5)'),
|
|
16
17
|
bfsDepth: zod_1.z.number().min(0).max(10).optional().describe('How many hops to follow relations from each seed (default 1; 0 = no expansion)'),
|
|
17
18
|
maxResults: zod_1.z.number().min(1).max(500).optional().describe('Maximum number of results to return (default 20)'),
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.register = register;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const manager_types_1 = require("../../../graphs/manager-types");
|
|
6
|
+
const defaults_1 = require("../../../lib/defaults");
|
|
6
7
|
function register(server, mgr) {
|
|
7
8
|
server.registerTool('update_task', {
|
|
8
9
|
description: 'Update an existing task. Only provided fields are changed. ' +
|
|
@@ -11,16 +12,16 @@ function register(server, mgr) {
|
|
|
11
12
|
'Use move_task for a simpler status-only change. ' +
|
|
12
13
|
'Pass expectedVersion to enable optimistic locking.',
|
|
13
14
|
inputSchema: {
|
|
14
|
-
taskId: zod_1.z.string().describe('Task ID to update'),
|
|
15
|
-
title: zod_1.z.string().optional().describe('New title'),
|
|
16
|
-
description: zod_1.z.string().optional().describe('New description'),
|
|
15
|
+
taskId: zod_1.z.string().max(500).describe('Task ID to update'),
|
|
16
|
+
title: zod_1.z.string().max(defaults_1.MAX_TITLE_LEN).optional().describe('New title'),
|
|
17
|
+
description: zod_1.z.string().max(defaults_1.MAX_DESCRIPTION_LEN).optional().describe('New description'),
|
|
17
18
|
status: zod_1.z.enum(['backlog', 'todo', 'in_progress', 'review', 'done', 'cancelled']).optional()
|
|
18
19
|
.describe('New status'),
|
|
19
20
|
priority: zod_1.z.enum(['critical', 'high', 'medium', 'low']).optional().describe('New priority'),
|
|
20
|
-
tags: zod_1.z.array(zod_1.z.string()).optional().describe('Replace tags array'),
|
|
21
|
+
tags: zod_1.z.array(zod_1.z.string().max(defaults_1.MAX_TAG_LEN)).max(defaults_1.MAX_TAGS_COUNT).optional().describe('Replace tags array'),
|
|
21
22
|
dueDate: zod_1.z.number().nullable().optional().describe('New due date (ms timestamp) or null to clear'),
|
|
22
23
|
estimate: zod_1.z.number().nullable().optional().describe('New estimate (hours) or null to clear'),
|
|
23
|
-
assignee: zod_1.z.string().nullable().optional().describe('Team member ID to assign, or null to unassign'),
|
|
24
|
+
assignee: zod_1.z.string().max(defaults_1.MAX_ASSIGNEE_LEN).nullable().optional().describe('Team member ID to assign, or null to unassign'),
|
|
24
25
|
expectedVersion: zod_1.z.number().int().positive().optional().describe('Current version for optimistic locking — request fails with version_conflict if the task has been updated since'),
|
|
25
26
|
},
|
|
26
27
|
}, async ({ taskId, title, description, status, priority, tags, dueDate, estimate, assignee, expectedVersion }) => {
|
|
@@ -44,7 +45,7 @@ function register(server, mgr) {
|
|
|
44
45
|
try {
|
|
45
46
|
const updated = await mgr.updateTask(taskId, patch, expectedVersion);
|
|
46
47
|
if (!updated) {
|
|
47
|
-
return { content: [{ type: 'text', text:
|
|
48
|
+
return { content: [{ type: 'text', text: 'Task not found' }], isError: true };
|
|
48
49
|
}
|
|
49
50
|
return { content: [{ type: 'text', text: JSON.stringify({ taskId, updated: true }, null, 2) }] };
|
|
50
51
|
}
|
package/dist/cli/indexer.js
CHANGED
|
@@ -270,6 +270,8 @@ function createProjectIndexer(docGraph, codeGraph, config, knowledgeGraph, fileI
|
|
|
270
270
|
for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
271
271
|
if (entry.name.startsWith('.') || watcher_1.ALWAYS_IGNORED.has(entry.name))
|
|
272
272
|
continue;
|
|
273
|
+
if (entry.isSymbolicLink())
|
|
274
|
+
continue;
|
|
273
275
|
const full = path_1.default.join(dir, entry.name);
|
|
274
276
|
if (entry.isDirectory()) {
|
|
275
277
|
const relDir = path_1.default.relative(config.projectDir, full);
|
package/dist/graphs/code.js
CHANGED
|
@@ -217,6 +217,10 @@ function loadCodeGraph(graphMemory, fresh = false, embeddingFingerprint) {
|
|
|
217
217
|
process.stderr.write(`[code-graph] Embedding config changed, re-indexing code graph\n`);
|
|
218
218
|
return graph;
|
|
219
219
|
}
|
|
220
|
+
if (!(0, graph_persistence_1.validateGraphStructure)(data.graph)) {
|
|
221
|
+
process.stderr.write(`[code-graph] Invalid graph structure in ${file}, starting fresh\n`);
|
|
222
|
+
return graph;
|
|
223
|
+
}
|
|
220
224
|
(0, embedding_codec_1.decompressEmbeddings)(data.graph);
|
|
221
225
|
graph.import(data.graph);
|
|
222
226
|
process.stderr.write(`[code-graph] Loaded ${graph.order} nodes, ${graph.size} edges\n`);
|
package/dist/graphs/docs.js
CHANGED
|
@@ -164,6 +164,10 @@ function loadGraph(graphMemory, fresh = false, embeddingFingerprint) {
|
|
|
164
164
|
process.stderr.write(`[graph] Embedding config changed, re-indexing docs graph\n`);
|
|
165
165
|
return graph;
|
|
166
166
|
}
|
|
167
|
+
if (!(0, graph_persistence_1.validateGraphStructure)(data.graph)) {
|
|
168
|
+
process.stderr.write(`[graph] Invalid graph structure in ${file}, starting fresh\n`);
|
|
169
|
+
return graph;
|
|
170
|
+
}
|
|
167
171
|
(0, embedding_codec_1.decompressEmbeddings)(data.graph);
|
|
168
172
|
graph.import(data.graph);
|
|
169
173
|
process.stderr.write(`[graph] Loaded ${graph.order} nodes, ${graph.size} edges\n`);
|
|
@@ -272,6 +272,10 @@ function loadFileIndexGraph(graphMemory, fresh = false, embeddingFingerprint) {
|
|
|
272
272
|
process.stderr.write(`[file-index] Embedding config changed, re-indexing file index\n`);
|
|
273
273
|
return graph;
|
|
274
274
|
}
|
|
275
|
+
if (!(0, graph_persistence_1.validateGraphStructure)(data.graph)) {
|
|
276
|
+
process.stderr.write(`[file-index] Invalid graph structure in ${file}, starting fresh\n`);
|
|
277
|
+
return graph;
|
|
278
|
+
}
|
|
275
279
|
(0, embedding_codec_1.decompressEmbeddings)(data.graph);
|
|
276
280
|
graph.import(data.graph);
|
|
277
281
|
process.stderr.write(`[file-index] Loaded ${graph.order} nodes, ${graph.size} edges\n`);
|
package/dist/graphs/knowledge.js
CHANGED
|
@@ -360,6 +360,10 @@ function loadKnowledgeGraph(graphMemory, fresh = false, embeddingFingerprint) {
|
|
|
360
360
|
process.stderr.write(`[knowledge-graph] Embedding config changed, re-indexing knowledge graph\n`);
|
|
361
361
|
return graph;
|
|
362
362
|
}
|
|
363
|
+
if (!(0, graph_persistence_1.validateGraphStructure)(data.graph)) {
|
|
364
|
+
process.stderr.write(`[knowledge-graph] Invalid graph structure in ${file}, starting fresh\n`);
|
|
365
|
+
return graph;
|
|
366
|
+
}
|
|
363
367
|
(0, embedding_codec_1.decompressEmbeddings)(data.graph);
|
|
364
368
|
graph.import(data.graph);
|
|
365
369
|
process.stderr.write(`[knowledge-graph] Loaded ${graph.order} nodes, ${graph.size} edges\n`);
|
|
@@ -433,6 +437,7 @@ class KnowledgeGraphManager {
|
|
|
433
437
|
mirrorTracker;
|
|
434
438
|
_bm25Index;
|
|
435
439
|
get externalGraphs() { return this.ext; }
|
|
440
|
+
get projectDir() { return this.ctx.projectDir; }
|
|
436
441
|
constructor(_graph, embedFns, ctx, ext = {}) {
|
|
437
442
|
this._graph = _graph;
|
|
438
443
|
this.embedFns = embedFns;
|
package/dist/graphs/skill.js
CHANGED
|
@@ -504,6 +504,10 @@ function loadSkillGraph(graphMemory, fresh = false, embeddingFingerprint) {
|
|
|
504
504
|
process.stderr.write(`[skill-graph] Embedding config changed, re-indexing skill graph\n`);
|
|
505
505
|
return graph;
|
|
506
506
|
}
|
|
507
|
+
if (!(0, graph_persistence_1.validateGraphStructure)(data.graph)) {
|
|
508
|
+
process.stderr.write(`[skill-graph] Invalid graph structure in ${file}, starting fresh\n`);
|
|
509
|
+
return graph;
|
|
510
|
+
}
|
|
507
511
|
(0, embedding_codec_1.decompressEmbeddings)(data.graph);
|
|
508
512
|
graph.import(data.graph);
|
|
509
513
|
process.stderr.write(`[skill-graph] Loaded ${graph.order} nodes, ${graph.size} edges\n`);
|
|
@@ -610,6 +614,7 @@ class SkillGraphManager {
|
|
|
610
614
|
taskGraph;
|
|
611
615
|
mirrorTracker;
|
|
612
616
|
_bm25Index;
|
|
617
|
+
get projectDir() { return this.ctx.projectDir; }
|
|
613
618
|
constructor(_graph, embedFns, ctx, ext = {}) {
|
|
614
619
|
this._graph = _graph;
|
|
615
620
|
this.embedFns = embedFns;
|
package/dist/graphs/task.js
CHANGED
|
@@ -525,6 +525,10 @@ function loadTaskGraph(graphMemory, fresh = false, embeddingFingerprint) {
|
|
|
525
525
|
process.stderr.write(`[task-graph] Embedding config changed, re-indexing task graph\n`);
|
|
526
526
|
return graph;
|
|
527
527
|
}
|
|
528
|
+
if (!(0, graph_persistence_1.validateGraphStructure)(data.graph)) {
|
|
529
|
+
process.stderr.write(`[task-graph] Invalid graph structure in ${file}, starting fresh\n`);
|
|
530
|
+
return graph;
|
|
531
|
+
}
|
|
528
532
|
(0, embedding_codec_1.decompressEmbeddings)(data.graph);
|
|
529
533
|
graph.import(data.graph);
|
|
530
534
|
process.stderr.write(`[task-graph] Loaded ${graph.order} nodes, ${graph.size} edges\n`);
|
|
@@ -591,6 +595,7 @@ class TaskGraphManager {
|
|
|
591
595
|
knowledgeGraph;
|
|
592
596
|
mirrorTracker;
|
|
593
597
|
_bm25Index;
|
|
598
|
+
get projectDir() { return this.ctx.projectDir; }
|
|
594
599
|
constructor(_graph, embedFns, ctx, ext = {}) {
|
|
595
600
|
this._graph = _graph;
|
|
596
601
|
this.embedFns = embedFns;
|
package/dist/lib/defaults.js
CHANGED
|
@@ -38,7 +38,7 @@ exports.MAX_TITLE_LEN = 500;
|
|
|
38
38
|
exports.MAX_NOTE_CONTENT_LEN = 1_000_000;
|
|
39
39
|
exports.MAX_TAG_LEN = 100;
|
|
40
40
|
exports.MAX_TAGS_COUNT = 100;
|
|
41
|
-
exports.MAX_SEARCH_QUERY_LEN =
|
|
41
|
+
exports.MAX_SEARCH_QUERY_LEN = 10_000;
|
|
42
42
|
exports.MAX_SEARCH_TOP_K = 500;
|
|
43
43
|
exports.MAX_DESCRIPTION_LEN = 500_000;
|
|
44
44
|
exports.MAX_ASSIGNEE_LEN = 100;
|
package/dist/lib/file-import.js
CHANGED
|
@@ -96,10 +96,16 @@ function extractTitleAndContent(body) {
|
|
|
96
96
|
*/
|
|
97
97
|
function extractId(filePath) {
|
|
98
98
|
const basename = path.basename(filePath, '.md');
|
|
99
|
+
let id;
|
|
99
100
|
if (basename === 'note' || basename === 'task' || basename === 'skill') {
|
|
100
|
-
|
|
101
|
+
id = path.basename(path.dirname(filePath));
|
|
101
102
|
}
|
|
102
|
-
|
|
103
|
+
else {
|
|
104
|
+
id = basename;
|
|
105
|
+
}
|
|
106
|
+
if (id === '..' || id === '.' || id.includes('\0'))
|
|
107
|
+
return '';
|
|
108
|
+
return id;
|
|
103
109
|
}
|
|
104
110
|
function parseNoteFile(filePath) {
|
|
105
111
|
try {
|
package/dist/lib/file-mirror.js
CHANGED
|
@@ -47,6 +47,7 @@ exports.mirrorTaskRelation = mirrorTaskRelation;
|
|
|
47
47
|
exports.mirrorSkillRelation = mirrorSkillRelation;
|
|
48
48
|
exports.mirrorAttachmentEvent = mirrorAttachmentEvent;
|
|
49
49
|
exports.deleteMirrorDir = deleteMirrorDir;
|
|
50
|
+
exports.sanitizeEntityId = sanitizeEntityId;
|
|
50
51
|
exports.sanitizeFilename = sanitizeFilename;
|
|
51
52
|
exports.writeAttachment = writeAttachment;
|
|
52
53
|
exports.deleteAttachment = deleteAttachment;
|
|
@@ -56,6 +57,10 @@ const path = __importStar(require("path"));
|
|
|
56
57
|
const crypto_1 = __importDefault(require("crypto"));
|
|
57
58
|
const frontmatter_1 = require("./frontmatter");
|
|
58
59
|
const events_log_1 = require("./events-log");
|
|
60
|
+
/** Sanitize a string for safe inclusion in log output. */
|
|
61
|
+
function sanitizeForLog(s) {
|
|
62
|
+
return s.replace(/[\r\n\t]/g, ' ').slice(0, 200);
|
|
63
|
+
}
|
|
59
64
|
/** Write to a temp file then rename — atomic on same filesystem. */
|
|
60
65
|
function atomicWriteFileSync(filePath, data, encoding) {
|
|
61
66
|
const tmp = `${filePath}.${crypto_1.default.randomBytes(4).toString('hex')}.tmp`;
|
|
@@ -80,7 +85,12 @@ function buildOutgoingRelations(entityId, relations) {
|
|
|
80
85
|
/** Append a 'created' event + write content.md + regenerate note.md snapshot. */
|
|
81
86
|
function mirrorNoteCreate(notesDir, noteId, attrs, relations) {
|
|
82
87
|
try {
|
|
83
|
-
const
|
|
88
|
+
const safeId = sanitizeEntityId(noteId);
|
|
89
|
+
if (!safeId) {
|
|
90
|
+
process.stderr.write(`[file-mirror] rejected invalid entity ID\n`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const entityDir = path.join(notesDir, safeId);
|
|
84
94
|
fs.mkdirSync(entityDir, { recursive: true });
|
|
85
95
|
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
86
96
|
if (!fs.existsSync(eventsPath)) {
|
|
@@ -101,13 +111,18 @@ function mirrorNoteCreate(notesDir, noteId, attrs, relations) {
|
|
|
101
111
|
(0, events_log_1.ensureGitattributes)(notesDir);
|
|
102
112
|
}
|
|
103
113
|
catch (err) {
|
|
104
|
-
process.stderr.write(`[file-mirror] failed to mirror note create ${noteId}: ${err}\n`);
|
|
114
|
+
process.stderr.write(`[file-mirror] failed to mirror note create ${sanitizeForLog(noteId)}: ${err}\n`);
|
|
105
115
|
}
|
|
106
116
|
}
|
|
107
117
|
/** Append an 'update' event + (if content changed) write content.md + regenerate note.md. */
|
|
108
118
|
function mirrorNoteUpdate(notesDir, noteId, patch, attrs, relations) {
|
|
109
119
|
try {
|
|
110
|
-
const
|
|
120
|
+
const safeId = sanitizeEntityId(noteId);
|
|
121
|
+
if (!safeId) {
|
|
122
|
+
process.stderr.write(`[file-mirror] rejected invalid entity ID\n`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const entityDir = path.join(notesDir, safeId);
|
|
111
126
|
fs.mkdirSync(entityDir, { recursive: true });
|
|
112
127
|
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
113
128
|
const delta = { op: 'update' };
|
|
@@ -127,7 +142,7 @@ function mirrorNoteUpdate(notesDir, noteId, patch, attrs, relations) {
|
|
|
127
142
|
_regenerateNoteSnapshot(notesDir, noteId, attrs, relations);
|
|
128
143
|
}
|
|
129
144
|
catch (err) {
|
|
130
|
-
process.stderr.write(`[file-mirror] failed to mirror note update ${noteId}: ${err}\n`);
|
|
145
|
+
process.stderr.write(`[file-mirror] failed to mirror note update ${sanitizeForLog(noteId)}: ${err}\n`);
|
|
131
146
|
}
|
|
132
147
|
}
|
|
133
148
|
function _regenerateNoteSnapshot(notesDir, noteId, attrs, relations) {
|
|
@@ -153,7 +168,12 @@ function _regenerateNoteSnapshot(notesDir, noteId, attrs, relations) {
|
|
|
153
168
|
/** Append a 'created' event + write description.md + regenerate task.md snapshot. */
|
|
154
169
|
function mirrorTaskCreate(tasksDir, taskId, attrs, relations) {
|
|
155
170
|
try {
|
|
156
|
-
const
|
|
171
|
+
const safeId = sanitizeEntityId(taskId);
|
|
172
|
+
if (!safeId) {
|
|
173
|
+
process.stderr.write(`[file-mirror] rejected invalid entity ID\n`);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const entityDir = path.join(tasksDir, safeId);
|
|
157
177
|
fs.mkdirSync(entityDir, { recursive: true });
|
|
158
178
|
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
159
179
|
if (!fs.existsSync(eventsPath)) {
|
|
@@ -179,13 +199,18 @@ function mirrorTaskCreate(tasksDir, taskId, attrs, relations) {
|
|
|
179
199
|
(0, events_log_1.ensureGitattributes)(tasksDir);
|
|
180
200
|
}
|
|
181
201
|
catch (err) {
|
|
182
|
-
process.stderr.write(`[file-mirror] failed to mirror task create ${taskId}: ${err}\n`);
|
|
202
|
+
process.stderr.write(`[file-mirror] failed to mirror task create ${sanitizeForLog(taskId)}: ${err}\n`);
|
|
183
203
|
}
|
|
184
204
|
}
|
|
185
205
|
/** Append an 'update' event + (if description changed) write description.md + regenerate task.md. */
|
|
186
206
|
function mirrorTaskUpdate(tasksDir, taskId, patch, attrs, relations) {
|
|
187
207
|
try {
|
|
188
|
-
const
|
|
208
|
+
const safeId = sanitizeEntityId(taskId);
|
|
209
|
+
if (!safeId) {
|
|
210
|
+
process.stderr.write(`[file-mirror] rejected invalid entity ID\n`);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const entityDir = path.join(tasksDir, safeId);
|
|
189
214
|
fs.mkdirSync(entityDir, { recursive: true });
|
|
190
215
|
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
191
216
|
const delta = { op: 'update' };
|
|
@@ -215,7 +240,7 @@ function mirrorTaskUpdate(tasksDir, taskId, patch, attrs, relations) {
|
|
|
215
240
|
_regenerateTaskSnapshot(tasksDir, taskId, attrs, relations);
|
|
216
241
|
}
|
|
217
242
|
catch (err) {
|
|
218
|
-
process.stderr.write(`[file-mirror] failed to mirror task update ${taskId}: ${err}\n`);
|
|
243
|
+
process.stderr.write(`[file-mirror] failed to mirror task update ${sanitizeForLog(taskId)}: ${err}\n`);
|
|
219
244
|
}
|
|
220
245
|
}
|
|
221
246
|
function _regenerateTaskSnapshot(tasksDir, taskId, attrs, relations) {
|
|
@@ -247,7 +272,12 @@ function _regenerateTaskSnapshot(tasksDir, taskId, attrs, relations) {
|
|
|
247
272
|
/** Append a 'created' event + write description.md + regenerate skill.md snapshot. */
|
|
248
273
|
function mirrorSkillCreate(skillsDir, skillId, attrs, relations) {
|
|
249
274
|
try {
|
|
250
|
-
const
|
|
275
|
+
const safeId = sanitizeEntityId(skillId);
|
|
276
|
+
if (!safeId) {
|
|
277
|
+
process.stderr.write(`[file-mirror] rejected invalid entity ID\n`);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const entityDir = path.join(skillsDir, safeId);
|
|
251
281
|
fs.mkdirSync(entityDir, { recursive: true });
|
|
252
282
|
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
253
283
|
if (!fs.existsSync(eventsPath)) {
|
|
@@ -276,13 +306,18 @@ function mirrorSkillCreate(skillsDir, skillId, attrs, relations) {
|
|
|
276
306
|
(0, events_log_1.ensureGitattributes)(skillsDir);
|
|
277
307
|
}
|
|
278
308
|
catch (err) {
|
|
279
|
-
process.stderr.write(`[file-mirror] failed to mirror skill create ${skillId}: ${err}\n`);
|
|
309
|
+
process.stderr.write(`[file-mirror] failed to mirror skill create ${sanitizeForLog(skillId)}: ${err}\n`);
|
|
280
310
|
}
|
|
281
311
|
}
|
|
282
312
|
/** Append an 'update' event + (if description changed) write description.md + regenerate skill.md. */
|
|
283
313
|
function mirrorSkillUpdate(skillsDir, skillId, patch, attrs, relations) {
|
|
284
314
|
try {
|
|
285
|
-
const
|
|
315
|
+
const safeId = sanitizeEntityId(skillId);
|
|
316
|
+
if (!safeId) {
|
|
317
|
+
process.stderr.write(`[file-mirror] rejected invalid entity ID\n`);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const entityDir = path.join(skillsDir, safeId);
|
|
286
321
|
fs.mkdirSync(entityDir, { recursive: true });
|
|
287
322
|
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
288
323
|
const delta = { op: 'update' };
|
|
@@ -318,7 +353,7 @@ function mirrorSkillUpdate(skillsDir, skillId, patch, attrs, relations) {
|
|
|
318
353
|
_regenerateSkillSnapshot(skillsDir, skillId, attrs, relations);
|
|
319
354
|
}
|
|
320
355
|
catch (err) {
|
|
321
|
-
process.stderr.write(`[file-mirror] failed to mirror skill update ${skillId}: ${err}\n`);
|
|
356
|
+
process.stderr.write(`[file-mirror] failed to mirror skill update ${sanitizeForLog(skillId)}: ${err}\n`);
|
|
322
357
|
}
|
|
323
358
|
}
|
|
324
359
|
function _regenerateSkillSnapshot(skillsDir, skillId, attrs, relations) {
|
|
@@ -364,7 +399,7 @@ function mirrorNoteRelation(notesDir, noteId, action, kind, to, attrs, relations
|
|
|
364
399
|
_regenerateNoteSnapshot(notesDir, noteId, attrs, relations);
|
|
365
400
|
}
|
|
366
401
|
catch (err) {
|
|
367
|
-
process.stderr.write(`[file-mirror] failed to mirror note relation ${noteId}: ${err}\n`);
|
|
402
|
+
process.stderr.write(`[file-mirror] failed to mirror note relation ${sanitizeForLog(noteId)}: ${err}\n`);
|
|
368
403
|
}
|
|
369
404
|
}
|
|
370
405
|
function mirrorTaskRelation(tasksDir, taskId, action, kind, to, attrs, relations, graph) {
|
|
@@ -378,7 +413,7 @@ function mirrorTaskRelation(tasksDir, taskId, action, kind, to, attrs, relations
|
|
|
378
413
|
_regenerateTaskSnapshot(tasksDir, taskId, attrs, relations);
|
|
379
414
|
}
|
|
380
415
|
catch (err) {
|
|
381
|
-
process.stderr.write(`[file-mirror] failed to mirror task relation ${taskId}: ${err}\n`);
|
|
416
|
+
process.stderr.write(`[file-mirror] failed to mirror task relation ${sanitizeForLog(taskId)}: ${err}\n`);
|
|
382
417
|
}
|
|
383
418
|
}
|
|
384
419
|
function mirrorSkillRelation(skillsDir, skillId, action, kind, to, attrs, relations, graph) {
|
|
@@ -392,7 +427,7 @@ function mirrorSkillRelation(skillsDir, skillId, action, kind, to, attrs, relati
|
|
|
392
427
|
_regenerateSkillSnapshot(skillsDir, skillId, attrs, relations);
|
|
393
428
|
}
|
|
394
429
|
catch (err) {
|
|
395
|
-
process.stderr.write(`[file-mirror] failed to mirror skill relation ${skillId}: ${err}\n`);
|
|
430
|
+
process.stderr.write(`[file-mirror] failed to mirror skill relation ${sanitizeForLog(skillId)}: ${err}\n`);
|
|
396
431
|
}
|
|
397
432
|
}
|
|
398
433
|
/** Append an attachment add/remove event. */
|
|
@@ -410,18 +445,30 @@ function mirrorAttachmentEvent(entityDir, action, file) {
|
|
|
410
445
|
// ---------------------------------------------------------------------------
|
|
411
446
|
/** Delete the entire mirror directory for a note, task or skill (including attachments). */
|
|
412
447
|
function deleteMirrorDir(dir, id) {
|
|
448
|
+
const safeId = sanitizeEntityId(id);
|
|
449
|
+
if (!safeId) {
|
|
450
|
+
process.stderr.write(`[file-mirror] rejected invalid entity ID\n`);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
413
453
|
try {
|
|
414
|
-
fs.rmSync(path.join(dir,
|
|
454
|
+
fs.rmSync(path.join(dir, safeId), { recursive: true, force: true });
|
|
415
455
|
}
|
|
416
456
|
catch (err) {
|
|
417
457
|
if (err.code !== 'ENOENT') {
|
|
418
|
-
process.stderr.write(`[file-mirror] failed to delete ${id}/: ${err}\n`);
|
|
458
|
+
process.stderr.write(`[file-mirror] failed to delete ${sanitizeForLog(id)}/: ${err}\n`);
|
|
419
459
|
}
|
|
420
460
|
}
|
|
421
461
|
}
|
|
422
462
|
// ---------------------------------------------------------------------------
|
|
423
463
|
// Attachment file helpers (paths now go through attachments/ subdir)
|
|
424
464
|
// ---------------------------------------------------------------------------
|
|
465
|
+
/** Sanitize an entity ID: extract basename, strip null bytes and path traversal. */
|
|
466
|
+
function sanitizeEntityId(id) {
|
|
467
|
+
const base = path.basename(id.replace(/\0/g, '').replace(/\\/g, '/')).trim();
|
|
468
|
+
if (base === '.' || base === '..')
|
|
469
|
+
return '';
|
|
470
|
+
return base;
|
|
471
|
+
}
|
|
425
472
|
/** Sanitize a filename: extract basename, strip null bytes and path traversal. */
|
|
426
473
|
function sanitizeFilename(name) {
|
|
427
474
|
// Normalize backslashes to forward slashes (path.basename on Unix doesn't treat \ as separator)
|
|
@@ -433,29 +480,38 @@ function sanitizeFilename(name) {
|
|
|
433
480
|
}
|
|
434
481
|
/** Write an attachment file to the entity's attachments/ subdirectory. */
|
|
435
482
|
function writeAttachment(baseDir, entityId, filename, data) {
|
|
483
|
+
const safeEntityId = sanitizeEntityId(entityId);
|
|
484
|
+
if (!safeEntityId)
|
|
485
|
+
throw new Error('Entity ID is empty after sanitization');
|
|
436
486
|
const safe = sanitizeFilename(filename);
|
|
437
487
|
if (!safe)
|
|
438
488
|
throw new Error('Attachment filename is empty after sanitization');
|
|
439
|
-
const attachmentsDir = path.join(baseDir,
|
|
489
|
+
const attachmentsDir = path.join(baseDir, safeEntityId, 'attachments');
|
|
440
490
|
fs.mkdirSync(attachmentsDir, { recursive: true });
|
|
441
491
|
fs.writeFileSync(path.join(attachmentsDir, safe), data);
|
|
442
492
|
}
|
|
443
493
|
/** Delete an attachment file from attachments/ subdir. Returns true if it existed. */
|
|
444
494
|
function deleteAttachment(baseDir, entityId, filename) {
|
|
445
|
-
const
|
|
495
|
+
const safeEntityId = sanitizeEntityId(entityId);
|
|
496
|
+
if (!safeEntityId)
|
|
497
|
+
return false;
|
|
498
|
+
const filePath = path.join(baseDir, safeEntityId, 'attachments', sanitizeFilename(filename));
|
|
446
499
|
try {
|
|
447
500
|
fs.unlinkSync(filePath);
|
|
448
501
|
return true;
|
|
449
502
|
}
|
|
450
503
|
catch (err) {
|
|
451
504
|
if (err.code !== 'ENOENT') {
|
|
452
|
-
process.stderr.write(`[file-mirror] failed to delete attachment ${filename}: ${err}\n`);
|
|
505
|
+
process.stderr.write(`[file-mirror] failed to delete attachment ${sanitizeForLog(filename)}: ${err}\n`);
|
|
453
506
|
}
|
|
454
507
|
return false;
|
|
455
508
|
}
|
|
456
509
|
}
|
|
457
510
|
/** Get the absolute path of an attachment in attachments/ subdir, or null if not found. */
|
|
458
511
|
function getAttachmentPath(baseDir, entityId, filename) {
|
|
459
|
-
const
|
|
512
|
+
const safeEntityId = sanitizeEntityId(entityId);
|
|
513
|
+
if (!safeEntityId)
|
|
514
|
+
return null;
|
|
515
|
+
const filePath = path.join(baseDir, safeEntityId, 'attachments', sanitizeFilename(filename));
|
|
460
516
|
return fs.existsSync(filePath) ? filePath : null;
|
|
461
517
|
}
|
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.readJsonWithTmpFallback = readJsonWithTmpFallback;
|
|
37
|
+
exports.validateGraphStructure = validateGraphStructure;
|
|
37
38
|
const fs = __importStar(require("fs"));
|
|
38
39
|
/**
|
|
39
40
|
* Try to read and parse a JSON file, falling back to .tmp if main file
|
|
@@ -66,3 +67,44 @@ function readJsonWithTmpFallback(file) {
|
|
|
66
67
|
}
|
|
67
68
|
return null;
|
|
68
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Basic structural validation for Graphology serialized graph data.
|
|
72
|
+
* Ensures the shape matches what graph.import() expects, preventing
|
|
73
|
+
* injection of unexpected properties.
|
|
74
|
+
*/
|
|
75
|
+
function validateGraphStructure(data) {
|
|
76
|
+
if (data == null || typeof data !== 'object')
|
|
77
|
+
return false;
|
|
78
|
+
const obj = data;
|
|
79
|
+
if (!Array.isArray(obj.nodes))
|
|
80
|
+
return false;
|
|
81
|
+
if (!Array.isArray(obj.edges))
|
|
82
|
+
return false;
|
|
83
|
+
for (const node of obj.nodes) {
|
|
84
|
+
if (node == null || typeof node !== 'object')
|
|
85
|
+
return false;
|
|
86
|
+
const n = node;
|
|
87
|
+
if (typeof n.key !== 'string')
|
|
88
|
+
return false;
|
|
89
|
+
if (n.attributes !== undefined) {
|
|
90
|
+
if (n.attributes == null || typeof n.attributes !== 'object')
|
|
91
|
+
return false;
|
|
92
|
+
if (Object.prototype.hasOwnProperty.call(n.attributes, '__proto__') || Object.prototype.hasOwnProperty.call(n.attributes, 'constructor'))
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
for (const edge of obj.edges) {
|
|
97
|
+
if (edge == null || typeof edge !== 'object')
|
|
98
|
+
return false;
|
|
99
|
+
const e = edge;
|
|
100
|
+
if (typeof e.source !== 'string' || typeof e.target !== 'string')
|
|
101
|
+
return false;
|
|
102
|
+
if (e.attributes !== undefined) {
|
|
103
|
+
if (e.attributes == null || typeof e.attributes !== 'object')
|
|
104
|
+
return false;
|
|
105
|
+
if (Object.prototype.hasOwnProperty.call(e.attributes, '__proto__') || Object.prototype.hasOwnProperty.call(e.attributes, 'constructor'))
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
}
|
package/dist/lib/jwt.js
CHANGED
|
@@ -20,16 +20,17 @@ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
|
20
20
|
// Password hashing (scrypt)
|
|
21
21
|
// ---------------------------------------------------------------------------
|
|
22
22
|
const SCRYPT_KEYLEN = 64;
|
|
23
|
-
const SCRYPT_COST =
|
|
23
|
+
const SCRYPT_COST = 65536; // N
|
|
24
24
|
const SCRYPT_BLOCK = 8; // r
|
|
25
25
|
const SCRYPT_PARALLEL = 1; // p
|
|
26
|
+
const SCRYPT_MAXMEM = 128 * 1024 * 1024; // 128 MiB (needed for N=65536, r=8)
|
|
26
27
|
/**
|
|
27
28
|
* Hash a password using scrypt. Returns a string: `$scrypt$N$r$p$salt$hash`
|
|
28
29
|
*/
|
|
29
30
|
async function hashPassword(password) {
|
|
30
31
|
const salt = crypto_1.default.randomBytes(16).toString('hex');
|
|
31
32
|
const derived = await new Promise((resolve, reject) => {
|
|
32
|
-
crypto_1.default.scrypt(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_COST, r: SCRYPT_BLOCK, p: SCRYPT_PARALLEL }, (err, key) => {
|
|
33
|
+
crypto_1.default.scrypt(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_COST, r: SCRYPT_BLOCK, p: SCRYPT_PARALLEL, maxmem: SCRYPT_MAXMEM }, (err, key) => {
|
|
33
34
|
if (err)
|
|
34
35
|
reject(err);
|
|
35
36
|
else
|
|
@@ -52,7 +53,7 @@ async function verifyPassword(password, storedHash) {
|
|
|
52
53
|
const salt = parts[5];
|
|
53
54
|
const expectedHash = parts[6];
|
|
54
55
|
const derived = await new Promise((resolve, reject) => {
|
|
55
|
-
crypto_1.default.scrypt(password, salt, SCRYPT_KEYLEN, { N, r, p }, (err, key) => {
|
|
56
|
+
crypto_1.default.scrypt(password, salt, SCRYPT_KEYLEN, { N, r, p, maxmem: SCRYPT_MAXMEM }, (err, key) => {
|
|
56
57
|
if (err)
|
|
57
58
|
reject(err);
|
|
58
59
|
else
|
|
@@ -8,15 +8,33 @@ exports.PromiseQueue = void 0;
|
|
|
8
8
|
* but the queue continues processing subsequent items.
|
|
9
9
|
*/
|
|
10
10
|
class PromiseQueue {
|
|
11
|
-
|
|
11
|
+
queue = [];
|
|
12
|
+
running = false;
|
|
12
13
|
/**
|
|
13
14
|
* Enqueue an async function. Returns a promise that resolves/rejects
|
|
14
15
|
* with the function's result once it has been executed in turn.
|
|
15
16
|
*/
|
|
16
17
|
enqueue(fn) {
|
|
17
18
|
return new Promise((resolve, reject) => {
|
|
18
|
-
this.
|
|
19
|
+
this.queue.push(async () => {
|
|
20
|
+
try {
|
|
21
|
+
resolve(await fn());
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
reject(e);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
if (!this.running)
|
|
28
|
+
this.drain();
|
|
19
29
|
});
|
|
20
30
|
}
|
|
31
|
+
async drain() {
|
|
32
|
+
this.running = true;
|
|
33
|
+
while (this.queue.length > 0) {
|
|
34
|
+
const task = this.queue.shift();
|
|
35
|
+
await task();
|
|
36
|
+
}
|
|
37
|
+
this.running = false;
|
|
38
|
+
}
|
|
21
39
|
}
|
|
22
40
|
exports.PromiseQueue = PromiseQueue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-Dw2cE7zH.js";import{
|
|
1
|
+
import{r as e}from"./rolldown-runtime-Dw2cE7zH.js";import{O as t,k as n}from"./vendor-markdown-DngssFHR.js";import{Y as r,nt as i,q as a,r as o}from"./vendor-mui-DXUYJbRC.js";import{D as s,E as c,I as l,O as u,P as d,S as f}from"./index-80sqSHwS.js";var p=e(n(),1),m=t();function h({note:e,onSubmit:t,onCancel:n,submitLabel:h=`Save`}){let[g,_]=(0,p.useState)(``),[v,y]=(0,p.useState)(``),[b,x]=(0,p.useState)([]),[S,C]=(0,p.useState)(!1),[w,T]=(0,p.useState)(!1);(0,p.useEffect)(()=>{e&&(_(e.title),y(e.content),x(e.tags??[]))},[e]);let E=async()=>{if(!g.trim()){T(!0);return}C(!0);try{await t({title:g.trim(),content:v.trim(),tags:b})}finally{C(!1)}};return(0,m.jsxs)(r,{component:`form`,id:`note-form`,onSubmit:e=>{e.preventDefault(),E()},sx:{display:`flex`,flexDirection:`column`,gap:3},children:[(0,m.jsx)(l,{title:`Details`,children:(0,m.jsxs)(u,{children:[(0,m.jsxs)(s,{fullWidth:!0,children:[(0,m.jsx)(c,{required:!0,children:`Title`}),(0,m.jsx)(o,{autoFocus:!0,fullWidth:!0,value:g,onChange:e=>{_(e.target.value),T(!1)},error:w,helperText:w?`Title is required`:void 0})]}),(0,m.jsxs)(s,{fullWidth:!0,children:[(0,m.jsx)(c,{children:`Content`}),(0,m.jsx)(f,{value:v,onChange:y,height:300})]}),(0,m.jsx)(s,{fullWidth:!0,children:(0,m.jsx)(d,{tags:b,editable:!0,onAdd:e=>x(t=>t.includes(e)?t:[...t,e]),onRemove:e=>x(t=>t.filter(t=>t!==e))})})]})}),(0,m.jsxs)(r,{sx:{display:`flex`,gap:1,justifyContent:`flex-end`},children:[(0,m.jsx)(a,{onClick:n,children:`Cancel`}),(0,m.jsx)(a,{variant:`contained`,onClick:E,disabled:S||!g.trim(),children:S?(0,m.jsx)(i,{size:20}):h})]})]})}export{h as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-Dw2cE7zH.js";import{
|
|
1
|
+
import{r as e}from"./rolldown-runtime-Dw2cE7zH.js";import{O as t,k as n}from"./vendor-markdown-DngssFHR.js";import{$ as r,Y as i,_ as a,nt as o,q as s,r as c,tt as l,v as u,y as d}from"./vendor-mui-DXUYJbRC.js";import{K as f,i as p}from"./vendor-mui-icons-YtgP6dg2.js";import{D as m,E as h,I as g,O as _,P as v,S as y}from"./index-80sqSHwS.js";var b=e(n(),1),x=t();function S({skill:e,onSubmit:t,onCancel:n,submitLabel:S=`Save`}){let[C,w]=(0,b.useState)(``),[T,E]=(0,b.useState)(``),[D,O]=(0,b.useState)([``]),[k,A]=(0,b.useState)([]),[j,M]=(0,b.useState)([]),[N,P]=(0,b.useState)([]),[F,I]=(0,b.useState)([]),[L,R]=(0,b.useState)(`user`),[z,B]=(0,b.useState)(1),[V,H]=(0,b.useState)(!1),[U,W]=(0,b.useState)(!1);(0,b.useEffect)(()=>{e&&(w(e.title),E(e.description),O(e.steps.length>0?e.steps:[``]),A(e.triggers??[]),M(e.inputHints??[]),P(e.filePatterns??[]),I(e.tags??[]),R(e.source),B(e.confidence))},[e]);let G=async()=>{if(!C.trim()){W(!0);return}H(!0);try{await t({title:C.trim(),description:T.trim(),steps:D.map(e=>e.trim()).filter(Boolean),triggers:k,inputHints:j,filePatterns:N,tags:F,source:L,confidence:z})}finally{H(!1)}},K=(e,t)=>{O(n=>n.map((n,r)=>r===e?t:n))},q=()=>{O(e=>[...e,``])},J=e=>{O(t=>t.filter((t,n)=>n!==e))};return(0,x.jsxs)(i,{component:`form`,id:`skill-form`,onSubmit:e=>{e.preventDefault(),G()},sx:{display:`flex`,flexDirection:`column`,gap:3},children:[(0,x.jsx)(g,{title:`Details`,children:(0,x.jsxs)(_,{children:[(0,x.jsxs)(m,{fullWidth:!0,children:[(0,x.jsx)(h,{required:!0,children:`Title`}),(0,x.jsx)(c,{autoFocus:!0,fullWidth:!0,value:C,onChange:e=>{w(e.target.value),W(!1)},error:U,helperText:U?`Title is required`:void 0})]}),(0,x.jsxs)(m,{fullWidth:!0,children:[(0,x.jsx)(h,{children:`Description`}),(0,x.jsx)(y,{value:T,onChange:E,height:200})]})]})}),(0,x.jsx)(g,{title:`Steps`,children:(0,x.jsxs)(i,{sx:{display:`flex`,flexDirection:`column`,gap:1},children:[D.map((e,t)=>(0,x.jsxs)(i,{sx:{display:`flex`,gap:1,alignItems:`center`},children:[(0,x.jsxs)(r,{variant:`body2`,sx:{minWidth:24,color:`text.secondary`},children:[t+1,`.`]}),(0,x.jsx)(c,{fullWidth:!0,size:`small`,value:e,onChange:e=>K(t,e.target.value),placeholder:`Step ${t+1}`}),D.length>1&&(0,x.jsx)(l,{size:`small`,onClick:()=>J(t),color:`error`,children:(0,x.jsx)(p,{fontSize:`small`})})]},t)),(0,x.jsx)(s,{size:`small`,startIcon:(0,x.jsx)(f,{}),onClick:q,sx:{alignSelf:`flex-start`},children:`Add Step`})]})}),(0,x.jsx)(g,{title:`Matching`,children:(0,x.jsxs)(_,{children:[(0,x.jsxs)(m,{fullWidth:!0,children:[(0,x.jsx)(h,{children:`Triggers`}),(0,x.jsx)(v,{tags:k,editable:!0,onAdd:e=>A(t=>t.includes(e)?t:[...t,e]),onRemove:e=>A(t=>t.filter(t=>t!==e))})]}),(0,x.jsxs)(m,{fullWidth:!0,children:[(0,x.jsx)(h,{children:`Input Hints`}),(0,x.jsx)(v,{tags:j,editable:!0,onAdd:e=>M(t=>t.includes(e)?t:[...t,e]),onRemove:e=>M(t=>t.filter(t=>t!==e))})]}),(0,x.jsxs)(m,{fullWidth:!0,children:[(0,x.jsx)(h,{children:`File Patterns`}),(0,x.jsx)(v,{tags:N,editable:!0,onAdd:e=>P(t=>t.includes(e)?t:[...t,e]),onRemove:e=>P(t=>t.filter(t=>t!==e))})]})]})}),(0,x.jsx)(g,{title:`Properties`,children:(0,x.jsxs)(_,{children:[(0,x.jsxs)(m,{children:[(0,x.jsx)(h,{children:`Source`}),(0,x.jsxs)(u,{fullWidth:!0,value:L,onChange:e=>R(e.target.value),children:[(0,x.jsx)(d,{value:`user`,children:`User`}),(0,x.jsx)(d,{value:`learned`,children:`Learned`})]})]}),(0,x.jsx)(m,{children:(0,x.jsxs)(i,{sx:{px:1},children:[(0,x.jsxs)(h,{children:[`Confidence: `,Math.round(z*100),`%`]}),(0,x.jsx)(a,{value:z,onChange:(e,t)=>B(t),min:0,max:1,step:.01,valueLabelDisplay:`auto`,valueLabelFormat:e=>`${Math.round(e*100)}%`})]})}),(0,x.jsx)(m,{fullWidth:!0,children:(0,x.jsx)(v,{tags:F,editable:!0,onAdd:e=>I(t=>t.includes(e)?t:[...t,e]),onRemove:e=>I(t=>t.filter(t=>t!==e))})})]})}),(0,x.jsxs)(i,{sx:{display:`flex`,gap:1,justifyContent:`flex-end`},children:[(0,x.jsx)(s,{onClick:n,children:`Cancel`}),(0,x.jsx)(s,{variant:`contained`,onClick:G,disabled:V||!C.trim(),children:V?(0,x.jsx)(o,{size:20}):S})]})]})}export{S as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-Dw2cE7zH.js";import{
|
|
1
|
+
import{r as e}from"./rolldown-runtime-Dw2cE7zH.js";import{O as t,k as n}from"./vendor-markdown-DngssFHR.js";import{l as r}from"./vendor-react-DxfYAwYK.js";import{Y as i,nt as a,q as o,r as s,v as c,y as l}from"./vendor-mui-DXUYJbRC.js";import{D as u,E as d,I as f,O as p,P as m,S as h,et as g,t as _}from"./index-80sqSHwS.js";var v=e(n(),1),y=t();function b({task:e,onSubmit:t,onCancel:n,submitLabel:b=`Save`}){let{projectId:x}=r(),[S,C]=(0,v.useState)(``),[w,T]=(0,v.useState)(``),[E,D]=(0,v.useState)(`todo`),[O,k]=(0,v.useState)(`medium`),[A,j]=(0,v.useState)([]),[M,N]=(0,v.useState)(``),[P,F]=(0,v.useState)(``),[I,L]=(0,v.useState)(``),[R,z]=(0,v.useState)([]),[B,V]=(0,v.useState)(!1),[H,U]=(0,v.useState)(!1);(0,v.useEffect)(()=>{e&&(C(e.title),T(e.description),D(e.status),k(e.priority),j(e.tags??[]),N(e.dueDate?new Date(e.dueDate).toISOString().split(`T`)[0]:``),F(e.estimate==null?``:String(e.estimate)),L(e.assignee??``))},[e]),(0,v.useEffect)(()=>{x&&g(x).then(z).catch(()=>{})},[x]);let W=async()=>{if(!S.trim()){U(!0);return}V(!0);try{await t({title:S.trim(),description:w.trim(),status:E,priority:O,tags:A,dueDate:M?new Date(M).getTime():null,estimate:P?Number(P):null,assignee:I||null})}finally{V(!1)}};return(0,y.jsxs)(i,{component:`form`,id:`task-form`,onSubmit:e=>{e.preventDefault(),W()},sx:{display:`flex`,flexDirection:`column`,gap:3},children:[(0,y.jsx)(f,{title:`Details`,children:(0,y.jsxs)(p,{children:[(0,y.jsxs)(u,{fullWidth:!0,children:[(0,y.jsx)(d,{required:!0,children:`Title`}),(0,y.jsx)(s,{autoFocus:!0,fullWidth:!0,value:S,onChange:e=>{C(e.target.value),U(!1)},error:H,helperText:H?`Title is required`:void 0})]}),(0,y.jsxs)(u,{fullWidth:!0,children:[(0,y.jsx)(d,{children:`Description`}),(0,y.jsx)(h,{value:w,onChange:T,height:250})]})]})}),(0,y.jsx)(f,{title:`Properties`,children:(0,y.jsxs)(p,{children:[(0,y.jsxs)(u,{children:[(0,y.jsx)(d,{children:`Status`}),(0,y.jsx)(c,{fullWidth:!0,value:E,onChange:e=>D(e.target.value),children:_.map(e=>(0,y.jsx)(l,{value:e.status,children:e.label},e.status))})]}),(0,y.jsxs)(u,{children:[(0,y.jsx)(d,{children:`Priority`}),(0,y.jsxs)(c,{fullWidth:!0,value:O,onChange:e=>k(e.target.value),children:[(0,y.jsx)(l,{value:`critical`,children:`Critical`}),(0,y.jsx)(l,{value:`high`,children:`High`}),(0,y.jsx)(l,{value:`medium`,children:`Medium`}),(0,y.jsx)(l,{value:`low`,children:`Low`})]})]}),(0,y.jsxs)(u,{children:[(0,y.jsx)(d,{children:`Due Date`}),(0,y.jsx)(s,{fullWidth:!0,type:`date`,value:M,onChange:e=>N(e.target.value)})]}),(0,y.jsxs)(u,{children:[(0,y.jsx)(d,{children:`Estimate (hours)`}),(0,y.jsx)(s,{fullWidth:!0,type:`number`,value:P,onChange:e=>F(e.target.value),slotProps:{input:{inputProps:{min:0,step:.5}}}})]}),(0,y.jsxs)(u,{children:[(0,y.jsx)(d,{children:`Assignee`}),(0,y.jsxs)(c,{fullWidth:!0,value:I,onChange:e=>L(e.target.value),displayEmpty:!0,renderValue:e=>e?R.find(t=>t.id===e)?.name||e:`Unassigned`,children:[(0,y.jsx)(l,{value:``,children:`Unassigned`}),R.map(e=>(0,y.jsx)(l,{value:e.id,children:e.name||e.id},e.id))]})]}),(0,y.jsx)(u,{fullWidth:!0,children:(0,y.jsx)(m,{tags:A,editable:!0,onAdd:e=>j(t=>t.includes(e)?t:[...t,e]),onRemove:e=>j(t=>t.filter(t=>t!==e))})})]})}),(0,y.jsxs)(i,{sx:{display:`flex`,gap:1,justifyContent:`flex-end`},children:[(0,y.jsx)(o,{onClick:n,children:`Cancel`}),(0,y.jsx)(o,{variant:`contained`,onClick:W,disabled:B||!S.trim(),children:B?(0,y.jsx)(a,{size:20}):b})]})]})}export{b as t};
|