@esotech/contextuate 2.0.0 → 2.1.1
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 +169 -1
- package/dist/commands/claude.d.ts +21 -0
- package/dist/commands/claude.js +213 -0
- package/dist/commands/context.d.ts +1 -0
- package/dist/commands/create.d.ts +3 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.js +67 -6
- package/dist/commands/install.d.ts +28 -0
- package/dist/commands/install.js +100 -11
- package/dist/commands/monitor.d.ts +55 -0
- package/dist/commands/monitor.js +1007 -0
- package/dist/commands/remove.d.ts +3 -0
- package/dist/commands/run.d.ts +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +113 -1
- package/dist/monitor/daemon/circuit-breaker.d.ts +121 -0
- package/dist/monitor/daemon/circuit-breaker.js +552 -0
- package/dist/monitor/daemon/cli.d.ts +8 -0
- package/dist/monitor/daemon/cli.js +82 -0
- package/dist/monitor/daemon/index.d.ts +137 -0
- package/dist/monitor/daemon/index.js +695 -0
- package/dist/monitor/daemon/notifier.d.ts +25 -0
- package/dist/monitor/daemon/notifier.js +98 -0
- package/dist/monitor/daemon/processor.d.ts +89 -0
- package/dist/monitor/daemon/processor.js +455 -0
- package/dist/monitor/daemon/state.d.ts +80 -0
- package/dist/monitor/daemon/state.js +162 -0
- package/dist/monitor/daemon/watcher.d.ts +47 -0
- package/dist/monitor/daemon/watcher.js +171 -0
- package/dist/monitor/daemon/wrapper-manager.d.ts +106 -0
- package/dist/monitor/daemon/wrapper-manager.js +374 -0
- package/dist/monitor/hooks/emit-event.js +652 -0
- package/dist/monitor/persistence/file-store.d.ts +88 -0
- package/dist/monitor/persistence/file-store.js +335 -0
- package/dist/monitor/persistence/index.d.ts +7 -0
- package/dist/monitor/persistence/index.js +10 -0
- package/dist/monitor/server/adapters/redis.d.ts +38 -0
- package/dist/monitor/server/adapters/redis.js +213 -0
- package/dist/monitor/server/adapters/unix-socket.d.ts +33 -0
- package/dist/monitor/server/adapters/unix-socket.js +182 -0
- package/dist/monitor/server/broker.d.ts +135 -0
- package/dist/monitor/server/broker.js +475 -0
- package/dist/monitor/server/cli.d.ts +8 -0
- package/dist/monitor/server/cli.js +98 -0
- package/dist/monitor/server/fastify.d.ts +16 -0
- package/dist/monitor/server/fastify.js +184 -0
- package/dist/monitor/server/index.d.ts +36 -0
- package/dist/monitor/server/index.js +153 -0
- package/dist/monitor/server/websocket.d.ts +80 -0
- package/dist/monitor/server/websocket.js +453 -0
- package/dist/monitor/ui/assets/index-4IssW9On.js +59 -0
- package/dist/monitor/ui/assets/index-vo9hLe5R.css +32 -0
- package/dist/monitor/ui/favicon.png +0 -0
- package/dist/monitor/ui/index.html +14 -0
- package/dist/monitor/ui/logo.png +0 -0
- package/dist/monitor/ui/logo.svg +1 -0
- package/dist/runtime/driver.d.ts +16 -0
- package/dist/runtime/tools.d.ts +10 -0
- package/dist/templates/README.md +33 -7
- package/dist/templates/agents/aegis.md +4 -0
- package/dist/templates/agents/archon.md +13 -22
- package/dist/templates/agents/atlas.md +4 -0
- package/dist/templates/agents/canvas.md +4 -0
- package/dist/templates/agents/chronicle.md +4 -0
- package/dist/templates/agents/chronos.md +4 -0
- package/dist/templates/agents/cipher.md +4 -0
- package/dist/templates/agents/crucible.md +4 -0
- package/dist/templates/agents/echo.md +4 -0
- package/dist/templates/agents/forge.md +4 -0
- package/dist/templates/agents/ledger.md +4 -0
- package/dist/templates/agents/meridian.md +4 -0
- package/dist/templates/agents/nexus.md +4 -0
- package/dist/templates/agents/pythia.md +217 -0
- package/dist/templates/agents/scribe.md +4 -0
- package/dist/templates/agents/sentinel.md +4 -0
- package/dist/templates/agents/{oracle.md → thoth.md} +11 -7
- package/dist/templates/agents/unity.md +4 -0
- package/dist/templates/agents/vox.md +4 -0
- package/dist/templates/agents/weaver.md +4 -0
- package/dist/templates/commands/consult.md +138 -0
- package/dist/templates/commands/orchestrate.md +173 -0
- package/dist/templates/framework-agents/documentation-expert.md +3 -3
- package/dist/templates/framework-agents/tools-expert.md +8 -8
- package/dist/templates/standards/agent-roles.md +68 -21
- package/dist/templates/standards/coding-standards.md +9 -26
- package/dist/templates/templates/context.md +17 -2
- package/dist/templates/templates/contextuate.md +21 -28
- package/dist/templates/tools/{agent-creator.tool.md → agent-creator.md} +3 -3
- package/dist/types/monitor.d.ts +660 -0
- package/dist/types/monitor.js +75 -0
- package/dist/utils/git.d.ts +9 -0
- package/dist/utils/tokens.d.ts +10 -0
- package/package.json +18 -5
- package/dist/templates/version.json +0 -8
- /package/dist/templates/templates/standards/{go.standards.md → go.md} +0 -0
- /package/dist/templates/templates/standards/{java.standards.md → java.md} +0 -0
- /package/dist/templates/templates/standards/{javascript.standards.md → javascript.md} +0 -0
- /package/dist/templates/templates/standards/{php.standards.md → php.md} +0 -0
- /package/dist/templates/templates/standards/{python.standards.md → python.md} +0 -0
- /package/dist/templates/tools/{quickref.tool.md → quickref.md} +0 -0
- /package/dist/templates/tools/{spawn.tool.md → spawn.md} +0 -0
- /package/dist/templates/tools/{standards-detector.tool.md → standards-detector.md} +0 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon State Management
|
|
3
|
+
*
|
|
4
|
+
* Manages persistent state for the monitor daemon including:
|
|
5
|
+
* - Last processed timestamp (for crash recovery)
|
|
6
|
+
* - Pending subagent spawns (for parent-child correlation)
|
|
7
|
+
* - Active subagent stacks (for virtual session routing)
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Pending subagent spawn tracking (adjusted to match broker.ts usage)
|
|
11
|
+
*/
|
|
12
|
+
interface PendingSubagentSpawn {
|
|
13
|
+
parentSessionId: string;
|
|
14
|
+
workingDirectory: string;
|
|
15
|
+
timestamp: number;
|
|
16
|
+
agentType?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Active subagent in the stack (adjusted to match broker.ts usage)
|
|
20
|
+
*/
|
|
21
|
+
interface ActiveSubagent {
|
|
22
|
+
virtualSessionId: string;
|
|
23
|
+
parentSessionId: string;
|
|
24
|
+
agentType?: string;
|
|
25
|
+
startTime: number;
|
|
26
|
+
}
|
|
27
|
+
export declare class StateManager {
|
|
28
|
+
private state;
|
|
29
|
+
private saveInterval;
|
|
30
|
+
constructor();
|
|
31
|
+
/**
|
|
32
|
+
* Load state from disk
|
|
33
|
+
*/
|
|
34
|
+
load(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Save state to disk
|
|
37
|
+
*/
|
|
38
|
+
save(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Start periodic state saving
|
|
41
|
+
*/
|
|
42
|
+
startPeriodicSave(intervalMs?: number): void;
|
|
43
|
+
/**
|
|
44
|
+
* Stop periodic state saving
|
|
45
|
+
*/
|
|
46
|
+
stopPeriodicSave(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Get last processed timestamp
|
|
49
|
+
*/
|
|
50
|
+
get lastProcessedTimestamp(): number;
|
|
51
|
+
/**
|
|
52
|
+
* Set last processed timestamp
|
|
53
|
+
*/
|
|
54
|
+
set lastProcessedTimestamp(timestamp: number);
|
|
55
|
+
/**
|
|
56
|
+
* Get pending subagent spawns
|
|
57
|
+
*/
|
|
58
|
+
get pendingSubagentSpawns(): PendingSubagentSpawn[];
|
|
59
|
+
/**
|
|
60
|
+
* Set pending subagent spawns
|
|
61
|
+
*/
|
|
62
|
+
set pendingSubagentSpawns(spawns: PendingSubagentSpawn[]);
|
|
63
|
+
/**
|
|
64
|
+
* Get active subagent stack for a session
|
|
65
|
+
*/
|
|
66
|
+
getActiveSubagentStack(sessionId: string): ActiveSubagent[];
|
|
67
|
+
/**
|
|
68
|
+
* Set active subagent stack for a session
|
|
69
|
+
*/
|
|
70
|
+
setActiveSubagentStack(sessionId: string, stack: ActiveSubagent[]): void;
|
|
71
|
+
/**
|
|
72
|
+
* Push a new subagent onto the stack for a session
|
|
73
|
+
*/
|
|
74
|
+
pushActiveSubagent(sessionId: string, subagent: ActiveSubagent): void;
|
|
75
|
+
/**
|
|
76
|
+
* Pop the top subagent from the stack for a session
|
|
77
|
+
*/
|
|
78
|
+
popActiveSubagent(sessionId: string): ActiveSubagent | undefined;
|
|
79
|
+
}
|
|
80
|
+
export {};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Daemon State Management
|
|
4
|
+
*
|
|
5
|
+
* Manages persistent state for the monitor daemon including:
|
|
6
|
+
* - Last processed timestamp (for crash recovery)
|
|
7
|
+
* - Pending subagent spawns (for parent-child correlation)
|
|
8
|
+
* - Active subagent stacks (for virtual session routing)
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.StateManager = void 0;
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const monitor_js_1 = require("../../types/monitor.js");
|
|
48
|
+
const PATHS = (0, monitor_js_1.getDefaultMonitorPaths)();
|
|
49
|
+
class StateManager {
|
|
50
|
+
constructor() {
|
|
51
|
+
this.saveInterval = null;
|
|
52
|
+
this.state = {
|
|
53
|
+
lastProcessedTimestamp: 0,
|
|
54
|
+
pendingSubagentSpawns: [],
|
|
55
|
+
activeSubagentStacks: {},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Load state from disk
|
|
60
|
+
*/
|
|
61
|
+
async load() {
|
|
62
|
+
try {
|
|
63
|
+
const data = await fs.promises.readFile(PATHS.daemonStateFile, 'utf8');
|
|
64
|
+
this.state = JSON.parse(data);
|
|
65
|
+
console.log(`[State] Loaded state, last processed: ${new Date(this.state.lastProcessedTimestamp).toISOString()}`);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
console.log('[State] No existing state file, starting fresh');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Save state to disk
|
|
73
|
+
*/
|
|
74
|
+
async save() {
|
|
75
|
+
try {
|
|
76
|
+
await fs.promises.mkdir(path.dirname(PATHS.daemonStateFile), { recursive: true });
|
|
77
|
+
await fs.promises.writeFile(PATHS.daemonStateFile, JSON.stringify(this.state, null, 2));
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
console.error('[State] Failed to save state:', err);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Start periodic state saving
|
|
85
|
+
*/
|
|
86
|
+
startPeriodicSave(intervalMs = 30000) {
|
|
87
|
+
this.saveInterval = setInterval(() => this.save(), intervalMs);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Stop periodic state saving
|
|
91
|
+
*/
|
|
92
|
+
stopPeriodicSave() {
|
|
93
|
+
if (this.saveInterval) {
|
|
94
|
+
clearInterval(this.saveInterval);
|
|
95
|
+
this.saveInterval = null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get last processed timestamp
|
|
100
|
+
*/
|
|
101
|
+
get lastProcessedTimestamp() {
|
|
102
|
+
return this.state.lastProcessedTimestamp;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Set last processed timestamp
|
|
106
|
+
*/
|
|
107
|
+
set lastProcessedTimestamp(timestamp) {
|
|
108
|
+
this.state.lastProcessedTimestamp = timestamp;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get pending subagent spawns
|
|
112
|
+
*/
|
|
113
|
+
get pendingSubagentSpawns() {
|
|
114
|
+
return this.state.pendingSubagentSpawns;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Set pending subagent spawns
|
|
118
|
+
*/
|
|
119
|
+
set pendingSubagentSpawns(spawns) {
|
|
120
|
+
this.state.pendingSubagentSpawns = spawns;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get active subagent stack for a session
|
|
124
|
+
*/
|
|
125
|
+
getActiveSubagentStack(sessionId) {
|
|
126
|
+
return this.state.activeSubagentStacks[sessionId] || [];
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Set active subagent stack for a session
|
|
130
|
+
*/
|
|
131
|
+
setActiveSubagentStack(sessionId, stack) {
|
|
132
|
+
if (stack.length === 0) {
|
|
133
|
+
delete this.state.activeSubagentStacks[sessionId];
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
this.state.activeSubagentStacks[sessionId] = stack;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Push a new subagent onto the stack for a session
|
|
141
|
+
*/
|
|
142
|
+
pushActiveSubagent(sessionId, subagent) {
|
|
143
|
+
if (!this.state.activeSubagentStacks[sessionId]) {
|
|
144
|
+
this.state.activeSubagentStacks[sessionId] = [];
|
|
145
|
+
}
|
|
146
|
+
this.state.activeSubagentStacks[sessionId].push(subagent);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Pop the top subagent from the stack for a session
|
|
150
|
+
*/
|
|
151
|
+
popActiveSubagent(sessionId) {
|
|
152
|
+
const stack = this.state.activeSubagentStacks[sessionId];
|
|
153
|
+
if (!stack || stack.length === 0)
|
|
154
|
+
return undefined;
|
|
155
|
+
const subagent = stack.pop();
|
|
156
|
+
if (stack.length === 0) {
|
|
157
|
+
delete this.state.activeSubagentStacks[sessionId];
|
|
158
|
+
}
|
|
159
|
+
return subagent;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
exports.StateManager = StateManager;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Watcher
|
|
3
|
+
*
|
|
4
|
+
* Watches the raw/ directory for new event files and processes them in order.
|
|
5
|
+
* Handles both existing unprocessed files and newly created files.
|
|
6
|
+
*/
|
|
7
|
+
import { MonitorEvent } from '../../types/monitor.js';
|
|
8
|
+
export type EventHandler = (event: MonitorEvent, filepath: string) => Promise<void>;
|
|
9
|
+
export declare class FileWatcher {
|
|
10
|
+
private watcher;
|
|
11
|
+
private processing;
|
|
12
|
+
private queue;
|
|
13
|
+
private handler;
|
|
14
|
+
private lastProcessedTimestamp;
|
|
15
|
+
/**
|
|
16
|
+
* Set the event handler callback
|
|
17
|
+
*/
|
|
18
|
+
setHandler(handler: EventHandler): void;
|
|
19
|
+
/**
|
|
20
|
+
* Set the last processed timestamp for checkpoint recovery
|
|
21
|
+
*/
|
|
22
|
+
setLastProcessedTimestamp(timestamp: number): void;
|
|
23
|
+
/**
|
|
24
|
+
* Start watching the raw directory
|
|
25
|
+
*/
|
|
26
|
+
start(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Stop watching
|
|
29
|
+
*/
|
|
30
|
+
stop(): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Process existing files that may not have been processed yet
|
|
33
|
+
*/
|
|
34
|
+
private processExistingFiles;
|
|
35
|
+
/**
|
|
36
|
+
* Add file to processing queue
|
|
37
|
+
*/
|
|
38
|
+
private enqueue;
|
|
39
|
+
/**
|
|
40
|
+
* Process all files in the queue
|
|
41
|
+
*/
|
|
42
|
+
private processQueue;
|
|
43
|
+
/**
|
|
44
|
+
* Process a single file
|
|
45
|
+
*/
|
|
46
|
+
private processFile;
|
|
47
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* File Watcher
|
|
4
|
+
*
|
|
5
|
+
* Watches the raw/ directory for new event files and processes them in order.
|
|
6
|
+
* Handles both existing unprocessed files and newly created files.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.FileWatcher = void 0;
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const monitor_js_1 = require("../../types/monitor.js");
|
|
46
|
+
const PATHS = (0, monitor_js_1.getDefaultMonitorPaths)();
|
|
47
|
+
class FileWatcher {
|
|
48
|
+
constructor() {
|
|
49
|
+
this.watcher = null;
|
|
50
|
+
this.processing = false;
|
|
51
|
+
this.queue = [];
|
|
52
|
+
this.handler = null;
|
|
53
|
+
this.lastProcessedTimestamp = 0;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Set the event handler callback
|
|
57
|
+
*/
|
|
58
|
+
setHandler(handler) {
|
|
59
|
+
this.handler = handler;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Set the last processed timestamp for checkpoint recovery
|
|
63
|
+
*/
|
|
64
|
+
setLastProcessedTimestamp(timestamp) {
|
|
65
|
+
this.lastProcessedTimestamp = timestamp;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Start watching the raw directory
|
|
69
|
+
*/
|
|
70
|
+
async start() {
|
|
71
|
+
// Ensure raw directory exists
|
|
72
|
+
await fs.promises.mkdir(PATHS.rawDir, { recursive: true });
|
|
73
|
+
// Process any existing unprocessed files first
|
|
74
|
+
await this.processExistingFiles();
|
|
75
|
+
// Start watching for new files
|
|
76
|
+
this.watcher = fs.watch(PATHS.rawDir, (eventType, filename) => {
|
|
77
|
+
if (eventType === 'rename' && filename && filename.endsWith('.json')) {
|
|
78
|
+
const filepath = path.join(PATHS.rawDir, filename);
|
|
79
|
+
this.enqueue(filepath);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
console.log(`[Watcher] Watching ${PATHS.rawDir} for new events`);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Stop watching
|
|
86
|
+
*/
|
|
87
|
+
async stop() {
|
|
88
|
+
if (this.watcher) {
|
|
89
|
+
this.watcher.close();
|
|
90
|
+
this.watcher = null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Process existing files that may not have been processed yet
|
|
95
|
+
*/
|
|
96
|
+
async processExistingFiles() {
|
|
97
|
+
try {
|
|
98
|
+
const files = await fs.promises.readdir(PATHS.rawDir);
|
|
99
|
+
const jsonFiles = files
|
|
100
|
+
.filter(f => f.endsWith('.json'))
|
|
101
|
+
.sort(); // Sort by timestamp (filename starts with timestamp)
|
|
102
|
+
let processedCount = 0;
|
|
103
|
+
for (const file of jsonFiles) {
|
|
104
|
+
const filepath = path.join(PATHS.rawDir, file);
|
|
105
|
+
// Extract timestamp from filename: {timestamp}-{sessionId}-{eventId}.json
|
|
106
|
+
const timestamp = parseInt(file.split('-')[0], 10);
|
|
107
|
+
// Skip if already processed (based on checkpoint)
|
|
108
|
+
if (timestamp <= this.lastProcessedTimestamp) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
await this.processFile(filepath);
|
|
112
|
+
processedCount++;
|
|
113
|
+
}
|
|
114
|
+
if (processedCount > 0) {
|
|
115
|
+
console.log(`[Watcher] Processed ${processedCount} existing events`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
console.error('[Watcher] Error processing existing files:', err);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Add file to processing queue
|
|
124
|
+
*/
|
|
125
|
+
enqueue(filepath) {
|
|
126
|
+
this.queue.push(filepath);
|
|
127
|
+
this.processQueue();
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Process all files in the queue
|
|
131
|
+
*/
|
|
132
|
+
async processQueue() {
|
|
133
|
+
if (this.processing || this.queue.length === 0)
|
|
134
|
+
return;
|
|
135
|
+
this.processing = true;
|
|
136
|
+
while (this.queue.length > 0) {
|
|
137
|
+
const filepath = this.queue.shift();
|
|
138
|
+
try {
|
|
139
|
+
// Small delay to ensure file is fully written
|
|
140
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
141
|
+
await this.processFile(filepath);
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
console.error(`[Watcher] Error processing ${filepath}:`, err);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
this.processing = false;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Process a single file
|
|
151
|
+
*/
|
|
152
|
+
async processFile(filepath) {
|
|
153
|
+
try {
|
|
154
|
+
// Check if file still exists (might have been processed already)
|
|
155
|
+
await fs.promises.access(filepath);
|
|
156
|
+
const content = await fs.promises.readFile(filepath, 'utf8');
|
|
157
|
+
const event = JSON.parse(content);
|
|
158
|
+
if (this.handler) {
|
|
159
|
+
await this.handler(event, filepath);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
if (err.code === 'ENOENT') {
|
|
164
|
+
// File already processed/moved, skip
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
throw err;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
exports.FileWatcher = FileWatcher;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrapper Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages Claude wrapper sessions with PTY.
|
|
5
|
+
* Spawns and manages Claude processes, handles input/output streaming,
|
|
6
|
+
* persists session state, and cleans up on exit.
|
|
7
|
+
*/
|
|
8
|
+
import * as pty from 'node-pty';
|
|
9
|
+
export interface WrapperSession {
|
|
10
|
+
wrapperId: string;
|
|
11
|
+
ptyProcess: pty.IPty;
|
|
12
|
+
pid: number;
|
|
13
|
+
claudeSessionId: string | null;
|
|
14
|
+
state: 'starting' | 'processing' | 'waiting_input' | 'ended';
|
|
15
|
+
cwd: string;
|
|
16
|
+
args: string[];
|
|
17
|
+
startTime: number;
|
|
18
|
+
cols: number;
|
|
19
|
+
rows: number;
|
|
20
|
+
}
|
|
21
|
+
export interface PersistedWrapper {
|
|
22
|
+
wrapperId: string;
|
|
23
|
+
pid: number;
|
|
24
|
+
claudeSessionId: string | null;
|
|
25
|
+
state: string;
|
|
26
|
+
cwd: string;
|
|
27
|
+
args: string[];
|
|
28
|
+
startTime: number;
|
|
29
|
+
cols: number;
|
|
30
|
+
rows: number;
|
|
31
|
+
}
|
|
32
|
+
export type WrapperEventCallback = (event: {
|
|
33
|
+
type: 'output' | 'state_changed' | 'started' | 'ended';
|
|
34
|
+
wrapperId: string;
|
|
35
|
+
data?: string;
|
|
36
|
+
state?: string;
|
|
37
|
+
claudeSessionId?: string;
|
|
38
|
+
exitCode?: number;
|
|
39
|
+
}) => void;
|
|
40
|
+
export declare class WrapperManager {
|
|
41
|
+
private wrappers;
|
|
42
|
+
private persistPath;
|
|
43
|
+
private onEvent;
|
|
44
|
+
constructor(persistPath: string, onEvent: WrapperEventCallback);
|
|
45
|
+
/**
|
|
46
|
+
* Initialize - load persisted wrappers and clean up dead ones
|
|
47
|
+
*/
|
|
48
|
+
initialize(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Load persisted wrappers and clean up any that are no longer running
|
|
51
|
+
*/
|
|
52
|
+
private loadAndCleanup;
|
|
53
|
+
/**
|
|
54
|
+
* Check if a process is still running
|
|
55
|
+
*/
|
|
56
|
+
private isProcessRunning;
|
|
57
|
+
/**
|
|
58
|
+
* Spawn a new Claude wrapper session
|
|
59
|
+
*/
|
|
60
|
+
spawn(options?: {
|
|
61
|
+
cwd?: string;
|
|
62
|
+
args?: string[];
|
|
63
|
+
cols?: number;
|
|
64
|
+
rows?: number;
|
|
65
|
+
}): Promise<string>;
|
|
66
|
+
/**
|
|
67
|
+
* Write input to a wrapper's PTY
|
|
68
|
+
*/
|
|
69
|
+
writeInput(wrapperId: string, input: string): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Resize a wrapper's PTY
|
|
72
|
+
*/
|
|
73
|
+
resize(wrapperId: string, cols: number, rows: number): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Kill a wrapper session
|
|
76
|
+
*/
|
|
77
|
+
kill(wrapperId: string): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Get all active wrappers
|
|
80
|
+
*/
|
|
81
|
+
getAll(): WrapperSession[];
|
|
82
|
+
/**
|
|
83
|
+
* Get a specific wrapper
|
|
84
|
+
*/
|
|
85
|
+
get(wrapperId: string): WrapperSession | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* Update wrapper state (e.g., from hook events)
|
|
88
|
+
*/
|
|
89
|
+
updateState(wrapperId: string, state: 'starting' | 'processing' | 'waiting_input' | 'ended', claudeSessionId?: string): void;
|
|
90
|
+
/**
|
|
91
|
+
* Shutdown all wrappers
|
|
92
|
+
*/
|
|
93
|
+
shutdown(): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Persist wrapper state to disk
|
|
96
|
+
*/
|
|
97
|
+
private persist;
|
|
98
|
+
/**
|
|
99
|
+
* Detect if output indicates Claude is waiting for input
|
|
100
|
+
*/
|
|
101
|
+
private detectInputPrompt;
|
|
102
|
+
/**
|
|
103
|
+
* Find the Claude executable
|
|
104
|
+
*/
|
|
105
|
+
private findClaudeExecutable;
|
|
106
|
+
}
|