@proletariat/cli 0.3.92 → 0.3.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/agent/auth.js +2 -6
- package/dist/commands/agent/auth.js.map +1 -1
- package/dist/commands/agent/cleanup.js +5 -5
- package/dist/commands/agent/cleanup.js.map +1 -1
- package/dist/commands/agent/gc.d.ts +17 -0
- package/dist/commands/agent/gc.js +75 -0
- package/dist/commands/agent/gc.js.map +1 -0
- package/dist/commands/agent/list.js +2 -2
- package/dist/commands/agent/list.js.map +1 -1
- package/dist/commands/agent/shell.js +3 -5
- package/dist/commands/agent/shell.js.map +1 -1
- package/dist/commands/branch/create.js +2 -2
- package/dist/commands/branch/create.js.map +1 -1
- package/dist/commands/claude/index.js +2 -2
- package/dist/commands/claude/index.js.map +1 -1
- package/dist/commands/claude/open.js +11 -5
- package/dist/commands/claude/open.js.map +1 -1
- package/dist/commands/config/index.js +88 -7
- package/dist/commands/config/index.js.map +1 -1
- package/dist/commands/db/repair.js +2 -2
- package/dist/commands/db/repair.js.map +1 -1
- package/dist/commands/docker/clean.js +2 -4
- package/dist/commands/docker/clean.js.map +1 -1
- package/dist/commands/docker/index.js +2 -4
- package/dist/commands/docker/index.js.map +1 -1
- package/dist/commands/docker/list.js +2 -4
- package/dist/commands/docker/list.js.map +1 -1
- package/dist/commands/docker/logs.js +2 -4
- package/dist/commands/docker/logs.js.map +1 -1
- package/dist/commands/docker/restart.js +2 -4
- package/dist/commands/docker/restart.js.map +1 -1
- package/dist/commands/docker/shell.js +2 -4
- package/dist/commands/docker/shell.js.map +1 -1
- package/dist/commands/docker/start.js +2 -4
- package/dist/commands/docker/start.js.map +1 -1
- package/dist/commands/docker/stop.js +2 -4
- package/dist/commands/docker/stop.js.map +1 -1
- package/dist/commands/docker/sync.js +2 -4
- package/dist/commands/docker/sync.js.map +1 -1
- package/dist/commands/execution/config.js +39 -0
- package/dist/commands/execution/config.js.map +1 -1
- package/dist/commands/execution/list.js +2 -4
- package/dist/commands/execution/list.js.map +1 -1
- package/dist/commands/execution/logs.js +2 -4
- package/dist/commands/execution/logs.js.map +1 -1
- package/dist/commands/execution/stop.js +2 -4
- package/dist/commands/execution/stop.js.map +1 -1
- package/dist/commands/execution/view.js +2 -4
- package/dist/commands/execution/view.js.map +1 -1
- package/dist/commands/gh/login.js +2 -1
- package/dist/commands/gh/login.js.map +1 -1
- package/dist/commands/gh/status.js +18 -2
- package/dist/commands/gh/status.js.map +1 -1
- package/dist/commands/hook/export.d.ts +17 -0
- package/dist/commands/hook/export.js +52 -0
- package/dist/commands/hook/export.js.map +1 -0
- package/dist/commands/hook/fire.d.ts +29 -0
- package/dist/commands/hook/fire.js +139 -0
- package/dist/commands/hook/fire.js.map +1 -0
- package/dist/commands/hook/index.d.ts +14 -0
- package/dist/commands/hook/index.js +53 -0
- package/dist/commands/hook/index.js.map +1 -0
- package/dist/commands/hook/list.d.ts +19 -0
- package/dist/commands/hook/list.js +136 -0
- package/dist/commands/hook/list.js.map +1 -0
- package/dist/commands/hook/preset.d.ts +22 -0
- package/dist/commands/hook/preset.js +76 -0
- package/dist/commands/hook/preset.js.map +1 -0
- package/dist/commands/mcp-server.js +2 -3
- package/dist/commands/mcp-server.js.map +1 -1
- package/dist/commands/orchestrate/index.d.ts +35 -0
- package/dist/commands/orchestrate/index.js +267 -0
- package/dist/commands/orchestrate/index.js.map +1 -0
- package/dist/commands/orchestrator/attach.js +2 -3
- package/dist/commands/orchestrator/attach.js.map +1 -1
- package/dist/commands/orchestrator/start.js +22 -31
- package/dist/commands/orchestrator/start.js.map +1 -1
- package/dist/commands/orchestrator/stop.js +21 -26
- package/dist/commands/orchestrator/stop.js.map +1 -1
- package/dist/commands/qa/index.js +3 -3
- package/dist/commands/qa/index.js.map +1 -1
- package/dist/commands/session/attach.js +5 -7
- package/dist/commands/session/attach.js.map +1 -1
- package/dist/commands/session/cleanup.js +2 -4
- package/dist/commands/session/cleanup.js.map +1 -1
- package/dist/commands/session/exec.js +2 -3
- package/dist/commands/session/exec.js.map +1 -1
- package/dist/commands/session/health.js +2 -4
- package/dist/commands/session/health.js.map +1 -1
- package/dist/commands/session/inspect.js +2 -3
- package/dist/commands/session/inspect.js.map +1 -1
- package/dist/commands/session/list.js +2 -4
- package/dist/commands/session/list.js.map +1 -1
- package/dist/commands/session/peek.js +3 -6
- package/dist/commands/session/peek.js.map +1 -1
- package/dist/commands/session/poke.js +2 -4
- package/dist/commands/session/poke.js.map +1 -1
- package/dist/commands/session/prune.js +2 -4
- package/dist/commands/session/prune.js.map +1 -1
- package/dist/commands/session/report.js +2 -4
- package/dist/commands/session/report.js.map +1 -1
- package/dist/commands/session/restart.js +2 -3
- package/dist/commands/session/restart.js.map +1 -1
- package/dist/commands/ticket/complete.js +20 -5
- package/dist/commands/ticket/complete.js.map +1 -1
- package/dist/commands/ticket/create.d.ts +3 -4
- package/dist/commands/ticket/create.js +69 -88
- package/dist/commands/ticket/create.js.map +1 -1
- package/dist/commands/ticket/edit.js +22 -14
- package/dist/commands/ticket/edit.js.map +1 -1
- package/dist/commands/ticket/reassign.js +35 -6
- package/dist/commands/ticket/reassign.js.map +1 -1
- package/dist/commands/ticket/resolve.js +15 -5
- package/dist/commands/ticket/resolve.js.map +1 -1
- package/dist/commands/ticket/update.js +31 -14
- package/dist/commands/ticket/update.js.map +1 -1
- package/dist/commands/ticket/view.js +9 -5
- package/dist/commands/ticket/view.js.map +1 -1
- package/dist/commands/work/complete.js +2 -3
- package/dist/commands/work/complete.js.map +1 -1
- package/dist/commands/work/hooks/add.js +2 -4
- package/dist/commands/work/hooks/add.js.map +1 -1
- package/dist/commands/work/hooks/list.js +2 -4
- package/dist/commands/work/hooks/list.js.map +1 -1
- package/dist/commands/work/hooks/remove.js +2 -4
- package/dist/commands/work/hooks/remove.js.map +1 -1
- package/dist/commands/work/hooks/toggle.js +2 -4
- package/dist/commands/work/hooks/toggle.js.map +1 -1
- package/dist/commands/work/index.js +4 -0
- package/dist/commands/work/index.js.map +1 -1
- package/dist/commands/work/ready.js +2 -3
- package/dist/commands/work/ready.js.map +1 -1
- package/dist/commands/work/rebase.d.ts +33 -0
- package/dist/commands/work/rebase.js +394 -0
- package/dist/commands/work/rebase.js.map +1 -0
- package/dist/commands/work/ship.d.ts +1 -0
- package/dist/commands/work/ship.js +49 -4
- package/dist/commands/work/ship.js.map +1 -1
- package/dist/commands/work/spawn.js +2 -4
- package/dist/commands/work/spawn.js.map +1 -1
- package/dist/commands/work/start.d.ts +7 -0
- package/dist/commands/work/start.js +190 -22
- package/dist/commands/work/start.js.map +1 -1
- package/dist/commands/work/stop.js +2 -4
- package/dist/commands/work/stop.js.map +1 -1
- package/dist/commands/work/watch.js +2 -4
- package/dist/commands/work/watch.js.map +1 -1
- package/dist/hooks/init.js +1 -1
- package/dist/hooks/init.js.map +1 -1
- package/dist/lib/agents/commands.d.ts +9 -3
- package/dist/lib/agents/commands.js +103 -88
- package/dist/lib/agents/commands.js.map +1 -1
- package/dist/lib/dashboard/server.js +1 -1
- package/dist/lib/dashboard/server.js.map +1 -1
- package/dist/lib/database/agents.d.ts +62 -4
- package/dist/lib/database/agents.js +161 -8
- package/dist/lib/database/agents.js.map +1 -1
- package/dist/lib/database/db-safety.js +4 -4
- package/dist/lib/database/db-safety.js.map +1 -1
- package/dist/lib/database/drizzle-schema.d.ts +2 -2
- package/dist/lib/database/drizzle-schema.js +2 -1
- package/dist/lib/database/drizzle-schema.js.map +1 -1
- package/dist/lib/database/index.d.ts +1 -1
- package/dist/lib/database/index.js +1 -1
- package/dist/lib/database/index.js.map +1 -1
- package/dist/lib/database/migrations/0013_agent_lifecycle_states.d.ts +11 -0
- package/dist/lib/database/migrations/0013_agent_lifecycle_states.js +47 -0
- package/dist/lib/database/migrations/0013_agent_lifecycle_states.js.map +1 -0
- package/dist/lib/database/migrations/0014_agent_work_lifecycle.d.ts +10 -0
- package/dist/lib/database/migrations/0014_agent_work_lifecycle.js +31 -0
- package/dist/lib/database/migrations/0014_agent_work_lifecycle.js.map +1 -0
- package/dist/lib/database/migrations/0015_orchestrate_hooks.d.ts +8 -0
- package/dist/lib/database/migrations/0015_orchestrate_hooks.js +35 -0
- package/dist/lib/database/migrations/0015_orchestrate_hooks.js.map +1 -0
- package/dist/lib/database/migrations/0016_schema_catchup.d.ts +15 -0
- package/dist/lib/database/migrations/0016_schema_catchup.js +154 -0
- package/dist/lib/database/migrations/0016_schema_catchup.js.map +1 -0
- package/dist/lib/database/migrations/index.js +8 -0
- package/dist/lib/database/migrations/index.js.map +1 -1
- package/dist/lib/database/pmo-bootstrap.js +2 -2
- package/dist/lib/database/pmo-bootstrap.js.map +1 -1
- package/dist/lib/database/workspace-schema.d.ts +1 -1
- package/dist/lib/database/workspace-schema.js +3 -1
- package/dist/lib/database/workspace-schema.js.map +1 -1
- package/dist/lib/events/events.d.ts +28 -0
- package/dist/lib/events/index.d.ts +1 -1
- package/dist/lib/events/index.js.map +1 -1
- package/dist/lib/execution/config.d.ts +2 -0
- package/dist/lib/execution/config.js +14 -0
- package/dist/lib/execution/config.js.map +1 -1
- package/dist/lib/execution/runners/devcontainer-tmux.js +1 -1
- package/dist/lib/execution/runners/devcontainer-tmux.js.map +1 -1
- package/dist/lib/execution/runners/devcontainer.js +9 -1
- package/dist/lib/execution/runners/devcontainer.js.map +1 -1
- package/dist/lib/execution/runners/docker-credentials.d.ts +8 -0
- package/dist/lib/execution/runners/docker-credentials.js +20 -3
- package/dist/lib/execution/runners/docker-credentials.js.map +1 -1
- package/dist/lib/execution/runners/docker-management.d.ts +31 -0
- package/dist/lib/execution/runners/docker-management.js +95 -0
- package/dist/lib/execution/runners/docker-management.js.map +1 -1
- package/dist/lib/execution/runners/index.d.ts +1 -1
- package/dist/lib/execution/runners/index.js +1 -1
- package/dist/lib/execution/runners/index.js.map +1 -1
- package/dist/lib/execution/runners/shared.d.ts +23 -1
- package/dist/lib/execution/runners/shared.js +112 -1
- package/dist/lib/execution/runners/shared.js.map +1 -1
- package/dist/lib/execution/spawner.js +8 -1
- package/dist/lib/execution/spawner.js.map +1 -1
- package/dist/lib/execution/types.js +2 -2
- package/dist/lib/linear/client.d.ts +13 -0
- package/dist/lib/linear/client.js +37 -0
- package/dist/lib/linear/client.js.map +1 -1
- package/dist/lib/orchestrate/actions.d.ts +20 -0
- package/dist/lib/orchestrate/actions.js +194 -0
- package/dist/lib/orchestrate/actions.js.map +1 -0
- package/dist/lib/orchestrate/config-loader.d.ts +31 -0
- package/dist/lib/orchestrate/config-loader.js +201 -0
- package/dist/lib/orchestrate/config-loader.js.map +1 -0
- package/dist/lib/orchestrate/engine.d.ts +94 -0
- package/dist/lib/orchestrate/engine.js +290 -0
- package/dist/lib/orchestrate/engine.js.map +1 -0
- package/dist/lib/orchestrate/index.d.ts +16 -0
- package/dist/lib/orchestrate/index.js +14 -0
- package/dist/lib/orchestrate/index.js.map +1 -0
- package/dist/lib/orchestrate/poller.d.ts +58 -0
- package/dist/lib/orchestrate/poller.js +282 -0
- package/dist/lib/orchestrate/poller.js.map +1 -0
- package/dist/lib/orchestrate/presets.d.ts +27 -0
- package/dist/lib/orchestrate/presets.js +65 -0
- package/dist/lib/orchestrate/presets.js.map +1 -0
- package/dist/lib/orchestrate/types.d.ts +93 -0
- package/dist/lib/orchestrate/types.js +46 -0
- package/dist/lib/orchestrate/types.js.map +1 -0
- package/dist/lib/pmo/storage/specs.js.map +1 -1
- package/dist/lib/pr/index.d.ts +28 -0
- package/dist/lib/pr/index.js +100 -0
- package/dist/lib/pr/index.js.map +1 -1
- package/dist/lib/providers/event-emitting-provider.d.ts +4 -2
- package/dist/lib/providers/event-emitting-provider.js +12 -0
- package/dist/lib/providers/event-emitting-provider.js.map +1 -1
- package/dist/lib/providers/index.d.ts +1 -1
- package/dist/lib/providers/index.js.map +1 -1
- package/dist/lib/providers/linear-provider.d.ts +4 -2
- package/dist/lib/providers/linear-provider.js +106 -0
- package/dist/lib/providers/linear-provider.js.map +1 -1
- package/dist/lib/providers/pmo-provider.d.ts +4 -2
- package/dist/lib/providers/pmo-provider.js +26 -0
- package/dist/lib/providers/pmo-provider.js.map +1 -1
- package/dist/lib/providers/types.d.ts +30 -1
- package/dist/lib/telemetry/analytics.d.ts +1 -1
- package/dist/lib/telemetry/analytics.js +16 -8
- package/dist/lib/telemetry/analytics.js.map +1 -1
- package/dist/lib/update-check.d.ts +8 -3
- package/dist/lib/update-check.js +16 -5
- package/dist/lib/update-check.js.map +1 -1
- package/dist/lib/work-lifecycle/hooks/types.d.ts +3 -2
- package/dist/lib/work-lifecycle/hooks/types.js +12 -0
- package/dist/lib/work-lifecycle/hooks/types.js.map +1 -1
- package/oclif.manifest.json +3623 -2968
- package/package.json +1 -1
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrate Engine
|
|
3
|
+
*
|
|
4
|
+
* The core daemon loop that:
|
|
5
|
+
* 1. Loads hook configuration from DB (synced from YAML/presets/CLI)
|
|
6
|
+
* 2. Subscribes to EventBus events
|
|
7
|
+
* 3. Executes matching hooks with mode-aware behavior
|
|
8
|
+
* 4. Optionally polls external sources for events
|
|
9
|
+
*
|
|
10
|
+
* The engine extends the existing HookManager with mode support
|
|
11
|
+
* and built-in action execution.
|
|
12
|
+
*/
|
|
13
|
+
import { execSync } from 'node:child_process';
|
|
14
|
+
import { getEventBus } from '../events/event-bus.js';
|
|
15
|
+
import { WorkHookStorage } from '../work-lifecycle/hooks/storage.js';
|
|
16
|
+
import { executeBuiltinAction, ACTION_HANDLERS } from './actions.js';
|
|
17
|
+
export class OrchestrateEngine {
|
|
18
|
+
db;
|
|
19
|
+
hookStorage;
|
|
20
|
+
unsubscribers = [];
|
|
21
|
+
log;
|
|
22
|
+
onConfirm;
|
|
23
|
+
onNotify;
|
|
24
|
+
running = false;
|
|
25
|
+
pendingConfirmations = [];
|
|
26
|
+
constructor(options) {
|
|
27
|
+
this.db = options.db;
|
|
28
|
+
this.hookStorage = new WorkHookStorage(options.db);
|
|
29
|
+
this.log = options.log ?? (() => { });
|
|
30
|
+
this.onConfirm = options.onConfirm;
|
|
31
|
+
this.onNotify = options.onNotify;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Start the orchestrate engine — subscribe to all orchestrate events.
|
|
35
|
+
*/
|
|
36
|
+
start() {
|
|
37
|
+
if (this.running)
|
|
38
|
+
return;
|
|
39
|
+
this.running = true;
|
|
40
|
+
const bus = getEventBus();
|
|
41
|
+
// Subscribe to standard RuntimeEventMap events
|
|
42
|
+
const standardEvents = [
|
|
43
|
+
'work:started',
|
|
44
|
+
'work:status_changed',
|
|
45
|
+
'work:pr_created',
|
|
46
|
+
'work:pr_merged',
|
|
47
|
+
'work:pr_closed',
|
|
48
|
+
'work:completed',
|
|
49
|
+
'agent:spawned',
|
|
50
|
+
'agent:stopped',
|
|
51
|
+
];
|
|
52
|
+
for (const eventName of standardEvents) {
|
|
53
|
+
this.unsubscribers.push(bus.on(eventName, (payload) => {
|
|
54
|
+
void this.handleEvent(eventName, payload);
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
this.log('[orchestrate] Engine started, listening for events');
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Stop the engine and clean up subscriptions.
|
|
61
|
+
*/
|
|
62
|
+
stop() {
|
|
63
|
+
this.running = false;
|
|
64
|
+
for (const unsub of this.unsubscribers) {
|
|
65
|
+
unsub();
|
|
66
|
+
}
|
|
67
|
+
this.unsubscribers = [];
|
|
68
|
+
this.pendingConfirmations = [];
|
|
69
|
+
this.log('[orchestrate] Engine stopped');
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Fire an event manually (used by `prlt hook fire`).
|
|
73
|
+
* This is the entry point for external event sources (GitHub Actions, polling, etc.).
|
|
74
|
+
*/
|
|
75
|
+
async fireEvent(event, ctx) {
|
|
76
|
+
return this.handleEvent(event, ctx);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get pending confirmations for hooks in confirm mode.
|
|
80
|
+
*/
|
|
81
|
+
getPendingConfirmations() {
|
|
82
|
+
return [...this.pendingConfirmations];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Approve a pending confirmation and execute it.
|
|
86
|
+
*/
|
|
87
|
+
async approveConfirmation(index) {
|
|
88
|
+
if (index < 0 || index >= this.pendingConfirmations.length)
|
|
89
|
+
return null;
|
|
90
|
+
const pending = this.pendingConfirmations.splice(index, 1)[0];
|
|
91
|
+
const result = executeBuiltinAction(pending.action, pending.ctx, pending.config);
|
|
92
|
+
this.log(`[orchestrate] Approved: ${pending.hookName} → ${pending.action} (${result.success ? 'success' : 'failed'})`);
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Deny a pending confirmation.
|
|
97
|
+
*/
|
|
98
|
+
denyConfirmation(index) {
|
|
99
|
+
if (index < 0 || index >= this.pendingConfirmations.length)
|
|
100
|
+
return false;
|
|
101
|
+
const pending = this.pendingConfirmations.splice(index, 1)[0];
|
|
102
|
+
this.log(`[orchestrate] Denied: ${pending.hookName} → ${pending.action}`);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
// ===========================================================================
|
|
106
|
+
// Private
|
|
107
|
+
// ===========================================================================
|
|
108
|
+
/**
|
|
109
|
+
* Handle an event by finding and executing matching hooks.
|
|
110
|
+
*/
|
|
111
|
+
async handleEvent(eventName, eventData) {
|
|
112
|
+
const results = [];
|
|
113
|
+
try {
|
|
114
|
+
// Query hooks matching this event, ordered by priority
|
|
115
|
+
const hooks = this.getHooksForEvent(eventName);
|
|
116
|
+
if (hooks.length === 0)
|
|
117
|
+
return results;
|
|
118
|
+
// Build event context from the payload
|
|
119
|
+
const ctx = this.buildContext(eventName, eventData);
|
|
120
|
+
for (const hook of hooks) {
|
|
121
|
+
const mode = (hook.mode || 'auto');
|
|
122
|
+
const config = hook.config ? JSON.parse(hook.config) : undefined;
|
|
123
|
+
// Determine action — either a built-in action name or the raw action_value
|
|
124
|
+
const actionName = this.resolveActionName(hook);
|
|
125
|
+
if (mode === 'off') {
|
|
126
|
+
results.push({ action: actionName, success: true, durationMs: 0, skipped: true });
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (mode === 'confirm') {
|
|
130
|
+
// Queue for confirmation
|
|
131
|
+
if (this.onConfirm) {
|
|
132
|
+
const approved = await this.onConfirm(hook.name, eventName, actionName);
|
|
133
|
+
if (!approved) {
|
|
134
|
+
this.log(`[orchestrate] Skipped (not confirmed): ${hook.name} → ${actionName}`);
|
|
135
|
+
results.push({ action: actionName, success: true, durationMs: 0, skipped: true });
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
// No confirm handler — queue for later approval
|
|
141
|
+
this.pendingConfirmations.push({
|
|
142
|
+
hookName: hook.name,
|
|
143
|
+
event: eventName,
|
|
144
|
+
action: actionName,
|
|
145
|
+
ctx,
|
|
146
|
+
config,
|
|
147
|
+
});
|
|
148
|
+
this.log(`[orchestrate] Queued for confirmation: ${hook.name} → ${actionName}`);
|
|
149
|
+
results.push({ action: actionName, success: true, durationMs: 0, awaitingConfirmation: true });
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Execute the action
|
|
154
|
+
const result = this.executeAction(actionName, ctx, config);
|
|
155
|
+
results.push(result);
|
|
156
|
+
this.log(`[orchestrate] ${hook.name} → ${actionName}: ${result.success ? 'success' : `failed: ${result.error}`} (${result.durationMs}ms)`);
|
|
157
|
+
// For notify mode, also fire the notification callback
|
|
158
|
+
if (mode === 'notify' && this.onNotify) {
|
|
159
|
+
this.onNotify(hook.name, eventName, actionName, result);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
this.log(`[orchestrate] Error handling event ${eventName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
165
|
+
}
|
|
166
|
+
return results;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get hooks for a given event from the database, ordered by priority.
|
|
170
|
+
*/
|
|
171
|
+
getHooksForEvent(eventName) {
|
|
172
|
+
try {
|
|
173
|
+
return this.db.prepare(`
|
|
174
|
+
SELECT id, name, event, action_type, action_value, enabled, description, mode, priority, config
|
|
175
|
+
FROM pmo_work_hooks
|
|
176
|
+
WHERE event = ? AND enabled = 1
|
|
177
|
+
ORDER BY priority ASC, created_at ASC
|
|
178
|
+
`).all(eventName);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// Fallback without mode/priority columns (pre-migration)
|
|
182
|
+
try {
|
|
183
|
+
const basic = this.db.prepare(`
|
|
184
|
+
SELECT id, name, event, action_type, action_value, enabled, description
|
|
185
|
+
FROM pmo_work_hooks
|
|
186
|
+
WHERE event = ? AND enabled = 1
|
|
187
|
+
ORDER BY created_at ASC
|
|
188
|
+
`).all(eventName);
|
|
189
|
+
return basic;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Build an OrchestrateEventContext from raw event data.
|
|
198
|
+
*/
|
|
199
|
+
buildContext(eventName, data) {
|
|
200
|
+
return {
|
|
201
|
+
event: eventName,
|
|
202
|
+
ticket: (data.ticketId ?? data.workItemId ?? data.ticket),
|
|
203
|
+
pr: (data.prNumber ?? data.pr),
|
|
204
|
+
branch: data.branch,
|
|
205
|
+
agent: (data.agentName ?? data.agent ?? data.sessionId),
|
|
206
|
+
container: data.containerId,
|
|
207
|
+
executionId: data.executionId,
|
|
208
|
+
prUrl: data.prUrl,
|
|
209
|
+
projectId: data.projectId,
|
|
210
|
+
...data,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Resolve the action name from a hook row.
|
|
215
|
+
* Built-in actions are referenced by name; shell hooks use the raw command.
|
|
216
|
+
*/
|
|
217
|
+
resolveActionName(hook) {
|
|
218
|
+
// If the action_value starts with "prlt hook fire", extract the --action value
|
|
219
|
+
const actionMatch = hook.action_value.match(/--action\s+(\S+)/);
|
|
220
|
+
if (actionMatch)
|
|
221
|
+
return actionMatch[1];
|
|
222
|
+
// If it's a known built-in action name directly
|
|
223
|
+
if (ACTION_HANDLERS[hook.action_value])
|
|
224
|
+
return hook.action_value;
|
|
225
|
+
// Otherwise it's a raw shell command — use the action_value as-is
|
|
226
|
+
return hook.action_value;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Execute an action — either built-in or shell fallback.
|
|
230
|
+
*/
|
|
231
|
+
executeAction(actionName, ctx, config) {
|
|
232
|
+
// Try built-in action first
|
|
233
|
+
if (ACTION_HANDLERS[actionName]) {
|
|
234
|
+
return executeBuiltinAction(actionName, ctx, config);
|
|
235
|
+
}
|
|
236
|
+
// Fallback to shell execution
|
|
237
|
+
const start = Date.now();
|
|
238
|
+
try {
|
|
239
|
+
const env = {
|
|
240
|
+
...process.env,
|
|
241
|
+
PRLT_HOOK_EVENT: ctx.event,
|
|
242
|
+
PRLT_HOOK_TICKET: ctx.ticket ?? '',
|
|
243
|
+
PRLT_HOOK_PR: ctx.pr ? String(ctx.pr) : '',
|
|
244
|
+
PRLT_HOOK_BRANCH: ctx.branch ?? '',
|
|
245
|
+
PRLT_HOOK_AGENT: ctx.agent ?? '',
|
|
246
|
+
};
|
|
247
|
+
execSync(actionName, { env, timeout: 30_000, stdio: 'pipe' });
|
|
248
|
+
return { action: actionName, success: true, durationMs: Date.now() - start };
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
return {
|
|
252
|
+
action: actionName,
|
|
253
|
+
success: false,
|
|
254
|
+
error: err instanceof Error ? err.message : String(err),
|
|
255
|
+
durationMs: Date.now() - start,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// =============================================================================
|
|
261
|
+
// Singleton
|
|
262
|
+
// =============================================================================
|
|
263
|
+
let _engine;
|
|
264
|
+
/**
|
|
265
|
+
* Initialize and start the orchestrate engine.
|
|
266
|
+
* Safe to call multiple times.
|
|
267
|
+
*/
|
|
268
|
+
export function initOrchestrateEngine(options) {
|
|
269
|
+
if (!_engine) {
|
|
270
|
+
_engine = new OrchestrateEngine(options);
|
|
271
|
+
_engine.start();
|
|
272
|
+
}
|
|
273
|
+
return _engine;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Get the running orchestrate engine instance.
|
|
277
|
+
*/
|
|
278
|
+
export function getOrchestrateEngine() {
|
|
279
|
+
return _engine;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Stop the orchestrate engine.
|
|
283
|
+
*/
|
|
284
|
+
export function stopOrchestrateEngine() {
|
|
285
|
+
if (_engine) {
|
|
286
|
+
_engine.stop();
|
|
287
|
+
_engine = undefined;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/lib/orchestrate/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAE7C,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAA;AACpE,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAyCpE,MAAM,OAAO,iBAAiB;IACpB,EAAE,CAAmB;IACrB,WAAW,CAAiB;IAC5B,aAAa,GAAsB,EAAE,CAAA;IACrC,GAAG,CAAuB;IAC1B,SAAS,CAAwE;IACjF,QAAQ,CAA6F;IACrG,OAAO,GAAG,KAAK,CAAA;IACf,oBAAoB,GAMvB,EAAE,CAAA;IAEP,YAAY,OAAiC;QAC3C,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,EAAE,CAAA;QACpB,IAAI,CAAC,WAAW,GAAG,IAAI,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAClD,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QACpC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAA;QAClC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAA;IAClC,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,IAAI,CAAC,OAAO;YAAE,OAAM;QACxB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QAEnB,MAAM,GAAG,GAAG,WAAW,EAAE,CAAA;QAEzB,+CAA+C;QAC/C,MAAM,cAAc,GAA+D;YACjF,cAAc;YACd,qBAAqB;YACrB,iBAAiB;YACjB,gBAAgB;YAChB,gBAAgB;YAChB,gBAAgB;YAChB,eAAe;YACf,eAAe;SAChB,CAAA;QAED,KAAK,MAAM,SAAS,IAAI,cAAc,EAAE,CAAC;YACvC,IAAI,CAAC,aAAa,CAAC,IAAI,CACrB,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE;gBAC5B,KAAK,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,OAA6C,CAAC,CAAA;YACjF,CAAC,CAAC,CACH,CAAA;QACH,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAA;IAChE,CAAC;IAED;;OAEG;IACH,IAAI;QACF,IAAI,CAAC,OAAO,GAAG,KAAK,CAAA;QACpB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvC,KAAK,EAAE,CAAA;QACT,CAAC;QACD,IAAI,CAAC,aAAa,GAAG,EAAE,CAAA;QACvB,IAAI,CAAC,oBAAoB,GAAG,EAAE,CAAA;QAC9B,IAAI,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAA;IAC1C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,SAAS,CAAC,KAAa,EAAE,GAA4B;QACzD,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,GAA8B,CAAC,CAAA;IAChE,CAAC;IAED;;OAEG;IACH,uBAAuB;QACrB,OAAO,CAAC,GAAG,IAAI,CAAC,oBAAoB,CAAC,CAAA;IACvC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,mBAAmB,CAAC,KAAa;QACrC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,oBAAoB,CAAC,MAAM;YAAE,OAAO,IAAI,CAAA;QAEvE,MAAM,OAAO,GAAG,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC7D,MAAM,MAAM,GAAG,oBAAoB,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;QAEhF,IAAI,CAAC,GAAG,CAAC,2BAA2B,OAAO,CAAC,QAAQ,MAAM,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAA;QACtH,OAAO,MAAM,CAAA;IACf,CAAC;IAED;;OAEG;IACH,gBAAgB,CAAC,KAAa;QAC5B,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,oBAAoB,CAAC,MAAM;YAAE,OAAO,KAAK,CAAA;QAExE,MAAM,OAAO,GAAG,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC7D,IAAI,CAAC,GAAG,CAAC,yBAAyB,OAAO,CAAC,QAAQ,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;QACzE,OAAO,IAAI,CAAA;IACb,CAAC;IAED,8EAA8E;IAC9E,UAAU;IACV,8EAA8E;IAE9E;;OAEG;IACK,KAAK,CAAC,WAAW,CAAC,SAAiB,EAAE,SAAkC;QAC7E,MAAM,OAAO,GAA8B,EAAE,CAAA;QAE7C,IAAI,CAAC;YACH,uDAAuD;YACvD,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAA;YAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,OAAO,CAAA;YAEtC,uCAAuC;YACvC,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;YAEnD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,MAAM,CAAa,CAAA;gBAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAA4B,CAAC,CAAC,CAAC,SAAS,CAAA;gBAE3F,2EAA2E;gBAC3E,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAA;gBAE/C,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;oBACnB,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;oBACjF,SAAQ;gBACV,CAAC;gBAED,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBACvB,yBAAyB;oBACzB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;wBACnB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,CAAA;wBACvE,IAAI,CAAC,QAAQ,EAAE,CAAC;4BACd,IAAI,CAAC,GAAG,CAAC,0CAA0C,IAAI,CAAC,IAAI,MAAM,UAAU,EAAE,CAAC,CAAA;4BAC/E,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;4BACjF,SAAQ;wBACV,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,gDAAgD;wBAChD,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC;4BAC7B,QAAQ,EAAE,IAAI,CAAC,IAAI;4BACnB,KAAK,EAAE,SAAS;4BAChB,MAAM,EAAE,UAAU;4BAClB,GAAG;4BACH,MAAM;yBACP,CAAC,CAAA;wBACF,IAAI,CAAC,GAAG,CAAC,0CAA0C,IAAI,CAAC,IAAI,MAAM,UAAU,EAAE,CAAC,CAAA;wBAC/E,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,oBAAoB,EAAE,IAAI,EAAE,CAAC,CAAA;wBAC9F,SAAQ;oBACV,CAAC;gBACH,CAAC;gBAED,qBAAqB;gBACrB,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;gBAC1D,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;gBAEpB,IAAI,CAAC,GAAG,CACN,iBAAiB,IAAI,CAAC,IAAI,MAAM,UAAU,KAAK,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,MAAM,CAAC,KAAK,EAAE,KAAK,MAAM,CAAC,UAAU,KAAK,CACjI,CAAA;gBAED,uDAAuD;gBACvD,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACvC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,CAAA;gBACzD,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,sCAAsC,SAAS,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAClH,CAAC;QAED,OAAO,OAAO,CAAA;IAChB,CAAC;IAED;;OAEG;IACK,gBAAgB,CAAC,SAAiB;QACxC,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;;;OAKtB,CAAC,CAAC,GAAG,CAAC,SAAS,CAAsB,CAAA;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,yDAAyD;YACzD,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;;;SAK7B,CAAC,CAAC,GAAG,CAAC,SAAS,CAAsB,CAAA;gBACtC,OAAO,KAAK,CAAA;YACd,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,EAAE,CAAA;YACX,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACK,YAAY,CAAC,SAAiB,EAAE,IAA6B;QACnE,OAAO;YACL,KAAK,EAAE,SAAS;YAChB,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,MAAM,CAAuB;YAC/E,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,EAAE,CAAuB;YACpD,MAAM,EAAE,IAAI,CAAC,MAA4B;YACzC,KAAK,EAAE,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,CAAuB;YAC7E,SAAS,EAAE,IAAI,CAAC,WAAiC;YACjD,WAAW,EAAE,IAAI,CAAC,WAAiC;YACnD,KAAK,EAAE,IAAI,CAAC,KAA2B;YACvC,SAAS,EAAE,IAAI,CAAC,SAA+B;YAC/C,GAAG,IAAI;SACR,CAAA;IACH,CAAC;IAED;;;OAGG;IACK,iBAAiB,CAAC,IAAqB;QAC7C,+EAA+E;QAC/E,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAA;QAC/D,IAAI,WAAW;YAAE,OAAO,WAAW,CAAC,CAAC,CAAC,CAAA;QAEtC,gDAAgD;QAChD,IAAI,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC;YAAE,OAAO,IAAI,CAAC,YAAY,CAAA;QAEhE,kEAAkE;QAClE,OAAO,IAAI,CAAC,YAAY,CAAA;IAC1B,CAAC;IAED;;OAEG;IACK,aAAa,CACnB,UAAkB,EAClB,GAA4B,EAC5B,MAAgC;QAEhC,4BAA4B;QAC5B,IAAI,eAAe,CAAC,UAAU,CAAC,EAAE,CAAC;YAChC,OAAO,oBAAoB,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;QACtD,CAAC;QAED,8BAA8B;QAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACxB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG;gBACV,GAAG,OAAO,CAAC,GAAG;gBACd,eAAe,EAAE,GAAG,CAAC,KAAK;gBAC1B,gBAAgB,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;gBAClC,YAAY,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;gBAC1C,gBAAgB,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;gBAClC,eAAe,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE;aACjC,CAAA;YACD,QAAQ,CAAC,UAAU,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;YAC7D,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAA;QAC9E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,MAAM,EAAE,UAAU;gBAClB,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;gBACvD,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;aAC/B,CAAA;QACH,CAAC;IACH,CAAC;CACF;AAED,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF,IAAI,OAAsC,CAAA;AAE1C;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAiC;IACrE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAA;QACxC,OAAO,CAAC,KAAK,EAAE,CAAA;IACjB,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB;IACnC,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,EAAE,CAAA;QACd,OAAO,GAAG,SAAS,CAAA;IACrB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrate module — public API.
|
|
3
|
+
*
|
|
4
|
+
* Event-driven pipeline automation daemon with HITL controls.
|
|
5
|
+
* Extends the work-lifecycle hook system with built-in actions,
|
|
6
|
+
* YAML config loading, presets, and mode-aware execution.
|
|
7
|
+
*/
|
|
8
|
+
export type { OrchestrateEvent, HookMode, BuiltinAction, HooksYaml, HookYamlEntry, WorkflowYaml, PresetName, OrchestrateEventContext, OrchestrateActionResult, } from './types.js';
|
|
9
|
+
export { ORCHESTRATE_EVENTS, HOOK_MODES, BUILTIN_ACTIONS, PRESET_NAMES, } from './types.js';
|
|
10
|
+
export { PRESETS, getPreset, } from './presets.js';
|
|
11
|
+
export { loadHooksYaml, loadWorkflowYaml, syncHooksFromYaml, applyPreset, exportHooksToYaml, } from './config-loader.js';
|
|
12
|
+
export { ACTION_HANDLERS, executeBuiltinAction, } from './actions.js';
|
|
13
|
+
export { OrchestrateEngine, initOrchestrateEngine, getOrchestrateEngine, stopOrchestrateEngine, } from './engine.js';
|
|
14
|
+
export type { OrchestrateEngineOptions } from './engine.js';
|
|
15
|
+
export { OrchestratePoller } from './poller.js';
|
|
16
|
+
export type { PollerOptions } from './poller.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrate module — public API.
|
|
3
|
+
*
|
|
4
|
+
* Event-driven pipeline automation daemon with HITL controls.
|
|
5
|
+
* Extends the work-lifecycle hook system with built-in actions,
|
|
6
|
+
* YAML config loading, presets, and mode-aware execution.
|
|
7
|
+
*/
|
|
8
|
+
export { ORCHESTRATE_EVENTS, HOOK_MODES, BUILTIN_ACTIONS, PRESET_NAMES, } from './types.js';
|
|
9
|
+
export { PRESETS, getPreset, } from './presets.js';
|
|
10
|
+
export { loadHooksYaml, loadWorkflowYaml, syncHooksFromYaml, applyPreset, exportHooksToYaml, } from './config-loader.js';
|
|
11
|
+
export { ACTION_HANDLERS, executeBuiltinAction, } from './actions.js';
|
|
12
|
+
export { OrchestrateEngine, initOrchestrateEngine, getOrchestrateEngine, stopOrchestrateEngine, } from './engine.js';
|
|
13
|
+
export { OrchestratePoller } from './poller.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/orchestrate/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAcH,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,eAAe,EACf,YAAY,GACb,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,OAAO,EACP,SAAS,GACV,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,iBAAiB,GAClB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,eAAe,EACf,oBAAoB,GACrB,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,aAAa,CAAA;AAIpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrate External Event Poller
|
|
3
|
+
*
|
|
4
|
+
* Polls external systems (GitHub, database) and fires orchestrate events
|
|
5
|
+
* when conditions are detected. This bridges external state changes into
|
|
6
|
+
* the internal event bus.
|
|
7
|
+
*
|
|
8
|
+
* Supported polls:
|
|
9
|
+
* - GitHub: open PR CI status, merge conflicts
|
|
10
|
+
* - Database: ready tickets, agent lifecycle states
|
|
11
|
+
*/
|
|
12
|
+
import type Database from 'better-sqlite3';
|
|
13
|
+
import type { OrchestrateEngine } from './engine.js';
|
|
14
|
+
export interface PollerOptions {
|
|
15
|
+
engine: OrchestrateEngine;
|
|
16
|
+
db: Database.Database;
|
|
17
|
+
log: (msg: string) => void;
|
|
18
|
+
/** Working directory for gh CLI commands */
|
|
19
|
+
cwd?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare class OrchestratePoller {
|
|
22
|
+
private engine;
|
|
23
|
+
private db;
|
|
24
|
+
private log;
|
|
25
|
+
private cwd?;
|
|
26
|
+
/** Tracks known PR state to detect transitions. */
|
|
27
|
+
private trackedPRs;
|
|
28
|
+
/** Track which tickets we've already fired on_ticket_ready for. */
|
|
29
|
+
private firedReadyTickets;
|
|
30
|
+
/** Track known agent lifecycle states. */
|
|
31
|
+
private lastAgentStates;
|
|
32
|
+
private ghAvailable;
|
|
33
|
+
constructor(options: PollerOptions);
|
|
34
|
+
/**
|
|
35
|
+
* Run one poll cycle. Called by the daemon on each interval.
|
|
36
|
+
*/
|
|
37
|
+
poll(): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Check for tickets in "Ready" (todo) status with no active agent.
|
|
40
|
+
* Fires on_ticket_ready for each.
|
|
41
|
+
*/
|
|
42
|
+
private pollReadyTickets;
|
|
43
|
+
/**
|
|
44
|
+
* Check agent_work table for lifecycle state changes.
|
|
45
|
+
* Fires on_agent_died, on_agent_completed, on_agent_idle as appropriate.
|
|
46
|
+
*/
|
|
47
|
+
private pollAgentLifecycle;
|
|
48
|
+
/**
|
|
49
|
+
* Check GitHub PRs for CI completion and merge conflicts.
|
|
50
|
+
* Fires on_ci_green, on_ci_failed, on_pr_conflicting, on_pr_merged.
|
|
51
|
+
*/
|
|
52
|
+
private pollGitHubPRs;
|
|
53
|
+
/**
|
|
54
|
+
* Extract ticket ID from branch name.
|
|
55
|
+
* Branch format: {ticketId}/{type}/{owner}/{agent}/{description}
|
|
56
|
+
*/
|
|
57
|
+
private extractTicketFromBranch;
|
|
58
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrate External Event Poller
|
|
3
|
+
*
|
|
4
|
+
* Polls external systems (GitHub, database) and fires orchestrate events
|
|
5
|
+
* when conditions are detected. This bridges external state changes into
|
|
6
|
+
* the internal event bus.
|
|
7
|
+
*
|
|
8
|
+
* Supported polls:
|
|
9
|
+
* - GitHub: open PR CI status, merge conflicts
|
|
10
|
+
* - Database: ready tickets, agent lifecycle states
|
|
11
|
+
*/
|
|
12
|
+
import { execSync } from 'node:child_process';
|
|
13
|
+
import { isGHInstalled, isGHAuthenticated, listOpenPRs, getPRChecks, getPRByNumber } from '../pr/index.js';
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// Poller
|
|
16
|
+
// =============================================================================
|
|
17
|
+
export class OrchestratePoller {
|
|
18
|
+
engine;
|
|
19
|
+
db;
|
|
20
|
+
log;
|
|
21
|
+
cwd;
|
|
22
|
+
/** Tracks known PR state to detect transitions. */
|
|
23
|
+
trackedPRs = new Map();
|
|
24
|
+
/** Track which tickets we've already fired on_ticket_ready for. */
|
|
25
|
+
firedReadyTickets = new Set();
|
|
26
|
+
/** Track known agent lifecycle states. */
|
|
27
|
+
lastAgentStates = new Map();
|
|
28
|
+
ghAvailable = null;
|
|
29
|
+
constructor(options) {
|
|
30
|
+
this.engine = options.engine;
|
|
31
|
+
this.db = options.db;
|
|
32
|
+
this.log = options.log;
|
|
33
|
+
this.cwd = options.cwd;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Run one poll cycle. Called by the daemon on each interval.
|
|
37
|
+
*/
|
|
38
|
+
async poll() {
|
|
39
|
+
await this.pollReadyTickets();
|
|
40
|
+
await this.pollAgentLifecycle();
|
|
41
|
+
await this.pollGitHubPRs();
|
|
42
|
+
}
|
|
43
|
+
// ===========================================================================
|
|
44
|
+
// Ticket Polling
|
|
45
|
+
// ===========================================================================
|
|
46
|
+
/**
|
|
47
|
+
* Check for tickets in "Ready" (todo) status with no active agent.
|
|
48
|
+
* Fires on_ticket_ready for each.
|
|
49
|
+
*/
|
|
50
|
+
async pollReadyTickets() {
|
|
51
|
+
try {
|
|
52
|
+
const readyTickets = this.db.prepare(`
|
|
53
|
+
SELECT t.id, t.title
|
|
54
|
+
FROM pmo_tickets t
|
|
55
|
+
JOIN pmo_workflow_statuses ws ON t.status_id = ws.id
|
|
56
|
+
WHERE ws.category = 'todo'
|
|
57
|
+
AND t.assignee IS NULL
|
|
58
|
+
AND t.id NOT IN (
|
|
59
|
+
SELECT ticket_id FROM agent_work WHERE status IN ('starting', 'running')
|
|
60
|
+
)
|
|
61
|
+
LIMIT 10
|
|
62
|
+
`).all();
|
|
63
|
+
for (const ticket of readyTickets) {
|
|
64
|
+
if (this.firedReadyTickets.has(ticket.id))
|
|
65
|
+
continue;
|
|
66
|
+
this.firedReadyTickets.add(ticket.id);
|
|
67
|
+
this.log(`[poll] Detected ready ticket: ${ticket.id}`);
|
|
68
|
+
await this.engine.fireEvent('on_ticket_ready', {
|
|
69
|
+
event: 'on_ticket_ready',
|
|
70
|
+
ticket: ticket.id,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Non-fatal polling error
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// ===========================================================================
|
|
79
|
+
// Agent Lifecycle Polling
|
|
80
|
+
// ===========================================================================
|
|
81
|
+
/**
|
|
82
|
+
* Check agent_work table for lifecycle state changes.
|
|
83
|
+
* Fires on_agent_died, on_agent_completed, on_agent_idle as appropriate.
|
|
84
|
+
*/
|
|
85
|
+
async pollAgentLifecycle() {
|
|
86
|
+
try {
|
|
87
|
+
const agents = this.db.prepare(`
|
|
88
|
+
SELECT id, ticket_id, agent_name, status, lifecycle_state, container_id, last_heartbeat
|
|
89
|
+
FROM agent_work
|
|
90
|
+
WHERE status IN ('starting', 'running', 'error')
|
|
91
|
+
LIMIT 50
|
|
92
|
+
`).all();
|
|
93
|
+
for (const agent of agents) {
|
|
94
|
+
const prevState = this.lastAgentStates.get(agent.id);
|
|
95
|
+
const currentState = agent.lifecycle_state || agent.status;
|
|
96
|
+
if (prevState === currentState)
|
|
97
|
+
continue;
|
|
98
|
+
this.lastAgentStates.set(agent.id, currentState);
|
|
99
|
+
// Skip the first observation (we only fire on transitions)
|
|
100
|
+
if (!prevState)
|
|
101
|
+
continue;
|
|
102
|
+
const ctx = {
|
|
103
|
+
event: '',
|
|
104
|
+
ticket: agent.ticket_id,
|
|
105
|
+
agent: agent.agent_name,
|
|
106
|
+
container: agent.container_id || undefined,
|
|
107
|
+
executionId: agent.id,
|
|
108
|
+
};
|
|
109
|
+
if (currentState === 'died' || (agent.status === 'error' && prevState !== 'error')) {
|
|
110
|
+
this.log(`[poll] Agent died: ${agent.agent_name} (${agent.ticket_id})`);
|
|
111
|
+
await this.engine.fireEvent('on_agent_died', { ...ctx, event: 'on_agent_died' });
|
|
112
|
+
}
|
|
113
|
+
else if (currentState === 'completed') {
|
|
114
|
+
this.log(`[poll] Agent completed: ${agent.agent_name} (${agent.ticket_id})`);
|
|
115
|
+
await this.engine.fireEvent('on_agent_completed', { ...ctx, event: 'on_agent_completed' });
|
|
116
|
+
}
|
|
117
|
+
else if (currentState === 'idle') {
|
|
118
|
+
this.log(`[poll] Agent idle: ${agent.agent_name} (${agent.ticket_id})`);
|
|
119
|
+
await this.engine.fireEvent('on_agent_idle', { ...ctx, event: 'on_agent_idle' });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Detect idle agents by heartbeat timeout (5 minutes)
|
|
123
|
+
try {
|
|
124
|
+
const idleAgents = this.db.prepare(`
|
|
125
|
+
SELECT id, ticket_id, agent_name, container_id
|
|
126
|
+
FROM agent_work
|
|
127
|
+
WHERE status = 'running'
|
|
128
|
+
AND last_heartbeat IS NOT NULL
|
|
129
|
+
AND datetime(last_heartbeat) < datetime('now', '-5 minutes')
|
|
130
|
+
AND (lifecycle_state IS NULL OR lifecycle_state = 'healthy')
|
|
131
|
+
LIMIT 10
|
|
132
|
+
`).all();
|
|
133
|
+
for (const agent of idleAgents) {
|
|
134
|
+
const prevState = this.lastAgentStates.get(agent.id);
|
|
135
|
+
if (prevState === 'idle')
|
|
136
|
+
continue;
|
|
137
|
+
this.lastAgentStates.set(agent.id, 'idle');
|
|
138
|
+
if (prevState) {
|
|
139
|
+
this.log(`[poll] Agent idle (heartbeat timeout): ${agent.agent_name}`);
|
|
140
|
+
await this.engine.fireEvent('on_agent_idle', {
|
|
141
|
+
event: 'on_agent_idle',
|
|
142
|
+
ticket: agent.ticket_id,
|
|
143
|
+
agent: agent.agent_name,
|
|
144
|
+
container: agent.container_id || undefined,
|
|
145
|
+
executionId: agent.id,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// last_heartbeat column might not exist yet
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// Non-fatal polling error
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// ===========================================================================
|
|
159
|
+
// GitHub PR Polling
|
|
160
|
+
// ===========================================================================
|
|
161
|
+
/**
|
|
162
|
+
* Check GitHub PRs for CI completion and merge conflicts.
|
|
163
|
+
* Fires on_ci_green, on_ci_failed, on_pr_conflicting, on_pr_merged.
|
|
164
|
+
*/
|
|
165
|
+
async pollGitHubPRs() {
|
|
166
|
+
if (this.ghAvailable === null) {
|
|
167
|
+
this.ghAvailable = isGHInstalled() && isGHAuthenticated();
|
|
168
|
+
}
|
|
169
|
+
if (!this.ghAvailable)
|
|
170
|
+
return;
|
|
171
|
+
try {
|
|
172
|
+
const openPRs = listOpenPRs(this.cwd);
|
|
173
|
+
for (const pr of openPRs) {
|
|
174
|
+
const tracked = this.trackedPRs.get(pr.number);
|
|
175
|
+
const ticketId = this.extractTicketFromBranch(pr.headBranch);
|
|
176
|
+
// --- CI Status ---
|
|
177
|
+
const checks = getPRChecks(pr.number, this.cwd);
|
|
178
|
+
let ciState = 'unknown';
|
|
179
|
+
if (checks.length > 0) {
|
|
180
|
+
const allDone = checks.every(c => c.status === 'COMPLETED' || c.conclusion);
|
|
181
|
+
if (allDone) {
|
|
182
|
+
const allPassed = checks.every(c => c.conclusion === 'SUCCESS' || c.conclusion === 'NEUTRAL' || c.conclusion === 'SKIPPED');
|
|
183
|
+
ciState = allPassed ? 'success' : 'failure';
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
ciState = 'pending';
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Fire CI events on state transition
|
|
190
|
+
if (tracked && ciState !== tracked.lastCIState && ciState !== 'pending' && ciState !== 'unknown') {
|
|
191
|
+
if (ciState === 'success') {
|
|
192
|
+
this.log(`[poll] CI green: PR #${pr.number}`);
|
|
193
|
+
await this.engine.fireEvent('on_ci_green', {
|
|
194
|
+
event: 'on_ci_green',
|
|
195
|
+
pr: pr.number,
|
|
196
|
+
branch: pr.headBranch,
|
|
197
|
+
ticket: ticketId,
|
|
198
|
+
prUrl: pr.url,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
else if (ciState === 'failure') {
|
|
202
|
+
this.log(`[poll] CI failed: PR #${pr.number}`);
|
|
203
|
+
await this.engine.fireEvent('on_ci_failed', {
|
|
204
|
+
event: 'on_ci_failed',
|
|
205
|
+
pr: pr.number,
|
|
206
|
+
branch: pr.headBranch,
|
|
207
|
+
ticket: ticketId,
|
|
208
|
+
prUrl: pr.url,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// --- Merge Conflicts ---
|
|
213
|
+
let mergeable = 'UNKNOWN';
|
|
214
|
+
try {
|
|
215
|
+
const mergeableResult = execSync(`gh pr view ${pr.number} --json mergeable -q .mergeable`, { cwd: this.cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
216
|
+
if (mergeableResult === 'CONFLICTING') {
|
|
217
|
+
mergeable = 'CONFLICTING';
|
|
218
|
+
}
|
|
219
|
+
else if (mergeableResult === 'MERGEABLE') {
|
|
220
|
+
mergeable = 'MERGEABLE';
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
// gh command failed
|
|
225
|
+
}
|
|
226
|
+
if (tracked &&
|
|
227
|
+
mergeable === 'CONFLICTING' &&
|
|
228
|
+
tracked.lastMergeable !== 'CONFLICTING') {
|
|
229
|
+
this.log(`[poll] PR conflicting: PR #${pr.number}`);
|
|
230
|
+
await this.engine.fireEvent('on_pr_conflicting', {
|
|
231
|
+
event: 'on_pr_conflicting',
|
|
232
|
+
pr: pr.number,
|
|
233
|
+
branch: pr.headBranch,
|
|
234
|
+
ticket: ticketId,
|
|
235
|
+
prUrl: pr.url,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
// Update tracking
|
|
239
|
+
this.trackedPRs.set(pr.number, {
|
|
240
|
+
number: pr.number,
|
|
241
|
+
lastCIState: ciState,
|
|
242
|
+
lastMergeable: mergeable,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
// Detect merged/closed PRs (were tracked but no longer open)
|
|
246
|
+
for (const [prNum, tracked] of this.trackedPRs) {
|
|
247
|
+
if (!openPRs.some(p => p.number === prNum)) {
|
|
248
|
+
// PR was open, now it's not — check if merged
|
|
249
|
+
const pr = getPRByNumber(prNum, this.cwd);
|
|
250
|
+
if (pr?.state === 'MERGED') {
|
|
251
|
+
const ticketId = this.extractTicketFromBranch(pr.headBranch);
|
|
252
|
+
this.log(`[poll] PR merged: PR #${prNum}`);
|
|
253
|
+
await this.engine.fireEvent('on_pr_merged', {
|
|
254
|
+
event: 'on_pr_merged',
|
|
255
|
+
pr: prNum,
|
|
256
|
+
branch: pr.headBranch,
|
|
257
|
+
ticket: ticketId,
|
|
258
|
+
prUrl: pr.url,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
this.trackedPRs.delete(prNum);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
// Non-fatal GitHub polling error
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// ===========================================================================
|
|
270
|
+
// Helpers
|
|
271
|
+
// ===========================================================================
|
|
272
|
+
/**
|
|
273
|
+
* Extract ticket ID from branch name.
|
|
274
|
+
* Branch format: {ticketId}/{type}/{owner}/{agent}/{description}
|
|
275
|
+
*/
|
|
276
|
+
extractTicketFromBranch(branch) {
|
|
277
|
+
// Match patterns like PRLT-123/... or TKT-456/...
|
|
278
|
+
const match = branch.match(/^([A-Z]+-\d+|TKT-\d+)\//);
|
|
279
|
+
return match ? match[1] : undefined;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
//# sourceMappingURL=poller.js.map
|