pm-orchestrator-runner 1.0.7 → 1.0.9
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 +59 -6
- package/dist/cli/index.d.ts +7 -5
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +191 -49
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +19 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/namespace.d.ts +86 -0
- package/dist/config/namespace.d.ts.map +1 -0
- package/dist/config/namespace.js +150 -0
- package/dist/config/namespace.js.map +1 -0
- package/dist/core/runner-core.d.ts +2 -0
- package/dist/core/runner-core.d.ts.map +1 -1
- package/dist/core/runner-core.js +27 -1
- package/dist/core/runner-core.js.map +1 -1
- package/dist/executor/claude-code-executor.d.ts +28 -0
- package/dist/executor/claude-code-executor.d.ts.map +1 -1
- package/dist/executor/claude-code-executor.js +184 -1
- package/dist/executor/claude-code-executor.js.map +1 -1
- package/dist/executor/deterministic-executor.d.ts +2 -1
- package/dist/executor/deterministic-executor.d.ts.map +1 -1
- package/dist/executor/deterministic-executor.js +7 -0
- package/dist/executor/deterministic-executor.js.map +1 -1
- package/dist/executor/recovery-executor.d.ts +2 -1
- package/dist/executor/recovery-executor.d.ts.map +1 -1
- package/dist/executor/recovery-executor.js +7 -0
- package/dist/executor/recovery-executor.js.map +1 -1
- package/dist/executor/stepwise-mock-executor.d.ts +76 -0
- package/dist/executor/stepwise-mock-executor.d.ts.map +1 -0
- package/dist/executor/stepwise-mock-executor.js +369 -0
- package/dist/executor/stepwise-mock-executor.js.map +1 -0
- package/dist/models/enums.d.ts +54 -0
- package/dist/models/enums.d.ts.map +1 -1
- package/dist/models/enums.js +59 -1
- package/dist/models/enums.js.map +1 -1
- package/dist/models/index.d.ts +4 -1
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +50 -2
- package/dist/models/index.js.map +1 -1
- package/dist/models/run.d.ts +82 -0
- package/dist/models/run.d.ts.map +1 -0
- package/dist/models/run.js +161 -0
- package/dist/models/run.js.map +1 -0
- package/dist/models/task-group.d.ts +164 -0
- package/dist/models/task-group.d.ts.map +1 -0
- package/dist/models/task-group.js +246 -0
- package/dist/models/task-group.js.map +1 -0
- package/dist/models/task.d.ts +7 -0
- package/dist/models/task.d.ts.map +1 -1
- package/dist/models/task.js.map +1 -1
- package/dist/models/thread.d.ts +53 -0
- package/dist/models/thread.d.ts.map +1 -0
- package/dist/models/thread.js +92 -0
- package/dist/models/thread.js.map +1 -0
- package/dist/pool/agent-pool.d.ts.map +1 -1
- package/dist/pool/agent-pool.js +2 -3
- package/dist/pool/agent-pool.js.map +1 -1
- package/dist/prompt/index.d.ts +8 -0
- package/dist/prompt/index.d.ts.map +1 -0
- package/dist/prompt/index.js +13 -0
- package/dist/prompt/index.js.map +1 -0
- package/dist/prompt/prompt-assembler.d.ts +145 -0
- package/dist/prompt/prompt-assembler.d.ts.map +1 -0
- package/dist/prompt/prompt-assembler.js +242 -0
- package/dist/prompt/prompt-assembler.js.map +1 -0
- package/dist/queue/index.d.ts +41 -0
- package/dist/queue/index.d.ts.map +1 -0
- package/dist/queue/index.js +42 -0
- package/dist/queue/index.js.map +1 -0
- package/dist/queue/queue-poller.d.ts +107 -0
- package/dist/queue/queue-poller.d.ts.map +1 -0
- package/dist/queue/queue-poller.js +181 -0
- package/dist/queue/queue-poller.js.map +1 -0
- package/dist/queue/queue-store.d.ts +163 -0
- package/dist/queue/queue-store.d.ts.map +1 -0
- package/dist/queue/queue-store.js +421 -0
- package/dist/queue/queue-store.js.map +1 -0
- package/dist/repl/index.d.ts +1 -0
- package/dist/repl/index.d.ts.map +1 -1
- package/dist/repl/index.js +3 -1
- package/dist/repl/index.js.map +1 -1
- package/dist/repl/repl-interface.d.ts +40 -5
- package/dist/repl/repl-interface.d.ts.map +1 -1
- package/dist/repl/repl-interface.js +95 -17
- package/dist/repl/repl-interface.js.map +1 -1
- package/dist/repl/two-pane-renderer.d.ts +160 -0
- package/dist/repl/two-pane-renderer.d.ts.map +1 -0
- package/dist/repl/two-pane-renderer.js +276 -0
- package/dist/repl/two-pane-renderer.js.map +1 -0
- package/dist/trace/trace-pack.d.ts +182 -0
- package/dist/trace/trace-pack.d.ts.map +1 -0
- package/dist/trace/trace-pack.js +407 -0
- package/dist/trace/trace-pack.js.map +1 -0
- package/dist/web/index.d.ts +45 -0
- package/dist/web/index.d.ts.map +1 -0
- package/dist/web/index.js +47 -0
- package/dist/web/index.js.map +1 -0
- package/dist/web/server.d.ts +71 -0
- package/dist/web/server.d.ts.map +1 -0
- package/dist/web/server.js +329 -0
- package/dist/web/server.js.map +1 -0
- package/package.json +10 -2
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Prompt Assembler
|
|
4
|
+
* Per spec/17_PROMPT_TEMPLATE.md
|
|
5
|
+
*
|
|
6
|
+
* Assembles prompts in fixed order (no caching, no omission):
|
|
7
|
+
* 1. global prelude
|
|
8
|
+
* 2. project prelude
|
|
9
|
+
* 3. task group prelude (dynamic)
|
|
10
|
+
* 4. user input
|
|
11
|
+
* 5. output format epilogue
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.PromptAssembler = exports.PromptAssemblerError = void 0;
|
|
48
|
+
const fs = __importStar(require("fs"));
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
/**
|
|
51
|
+
* PromptAssembler Error
|
|
52
|
+
*/
|
|
53
|
+
class PromptAssemblerError extends Error {
|
|
54
|
+
constructor(message) {
|
|
55
|
+
super(message);
|
|
56
|
+
this.name = 'PromptAssemblerError';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.PromptAssemblerError = PromptAssemblerError;
|
|
60
|
+
/**
|
|
61
|
+
* Prompt Assembler
|
|
62
|
+
*
|
|
63
|
+
* Per spec/17_PROMPT_TEMPLATE.md:
|
|
64
|
+
* - Always assemble in fixed order
|
|
65
|
+
* - No caching (rebuild every time)
|
|
66
|
+
* - No omission
|
|
67
|
+
*/
|
|
68
|
+
class PromptAssembler {
|
|
69
|
+
projectPath;
|
|
70
|
+
templateDir;
|
|
71
|
+
constructor(config) {
|
|
72
|
+
this.projectPath = config.projectPath;
|
|
73
|
+
this.templateDir = config.templateDir || '.claude/prompt-templates';
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Assemble the prompt from all sections
|
|
77
|
+
*
|
|
78
|
+
* Per spec/17_PROMPT_TEMPLATE.md:
|
|
79
|
+
* Order is fixed and non-negotiable:
|
|
80
|
+
* 1. global prelude
|
|
81
|
+
* 2. project prelude
|
|
82
|
+
* 3. task group prelude
|
|
83
|
+
* 4. user input
|
|
84
|
+
* 5. output format epilogue
|
|
85
|
+
*
|
|
86
|
+
* @param userInput - The user's input (task.naturalLanguageTask)
|
|
87
|
+
* @param taskGroupContext - Optional task group context for prelude generation
|
|
88
|
+
* @returns AssemblyResult with assembled prompt and sections
|
|
89
|
+
* @throws PromptAssemblerError if user input is empty
|
|
90
|
+
*/
|
|
91
|
+
assemble(userInput, taskGroupContext) {
|
|
92
|
+
// Fail-closed: user input must not be empty
|
|
93
|
+
if (!userInput || userInput.trim().length === 0) {
|
|
94
|
+
throw new PromptAssemblerError('User input is required (fail-closed: empty input not allowed)');
|
|
95
|
+
}
|
|
96
|
+
// Load/build each section (no caching per spec)
|
|
97
|
+
const sections = {
|
|
98
|
+
globalPrelude: this.loadGlobalPrelude(),
|
|
99
|
+
projectPrelude: this.loadProjectPrelude(),
|
|
100
|
+
taskGroupPrelude: taskGroupContext ? this.buildTaskGroupPrelude(taskGroupContext) : '',
|
|
101
|
+
userInput: userInput.trim(),
|
|
102
|
+
outputEpilogue: this.loadOutputEpilogue(),
|
|
103
|
+
};
|
|
104
|
+
// Assemble in fixed order
|
|
105
|
+
const parts = [];
|
|
106
|
+
if (sections.globalPrelude) {
|
|
107
|
+
parts.push(sections.globalPrelude);
|
|
108
|
+
}
|
|
109
|
+
if (sections.projectPrelude) {
|
|
110
|
+
parts.push(sections.projectPrelude);
|
|
111
|
+
}
|
|
112
|
+
if (sections.taskGroupPrelude) {
|
|
113
|
+
parts.push(sections.taskGroupPrelude);
|
|
114
|
+
}
|
|
115
|
+
// User input is always included (already validated non-empty)
|
|
116
|
+
parts.push(sections.userInput);
|
|
117
|
+
if (sections.outputEpilogue) {
|
|
118
|
+
parts.push(sections.outputEpilogue);
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
prompt: parts.join('\n\n'),
|
|
122
|
+
sections,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Load global prelude from template file
|
|
127
|
+
*
|
|
128
|
+
* Per spec/17_PROMPT_TEMPLATE.md:
|
|
129
|
+
* - File: .claude/prompt-templates/global-prelude.md
|
|
130
|
+
* - Contains: rules, prohibitions, output format assumptions
|
|
131
|
+
*
|
|
132
|
+
* @returns Global prelude content (empty string if file not found)
|
|
133
|
+
*/
|
|
134
|
+
loadGlobalPrelude() {
|
|
135
|
+
return this.loadTemplateFile('global-prelude.md');
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Load project prelude from template file
|
|
139
|
+
*
|
|
140
|
+
* Per spec/17_PROMPT_TEMPLATE.md:
|
|
141
|
+
* - File: .claude/prompt-templates/project-prelude.md
|
|
142
|
+
* - Contains: project-specific constraints, file structure, tech constraints
|
|
143
|
+
*
|
|
144
|
+
* @returns Project prelude content (empty string if file not found)
|
|
145
|
+
*/
|
|
146
|
+
loadProjectPrelude() {
|
|
147
|
+
return this.loadTemplateFile('project-prelude.md');
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Build task group prelude dynamically
|
|
151
|
+
*
|
|
152
|
+
* Per spec/17_PROMPT_TEMPLATE.md:
|
|
153
|
+
* - Dynamically generated from TaskGroupContext
|
|
154
|
+
* - Contains: conversation thread purpose, prerequisites, context summary
|
|
155
|
+
*
|
|
156
|
+
* Per spec/16_TASK_GROUP.md L100-106:
|
|
157
|
+
* - task_group_id: for context isolation
|
|
158
|
+
* - conversation_history: for context continuity
|
|
159
|
+
* - working_files: current working set
|
|
160
|
+
* - last_task_result: for building on previous work
|
|
161
|
+
*
|
|
162
|
+
* @param context - Task group context
|
|
163
|
+
* @returns Task group prelude content
|
|
164
|
+
*/
|
|
165
|
+
buildTaskGroupPrelude(context) {
|
|
166
|
+
const lines = [];
|
|
167
|
+
lines.push(`## Task Group Context`);
|
|
168
|
+
lines.push(`Task Group ID: ${context.task_group_id}`);
|
|
169
|
+
lines.push('');
|
|
170
|
+
// Working files
|
|
171
|
+
if (context.working_files.length > 0) {
|
|
172
|
+
lines.push('### Working Files');
|
|
173
|
+
for (const file of context.working_files) {
|
|
174
|
+
lines.push(`- ${file}`);
|
|
175
|
+
}
|
|
176
|
+
lines.push('');
|
|
177
|
+
}
|
|
178
|
+
// Last task result
|
|
179
|
+
if (context.last_task_result) {
|
|
180
|
+
lines.push('### Previous Task Result');
|
|
181
|
+
lines.push(`- Task: ${context.last_task_result.task_id}`);
|
|
182
|
+
lines.push(`- Status: ${context.last_task_result.status}`);
|
|
183
|
+
lines.push(`- Summary: ${context.last_task_result.summary}`);
|
|
184
|
+
if (context.last_task_result.files_modified.length > 0) {
|
|
185
|
+
lines.push(`- Files Modified: ${context.last_task_result.files_modified.join(', ')}`);
|
|
186
|
+
}
|
|
187
|
+
if (context.last_task_result.error) {
|
|
188
|
+
lines.push(`- Error: ${context.last_task_result.error}`);
|
|
189
|
+
}
|
|
190
|
+
lines.push('');
|
|
191
|
+
}
|
|
192
|
+
// Conversation history summary (last 5 entries to keep prelude manageable)
|
|
193
|
+
if (context.conversation_history.length > 0) {
|
|
194
|
+
lines.push('### Recent Conversation');
|
|
195
|
+
const recentEntries = context.conversation_history.slice(-5);
|
|
196
|
+
for (const entry of recentEntries) {
|
|
197
|
+
const preview = entry.content.length > 100
|
|
198
|
+
? entry.content.substring(0, 100) + '...'
|
|
199
|
+
: entry.content;
|
|
200
|
+
lines.push(`- [${entry.role}] ${preview}`);
|
|
201
|
+
}
|
|
202
|
+
lines.push('');
|
|
203
|
+
}
|
|
204
|
+
return lines.join('\n');
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Load output format epilogue from template file
|
|
208
|
+
*
|
|
209
|
+
* Per spec/17_PROMPT_TEMPLATE.md:
|
|
210
|
+
* - File: .claude/prompt-templates/output-epilogue.md
|
|
211
|
+
* - Contains: required report format, evidence output format, completion conditions
|
|
212
|
+
*
|
|
213
|
+
* @returns Output epilogue content (empty string if file not found)
|
|
214
|
+
*/
|
|
215
|
+
loadOutputEpilogue() {
|
|
216
|
+
return this.loadTemplateFile('output-epilogue.md');
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Load template file content
|
|
220
|
+
*
|
|
221
|
+
* Per spec/17_PROMPT_TEMPLATE.md:
|
|
222
|
+
* - Template files are in .claude/prompt-templates/
|
|
223
|
+
* - If file doesn't exist, return empty string (not an error)
|
|
224
|
+
*
|
|
225
|
+
* @param filename - Template filename
|
|
226
|
+
* @returns File content or empty string
|
|
227
|
+
*/
|
|
228
|
+
loadTemplateFile(filename) {
|
|
229
|
+
const templatePath = path.join(this.projectPath, this.templateDir, filename);
|
|
230
|
+
try {
|
|
231
|
+
if (fs.existsSync(templatePath)) {
|
|
232
|
+
return fs.readFileSync(templatePath, 'utf-8').trim();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
// File read error - treat as non-existent
|
|
237
|
+
}
|
|
238
|
+
return '';
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
exports.PromptAssembler = PromptAssembler;
|
|
242
|
+
//# sourceMappingURL=prompt-assembler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-assembler.js","sourceRoot":"","sources":["../../src/prompt/prompt-assembler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue Module
|
|
3
|
+
* Per spec/20_QUEUE_STORE.md
|
|
4
|
+
*
|
|
5
|
+
* Exports:
|
|
6
|
+
* - QueueStore: DynamoDB-backed queue storage
|
|
7
|
+
* - QueuePoller: Polling and task execution
|
|
8
|
+
* - createNamespacedQueueStore: Factory for namespace-separated QueueStore
|
|
9
|
+
*/
|
|
10
|
+
export { QueueStore, QueueStoreConfig, QueueItem, QueueItemStatus, ClaimResult, TaskGroupSummary, } from './queue-store';
|
|
11
|
+
export { QueuePoller, QueuePollerConfig, QueuePollerState, QueuePollerEvents, TaskExecutor, } from './queue-poller';
|
|
12
|
+
import { QueueStore, QueueStoreConfig } from './queue-store';
|
|
13
|
+
/**
|
|
14
|
+
* Namespace configuration for QueueStore
|
|
15
|
+
* Per spec/21_STABLE_DEV.md
|
|
16
|
+
*/
|
|
17
|
+
export interface NamespaceQueueConfig {
|
|
18
|
+
/** Namespace name (e.g., 'stable', 'dev') */
|
|
19
|
+
namespace: string;
|
|
20
|
+
/** Table name derived from namespace */
|
|
21
|
+
tableName: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Create a QueueStore instance with namespace separation
|
|
25
|
+
* Per spec/21_STABLE_DEV.md
|
|
26
|
+
*
|
|
27
|
+
* @param namespaceConfig - Namespace configuration with tableName
|
|
28
|
+
* @param storeConfig - Optional additional QueueStore configuration
|
|
29
|
+
* @returns Configured QueueStore instance
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const namespaceConfig = buildNamespaceConfig({ namespace: 'dev', projectRoot: '/path' });
|
|
34
|
+
* const store = createNamespacedQueueStore({
|
|
35
|
+
* namespace: namespaceConfig.namespace,
|
|
36
|
+
* tableName: namespaceConfig.tableName,
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function createNamespacedQueueStore(namespaceConfig: NamespaceQueueConfig, storeConfig?: Omit<QueueStoreConfig, 'tableName'>): QueueStore;
|
|
41
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/queue/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,WAAW,EACX,gBAAgB,GACjB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,GACb,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAE7D;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,oBAAoB,EACrC,WAAW,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,WAAW,CAAC,GAChD,UAAU,CAKZ"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Queue Module
|
|
4
|
+
* Per spec/20_QUEUE_STORE.md
|
|
5
|
+
*
|
|
6
|
+
* Exports:
|
|
7
|
+
* - QueueStore: DynamoDB-backed queue storage
|
|
8
|
+
* - QueuePoller: Polling and task execution
|
|
9
|
+
* - createNamespacedQueueStore: Factory for namespace-separated QueueStore
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.QueuePoller = exports.QueueStore = void 0;
|
|
13
|
+
exports.createNamespacedQueueStore = createNamespacedQueueStore;
|
|
14
|
+
var queue_store_1 = require("./queue-store");
|
|
15
|
+
Object.defineProperty(exports, "QueueStore", { enumerable: true, get: function () { return queue_store_1.QueueStore; } });
|
|
16
|
+
var queue_poller_1 = require("./queue-poller");
|
|
17
|
+
Object.defineProperty(exports, "QueuePoller", { enumerable: true, get: function () { return queue_poller_1.QueuePoller; } });
|
|
18
|
+
const queue_store_2 = require("./queue-store");
|
|
19
|
+
/**
|
|
20
|
+
* Create a QueueStore instance with namespace separation
|
|
21
|
+
* Per spec/21_STABLE_DEV.md
|
|
22
|
+
*
|
|
23
|
+
* @param namespaceConfig - Namespace configuration with tableName
|
|
24
|
+
* @param storeConfig - Optional additional QueueStore configuration
|
|
25
|
+
* @returns Configured QueueStore instance
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const namespaceConfig = buildNamespaceConfig({ namespace: 'dev', projectRoot: '/path' });
|
|
30
|
+
* const store = createNamespacedQueueStore({
|
|
31
|
+
* namespace: namespaceConfig.namespace,
|
|
32
|
+
* tableName: namespaceConfig.tableName,
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
function createNamespacedQueueStore(namespaceConfig, storeConfig) {
|
|
37
|
+
return new queue_store_2.QueueStore({
|
|
38
|
+
...storeConfig,
|
|
39
|
+
tableName: namespaceConfig.tableName,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/queue/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAiDH,gEAQC;AAvDD,6CAOuB;AANrB,yGAAA,UAAU,OAAA;AAQZ,+CAMwB;AALtB,2GAAA,WAAW,OAAA;AAOb,+CAA6D;AAa7D;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,0BAA0B,CACxC,eAAqC,EACrC,WAAiD;IAEjD,OAAO,IAAI,wBAAU,CAAC;QACpB,GAAG,WAAW;QACd,SAAS,EAAE,eAAe,CAAC,SAAS;KACrC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue Poller - Polls queue and executes tasks
|
|
3
|
+
* Per spec/20_QUEUE_STORE.md
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Polling interval configurable (default 1000ms)
|
|
7
|
+
* - 1 task per tick
|
|
8
|
+
* - In-flight limit: 1 (no concurrent execution)
|
|
9
|
+
* - Fail-closed error handling
|
|
10
|
+
*/
|
|
11
|
+
import { EventEmitter } from 'events';
|
|
12
|
+
import { QueueStore, QueueItem } from './queue-store';
|
|
13
|
+
/**
|
|
14
|
+
* Task executor function type
|
|
15
|
+
* Returns status and optional error message
|
|
16
|
+
*/
|
|
17
|
+
export type TaskExecutor = (item: QueueItem) => Promise<{
|
|
18
|
+
status: 'COMPLETE' | 'ERROR';
|
|
19
|
+
errorMessage?: string;
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
* Poller configuration
|
|
23
|
+
*/
|
|
24
|
+
export interface QueuePollerConfig {
|
|
25
|
+
/** Polling interval in milliseconds (default: 1000) */
|
|
26
|
+
pollIntervalMs?: number;
|
|
27
|
+
/** Max stale task age in milliseconds (default: 5 minutes) */
|
|
28
|
+
maxStaleTaskAgeMs?: number;
|
|
29
|
+
/** Recover stale tasks on startup (default: true) */
|
|
30
|
+
recoverOnStartup?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Poller state
|
|
34
|
+
*/
|
|
35
|
+
export interface QueuePollerState {
|
|
36
|
+
isRunning: boolean;
|
|
37
|
+
inFlight: QueueItem | null;
|
|
38
|
+
lastPollAt: string | null;
|
|
39
|
+
tasksProcessed: number;
|
|
40
|
+
errors: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Poller events
|
|
44
|
+
*/
|
|
45
|
+
export interface QueuePollerEvents {
|
|
46
|
+
started: [];
|
|
47
|
+
stopped: [];
|
|
48
|
+
poll: [{
|
|
49
|
+
queuedCount: number;
|
|
50
|
+
}];
|
|
51
|
+
claimed: [QueueItem];
|
|
52
|
+
completed: [QueueItem];
|
|
53
|
+
error: [QueueItem, Error];
|
|
54
|
+
'no-task': [];
|
|
55
|
+
'already-claimed': [string];
|
|
56
|
+
'stale-recovered': [number];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Queue Poller
|
|
60
|
+
* Polls the queue store and executes tasks
|
|
61
|
+
*/
|
|
62
|
+
export declare class QueuePoller extends EventEmitter {
|
|
63
|
+
private readonly store;
|
|
64
|
+
private readonly executor;
|
|
65
|
+
private readonly pollIntervalMs;
|
|
66
|
+
private readonly maxStaleTaskAgeMs;
|
|
67
|
+
private readonly recoverOnStartup;
|
|
68
|
+
private pollTimer;
|
|
69
|
+
private inFlight;
|
|
70
|
+
private isRunning;
|
|
71
|
+
private lastPollAt;
|
|
72
|
+
private tasksProcessed;
|
|
73
|
+
private errors;
|
|
74
|
+
constructor(store: QueueStore, executor: TaskExecutor, config?: QueuePollerConfig);
|
|
75
|
+
/**
|
|
76
|
+
* Start polling
|
|
77
|
+
*/
|
|
78
|
+
start(): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Stop polling
|
|
81
|
+
*/
|
|
82
|
+
stop(): void;
|
|
83
|
+
/**
|
|
84
|
+
* Single poll iteration
|
|
85
|
+
* - Skip if task in-flight
|
|
86
|
+
* - Claim oldest QUEUED task
|
|
87
|
+
* - Execute and update status
|
|
88
|
+
*/
|
|
89
|
+
poll(): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Get current state
|
|
92
|
+
*/
|
|
93
|
+
getState(): QueuePollerState;
|
|
94
|
+
/**
|
|
95
|
+
* Check if poller is running
|
|
96
|
+
*/
|
|
97
|
+
isActive(): boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Check if task is in-flight
|
|
100
|
+
*/
|
|
101
|
+
hasInFlight(): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Get in-flight task
|
|
104
|
+
*/
|
|
105
|
+
getInFlight(): QueueItem | null;
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=queue-poller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-poller.d.ts","sourceRoot":"","sources":["../../src/queue/queue-poller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAmB,MAAM,eAAe,CAAC;AAEvE;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CACzB,IAAI,EAAE,SAAS,KACZ,OAAO,CAAC;IAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8DAA8D;IAC9D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,SAAS,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,EAAE,CAAC;IACZ,OAAO,EAAE,EAAE,CAAC;IACZ,IAAI,EAAE,CAAC;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC;IACrB,SAAS,EAAE,CAAC,SAAS,CAAC,CAAC;IACvB,KAAK,EAAE,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC1B,SAAS,EAAE,EAAE,CAAC;IACd,iBAAiB,EAAE,CAAC,MAAM,CAAC,CAAC;IAC5B,iBAAiB,EAAE,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED;;;GAGG;AACH,qBAAa,WAAY,SAAQ,YAAY;IAC3C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAE3C,OAAO,CAAC,SAAS,CAA+C;IAChE,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,MAAM,CAAa;gBAGzB,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,YAAY,EACtB,MAAM,GAAE,iBAAsB;IAUhC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmC5B;;OAEG;IACH,IAAI,IAAI,IAAI;IAeZ;;;;;OAKG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoE3B;;OAEG;IACH,QAAQ,IAAI,gBAAgB;IAU5B;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,WAAW,IAAI,SAAS,GAAG,IAAI;CAGhC"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Queue Poller - Polls queue and executes tasks
|
|
4
|
+
* Per spec/20_QUEUE_STORE.md
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Polling interval configurable (default 1000ms)
|
|
8
|
+
* - 1 task per tick
|
|
9
|
+
* - In-flight limit: 1 (no concurrent execution)
|
|
10
|
+
* - Fail-closed error handling
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.QueuePoller = void 0;
|
|
14
|
+
const events_1 = require("events");
|
|
15
|
+
/**
|
|
16
|
+
* Queue Poller
|
|
17
|
+
* Polls the queue store and executes tasks
|
|
18
|
+
*/
|
|
19
|
+
class QueuePoller extends events_1.EventEmitter {
|
|
20
|
+
store;
|
|
21
|
+
executor;
|
|
22
|
+
pollIntervalMs;
|
|
23
|
+
maxStaleTaskAgeMs;
|
|
24
|
+
recoverOnStartup;
|
|
25
|
+
pollTimer = null;
|
|
26
|
+
inFlight = null;
|
|
27
|
+
isRunning = false;
|
|
28
|
+
lastPollAt = null;
|
|
29
|
+
tasksProcessed = 0;
|
|
30
|
+
errors = 0;
|
|
31
|
+
constructor(store, executor, config = {}) {
|
|
32
|
+
super();
|
|
33
|
+
this.store = store;
|
|
34
|
+
this.executor = executor;
|
|
35
|
+
this.pollIntervalMs = config.pollIntervalMs ?? 1000;
|
|
36
|
+
this.maxStaleTaskAgeMs = config.maxStaleTaskAgeMs ?? 5 * 60 * 1000;
|
|
37
|
+
this.recoverOnStartup = config.recoverOnStartup ?? true;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Start polling
|
|
41
|
+
*/
|
|
42
|
+
async start() {
|
|
43
|
+
if (this.isRunning) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
this.isRunning = true;
|
|
47
|
+
// Recover stale tasks on startup (fail-closed)
|
|
48
|
+
if (this.recoverOnStartup) {
|
|
49
|
+
try {
|
|
50
|
+
const recovered = await this.store.recoverStaleTasks(this.maxStaleTaskAgeMs);
|
|
51
|
+
if (recovered > 0) {
|
|
52
|
+
this.emit('stale-recovered', recovered);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
// Log but don't fail startup
|
|
57
|
+
// eslint-disable-next-line no-console
|
|
58
|
+
console.error('[QueuePoller] Failed to recover stale tasks:', error);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
this.emit('started');
|
|
62
|
+
// Start polling loop
|
|
63
|
+
this.pollTimer = setInterval(() => {
|
|
64
|
+
this.poll().catch(error => {
|
|
65
|
+
// eslint-disable-next-line no-console
|
|
66
|
+
console.error('[QueuePoller] Poll error:', error);
|
|
67
|
+
});
|
|
68
|
+
}, this.pollIntervalMs);
|
|
69
|
+
// Immediate first poll
|
|
70
|
+
await this.poll();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Stop polling
|
|
74
|
+
*/
|
|
75
|
+
stop() {
|
|
76
|
+
if (!this.isRunning) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.isRunning = false;
|
|
80
|
+
if (this.pollTimer) {
|
|
81
|
+
clearInterval(this.pollTimer);
|
|
82
|
+
this.pollTimer = null;
|
|
83
|
+
}
|
|
84
|
+
this.emit('stopped');
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Single poll iteration
|
|
88
|
+
* - Skip if task in-flight
|
|
89
|
+
* - Claim oldest QUEUED task
|
|
90
|
+
* - Execute and update status
|
|
91
|
+
*/
|
|
92
|
+
async poll() {
|
|
93
|
+
if (!this.isRunning) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
this.lastPollAt = new Date().toISOString();
|
|
97
|
+
// In-flight limit: 1
|
|
98
|
+
if (this.inFlight) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Try to claim a task
|
|
102
|
+
const claimResult = await this.store.claim();
|
|
103
|
+
if (!claimResult.success) {
|
|
104
|
+
if (claimResult.error) {
|
|
105
|
+
// Task was claimed by another process
|
|
106
|
+
this.emit('already-claimed', claimResult.error);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// No tasks in queue
|
|
110
|
+
this.emit('no-task');
|
|
111
|
+
}
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const item = claimResult.item;
|
|
115
|
+
this.inFlight = item;
|
|
116
|
+
this.emit('claimed', item);
|
|
117
|
+
try {
|
|
118
|
+
// Execute the task
|
|
119
|
+
const result = await this.executor(item);
|
|
120
|
+
// Update status
|
|
121
|
+
await this.store.updateStatus(item.task_id, result.status, result.errorMessage);
|
|
122
|
+
if (result.status === 'COMPLETE') {
|
|
123
|
+
this.tasksProcessed++;
|
|
124
|
+
this.emit('completed', item);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
this.errors++;
|
|
128
|
+
this.emit('error', item, new Error(result.errorMessage || 'Task failed'));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
// Fail-closed: mark as ERROR
|
|
133
|
+
this.errors++;
|
|
134
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
135
|
+
try {
|
|
136
|
+
await this.store.updateStatus(item.task_id, 'ERROR', errorMessage);
|
|
137
|
+
}
|
|
138
|
+
catch (updateError) {
|
|
139
|
+
// Log but don't throw - we've already failed
|
|
140
|
+
// eslint-disable-next-line no-console
|
|
141
|
+
console.error('[QueuePoller] Failed to update error status:', updateError);
|
|
142
|
+
}
|
|
143
|
+
this.emit('error', item, error instanceof Error ? error : new Error(String(error)));
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
this.inFlight = null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get current state
|
|
151
|
+
*/
|
|
152
|
+
getState() {
|
|
153
|
+
return {
|
|
154
|
+
isRunning: this.isRunning,
|
|
155
|
+
inFlight: this.inFlight,
|
|
156
|
+
lastPollAt: this.lastPollAt,
|
|
157
|
+
tasksProcessed: this.tasksProcessed,
|
|
158
|
+
errors: this.errors,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Check if poller is running
|
|
163
|
+
*/
|
|
164
|
+
isActive() {
|
|
165
|
+
return this.isRunning;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Check if task is in-flight
|
|
169
|
+
*/
|
|
170
|
+
hasInFlight() {
|
|
171
|
+
return this.inFlight !== null;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get in-flight task
|
|
175
|
+
*/
|
|
176
|
+
getInFlight() {
|
|
177
|
+
return this.inFlight;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
exports.QueuePoller = QueuePoller;
|
|
181
|
+
//# sourceMappingURL=queue-poller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-poller.js","sourceRoot":"","sources":["../../src/queue/queue-poller.ts"],"names":[],"mappings":"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"}
|