smol-symphony 0.1.0
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/AGENTS.md +73 -0
- package/DESIGN.md +453 -0
- package/LICENSE +21 -0
- package/PRODUCT.md +106 -0
- package/README.md +232 -0
- package/SPEC.md +2169 -0
- package/WORKFLOW.md +269 -0
- package/WORKFLOW.template.md +307 -0
- package/dist/agent/acp.js +304 -0
- package/dist/agent/acp.js.map +1 -0
- package/dist/agent/adapters.js +275 -0
- package/dist/agent/adapters.js.map +1 -0
- package/dist/agent/codex.js +439 -0
- package/dist/agent/codex.js.map +1 -0
- package/dist/agent/runner.js +394 -0
- package/dist/agent/runner.js.map +1 -0
- package/dist/agent/smolvm.js +174 -0
- package/dist/agent/smolvm.js.map +1 -0
- package/dist/bin/symphony.js +205 -0
- package/dist/bin/symphony.js.map +1 -0
- package/dist/http.js +1189 -0
- package/dist/http.js.map +1 -0
- package/dist/logging.js +45 -0
- package/dist/logging.js.map +1 -0
- package/dist/mcp.js +478 -0
- package/dist/mcp.js.map +1 -0
- package/dist/orchestrator.js +683 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/prompt.js +65 -0
- package/dist/prompt.js.map +1 -0
- package/dist/trackers/local.js +344 -0
- package/dist/trackers/local.js.map +1 -0
- package/dist/trackers/types.js +10 -0
- package/dist/trackers/types.js.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/workflow.js +385 -0
- package/dist/workflow.js.map +1 -0
- package/dist/workspace.js +196 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +68 -0
- package/scripts/build-vm.sh +67 -0
package/dist/workflow.js
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
// WORKFLOW.md loader, watcher, and typed config view (SPEC §5, §6).
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { existsSync, statSync } from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import chokidar from 'chokidar';
|
|
7
|
+
import { parse as parseYaml } from 'yaml';
|
|
8
|
+
import { log } from './logging.js';
|
|
9
|
+
import { isKnownAdapter } from './agent/adapters.js';
|
|
10
|
+
export class WorkflowError extends Error {
|
|
11
|
+
code;
|
|
12
|
+
constructor(code, message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.name = 'WorkflowError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// §5.2: split YAML front matter from prompt body.
|
|
19
|
+
export function splitFrontMatter(text) {
|
|
20
|
+
if (!text.startsWith('---')) {
|
|
21
|
+
return { config: {}, body: text.trim() };
|
|
22
|
+
}
|
|
23
|
+
const lines = text.split(/\r?\n/);
|
|
24
|
+
// First and closing fences must be exactly `---` (with optional trailing whitespace),
|
|
25
|
+
// unindented. Otherwise an indented `---` inside a multiline YAML hook script would be
|
|
26
|
+
// mistaken for the closing fence.
|
|
27
|
+
const isFence = (line) => /^---\s*$/.test(line ?? '');
|
|
28
|
+
if (!isFence(lines[0])) {
|
|
29
|
+
return { config: {}, body: text.trim() };
|
|
30
|
+
}
|
|
31
|
+
let endIdx = -1;
|
|
32
|
+
for (let i = 1; i < lines.length; i++) {
|
|
33
|
+
if (isFence(lines[i])) {
|
|
34
|
+
endIdx = i;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (endIdx < 0) {
|
|
39
|
+
throw new WorkflowError('workflow_parse_error', 'unterminated YAML front matter');
|
|
40
|
+
}
|
|
41
|
+
const fmText = lines.slice(1, endIdx).join('\n');
|
|
42
|
+
const body = lines.slice(endIdx + 1).join('\n').trim();
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = fmText.trim().length === 0 ? {} : parseYaml(fmText);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
throw new WorkflowError('workflow_parse_error', `invalid YAML front matter: ${err.message}`);
|
|
49
|
+
}
|
|
50
|
+
if (parsed === null || parsed === undefined)
|
|
51
|
+
parsed = {};
|
|
52
|
+
if (typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
53
|
+
throw new WorkflowError('workflow_front_matter_not_a_map', 'YAML front matter must decode to a map');
|
|
54
|
+
}
|
|
55
|
+
return { config: parsed, body };
|
|
56
|
+
}
|
|
57
|
+
export async function loadWorkflow(workflowPath) {
|
|
58
|
+
let text;
|
|
59
|
+
try {
|
|
60
|
+
text = await readFile(workflowPath, 'utf8');
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
throw new WorkflowError('missing_workflow_file', `cannot read ${workflowPath}: ${err.message}`);
|
|
64
|
+
}
|
|
65
|
+
const { config, body } = splitFrontMatter(text);
|
|
66
|
+
return { config, prompt_template: body };
|
|
67
|
+
}
|
|
68
|
+
// $VAR / ~ expansion for path/command fields (§6.1).
|
|
69
|
+
export function expandVar(value) {
|
|
70
|
+
if (typeof value !== 'string')
|
|
71
|
+
return value;
|
|
72
|
+
let s = value;
|
|
73
|
+
if (s.startsWith('~/') || s === '~') {
|
|
74
|
+
s = s === '~' ? os.homedir() : path.join(os.homedir(), s.slice(2));
|
|
75
|
+
}
|
|
76
|
+
const m = s.match(/^\$([A-Z_][A-Z0-9_]*)$/);
|
|
77
|
+
if (m) {
|
|
78
|
+
const envVal = process.env[m[1]];
|
|
79
|
+
return envVal ?? '';
|
|
80
|
+
}
|
|
81
|
+
return s;
|
|
82
|
+
}
|
|
83
|
+
function asString(v) {
|
|
84
|
+
if (typeof v === 'string')
|
|
85
|
+
return v;
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
function asInt(v, fallback) {
|
|
89
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
90
|
+
return Math.trunc(v);
|
|
91
|
+
if (typeof v === 'string' && /^-?\d+$/.test(v))
|
|
92
|
+
return parseInt(v, 10);
|
|
93
|
+
return fallback;
|
|
94
|
+
}
|
|
95
|
+
function asStringList(v, fallback) {
|
|
96
|
+
if (Array.isArray(v))
|
|
97
|
+
return v.filter((x) => typeof x === 'string');
|
|
98
|
+
return fallback;
|
|
99
|
+
}
|
|
100
|
+
function asMapStrPosInt(v) {
|
|
101
|
+
const out = {};
|
|
102
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
103
|
+
for (const [k, raw] of Object.entries(v)) {
|
|
104
|
+
const n = asInt(raw, 0);
|
|
105
|
+
if (n > 0)
|
|
106
|
+
out[k.toLowerCase()] = n;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
function getObject(parent, key) {
|
|
112
|
+
const v = parent[key];
|
|
113
|
+
if (v && typeof v === 'object' && !Array.isArray(v))
|
|
114
|
+
return v;
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
// Build a fully typed ServiceConfig from a parsed front matter map (§6.1).
|
|
118
|
+
export function buildServiceConfig(raw, workflowPath) {
|
|
119
|
+
const workflowAbs = path.resolve(workflowPath);
|
|
120
|
+
const workflowDir = path.dirname(workflowAbs);
|
|
121
|
+
// tracker (§5.3.1)
|
|
122
|
+
const trackerRaw = getObject(raw, 'tracker');
|
|
123
|
+
const trackerKind = (asString(trackerRaw['kind']) ?? '').trim();
|
|
124
|
+
const apiKeyRaw = asString(trackerRaw['api_key']);
|
|
125
|
+
const apiKeyResolved = apiKeyRaw ? expandVar(apiKeyRaw) : null;
|
|
126
|
+
const trackerEndpointDefault = trackerKind === 'linear' ? 'https://api.linear.app/graphql' : null;
|
|
127
|
+
// local-tracker extension: optional `tracker.root` path.
|
|
128
|
+
const trackerRootRaw = asString(trackerRaw['root']);
|
|
129
|
+
let trackerRoot = null;
|
|
130
|
+
if (trackerRootRaw) {
|
|
131
|
+
const expanded = expandVar(trackerRootRaw);
|
|
132
|
+
if (expanded === '') {
|
|
133
|
+
throw new WorkflowError('workflow_parse_error', `tracker.root references an unset variable: ${trackerRootRaw}`);
|
|
134
|
+
}
|
|
135
|
+
trackerRoot = path.isAbsolute(expanded) ? expanded : path.resolve(workflowDir, expanded);
|
|
136
|
+
}
|
|
137
|
+
else if (trackerKind === 'local') {
|
|
138
|
+
// Default local tracker root: <workflow-dir>/issues
|
|
139
|
+
trackerRoot = path.resolve(workflowDir, 'issues');
|
|
140
|
+
}
|
|
141
|
+
const tracker = {
|
|
142
|
+
kind: trackerKind,
|
|
143
|
+
endpoint: asString(trackerRaw['endpoint']) ?? trackerEndpointDefault,
|
|
144
|
+
api_key: apiKeyResolved && apiKeyResolved.length > 0 ? apiKeyResolved : null,
|
|
145
|
+
project_slug: asString(trackerRaw['project_slug']),
|
|
146
|
+
active_states: asStringList(trackerRaw['active_states'], ['Todo', 'In Progress']),
|
|
147
|
+
terminal_states: asStringList(trackerRaw['terminal_states'], [
|
|
148
|
+
'Closed',
|
|
149
|
+
'Cancelled',
|
|
150
|
+
'Canceled',
|
|
151
|
+
'Duplicate',
|
|
152
|
+
'Done',
|
|
153
|
+
]),
|
|
154
|
+
root: trackerRoot,
|
|
155
|
+
};
|
|
156
|
+
// polling (§5.3.2)
|
|
157
|
+
const pollingRaw = getObject(raw, 'polling');
|
|
158
|
+
const polling = {
|
|
159
|
+
interval_ms: asInt(pollingRaw['interval_ms'], 30_000),
|
|
160
|
+
};
|
|
161
|
+
// workspace (§5.3.3)
|
|
162
|
+
const workspaceRaw = getObject(raw, 'workspace');
|
|
163
|
+
const wsRootInput = asString(workspaceRaw['root']);
|
|
164
|
+
let workspaceRoot;
|
|
165
|
+
if (wsRootInput) {
|
|
166
|
+
const expanded = expandVar(wsRootInput);
|
|
167
|
+
if (expanded === '') {
|
|
168
|
+
throw new WorkflowError('workflow_parse_error', `workspace.root references an unset variable: ${wsRootInput}`);
|
|
169
|
+
}
|
|
170
|
+
workspaceRoot = path.isAbsolute(expanded) ? expanded : path.resolve(workflowDir, expanded);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
workspaceRoot = path.join(os.tmpdir(), 'symphony_workspaces');
|
|
174
|
+
}
|
|
175
|
+
const workspace = { root: path.resolve(workspaceRoot) };
|
|
176
|
+
// hooks (§5.3.4)
|
|
177
|
+
const hooksRaw = getObject(raw, 'hooks');
|
|
178
|
+
const hooks = {
|
|
179
|
+
after_create: asString(hooksRaw['after_create']),
|
|
180
|
+
before_run: asString(hooksRaw['before_run']),
|
|
181
|
+
after_run: asString(hooksRaw['after_run']),
|
|
182
|
+
before_remove: asString(hooksRaw['before_remove']),
|
|
183
|
+
timeout_ms: asInt(hooksRaw['timeout_ms'], 60_000),
|
|
184
|
+
};
|
|
185
|
+
if (hooks.timeout_ms <= 0) {
|
|
186
|
+
throw new WorkflowError('workflow_parse_error', 'hooks.timeout_ms must be positive');
|
|
187
|
+
}
|
|
188
|
+
// agent (§5.3.5)
|
|
189
|
+
const agentRaw = getObject(raw, 'agent');
|
|
190
|
+
const maxTurns = asInt(agentRaw['max_turns'], 20);
|
|
191
|
+
if (maxTurns <= 0) {
|
|
192
|
+
throw new WorkflowError('workflow_parse_error', 'agent.max_turns must be positive');
|
|
193
|
+
}
|
|
194
|
+
const agent = {
|
|
195
|
+
max_concurrent_agents: asInt(agentRaw['max_concurrent_agents'], 10),
|
|
196
|
+
max_turns: maxTurns,
|
|
197
|
+
max_retry_backoff_ms: asInt(agentRaw['max_retry_backoff_ms'], 300_000),
|
|
198
|
+
max_concurrent_agents_by_state: asMapStrPosInt(agentRaw['max_concurrent_agents_by_state']),
|
|
199
|
+
};
|
|
200
|
+
// acp (Symphony extension; supersedes the §5.3.6 `codex` block). `adapter` selects
|
|
201
|
+
// one of symphony's known profiles (claude, codex). `command` is optional: when
|
|
202
|
+
// null/unset, symphony auto-derives the launch command from the adapter profile and
|
|
203
|
+
// stages the host credential file into the workspace. Override `command` only if
|
|
204
|
+
// you need a custom launch (testing a forked adapter, a non-default binary path,
|
|
205
|
+
// etc.) — overriding also opts out of credential staging.
|
|
206
|
+
const acpRaw = getObject(raw, 'acp');
|
|
207
|
+
const acp = {
|
|
208
|
+
adapter: asString(acpRaw['adapter']) ?? 'claude',
|
|
209
|
+
command: asString(acpRaw['command']),
|
|
210
|
+
shell: asString(acpRaw['shell']) ?? 'bash',
|
|
211
|
+
prompt_timeout_ms: asInt(acpRaw['prompt_timeout_ms'], 3_600_000),
|
|
212
|
+
read_timeout_ms: asInt(acpRaw['read_timeout_ms'], 30_000),
|
|
213
|
+
stall_timeout_ms: asInt(acpRaw['stall_timeout_ms'], 300_000),
|
|
214
|
+
};
|
|
215
|
+
// smolvm extension
|
|
216
|
+
const smolvmRaw = getObject(raw, 'smolvm');
|
|
217
|
+
const fromRaw = asString(smolvmRaw['from']);
|
|
218
|
+
let from = null;
|
|
219
|
+
if (fromRaw) {
|
|
220
|
+
const expanded = expandVar(fromRaw);
|
|
221
|
+
if (expanded === '') {
|
|
222
|
+
throw new WorkflowError('workflow_parse_error', `smolvm.from references an unset variable: ${fromRaw}`);
|
|
223
|
+
}
|
|
224
|
+
from = path.isAbsolute(expanded) ? expanded : path.resolve(workflowDir, expanded);
|
|
225
|
+
}
|
|
226
|
+
const volumesRaw = smolvmRaw['volumes'];
|
|
227
|
+
const volumes = Array.isArray(volumesRaw)
|
|
228
|
+
? volumesRaw.flatMap((v) => {
|
|
229
|
+
if (!v || typeof v !== 'object' || Array.isArray(v))
|
|
230
|
+
return [];
|
|
231
|
+
const m = v;
|
|
232
|
+
const hostRaw = asString(m['host']);
|
|
233
|
+
const guest = asString(m['guest']);
|
|
234
|
+
if (!hostRaw || !guest)
|
|
235
|
+
return [];
|
|
236
|
+
const expandedHost = expandVar(hostRaw);
|
|
237
|
+
if (expandedHost === '')
|
|
238
|
+
return [];
|
|
239
|
+
const host = path.isAbsolute(expandedHost)
|
|
240
|
+
? expandedHost
|
|
241
|
+
: path.resolve(workflowDir, expandedHost);
|
|
242
|
+
const readonly = m['readonly'] === true;
|
|
243
|
+
return [{ host, guest, readonly }];
|
|
244
|
+
})
|
|
245
|
+
: [];
|
|
246
|
+
const smolvm = {
|
|
247
|
+
image: asString(smolvmRaw['image']),
|
|
248
|
+
from,
|
|
249
|
+
cpus: asInt(smolvmRaw['cpus'], 2),
|
|
250
|
+
mem_mib: asInt(smolvmRaw['mem_mib'], 2048),
|
|
251
|
+
net: smolvmRaw['net'] !== false,
|
|
252
|
+
bin_path: asString(smolvmRaw['bin_path']),
|
|
253
|
+
volumes,
|
|
254
|
+
// Default forwarded credentials cover all three shipped ACP adapters so workflows that
|
|
255
|
+
// do not override `smolvm.forward_env` still authenticate after the default-adapter
|
|
256
|
+
// switch to claude-agent-acp.
|
|
257
|
+
forward_env: asStringList(smolvmRaw['forward_env'], [
|
|
258
|
+
'OPENAI_API_KEY',
|
|
259
|
+
'ANTHROPIC_API_KEY',
|
|
260
|
+
]),
|
|
261
|
+
endpoint: asString(smolvmRaw['endpoint']) ??
|
|
262
|
+
`unix://${process.env.XDG_RUNTIME_DIR ?? '/run/user/1000'}/smolvm.sock`,
|
|
263
|
+
};
|
|
264
|
+
// server extension (§13.7)
|
|
265
|
+
const serverRaw = getObject(raw, 'server');
|
|
266
|
+
const server = {
|
|
267
|
+
port: typeof serverRaw['port'] === 'number' ? serverRaw['port'] : null,
|
|
268
|
+
host: asString(serverRaw['host']) ?? '127.0.0.1',
|
|
269
|
+
};
|
|
270
|
+
// mcp extension: per-issue MCP server (mark_done + request_human_steering tools) injected
|
|
271
|
+
// into each ACP session. `host` defaults to the QEMU slirp gateway; the port is the
|
|
272
|
+
// actually-bound HTTP server's port (resolved at runtime, not config-parse time, so
|
|
273
|
+
// `--port` and an unset server.port can never desync). `host_url` is an explicit full-URL
|
|
274
|
+
// override for cases where the VM can't reach the orchestrator via the host gateway.
|
|
275
|
+
const mcpRaw = getObject(raw, 'mcp');
|
|
276
|
+
const mcpEnabledRaw = mcpRaw['enabled'];
|
|
277
|
+
const mcpEnabled = mcpEnabledRaw === undefined ? true : mcpEnabledRaw !== false;
|
|
278
|
+
const mcp = {
|
|
279
|
+
enabled: mcpEnabled,
|
|
280
|
+
// 127.0.0.1 works for smolvm because its VM network intercepts loopback
|
|
281
|
+
// traffic and forwards it to the host's loopback. (Empirically verified;
|
|
282
|
+
// 10.0.2.2 — the QEMU slirp gateway — is NOT reachable here.) Other VMMs
|
|
283
|
+
// can override via the `host` field in the WORKFLOW.md mcp block.
|
|
284
|
+
host: asString(mcpRaw['host']) ?? '127.0.0.1',
|
|
285
|
+
explicit_host_url: asString(mcpRaw['host_url']),
|
|
286
|
+
};
|
|
287
|
+
return {
|
|
288
|
+
workflow_path: workflowAbs,
|
|
289
|
+
workflow_dir: workflowDir,
|
|
290
|
+
tracker,
|
|
291
|
+
polling,
|
|
292
|
+
workspace,
|
|
293
|
+
hooks,
|
|
294
|
+
agent,
|
|
295
|
+
acp,
|
|
296
|
+
smolvm,
|
|
297
|
+
server,
|
|
298
|
+
mcp,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
// §6.3 dispatch preflight validation.
|
|
302
|
+
export function validateDispatch(cfg) {
|
|
303
|
+
if (!cfg.tracker.kind)
|
|
304
|
+
return 'tracker.kind is required';
|
|
305
|
+
if (cfg.tracker.kind !== 'linear' && cfg.tracker.kind !== 'local') {
|
|
306
|
+
return `unsupported_tracker_kind: ${cfg.tracker.kind}`;
|
|
307
|
+
}
|
|
308
|
+
if (cfg.tracker.kind === 'linear') {
|
|
309
|
+
if (!cfg.tracker.api_key)
|
|
310
|
+
return 'missing_tracker_api_key';
|
|
311
|
+
if (!cfg.tracker.project_slug)
|
|
312
|
+
return 'missing_tracker_project_slug';
|
|
313
|
+
}
|
|
314
|
+
if (cfg.tracker.kind === 'local') {
|
|
315
|
+
if (!cfg.tracker.root)
|
|
316
|
+
return 'tracker.root must be set for local tracker';
|
|
317
|
+
if (!existsSync(cfg.tracker.root) || !statSync(cfg.tracker.root).isDirectory()) {
|
|
318
|
+
return `tracker.root not found or not a directory: ${cfg.tracker.root}`;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// When acp.command is explicitly set, the operator owns the launch (and credential
|
|
322
|
+
// staging). Otherwise the adapter id must be one symphony has a profile for, so
|
|
323
|
+
// the runner can auto-derive the command and ship credentials.
|
|
324
|
+
if (cfg.acp.command !== null) {
|
|
325
|
+
if (!cfg.acp.command.trim())
|
|
326
|
+
return 'acp.command must be non-empty when set';
|
|
327
|
+
}
|
|
328
|
+
else if (!isKnownAdapter(cfg.acp.adapter)) {
|
|
329
|
+
return `acp.adapter "${cfg.acp.adapter}" is not a known profile; set acp.command to override or use one of: claude, codex`;
|
|
330
|
+
}
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
// Build + watch a workflow source. Throws on initial load failure.
|
|
334
|
+
export async function watchWorkflow(workflowPath) {
|
|
335
|
+
const workflowAbs = path.resolve(workflowPath);
|
|
336
|
+
const initialDef = await loadWorkflow(workflowAbs);
|
|
337
|
+
const initialCfg = buildServiceConfig(initialDef.config, workflowAbs);
|
|
338
|
+
let current = { definition: initialDef, config: initialCfg };
|
|
339
|
+
const listeners = new Set();
|
|
340
|
+
const watcher = chokidar.watch(workflowAbs, {
|
|
341
|
+
ignoreInitial: true,
|
|
342
|
+
persistent: true,
|
|
343
|
+
});
|
|
344
|
+
let reloadInFlight = null;
|
|
345
|
+
const reload = async () => {
|
|
346
|
+
if (reloadInFlight)
|
|
347
|
+
return reloadInFlight;
|
|
348
|
+
reloadInFlight = (async () => {
|
|
349
|
+
try {
|
|
350
|
+
const def = await loadWorkflow(workflowAbs);
|
|
351
|
+
const cfg = buildServiceConfig(def.config, workflowAbs);
|
|
352
|
+
current = { definition: def, config: cfg };
|
|
353
|
+
log.info('workflow reloaded', { path: workflowAbs });
|
|
354
|
+
for (const cb of listeners)
|
|
355
|
+
cb({ definition: def, config: cfg });
|
|
356
|
+
}
|
|
357
|
+
catch (err) {
|
|
358
|
+
const e = err instanceof WorkflowError
|
|
359
|
+
? err
|
|
360
|
+
: new WorkflowError('workflow_parse_error', err.message);
|
|
361
|
+
log.warn('workflow reload failed; keeping last good config', { error: e.message, code: e.code });
|
|
362
|
+
for (const cb of listeners)
|
|
363
|
+
cb({ error: e });
|
|
364
|
+
}
|
|
365
|
+
finally {
|
|
366
|
+
reloadInFlight = null;
|
|
367
|
+
}
|
|
368
|
+
})();
|
|
369
|
+
return reloadInFlight;
|
|
370
|
+
};
|
|
371
|
+
watcher.on('change', () => void reload());
|
|
372
|
+
watcher.on('add', () => void reload());
|
|
373
|
+
// §5.5: workflow read errors must block dispatch. If the file is deleted or temporarily
|
|
374
|
+
// renamed, surface a missing_workflow_file error so the orchestrator knows.
|
|
375
|
+
watcher.on('unlink', () => void reload());
|
|
376
|
+
return {
|
|
377
|
+
current: () => current,
|
|
378
|
+
onChange: (cb) => {
|
|
379
|
+
listeners.add(cb);
|
|
380
|
+
return () => listeners.delete(cb);
|
|
381
|
+
},
|
|
382
|
+
stop: () => watcher.close(),
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
//# sourceMappingURL=workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"AAAA,oEAAoE;AAEpE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAc1C,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,OAAO,aAAc,SAAQ,KAAK;IACnB;IAAnB,YAAmB,IAAY,EAAE,OAAe;QAC9C,KAAK,CAAC,OAAO,CAAC,CAAC;QADE,SAAI,GAAJ,IAAI,CAAQ;QAE7B,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,kDAAkD;AAClD,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;IAC3C,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClC,sFAAsF;IACtF,uFAAuF;IACvF,kCAAkC;IAClC,MAAM,OAAO,GAAG,CAAC,IAAwB,EAAW,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IACnF,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;IAC3C,CAAC;IACD,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACtB,MAAM,GAAG,CAAC,CAAC;YACX,MAAM;QACR,CAAC;IACH,CAAC;IACD,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QACf,MAAM,IAAI,aAAa,CAAC,sBAAsB,EAAE,gCAAgC,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,aAAa,CAAC,sBAAsB,EAAE,8BAA+B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IAC1G,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS;QAAE,MAAM,GAAG,EAAE,CAAC;IACzD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,aAAa,CAAC,iCAAiC,EAAE,wCAAwC,CAAC,CAAC;IACvG,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,MAAiC,EAAE,IAAI,EAAE,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,YAAoB;IACrD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,aAAa,CAAC,uBAAuB,EAAE,eAAe,YAAY,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7G,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAChD,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;AAC3C,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,CAAC,GAAG,KAAK,CAAC;IACd,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QACpC,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,IAAI,CAAC,EAAE,CAAC;QACN,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC;QAClC,OAAO,MAAM,IAAI,EAAE,CAAC;IACtB,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,QAAQ,CAAC,CAAU;IAC1B,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IACpC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,KAAK,CAAC,CAAU,EAAE,QAAgB;IACzC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACtE,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACvE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,YAAY,CAAC,CAAU,EAAE,QAAkB;IAClD,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;IACjF,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,cAAc,CAAC,CAAU;IAChC,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,KAAK,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAA4B,CAAC,EAAE,CAAC;YACpE,MAAM,CAAC,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACxB,IAAI,CAAC,GAAG,CAAC;gBAAE,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,SAAS,CAAC,MAA+B,EAAE,GAAW;IAC7D,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,CAA4B,CAAC;IACzF,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,kBAAkB,CAChC,GAA4B,EAC5B,YAAoB;IAEpB,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAE9C,mBAAmB;IACnB,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAChE,MAAM,SAAS,GAAG,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;IAClD,MAAM,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/D,MAAM,sBAAsB,GAAG,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,gCAAgC,CAAC,CAAC,CAAC,IAAI,CAAC;IAClG,yDAAyD;IACzD,MAAM,cAAc,GAAG,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IACpD,IAAI,WAAW,GAAkB,IAAI,CAAC;IACtC,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,QAAQ,GAAG,SAAS,CAAC,cAAc,CAAC,CAAC;QAC3C,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,8CAA8C,cAAc,EAAE,CAC/D,CAAC;QACJ,CAAC;QACD,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAC3F,CAAC;SAAM,IAAI,WAAW,KAAK,OAAO,EAAE,CAAC;QACnC,oDAAoD;QACpD,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACpD,CAAC;IACD,MAAM,OAAO,GAAkB;QAC7B,IAAI,EAAE,WAAW;QACjB,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,IAAI,sBAAsB;QACpE,OAAO,EAAE,cAAc,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI;QAC5E,YAAY,EAAE,QAAQ,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;QAClD,aAAa,EAAE,YAAY,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACjF,eAAe,EAAE,YAAY,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE;YAC3D,QAAQ;YACR,WAAW;YACX,UAAU;YACV,WAAW;YACX,MAAM;SACP,CAAC;QACF,IAAI,EAAE,WAAW;KAClB,CAAC;IAEF,mBAAmB;IACnB,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAkB;QAC7B,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;KACtD,CAAC;IAEF,qBAAqB;IACrB,MAAM,YAAY,GAAG,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACjD,MAAM,WAAW,GAAG,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;IACnD,IAAI,aAAqB,CAAC;IAC1B,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,QAAQ,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;QACxC,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,gDAAgD,WAAW,EAAE,CAC9D,CAAC;QACJ,CAAC;QACD,aAAa,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAC7F,CAAC;SAAM,CAAC;QACN,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,SAAS,GAAoB,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;IAEzE,iBAAiB;IACjB,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACzC,MAAM,KAAK,GAAgB;QACzB,YAAY,EAAE,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;QAChD,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QAC5C,SAAS,EAAE,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC1C,aAAa,EAAE,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;QAClD,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;KAClD,CAAC;IACF,IAAI,KAAK,CAAC,UAAU,IAAI,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,aAAa,CAAC,sBAAsB,EAAE,mCAAmC,CAAC,CAAC;IACvF,CAAC;IAED,iBAAiB;IACjB,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC;IAClD,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAClB,MAAM,IAAI,aAAa,CAAC,sBAAsB,EAAE,kCAAkC,CAAC,CAAC;IACtF,CAAC;IACD,MAAM,KAAK,GAAgB;QACzB,qBAAqB,EAAE,KAAK,CAAC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,EAAE,CAAC;QACnE,SAAS,EAAE,QAAQ;QACnB,oBAAoB,EAAE,KAAK,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC;QACtE,8BAA8B,EAAE,cAAc,CAAC,QAAQ,CAAC,gCAAgC,CAAC,CAAC;KAC3F,CAAC;IAEF,mFAAmF;IACnF,gFAAgF;IAChF,oFAAoF;IACpF,iFAAiF;IACjF,iFAAiF;IACjF,0DAA0D;IAC1D,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACrC,MAAM,GAAG,GAAc;QACrB,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,QAAQ;QAChD,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACpC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,MAAM;QAC1C,iBAAiB,EAAE,KAAK,CAAC,MAAM,CAAC,mBAAmB,CAAC,EAAE,SAAS,CAAC;QAChE,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;QACzD,gBAAgB,EAAE,KAAK,CAAC,MAAM,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;KAC7D,CAAC;IAEF,mBAAmB;IACnB,MAAM,SAAS,GAAG,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5C,IAAI,IAAI,GAAkB,IAAI,CAAC;IAC/B,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QACpC,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,6CAA6C,OAAO,EAAE,CACvD,CAAC;QACJ,CAAC;QACD,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,UAAU,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;QACvC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YACvB,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;gBAAE,OAAO,EAAE,CAAC;YAC/D,MAAM,CAAC,GAAG,CAA4B,CAAC;YACvC,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;YACpC,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACnC,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK;gBAAE,OAAO,EAAE,CAAC;YAClC,MAAM,YAAY,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;YACxC,IAAI,YAAY,KAAK,EAAE;gBAAE,OAAO,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;gBACxC,CAAC,CAAC,YAAY;gBACd,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;YAC5C,MAAM,QAAQ,GAAG,CAAC,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;YACxC,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACrC,CAAC,CAAC;QACJ,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,MAAM,GAAiB;QAC3B,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACnC,IAAI;QACJ,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACjC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC;QAC1C,GAAG,EAAE,SAAS,CAAC,KAAK,CAAC,KAAK,KAAK;QAC/B,QAAQ,EAAE,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACzC,OAAO;QACP,uFAAuF;QACvF,oFAAoF;QACpF,8BAA8B;QAC9B,WAAW,EAAE,YAAY,CAAC,SAAS,CAAC,aAAa,CAAC,EAAE;YAClD,gBAAgB;YAChB,mBAAmB;SACpB,CAAC;QACF,QAAQ,EACN,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YAC/B,UAAU,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,gBAAgB,cAAc;KAC1E,CAAC;IAEF,2BAA2B;IAC3B,MAAM,SAAS,GAAG,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAiB;QAC3B,IAAI,EAAE,OAAO,SAAS,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,SAAS,CAAC,MAAM,CAAY,CAAC,CAAC,CAAC,IAAI;QAClF,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,WAAW;KACjD,CAAC;IAEF,0FAA0F;IAC1F,oFAAoF;IACpF,oFAAoF;IACpF,0FAA0F;IAC1F,qFAAqF;IACrF,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACrC,MAAM,aAAa,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,KAAK,KAAK,CAAC;IAChF,MAAM,GAAG,GAAc;QACrB,OAAO,EAAE,UAAU;QACnB,wEAAwE;QACxE,yEAAyE;QACzE,yEAAyE;QACzE,kEAAkE;QAClE,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,WAAW;QAC7C,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;KAChD,CAAC;IAEF,OAAO;QACL,aAAa,EAAE,WAAW;QAC1B,YAAY,EAAE,WAAW;QACzB,OAAO;QACP,OAAO;QACP,SAAS;QACT,KAAK;QACL,KAAK;QACL,GAAG;QACH,MAAM;QACN,MAAM;QACN,GAAG;KACJ,CAAC;AACJ,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,gBAAgB,CAAC,GAAkB;IACjD,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI;QAAE,OAAO,0BAA0B,CAAC;IACzD,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAClE,OAAO,6BAA6B,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACzD,CAAC;IACD,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAClC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO;YAAE,OAAO,yBAAyB,CAAC;QAC3D,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY;YAAE,OAAO,8BAA8B,CAAC;IACvE,CAAC;IACD,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI;YAAE,OAAO,4CAA4C,CAAC;QAC3E,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YAC/E,OAAO,8CAA8C,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAC1E,CAAC;IACH,CAAC;IACD,mFAAmF;IACnF,gFAAgF;IAChF,+DAA+D;IAC/D,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE;YAAE,OAAO,wCAAwC,CAAC;IAC/E,CAAC;SAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5C,OAAO,gBAAgB,GAAG,CAAC,GAAG,CAAC,OAAO,oFAAoF,CAAC;IAC7H,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAYD,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,YAAoB;IACtD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,kBAAkB,CAAC,UAAU,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACtE,IAAI,OAAO,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC7D,MAAM,SAAS,GAAG,IAAI,GAAG,EAA0B,CAAC;IAEpD,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE;QAC1C,aAAa,EAAE,IAAI;QACnB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAC;IAEH,IAAI,cAAc,GAAyB,IAAI,CAAC;IAEhD,MAAM,MAAM,GAAG,KAAK,IAAI,EAAE;QACxB,IAAI,cAAc;YAAE,OAAO,cAAc,CAAC;QAC1C,cAAc,GAAG,CAAC,KAAK,IAAI,EAAE;YAC3B,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;gBAC5C,MAAM,GAAG,GAAG,kBAAkB,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;gBACxD,OAAO,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;gBAC3C,GAAG,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;gBACrD,KAAK,MAAM,EAAE,IAAI,SAAS;oBAAE,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YACnE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,GACL,GAAG,YAAY,aAAa;oBAC1B,CAAC,CAAC,GAAG;oBACL,CAAC,CAAC,IAAI,aAAa,CAAC,sBAAsB,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;gBACxE,GAAG,CAAC,IAAI,CAAC,kDAAkD,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;gBACjG,KAAK,MAAM,EAAE,IAAI,SAAS;oBAAE,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;YAC/C,CAAC;oBAAS,CAAC;gBACT,cAAc,GAAG,IAAI,CAAC;YACxB,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,cAAc,CAAC;IACxB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC;IACvC,wFAAwF;IACxF,4EAA4E;IAC5E,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC;IAE1C,OAAO;QACL,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO;QACtB,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE;YACf,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE;KAC5B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// Workspace manager (SPEC §9). Per-issue workspace dirs under workspace.root.
|
|
2
|
+
//
|
|
3
|
+
// Safety invariants enforced:
|
|
4
|
+
// 1. Workspace path is rooted at workspace.root (containment check).
|
|
5
|
+
// 2. Workspace key is sanitized — only [A-Za-z0-9._-] allowed.
|
|
6
|
+
// 3. Agent cwd MUST equal the workspace path (enforced by callers via runWithCwd).
|
|
7
|
+
import { mkdir, rm, lstat } from 'node:fs/promises';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { spawn } from 'node:child_process';
|
|
10
|
+
export class WorkspaceError extends Error {
|
|
11
|
+
code;
|
|
12
|
+
constructor(code, message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.name = 'WorkspaceError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// §9.5 Invariant 3.
|
|
19
|
+
export function sanitizeWorkspaceKey(identifier) {
|
|
20
|
+
return identifier.replace(/[^A-Za-z0-9._-]/g, '_');
|
|
21
|
+
}
|
|
22
|
+
// §9.5 Invariant 2: workspace path MUST be contained within workspace root.
|
|
23
|
+
//
|
|
24
|
+
// We deliberately check for a `..` path component rather than a `..` prefix on the
|
|
25
|
+
// relative path: an identifier like `..fix` sanitizes to a perfectly contained child
|
|
26
|
+
// directory whose relative path begins with the two-character substring `..` but does
|
|
27
|
+
// not actually escape the root.
|
|
28
|
+
export function assertContained(workspaceRoot, candidate) {
|
|
29
|
+
const rootAbs = path.resolve(workspaceRoot);
|
|
30
|
+
const candAbs = path.resolve(candidate);
|
|
31
|
+
if (candAbs === rootAbs) {
|
|
32
|
+
throw new WorkspaceError('invalid_workspace_path', `workspace path must not equal workspace root (${rootAbs})`);
|
|
33
|
+
}
|
|
34
|
+
const rel = path.relative(rootAbs, candAbs);
|
|
35
|
+
if (rel === '' || path.isAbsolute(rel)) {
|
|
36
|
+
throw new WorkspaceError('invalid_workspace_path', `workspace path ${candAbs} is not contained within ${rootAbs}`);
|
|
37
|
+
}
|
|
38
|
+
const parts = rel.split(path.sep);
|
|
39
|
+
if (parts.some((p) => p === '..')) {
|
|
40
|
+
throw new WorkspaceError('invalid_workspace_path', `workspace path ${candAbs} is not contained within ${rootAbs}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// A hook failed when it timed out, exited with a non-zero status, or was terminated by a
|
|
44
|
+
// signal. The signal-termination case is important — otherwise a fatal `before_run` script
|
|
45
|
+
// that calls `kill -TERM $$` would look successful (exit_code=null, timed_out=false).
|
|
46
|
+
function hookFailed(res) {
|
|
47
|
+
return res.timed_out || res.signal !== null || res.exit_code !== 0;
|
|
48
|
+
}
|
|
49
|
+
function hookFailureReason(res) {
|
|
50
|
+
if (res.timed_out)
|
|
51
|
+
return 'timed out';
|
|
52
|
+
if (res.signal !== null)
|
|
53
|
+
return `terminated by signal ${res.signal}`;
|
|
54
|
+
return `exited with code ${res.exit_code}`;
|
|
55
|
+
}
|
|
56
|
+
// Execute a hook script (POSIX `sh -lc`). Returns result so callers can decide on failure
|
|
57
|
+
// semantics (§9.4).
|
|
58
|
+
export async function runHookScript(script, cwd, timeoutMs) {
|
|
59
|
+
return new Promise((resolve) => {
|
|
60
|
+
const child = spawn('sh', ['-lc', script], {
|
|
61
|
+
cwd,
|
|
62
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
63
|
+
env: process.env,
|
|
64
|
+
});
|
|
65
|
+
let stdout = '';
|
|
66
|
+
let stderr = '';
|
|
67
|
+
let timedOut = false;
|
|
68
|
+
child.stdout?.on('data', (b) => {
|
|
69
|
+
stdout += b.toString('utf8');
|
|
70
|
+
if (stdout.length > 65_536)
|
|
71
|
+
stdout = stdout.slice(0, 65_536);
|
|
72
|
+
});
|
|
73
|
+
child.stderr?.on('data', (b) => {
|
|
74
|
+
stderr += b.toString('utf8');
|
|
75
|
+
if (stderr.length > 65_536)
|
|
76
|
+
stderr = stderr.slice(0, 65_536);
|
|
77
|
+
});
|
|
78
|
+
const t = setTimeout(() => {
|
|
79
|
+
timedOut = true;
|
|
80
|
+
child.kill('SIGKILL');
|
|
81
|
+
}, timeoutMs);
|
|
82
|
+
child.on('error', () => {
|
|
83
|
+
clearTimeout(t);
|
|
84
|
+
resolve({ ran: true, exit_code: null, signal: null, timed_out: timedOut, stdout, stderr });
|
|
85
|
+
});
|
|
86
|
+
child.on('close', (code, signal) => {
|
|
87
|
+
clearTimeout(t);
|
|
88
|
+
resolve({
|
|
89
|
+
ran: true,
|
|
90
|
+
exit_code: code,
|
|
91
|
+
signal: signal ?? null,
|
|
92
|
+
timed_out: timedOut,
|
|
93
|
+
stdout,
|
|
94
|
+
stderr,
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
export class WorkspaceManager {
|
|
100
|
+
cfg;
|
|
101
|
+
constructor(cfg) {
|
|
102
|
+
this.cfg = cfg;
|
|
103
|
+
}
|
|
104
|
+
updateConfig(cfg) {
|
|
105
|
+
this.cfg = cfg;
|
|
106
|
+
}
|
|
107
|
+
workspacePathFor(identifier) {
|
|
108
|
+
const key = sanitizeWorkspaceKey(identifier);
|
|
109
|
+
if (key.length === 0) {
|
|
110
|
+
throw new WorkspaceError('invalid_workspace_key', `cannot sanitize identifier: ${identifier}`);
|
|
111
|
+
}
|
|
112
|
+
return path.join(this.cfg.workspace.root, key);
|
|
113
|
+
}
|
|
114
|
+
// Ensure the per-issue workspace directory exists. Runs after_create when created_now.
|
|
115
|
+
async ensureFor(identifier) {
|
|
116
|
+
const workspaceRoot = this.cfg.workspace.root;
|
|
117
|
+
await mkdir(workspaceRoot, { recursive: true });
|
|
118
|
+
const wsPath = this.workspacePathFor(identifier);
|
|
119
|
+
assertContained(workspaceRoot, wsPath);
|
|
120
|
+
let createdNow = false;
|
|
121
|
+
try {
|
|
122
|
+
// Use lstat so symlinks at the workspace path are rejected: a symlink could redirect
|
|
123
|
+
// hook execution and the returned cwd outside the workspace root, which violates the
|
|
124
|
+
// §9.5 containment invariant.
|
|
125
|
+
const st = await lstat(wsPath);
|
|
126
|
+
if (st.isSymbolicLink()) {
|
|
127
|
+
throw new WorkspaceError('workspace_path_is_symlink', `workspace path ${wsPath} is a symlink; refusing to use`);
|
|
128
|
+
}
|
|
129
|
+
if (!st.isDirectory()) {
|
|
130
|
+
throw new WorkspaceError('workspace_path_not_directory', `expected directory at ${wsPath}, found a non-directory`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
if (err.code === 'ENOENT') {
|
|
135
|
+
await mkdir(wsPath, { recursive: true });
|
|
136
|
+
createdNow = true;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
throw err;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (createdNow && this.cfg.hooks.after_create) {
|
|
143
|
+
const res = await runHookScript(this.cfg.hooks.after_create, wsPath, this.cfg.hooks.timeout_ms);
|
|
144
|
+
if (hookFailed(res)) {
|
|
145
|
+
// §9.4: after_create failure is fatal — also unwind the partially prepared directory.
|
|
146
|
+
try {
|
|
147
|
+
await rm(wsPath, { recursive: true, force: true });
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Best-effort cleanup.
|
|
151
|
+
}
|
|
152
|
+
throw new WorkspaceError('after_create_failed', `after_create hook ${hookFailureReason(res)}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
path: wsPath,
|
|
157
|
+
workspace_key: sanitizeWorkspaceKey(identifier),
|
|
158
|
+
created_now: createdNow,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
// Run before_run; throw on failure/timeout (§9.4).
|
|
162
|
+
async runBeforeRun(workspacePath, hooks) {
|
|
163
|
+
if (!hooks.before_run)
|
|
164
|
+
return;
|
|
165
|
+
const res = await runHookScript(hooks.before_run, workspacePath, hooks.timeout_ms);
|
|
166
|
+
if (hookFailed(res)) {
|
|
167
|
+
throw new WorkspaceError('before_run_failed', `before_run hook ${hookFailureReason(res)}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Best-effort after_run hook (§9.4: failure logged and ignored — caller logs).
|
|
171
|
+
async runAfterRunBestEffort(workspacePath, hooks) {
|
|
172
|
+
if (!hooks.after_run)
|
|
173
|
+
return null;
|
|
174
|
+
return runHookScript(hooks.after_run, workspacePath, hooks.timeout_ms);
|
|
175
|
+
}
|
|
176
|
+
// Best-effort before_remove + filesystem removal (§9.4).
|
|
177
|
+
async remove(identifier, hooks) {
|
|
178
|
+
const wsPath = this.workspacePathFor(identifier);
|
|
179
|
+
assertContained(this.cfg.workspace.root, wsPath);
|
|
180
|
+
let hookResult = null;
|
|
181
|
+
try {
|
|
182
|
+
const st = await lstat(wsPath);
|
|
183
|
+
if (st.isSymbolicLink() || !st.isDirectory())
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
if (hooks.before_remove) {
|
|
190
|
+
hookResult = await runHookScript(hooks.before_remove, wsPath, hooks.timeout_ms);
|
|
191
|
+
}
|
|
192
|
+
await rm(wsPath, { recursive: true, force: true });
|
|
193
|
+
return hookResult;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,EAAE;AACF,8BAA8B;AAC9B,qEAAqE;AACrE,+DAA+D;AAC/D,mFAAmF;AAEnF,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAG3C,MAAM,OAAO,cAAe,SAAQ,KAAK;IACpB;IAAnB,YAAmB,IAAY,EAAE,OAAe;QAC9C,KAAK,CAAC,OAAO,CAAC,CAAC;QADE,SAAI,GAAJ,IAAI,CAAQ;QAE7B,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,oBAAoB;AACpB,MAAM,UAAU,oBAAoB,CAAC,UAAkB;IACrD,OAAO,UAAU,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;AACrD,CAAC;AAED,4EAA4E;AAC5E,EAAE;AACF,mFAAmF;AACnF,qFAAqF;AACrF,sFAAsF;AACtF,gCAAgC;AAChC,MAAM,UAAU,eAAe,CAAC,aAAqB,EAAE,SAAiB;IACtE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,cAAc,CACtB,wBAAwB,EACxB,iDAAiD,OAAO,GAAG,CAC5D,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC5C,IAAI,GAAG,KAAK,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,cAAc,CACtB,wBAAwB,EACxB,kBAAkB,OAAO,4BAA4B,OAAO,EAAE,CAC/D,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,cAAc,CACtB,wBAAwB,EACxB,kBAAkB,OAAO,4BAA4B,OAAO,EAAE,CAC/D,CAAC;IACJ,CAAC;AACH,CAAC;AAWD,yFAAyF;AACzF,2FAA2F;AAC3F,sFAAsF;AACtF,SAAS,UAAU,CAAC,GAAe;IACjC,OAAO,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC,SAAS,KAAK,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAe;IACxC,IAAI,GAAG,CAAC,SAAS;QAAE,OAAO,WAAW,CAAC;IACtC,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI;QAAE,OAAO,wBAAwB,GAAG,CAAC,MAAM,EAAE,CAAC;IACrE,OAAO,oBAAoB,GAAG,CAAC,SAAS,EAAE,CAAC;AAC7C,CAAC;AAED,0FAA0F;AAC1F,oBAAoB;AACpB,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,GAAW,EACX,SAAiB;IAEjB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE;YACzC,GAAG;YACH,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;YACjC,GAAG,EAAE,OAAO,CAAC,GAAG;SACjB,CAAC,CAAC;QACH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;YAC7B,MAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC7B,IAAI,MAAM,CAAC,MAAM,GAAG,MAAM;gBAAE,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;YAC7B,MAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC7B,IAAI,MAAM,CAAC,MAAM,GAAG,MAAM;gBAAE,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE;YACxB,QAAQ,GAAG,IAAI,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC,EAAE,SAAS,CAAC,CAAC;QACd,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACrB,YAAY,CAAC,CAAC,CAAC,CAAC;YAChB,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7F,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YACjC,YAAY,CAAC,CAAC,CAAC,CAAC;YAChB,OAAO,CAAC;gBACN,GAAG,EAAE,IAAI;gBACT,SAAS,EAAE,IAAI;gBACf,MAAM,EAAE,MAAM,IAAI,IAAI;gBACtB,SAAS,EAAE,QAAQ;gBACnB,MAAM;gBACN,MAAM;aACP,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,OAAO,gBAAgB;IACP;IAApB,YAAoB,GAAkB;QAAlB,QAAG,GAAH,GAAG,CAAe;IAAG,CAAC;IAE1C,YAAY,CAAC,GAAkB;QAC7B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAED,gBAAgB,CAAC,UAAkB;QACjC,MAAM,GAAG,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,cAAc,CAAC,uBAAuB,EAAE,+BAA+B,UAAU,EAAE,CAAC,CAAC;QACjG,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACjD,CAAC;IAED,uFAAuF;IACvF,KAAK,CAAC,SAAS,CAAC,UAAkB;QAChC,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC;QAC9C,MAAM,KAAK,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QACjD,eAAe,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QAEvC,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC;YACH,qFAAqF;YACrF,qFAAqF;YACrF,8BAA8B;YAC9B,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;YAC/B,IAAI,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC;gBACxB,MAAM,IAAI,cAAc,CACtB,2BAA2B,EAC3B,kBAAkB,MAAM,gCAAgC,CACzD,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;gBACtB,MAAM,IAAI,cAAc,CACtB,8BAA8B,EAC9B,yBAAyB,MAAM,yBAAyB,CACzD,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrD,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBACzC,UAAU,GAAG,IAAI,CAAC;YACpB,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;QAED,IAAI,UAAU,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;YAC9C,MAAM,GAAG,GAAG,MAAM,aAAa,CAC7B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,EAC3B,MAAM,EACN,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAC1B,CAAC;YACF,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpB,sFAAsF;gBACtF,IAAI,CAAC;oBACH,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBACrD,CAAC;gBAAC,MAAM,CAAC;oBACP,uBAAuB;gBACzB,CAAC;gBACD,MAAM,IAAI,cAAc,CACtB,qBAAqB,EACrB,qBAAqB,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAC9C,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO;YACL,IAAI,EAAE,MAAM;YACZ,aAAa,EAAE,oBAAoB,CAAC,UAAU,CAAC;YAC/C,WAAW,EAAE,UAAU;SACxB,CAAC;IACJ,CAAC;IAED,mDAAmD;IACnD,KAAK,CAAC,YAAY,CAAC,aAAqB,EAAE,KAAkB;QAC1D,IAAI,CAAC,KAAK,CAAC,UAAU;YAAE,OAAO;QAC9B,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,UAAU,EAAE,aAAa,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QACnF,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,IAAI,cAAc,CAAC,mBAAmB,EAAE,mBAAmB,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7F,CAAC;IACH,CAAC;IAED,+EAA+E;IAC/E,KAAK,CAAC,qBAAqB,CAAC,aAAqB,EAAE,KAAkB;QACnE,IAAI,CAAC,KAAK,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC;QAClC,OAAO,aAAa,CAAC,KAAK,CAAC,SAAS,EAAE,aAAa,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACzE,CAAC;IAED,yDAAyD;IACzD,KAAK,CAAC,MAAM,CAAC,UAAkB,EAAE,KAAkB;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QACjD,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACjD,IAAI,UAAU,GAAsB,IAAI,CAAC;QACzC,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;YAC/B,IAAI,EAAE,CAAC,cAAc,EAAE,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE;gBAAE,OAAO,IAAI,CAAC;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;YACxB,UAAU,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QAClF,CAAC;QACD,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,OAAO,UAAU,CAAC;IACpB,CAAC;CACF"}
|