maskweaver 0.9.4 → 0.9.6
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.ko.md +638 -592
- package/README.md +671 -667
- package/dist/cli/doctor.js +5 -21
- package/dist/cli/install.d.ts +0 -8
- package/dist/cli/install.js +0 -39
- package/dist/context/config.d.ts +0 -22
- package/dist/context/config.js +0 -28
- package/dist/context/feature.d.ts +0 -39
- package/dist/context/feature.js +0 -77
- package/dist/context/files.d.ts +0 -13
- package/dist/context/files.js +1 -24
- package/dist/context/index.d.ts +0 -7
- package/dist/context/index.js +0 -12
- package/dist/context/project.d.ts +0 -21
- package/dist/context/project.js +0 -30
- package/dist/context/types.d.ts +0 -48
- package/dist/context/types.js +0 -12
- package/dist/context/utils.d.ts +0 -18
- package/dist/context/utils.js +0 -27
- package/dist/core/engine/promptBuilder.d.ts +0 -17
- package/dist/core/engine/promptBuilder.js +0 -28
- package/dist/core/index.d.ts +0 -6
- package/dist/core/index.js +0 -9
- package/dist/core/loader/MaskLoader.d.ts +0 -23
- package/dist/core/loader/MaskLoader.js +0 -29
- package/dist/core/schema/types.d.ts +0 -47
- package/dist/core/schema/types.js +0 -6
- package/dist/core/schema/validator.d.ts +0 -14
- package/dist/core/schema/validator.js +0 -18
- package/dist/i18n/index.d.ts +0 -18
- package/dist/i18n/index.js +4 -23
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -8
- package/dist/lib.d.ts +0 -5
- package/dist/lib.js +0 -12
- package/dist/memory/chunking.d.ts +0 -22
- package/dist/memory/chunking.js +2 -37
- package/dist/memory/core.d.ts +0 -29
- package/dist/memory/core.js +1 -52
- package/dist/memory/index.d.ts +0 -5
- package/dist/memory/index.js +0 -10
- package/dist/memory/indexer.d.ts +0 -21
- package/dist/memory/indexer.js +0 -44
- package/dist/memory/providers/examples.d.ts +0 -5
- package/dist/memory/providers/examples.js +4 -64
- package/dist/memory/providers/factory.d.ts +0 -44
- package/dist/memory/providers/factory.js +0 -46
- package/dist/memory/providers/index.d.ts +0 -26
- package/dist/memory/providers/index.js +0 -28
- package/dist/memory/providers/ollama.d.ts +0 -6
- package/dist/memory/providers/ollama.js +1 -8
- package/dist/memory/providers/openai.d.ts +0 -6
- package/dist/memory/providers/openai.js +1 -8
- package/dist/memory/providers/openrouter.d.ts +0 -6
- package/dist/memory/providers/openrouter.js +0 -8
- package/dist/memory/providers/text-only.d.ts +0 -13
- package/dist/memory/providers/text-only.js +0 -17
- package/dist/memory/providers/types.d.ts +0 -39
- package/dist/memory/providers/types.js +0 -7
- package/dist/memory/providers/voyage.d.ts +0 -22
- package/dist/memory/providers/voyage.js +1 -24
- package/dist/memory/search/hybrid.d.ts +0 -12
- package/dist/memory/search/hybrid.js +1 -22
- package/dist/memory/store/sqlite.d.ts +0 -72
- package/dist/memory/store/sqlite.js +4 -127
- package/dist/plugin/config/index.d.ts +0 -112
- package/dist/plugin/config/index.js +0 -115
- package/dist/plugin/index.d.ts +0 -13
- package/dist/plugin/index.js +1 -123
- package/dist/plugin/tools/command-registry.d.ts +0 -6
- package/dist/plugin/tools/command-registry.js +0 -14
- package/dist/plugin/tools/context.d.ts +0 -12
- package/dist/plugin/tools/context.js +0 -58
- package/dist/plugin/tools/maskSave.d.ts +0 -3
- package/dist/plugin/tools/maskSave.js +0 -3
- package/dist/plugin/tools/memoryGet.d.ts +0 -3
- package/dist/plugin/tools/memoryGet.js +0 -3
- package/dist/plugin/tools/memoryIndexer.d.ts +0 -3
- package/dist/plugin/tools/memoryIndexer.js +0 -10
- package/dist/plugin/tools/memorySearch.d.ts +0 -31
- package/dist/plugin/tools/memorySearch.js +0 -79
- package/dist/plugin/tools/memoryWrite.d.ts +0 -8
- package/dist/plugin/tools/memoryWrite.js +0 -32
- package/dist/plugin/tools/retrospect.d.ts +0 -3
- package/dist/plugin/tools/retrospect.js +0 -3
- package/dist/plugin/tools/slashcommand.d.ts +0 -11
- package/dist/plugin/tools/slashcommand.js +0 -38
- package/dist/plugin/tools/squad.d.ts +0 -12
- package/dist/plugin/tools/squad.js +11 -83
- package/dist/plugin/tools/weave.d.ts +0 -6
- package/dist/plugin/tools/weave.js +0 -78
- package/dist/plugin/types.d.ts +0 -20
- package/dist/plugin/types.js +0 -7
- package/dist/retrospect/index.d.ts +0 -7
- package/dist/retrospect/index.js +0 -9
- package/dist/retrospect/mask-save.d.ts +0 -12
- package/dist/retrospect/mask-save.js +1 -80
- package/dist/retrospect/retrospect.d.ts +0 -18
- package/dist/retrospect/retrospect.js +0 -63
- package/dist/retrospect/strategies/base.d.ts +0 -15
- package/dist/retrospect/strategies/base.js +0 -7
- package/dist/retrospect/strategies/deep.d.ts +0 -12
- package/dist/retrospect/strategies/deep.js +0 -24
- package/dist/retrospect/strategies/index.d.ts +0 -12
- package/dist/retrospect/strategies/index.js +0 -12
- package/dist/retrospect/strategies/quick.d.ts +0 -12
- package/dist/retrospect/strategies/quick.js +0 -19
- package/dist/retrospect/strategies/standard.d.ts +0 -12
- package/dist/retrospect/strategies/standard.js +0 -15
- package/dist/retrospect/types.d.ts +0 -7
- package/dist/retrospect/types.js +0 -7
- package/dist/shared/config.d.ts +0 -105
- package/dist/shared/config.js +0 -33
- package/dist/shared/errors.d.ts +0 -18
- package/dist/shared/errors.js +0 -19
- package/dist/shared/generate-agents.d.ts +0 -69
- package/dist/shared/generate-agents.js +2 -86
- package/dist/shared/image.d.ts +0 -67
- package/dist/shared/image.js +6 -104
- package/dist/shared/index.d.ts +0 -5
- package/dist/shared/index.js +0 -7
- package/dist/shared/model-registry.d.ts +0 -72
- package/dist/shared/model-registry.js +5 -95
- package/dist/shared/types.d.ts +0 -15
- package/dist/shared/types.js +0 -3
- package/dist/shared-context/dag.d.ts +0 -105
- package/dist/shared-context/dag.js +3 -114
- package/dist/shared-context/index.d.ts +0 -5
- package/dist/shared-context/index.js +0 -15
- package/dist/shared-context/logger.d.ts +0 -37
- package/dist/shared-context/logger.js +0 -41
- package/dist/shared-context/parallel-executor.d.ts +0 -54
- package/dist/shared-context/parallel-executor.js +4 -56
- package/dist/shared-context/session.d.ts +0 -56
- package/dist/shared-context/session.js +0 -47
- package/dist/shared-context/squad.d.ts +0 -68
- package/dist/shared-context/squad.js +0 -63
- package/dist/shared-context/storage.d.ts +0 -132
- package/dist/shared-context/storage.js +0 -116
- package/dist/shared-context/task.d.ts +0 -120
- package/dist/shared-context/task.js +0 -152
- package/dist/shared-context/test/dag.test.js +9 -14
- package/dist/shared-context/test/logger.test.d.ts +0 -8
- package/dist/shared-context/test/logger.test.js +0 -52
- package/dist/shared-context/test/session.test.d.ts +0 -7
- package/dist/shared-context/test/session.test.js +0 -63
- package/dist/shared-context/test/squad.test.d.ts +0 -10
- package/dist/shared-context/test/squad.test.js +2 -68
- package/dist/shared-context/test/storage.test.d.ts +0 -8
- package/dist/shared-context/test/storage.test.js +0 -68
- package/dist/shared-context/test/task.test.d.ts +0 -7
- package/dist/shared-context/test/task.test.js +0 -54
- package/dist/shared-context/test/watchdog.test.d.ts +0 -7
- package/dist/shared-context/test/watchdog.test.js +3 -58
- package/dist/shared-context/types.d.ts +0 -215
- package/dist/shared-context/types.js +0 -125
- package/dist/shared-context/watchdog.d.ts +0 -127
- package/dist/shared-context/watchdog.js +0 -148
- package/dist/shared-context/worktree.d.ts +0 -68
- package/dist/shared-context/worktree.js +2 -34
- package/dist/verify/budget.d.ts +0 -29
- package/dist/verify/budget.js +0 -34
- package/dist/verify/critical-files.d.ts +0 -17
- package/dist/verify/critical-files.js +0 -37
- package/dist/verify/escalation.d.ts +0 -20
- package/dist/verify/escalation.js +0 -22
- package/dist/verify/index.d.ts +0 -5
- package/dist/verify/index.js +0 -11
- package/dist/verify/prompts.d.ts +0 -20
- package/dist/verify/prompts.js +0 -20
- package/dist/verify/types.d.ts +0 -26
- package/dist/verify/types.js +1 -12
- package/dist/verify/verifier.d.ts +0 -29
- package/dist/verify/verifier.js +0 -54
- package/dist/version.d.ts +1 -16
- package/dist/version.js +1 -16
- package/dist/weave/bridge.d.ts +0 -35
- package/dist/weave/bridge.js +0 -51
- package/dist/weave/environment/detector.d.ts +0 -6
- package/dist/weave/environment/detector.js +4 -45
- package/dist/weave/environment/index.d.ts +0 -19
- package/dist/weave/environment/index.js +1 -39
- package/dist/weave/environment/issues.d.ts +0 -35
- package/dist/weave/environment/issues.js +0 -59
- package/dist/weave/git.d.ts +0 -8
- package/dist/weave/git.js +0 -8
- package/dist/weave/index.d.ts +0 -13
- package/dist/weave/index.js +2 -28
- package/dist/weave/knowledge/global.d.ts +0 -39
- package/dist/weave/knowledge/global.js +2 -78
- package/dist/weave/loop.js +0 -3
- package/dist/weave/orchestrator.d.ts +0 -69
- package/dist/weave/orchestrator.js +1 -101
- package/dist/weave/phase-manager.d.ts +0 -64
- package/dist/weave/phase-manager.js +0 -89
- package/dist/weave/security/secret-scan.d.ts +0 -14
- package/dist/weave/security/secret-scan.js +0 -19
- package/dist/weave/stages/build.js +0 -15
- package/dist/weave/stages/execute.d.ts +0 -42
- package/dist/weave/stages/execute.js +4 -86
- package/dist/weave/stages/handoff.d.ts +0 -7
- package/dist/weave/stages/handoff.js +0 -43
- package/dist/weave/stages/index.d.ts +0 -3
- package/dist/weave/stages/index.js +0 -3
- package/dist/weave/stages/intake.d.ts +0 -8
- package/dist/weave/stages/intake.js +5 -65
- package/dist/weave/stages/map.d.ts +0 -1
- package/dist/weave/stages/openspec.d.ts +0 -1
- package/dist/weave/stages/plan.d.ts +0 -11
- package/dist/weave/stages/plan.js +1 -53
- package/dist/weave/stages/refine.d.ts +0 -7
- package/dist/weave/stages/refine.js +0 -7
- package/dist/weave/stages/research.d.ts +0 -6
- package/dist/weave/stages/research.js +0 -6
- package/dist/weave/stages/spec.d.ts +0 -12
- package/dist/weave/stages/spec.js +0 -17
- package/dist/weave/types.d.ts +0 -20
- package/dist/weave/types.js +0 -5
- package/dist/weave/verification/commands.d.ts +0 -12
- package/dist/weave/verification/commands.js +0 -19
- package/dist/weave/verification/index.d.ts +0 -6
- package/dist/weave/verification/index.js +1 -19
- package/dist/weave/verification/playwright.d.ts +0 -47
- package/dist/weave/verification/playwright.js +1 -90
- package/dist/weave/worktree.d.ts +0 -16
- package/dist/weave/worktree.js +0 -23
- package/dist/weave/yaml-repair.d.ts +0 -39
- package/dist/weave/yaml-repair.js +13 -116
- package/package.json +1 -1
|
@@ -1,158 +1,26 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Storage Adapter
|
|
3
|
-
*
|
|
4
|
-
* File-based storage with abstraction for future database migration.
|
|
5
|
-
* Implements atomic writes and path traversal prevention.
|
|
6
|
-
*
|
|
7
|
-
* @author Kent Beck's Dummy Human
|
|
8
|
-
*/
|
|
9
|
-
/**
|
|
10
|
-
* Options for acquiring a file lock.
|
|
11
|
-
*
|
|
12
|
-
* Distributed lock semantics:
|
|
13
|
-
* - timeout: How long to wait for lock acquisition before giving up
|
|
14
|
-
* - stale: Duration after which an unreleased lock is considered abandoned
|
|
15
|
-
* - retries: Number of acquisition attempts with exponential backoff
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* // Conservative settings for critical sections
|
|
19
|
-
* const opts: LockOptions = { timeout: 10000, stale: 60000, retries: 5 };
|
|
20
|
-
*/
|
|
21
1
|
export interface LockOptions {
|
|
22
|
-
/** Lock acquisition timeout in milliseconds (default: 5000) */
|
|
23
2
|
timeout?: number;
|
|
24
|
-
/** Time after which lock is considered stale and can be stolen (default: 30000) */
|
|
25
3
|
stale?: number;
|
|
26
|
-
/** Number of retry attempts (default: 3) */
|
|
27
4
|
retries?: number;
|
|
28
5
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Abstract storage interface for data persistence.
|
|
31
|
-
* Enables swapping file storage with database or cloud storage.
|
|
32
|
-
*/
|
|
33
6
|
export interface StorageAdapter {
|
|
34
|
-
/** Read and parse JSON data from the given path */
|
|
35
7
|
read<T>(path: string): Promise<T | null>;
|
|
36
|
-
/** Write data as JSON to the given path (atomic) */
|
|
37
8
|
write<T>(path: string, data: T): Promise<void>;
|
|
38
|
-
/** Append a line to the given file */
|
|
39
9
|
append(path: string, line: string): Promise<void>;
|
|
40
|
-
/** Check if a path exists */
|
|
41
10
|
exists(path: string): boolean;
|
|
42
|
-
/** Ensure a directory exists, creating it if necessary */
|
|
43
11
|
ensureDir(path: string): Promise<void>;
|
|
44
|
-
/** Get the absolute path for a relative storage path */
|
|
45
12
|
getFullPath(path: string): string;
|
|
46
|
-
/**
|
|
47
|
-
* Acquire an exclusive lock on a file path.
|
|
48
|
-
* Returns an unlock function that MUST be called to release the lock.
|
|
49
|
-
*
|
|
50
|
-
* Correctness guarantees:
|
|
51
|
-
* - Mutual exclusion: Only one holder at a time
|
|
52
|
-
* - Stale lock detection: Abandoned locks are automatically reclaimable
|
|
53
|
-
* - Fencing: Use returned unlock function within try/finally
|
|
54
|
-
*
|
|
55
|
-
* @param path - Relative path to lock
|
|
56
|
-
* @param options - Lock behavior options
|
|
57
|
-
* @returns Unlock function - call to release the lock
|
|
58
|
-
* @throws {StorageError} If lock cannot be acquired within timeout
|
|
59
|
-
*
|
|
60
|
-
* @example
|
|
61
|
-
* const unlock = await storage.lock("state.json", { timeout: 5000 });
|
|
62
|
-
* try {
|
|
63
|
-
* const data = await storage.read("state.json");
|
|
64
|
-
* await storage.write("state.json", { ...data, updated: true });
|
|
65
|
-
* } finally {
|
|
66
|
-
* await unlock();
|
|
67
|
-
* }
|
|
68
|
-
*/
|
|
69
13
|
lock(path: string, options?: LockOptions): Promise<() => Promise<void>>;
|
|
70
14
|
}
|
|
71
|
-
/**
|
|
72
|
-
* Validate that a path is within the allowed base directory.
|
|
73
|
-
* Prevents path traversal attacks (e.g., "../../../etc/passwd").
|
|
74
|
-
*
|
|
75
|
-
* @param fullPath - The full path to validate
|
|
76
|
-
* @param baseDir - The base directory that should contain the path
|
|
77
|
-
* @returns True if the path is safe and within baseDir
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* validatePath("/data/sessions/abc/file.json", "/data/sessions"); // true
|
|
81
|
-
* validatePath("/data/../etc/passwd", "/data/sessions"); // false
|
|
82
|
-
*/
|
|
83
15
|
export declare function validatePath(fullPath: string, baseDir: string): boolean;
|
|
84
|
-
/**
|
|
85
|
-
* File-based implementation of the StorageAdapter interface.
|
|
86
|
-
* All paths are relative to the baseDir and validated for security.
|
|
87
|
-
*
|
|
88
|
-
* @example
|
|
89
|
-
* const storage = new FileStorageAdapter("/data/sessions");
|
|
90
|
-
* await storage.write("session-1/manifest.json", { id: "session-1" });
|
|
91
|
-
* const data = await storage.read("session-1/manifest.json");
|
|
92
|
-
*/
|
|
93
16
|
export declare class FileStorageAdapter implements StorageAdapter {
|
|
94
17
|
private readonly baseDir;
|
|
95
|
-
/**
|
|
96
|
-
* Create a new file storage adapter.
|
|
97
|
-
*
|
|
98
|
-
* @param baseDir - The root directory for all storage operations
|
|
99
|
-
*/
|
|
100
18
|
constructor(baseDir: string);
|
|
101
|
-
/**
|
|
102
|
-
* Read and parse a JSON file from storage.
|
|
103
|
-
*
|
|
104
|
-
* @param path - Relative path within baseDir
|
|
105
|
-
* @returns Parsed JSON data, or null if file doesn't exist
|
|
106
|
-
* @throws {ValidationError} If path escapes baseDir
|
|
107
|
-
* @throws {StorageError} If file read or parse fails
|
|
108
|
-
*/
|
|
109
19
|
read<T>(path: string): Promise<T | null>;
|
|
110
|
-
/**
|
|
111
|
-
* Write data to storage as formatted JSON (atomic operation).
|
|
112
|
-
*
|
|
113
|
-
* @param path - Relative path within baseDir
|
|
114
|
-
* @param data - Data to serialize and write
|
|
115
|
-
* @throws {ValidationError} If path escapes baseDir
|
|
116
|
-
* @throws {StorageError} If write fails
|
|
117
|
-
*/
|
|
118
20
|
write<T>(path: string, data: T): Promise<void>;
|
|
119
|
-
/**
|
|
120
|
-
* Append a line to a file (for log files).
|
|
121
|
-
*
|
|
122
|
-
* @param path - Relative path within baseDir
|
|
123
|
-
* @param line - Line to append (newline added automatically)
|
|
124
|
-
* @throws {ValidationError} If path escapes baseDir
|
|
125
|
-
* @throws {StorageError} If append fails
|
|
126
|
-
*/
|
|
127
21
|
append(path: string, line: string): Promise<void>;
|
|
128
|
-
/**
|
|
129
|
-
* Check if a path exists in storage.
|
|
130
|
-
*
|
|
131
|
-
* @param path - Relative path within baseDir
|
|
132
|
-
* @returns True if the path exists
|
|
133
|
-
*/
|
|
134
22
|
exists(path: string): boolean;
|
|
135
|
-
/**
|
|
136
|
-
* Get the absolute filesystem path for a relative storage path.
|
|
137
|
-
*
|
|
138
|
-
* @param path - Relative path within baseDir
|
|
139
|
-
* @returns Absolute filesystem path
|
|
140
|
-
*/
|
|
141
23
|
getFullPath(path: string): string;
|
|
142
|
-
/**
|
|
143
|
-
* Ensure a directory exists, creating it recursively if necessary.
|
|
144
|
-
*
|
|
145
|
-
* @param path - Relative directory path within baseDir
|
|
146
|
-
*/
|
|
147
24
|
ensureDir(path: string): Promise<void>;
|
|
148
|
-
/**
|
|
149
|
-
* Acquire an exclusive lock on a file path.
|
|
150
|
-
* Uses .lock files with PID tracking for stale lock detection.
|
|
151
|
-
*
|
|
152
|
-
* @param path - Relative path to lock
|
|
153
|
-
* @param options - Lock behavior options
|
|
154
|
-
* @returns Unlock function that MUST be called to release
|
|
155
|
-
* @throws {StorageError} If lock cannot be acquired within timeout
|
|
156
|
-
*/
|
|
157
25
|
lock(path: string, options?: LockOptions): Promise<() => Promise<void>>;
|
|
158
26
|
}
|
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Storage Adapter
|
|
3
|
-
*
|
|
4
|
-
* File-based storage with abstraction for future database migration.
|
|
5
|
-
* Implements atomic writes and path traversal prevention.
|
|
6
|
-
*
|
|
7
|
-
* @author Kent Beck's Dummy Human
|
|
8
|
-
*/
|
|
9
1
|
import { join, resolve } from "path";
|
|
10
2
|
import { mkdir, rename, readFile, appendFile, writeFile, unlink } from "fs/promises";
|
|
11
3
|
import { existsSync } from "fs";
|
|
12
4
|
import { StorageError, ValidationError } from "../shared/errors.js";
|
|
13
5
|
import { LIMITS } from "./types.js";
|
|
14
|
-
/**
|
|
15
|
-
* Check if a process is still running.
|
|
16
|
-
*/
|
|
17
6
|
function isProcessAlive(pid) {
|
|
18
7
|
try {
|
|
19
8
|
process.kill(pid, 0);
|
|
@@ -23,74 +12,24 @@ function isProcessAlive(pid) {
|
|
|
23
12
|
return false;
|
|
24
13
|
}
|
|
25
14
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Sleep for specified milliseconds.
|
|
28
|
-
*/
|
|
29
15
|
function sleep(ms) {
|
|
30
16
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
31
17
|
}
|
|
32
|
-
// ============================================================================
|
|
33
|
-
// Path Validation
|
|
34
|
-
// ============================================================================
|
|
35
|
-
/**
|
|
36
|
-
* Validate that a path is within the allowed base directory.
|
|
37
|
-
* Prevents path traversal attacks (e.g., "../../../etc/passwd").
|
|
38
|
-
*
|
|
39
|
-
* @param fullPath - The full path to validate
|
|
40
|
-
* @param baseDir - The base directory that should contain the path
|
|
41
|
-
* @returns True if the path is safe and within baseDir
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* validatePath("/data/sessions/abc/file.json", "/data/sessions"); // true
|
|
45
|
-
* validatePath("/data/../etc/passwd", "/data/sessions"); // false
|
|
46
|
-
*/
|
|
47
18
|
export function validatePath(fullPath, baseDir) {
|
|
48
19
|
const resolvedFull = resolve(fullPath);
|
|
49
20
|
const resolvedBase = resolve(baseDir);
|
|
50
21
|
return resolvedFull.startsWith(resolvedBase) && !fullPath.includes("..");
|
|
51
22
|
}
|
|
52
|
-
// ============================================================================
|
|
53
|
-
// Atomic Write Helper
|
|
54
|
-
// ============================================================================
|
|
55
|
-
/**
|
|
56
|
-
* Write data atomically by first writing to a temp file then renaming.
|
|
57
|
-
* Prevents partial writes and data corruption on crash.
|
|
58
|
-
*/
|
|
59
23
|
async function atomicWrite(filePath, data) {
|
|
60
24
|
const tempPath = `${filePath}.tmp.${Date.now()}`;
|
|
61
25
|
await writeFile(tempPath, data, "utf-8");
|
|
62
26
|
await rename(tempPath, filePath);
|
|
63
27
|
}
|
|
64
|
-
// ============================================================================
|
|
65
|
-
// File Storage Adapter
|
|
66
|
-
// ============================================================================
|
|
67
|
-
/**
|
|
68
|
-
* File-based implementation of the StorageAdapter interface.
|
|
69
|
-
* All paths are relative to the baseDir and validated for security.
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* const storage = new FileStorageAdapter("/data/sessions");
|
|
73
|
-
* await storage.write("session-1/manifest.json", { id: "session-1" });
|
|
74
|
-
* const data = await storage.read("session-1/manifest.json");
|
|
75
|
-
*/
|
|
76
28
|
export class FileStorageAdapter {
|
|
77
29
|
baseDir;
|
|
78
|
-
/**
|
|
79
|
-
* Create a new file storage adapter.
|
|
80
|
-
*
|
|
81
|
-
* @param baseDir - The root directory for all storage operations
|
|
82
|
-
*/
|
|
83
30
|
constructor(baseDir) {
|
|
84
31
|
this.baseDir = baseDir;
|
|
85
32
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Read and parse a JSON file from storage.
|
|
88
|
-
*
|
|
89
|
-
* @param path - Relative path within baseDir
|
|
90
|
-
* @returns Parsed JSON data, or null if file doesn't exist
|
|
91
|
-
* @throws {ValidationError} If path escapes baseDir
|
|
92
|
-
* @throws {StorageError} If file read or parse fails
|
|
93
|
-
*/
|
|
94
33
|
async read(path) {
|
|
95
34
|
const fullPath = join(this.baseDir, path);
|
|
96
35
|
if (!validatePath(fullPath, this.baseDir)) {
|
|
@@ -114,14 +53,6 @@ export class FileStorageAdapter {
|
|
|
114
53
|
});
|
|
115
54
|
}
|
|
116
55
|
}
|
|
117
|
-
/**
|
|
118
|
-
* Write data to storage as formatted JSON (atomic operation).
|
|
119
|
-
*
|
|
120
|
-
* @param path - Relative path within baseDir
|
|
121
|
-
* @param data - Data to serialize and write
|
|
122
|
-
* @throws {ValidationError} If path escapes baseDir
|
|
123
|
-
* @throws {StorageError} If write fails
|
|
124
|
-
*/
|
|
125
56
|
async write(path, data) {
|
|
126
57
|
const fullPath = join(this.baseDir, path);
|
|
127
58
|
if (!validatePath(fullPath, this.baseDir)) {
|
|
@@ -143,14 +74,6 @@ export class FileStorageAdapter {
|
|
|
143
74
|
});
|
|
144
75
|
}
|
|
145
76
|
}
|
|
146
|
-
/**
|
|
147
|
-
* Append a line to a file (for log files).
|
|
148
|
-
*
|
|
149
|
-
* @param path - Relative path within baseDir
|
|
150
|
-
* @param line - Line to append (newline added automatically)
|
|
151
|
-
* @throws {ValidationError} If path escapes baseDir
|
|
152
|
-
* @throws {StorageError} If append fails
|
|
153
|
-
*/
|
|
154
77
|
async append(path, line) {
|
|
155
78
|
const fullPath = join(this.baseDir, path);
|
|
156
79
|
if (!validatePath(fullPath, this.baseDir)) {
|
|
@@ -171,44 +94,18 @@ export class FileStorageAdapter {
|
|
|
171
94
|
});
|
|
172
95
|
}
|
|
173
96
|
}
|
|
174
|
-
/**
|
|
175
|
-
* Check if a path exists in storage.
|
|
176
|
-
*
|
|
177
|
-
* @param path - Relative path within baseDir
|
|
178
|
-
* @returns True if the path exists
|
|
179
|
-
*/
|
|
180
97
|
exists(path) {
|
|
181
98
|
return existsSync(join(this.baseDir, path));
|
|
182
99
|
}
|
|
183
|
-
/**
|
|
184
|
-
* Get the absolute filesystem path for a relative storage path.
|
|
185
|
-
*
|
|
186
|
-
* @param path - Relative path within baseDir
|
|
187
|
-
* @returns Absolute filesystem path
|
|
188
|
-
*/
|
|
189
100
|
getFullPath(path) {
|
|
190
101
|
return join(this.baseDir, path);
|
|
191
102
|
}
|
|
192
|
-
/**
|
|
193
|
-
* Ensure a directory exists, creating it recursively if necessary.
|
|
194
|
-
*
|
|
195
|
-
* @param path - Relative directory path within baseDir
|
|
196
|
-
*/
|
|
197
103
|
async ensureDir(path) {
|
|
198
104
|
const fullPath = join(this.baseDir, path);
|
|
199
105
|
if (!existsSync(fullPath)) {
|
|
200
106
|
await mkdir(fullPath, { recursive: true });
|
|
201
107
|
}
|
|
202
108
|
}
|
|
203
|
-
/**
|
|
204
|
-
* Acquire an exclusive lock on a file path.
|
|
205
|
-
* Uses .lock files with PID tracking for stale lock detection.
|
|
206
|
-
*
|
|
207
|
-
* @param path - Relative path to lock
|
|
208
|
-
* @param options - Lock behavior options
|
|
209
|
-
* @returns Unlock function that MUST be called to release
|
|
210
|
-
* @throws {StorageError} If lock cannot be acquired within timeout
|
|
211
|
-
*/
|
|
212
109
|
async lock(path, options) {
|
|
213
110
|
const timeout = options?.timeout ?? LIMITS.lockTimeout;
|
|
214
111
|
const staleThreshold = options?.stale ?? LIMITS.lockStale;
|
|
@@ -236,7 +133,6 @@ export class FileStorageAdapter {
|
|
|
236
133
|
});
|
|
237
134
|
}
|
|
238
135
|
attempt++;
|
|
239
|
-
// Check if lock file exists
|
|
240
136
|
if (existsSync(fullLockPath)) {
|
|
241
137
|
try {
|
|
242
138
|
const content = await readFile(fullLockPath, "utf-8");
|
|
@@ -249,7 +145,6 @@ export class FileStorageAdapter {
|
|
|
249
145
|
await unlink(fullLockPath);
|
|
250
146
|
}
|
|
251
147
|
catch {
|
|
252
|
-
// Another process may have removed it
|
|
253
148
|
}
|
|
254
149
|
}
|
|
255
150
|
else {
|
|
@@ -274,11 +169,9 @@ export class FileStorageAdapter {
|
|
|
274
169
|
await unlink(fullLockPath);
|
|
275
170
|
}
|
|
276
171
|
catch {
|
|
277
|
-
// Continue - file might have been removed
|
|
278
172
|
}
|
|
279
173
|
}
|
|
280
174
|
}
|
|
281
|
-
// Attempt to create lock file atomically
|
|
282
175
|
const lockContent = {
|
|
283
176
|
pid: process.pid,
|
|
284
177
|
createdAt: new Date().toISOString(),
|
|
@@ -289,7 +182,6 @@ export class FileStorageAdapter {
|
|
|
289
182
|
await writeFile(fullLockPath, JSON.stringify(lockContent, null, 2), {
|
|
290
183
|
flag: "wx",
|
|
291
184
|
});
|
|
292
|
-
// Success - return unlock function
|
|
293
185
|
let released = false;
|
|
294
186
|
const unlock = async () => {
|
|
295
187
|
if (released)
|
|
@@ -304,7 +196,6 @@ export class FileStorageAdapter {
|
|
|
304
196
|
}
|
|
305
197
|
}
|
|
306
198
|
catch {
|
|
307
|
-
// Best effort release
|
|
308
199
|
}
|
|
309
200
|
finally {
|
|
310
201
|
released = true;
|
|
@@ -335,13 +226,6 @@ export class FileStorageAdapter {
|
|
|
335
226
|
}
|
|
336
227
|
}
|
|
337
228
|
}
|
|
338
|
-
// ============================================================================
|
|
339
|
-
// Utility Functions
|
|
340
|
-
// ============================================================================
|
|
341
|
-
/**
|
|
342
|
-
* Extract directory portion from a path.
|
|
343
|
-
* Works with both forward slashes and backslashes.
|
|
344
|
-
*/
|
|
345
229
|
function dirname(path) {
|
|
346
230
|
return path.split(/[/\\]/).slice(0, -1).join("/");
|
|
347
231
|
}
|
|
@@ -1,131 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Task Management
|
|
3
|
-
*
|
|
4
|
-
* Tasks are the atomic units of work within a squad.
|
|
5
|
-
* "Make the implicit explicit" - Eric Evans
|
|
6
|
-
*
|
|
7
|
-
* This module embodies the Task aggregate, ensuring domain invariants:
|
|
8
|
-
* - A task always belongs to exactly one squad
|
|
9
|
-
* - Task count per squad is bounded (LIMITS.maxTasksPerSquad)
|
|
10
|
-
* - Task identity is immutable once assigned
|
|
11
|
-
*
|
|
12
|
-
* @author Kent Beck's Dummy Human
|
|
13
|
-
*/
|
|
14
1
|
import type { CreateTaskOptions, TaskState, Status, TaskResult } from "./types.js";
|
|
15
2
|
import type { Session } from "./session.js";
|
|
16
|
-
/**
|
|
17
|
-
* Assign a new task to an agent within a squad.
|
|
18
|
-
*
|
|
19
|
-
* This is a domain operation that enforces invariants:
|
|
20
|
-
* 1. The target squad must exist
|
|
21
|
-
* 2. The squad's task capacity must not be exceeded
|
|
22
|
-
* 3. Task options must be valid at the boundary
|
|
23
|
-
*
|
|
24
|
-
* The task starts in "pending" status, awaiting execution.
|
|
25
|
-
*
|
|
26
|
-
* @param session - The parent session containing the squad
|
|
27
|
-
* @param squadId - ID of the squad to assign the task to
|
|
28
|
-
* @param options - Task creation options (assignee, description, priority, etc.)
|
|
29
|
-
* @returns The newly created TaskState
|
|
30
|
-
* @throws {StorageError} If the squad doesn't exist
|
|
31
|
-
* @throws {ValidationError} If task limit is exceeded or options are invalid
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* const task = await assignTask(session, "squad-a1b2c3d4", {
|
|
35
|
-
* assignee: "worker-1",
|
|
36
|
-
* description: "Implement user login form",
|
|
37
|
-
* priority: "high",
|
|
38
|
-
* dependencies: ["task-setup"]
|
|
39
|
-
* });
|
|
40
|
-
* console.log(`Assigned task ${task.taskId} to ${task.assignee}`);
|
|
41
|
-
*/
|
|
42
3
|
export declare function assignTask(session: Session, squadId: string, options: CreateTaskOptions): Promise<TaskState>;
|
|
43
|
-
/**
|
|
44
|
-
* Get a specific task from a squad by taskId.
|
|
45
|
-
*
|
|
46
|
-
* "Make implicit concepts explicit" - Eric Evans
|
|
47
|
-
* Task lookup is a first-class domain operation, not hidden in aggregate traversal.
|
|
48
|
-
*
|
|
49
|
-
* @param session - Parent session
|
|
50
|
-
* @param squadId - Squad containing the task
|
|
51
|
-
* @param taskId - Task ID to find
|
|
52
|
-
* @returns TaskState or null if not found (no error thrown for missing task)
|
|
53
|
-
* @throws {StorageError} If the squad doesn't exist
|
|
54
|
-
*
|
|
55
|
-
* @example
|
|
56
|
-
* const task = await getTask(session, "squad-a1b2c3d4", "task-12345678");
|
|
57
|
-
* if (task) {
|
|
58
|
-
* console.log(`Task ${task.taskId} is ${task.status}`);
|
|
59
|
-
* }
|
|
60
|
-
*/
|
|
61
4
|
export declare function getTask(session: Session, squadId: string, taskId: string): Promise<TaskState | null>;
|
|
62
|
-
/**
|
|
63
|
-
* Update a task's status and optionally record progress.
|
|
64
|
-
* This is the primary way to advance task lifecycle.
|
|
65
|
-
*
|
|
66
|
-
* DDD Principle: All state mutations go through the Aggregate Root (Squad).
|
|
67
|
-
* "Make implicit concepts explicit" - Eric Evans
|
|
68
|
-
*
|
|
69
|
-
* @param session - Parent session
|
|
70
|
-
* @param squadId - Squad containing the task
|
|
71
|
-
* @param taskId - Task to update
|
|
72
|
-
* @param updates - Partial updates (status, result, etc.)
|
|
73
|
-
* @returns Updated TaskState
|
|
74
|
-
* @throws {StorageError} If squad or task not found
|
|
75
|
-
*
|
|
76
|
-
* @example
|
|
77
|
-
* // Start a task
|
|
78
|
-
* const updated = await updateTask(session, squadId, taskId, {
|
|
79
|
-
* status: "active",
|
|
80
|
-
* startedAt: new Date().toISOString()
|
|
81
|
-
* });
|
|
82
|
-
*
|
|
83
|
-
* @example
|
|
84
|
-
* // Complete a task with result
|
|
85
|
-
* const completed = await updateTask(session, squadId, taskId, {
|
|
86
|
-
* status: "completed",
|
|
87
|
-
* completedAt: new Date().toISOString(),
|
|
88
|
-
* result: { success: true, output: "Feature implemented" }
|
|
89
|
-
* });
|
|
90
|
-
*/
|
|
91
5
|
export declare function updateTask(session: Session, squadId: string, taskId: string, updates: {
|
|
92
6
|
status?: Status;
|
|
93
7
|
startedAt?: string;
|
|
94
8
|
completedAt?: string;
|
|
95
9
|
result?: unknown;
|
|
96
10
|
}): Promise<TaskState>;
|
|
97
|
-
/**
|
|
98
|
-
* Complete a task with a result.
|
|
99
|
-
*
|
|
100
|
-
* "Make implicit concepts explicit" - Eric Evans
|
|
101
|
-
* Task completion is a first-class domain operation with its own semantics.
|
|
102
|
-
*
|
|
103
|
-
* This is a convenience wrapper around updateTask() that:
|
|
104
|
-
* - Validates the TaskResult at the boundary
|
|
105
|
-
* - Sets the appropriate status based on result.success
|
|
106
|
-
* - Records completedAt timestamp
|
|
107
|
-
*
|
|
108
|
-
* @param session - Parent session
|
|
109
|
-
* @param squadId - Squad containing the task
|
|
110
|
-
* @param taskId - Task to complete
|
|
111
|
-
* @param result - TaskResult with success/failure info
|
|
112
|
-
* @returns Updated TaskState
|
|
113
|
-
* @throws {StorageError} If squad or task not found
|
|
114
|
-
* @throws {ValidationError} If result is invalid
|
|
115
|
-
*
|
|
116
|
-
* @example
|
|
117
|
-
* // Success case
|
|
118
|
-
* const completed = await completeTask(session, squadId, taskId, {
|
|
119
|
-
* success: true,
|
|
120
|
-
* output: { files: ["src/auth/login.ts"] },
|
|
121
|
-
* metrics: { duration: 45000, tokensUsed: 1500 }
|
|
122
|
-
* });
|
|
123
|
-
*
|
|
124
|
-
* @example
|
|
125
|
-
* // Failure case
|
|
126
|
-
* const failed = await completeTask(session, squadId, taskId, {
|
|
127
|
-
* success: false,
|
|
128
|
-
* error: { code: "TIMEOUT", message: "Task exceeded timeout" }
|
|
129
|
-
* });
|
|
130
|
-
*/
|
|
131
11
|
export declare function completeTask(session: Session, squadId: string, taskId: string, result: TaskResult): Promise<TaskState>;
|