@cliangdev/conductor 0.1.2

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.
Files changed (130) hide show
  1. package/README.md +141 -0
  2. package/assets/claude/agents/researcher.md +147 -0
  3. package/assets/claude/commands/conductor/implement.md +393 -0
  4. package/assets/claude/commands/conductor/prd.md +291 -0
  5. package/assets/claude/skills/conductor-coder/SKILL.md +214 -0
  6. package/assets/claude/skills/ux-ui-design/SKILL.md +346 -0
  7. package/assets/claude/skills/ux-ui-design/references/design-tokens.md +359 -0
  8. package/dist/commands/config.d.ts +3 -0
  9. package/dist/commands/config.d.ts.map +1 -0
  10. package/dist/commands/config.js +106 -0
  11. package/dist/commands/config.js.map +1 -0
  12. package/dist/commands/dashboard.d.ts +9 -0
  13. package/dist/commands/dashboard.d.ts.map +1 -0
  14. package/dist/commands/dashboard.js +159 -0
  15. package/dist/commands/dashboard.js.map +1 -0
  16. package/dist/commands/doctor.d.ts +6 -0
  17. package/dist/commands/doctor.d.ts.map +1 -0
  18. package/dist/commands/doctor.js +126 -0
  19. package/dist/commands/doctor.js.map +1 -0
  20. package/dist/commands/init.d.ts +19 -0
  21. package/dist/commands/init.d.ts.map +1 -0
  22. package/dist/commands/init.js +276 -0
  23. package/dist/commands/init.js.map +1 -0
  24. package/dist/commands/login.d.ts +3 -0
  25. package/dist/commands/login.d.ts.map +1 -0
  26. package/dist/commands/login.js +161 -0
  27. package/dist/commands/login.js.map +1 -0
  28. package/dist/commands/logout.d.ts +3 -0
  29. package/dist/commands/logout.d.ts.map +1 -0
  30. package/dist/commands/logout.js +14 -0
  31. package/dist/commands/logout.js.map +1 -0
  32. package/dist/commands/mcp.d.ts +3 -0
  33. package/dist/commands/mcp.d.ts.map +1 -0
  34. package/dist/commands/mcp.js +10 -0
  35. package/dist/commands/mcp.js.map +1 -0
  36. package/dist/commands/start.d.ts +7 -0
  37. package/dist/commands/start.d.ts.map +1 -0
  38. package/dist/commands/start.js +74 -0
  39. package/dist/commands/start.js.map +1 -0
  40. package/dist/commands/status.d.ts +9 -0
  41. package/dist/commands/status.d.ts.map +1 -0
  42. package/dist/commands/status.js +126 -0
  43. package/dist/commands/status.js.map +1 -0
  44. package/dist/commands/stop.d.ts +4 -0
  45. package/dist/commands/stop.d.ts.map +1 -0
  46. package/dist/commands/stop.js +30 -0
  47. package/dist/commands/stop.js.map +1 -0
  48. package/dist/daemon/poller.d.ts +18 -0
  49. package/dist/daemon/poller.d.ts.map +1 -0
  50. package/dist/daemon/poller.js +96 -0
  51. package/dist/daemon/poller.js.map +1 -0
  52. package/dist/daemon/run-lifecycle.d.ts +21 -0
  53. package/dist/daemon/run-lifecycle.d.ts.map +1 -0
  54. package/dist/daemon/run-lifecycle.js +53 -0
  55. package/dist/daemon/run-lifecycle.js.map +1 -0
  56. package/dist/daemon/run-queue.d.ts +12 -0
  57. package/dist/daemon/run-queue.d.ts.map +1 -0
  58. package/dist/daemon/run-queue.js +59 -0
  59. package/dist/daemon/run-queue.js.map +1 -0
  60. package/dist/daemon/runner.d.ts +31 -0
  61. package/dist/daemon/runner.d.ts.map +1 -0
  62. package/dist/daemon/runner.js +91 -0
  63. package/dist/daemon/runner.js.map +1 -0
  64. package/dist/daemon/state.d.ts +22 -0
  65. package/dist/daemon/state.d.ts.map +1 -0
  66. package/dist/daemon/state.js +29 -0
  67. package/dist/daemon/state.js.map +1 -0
  68. package/dist/daemon/watcher.d.ts +26 -0
  69. package/dist/daemon/watcher.d.ts.map +1 -0
  70. package/dist/daemon/watcher.js +330 -0
  71. package/dist/daemon/watcher.js.map +1 -0
  72. package/dist/index.d.ts +3 -0
  73. package/dist/index.d.ts.map +1 -0
  74. package/dist/index.js +44 -0
  75. package/dist/index.js.map +1 -0
  76. package/dist/lib/api.d.ts +8 -0
  77. package/dist/lib/api.d.ts.map +1 -0
  78. package/dist/lib/api.js +55 -0
  79. package/dist/lib/api.js.map +1 -0
  80. package/dist/lib/config.d.ts +16 -0
  81. package/dist/lib/config.d.ts.map +1 -0
  82. package/dist/lib/config.js +48 -0
  83. package/dist/lib/config.js.map +1 -0
  84. package/dist/lib/next-steps.d.ts +2 -0
  85. package/dist/lib/next-steps.d.ts.map +1 -0
  86. package/dist/lib/next-steps.js +11 -0
  87. package/dist/lib/next-steps.js.map +1 -0
  88. package/dist/lib/oauth-server.d.ts +10 -0
  89. package/dist/lib/oauth-server.d.ts.map +1 -0
  90. package/dist/lib/oauth-server.js +67 -0
  91. package/dist/lib/oauth-server.js.map +1 -0
  92. package/dist/lib/plugin-assets.d.ts +19 -0
  93. package/dist/lib/plugin-assets.d.ts.map +1 -0
  94. package/dist/lib/plugin-assets.js +121 -0
  95. package/dist/lib/plugin-assets.js.map +1 -0
  96. package/dist/mcp/api.d.ts +7 -0
  97. package/dist/mcp/api.d.ts.map +1 -0
  98. package/dist/mcp/api.js +36 -0
  99. package/dist/mcp/api.js.map +1 -0
  100. package/dist/mcp/config.d.ts +11 -0
  101. package/dist/mcp/config.d.ts.map +1 -0
  102. package/dist/mcp/config.js +31 -0
  103. package/dist/mcp/config.js.map +1 -0
  104. package/dist/mcp/files.d.ts +9 -0
  105. package/dist/mcp/files.d.ts.map +1 -0
  106. package/dist/mcp/files.js +43 -0
  107. package/dist/mcp/files.js.map +1 -0
  108. package/dist/mcp/index.d.ts +3 -0
  109. package/dist/mcp/index.d.ts.map +1 -0
  110. package/dist/mcp/index.js +234 -0
  111. package/dist/mcp/index.js.map +1 -0
  112. package/dist/mcp/queue.d.ts +8 -0
  113. package/dist/mcp/queue.d.ts.map +1 -0
  114. package/dist/mcp/queue.js +19 -0
  115. package/dist/mcp/queue.js.map +1 -0
  116. package/dist/mcp/tools/comments.d.ts +6 -0
  117. package/dist/mcp/tools/comments.d.ts.map +1 -0
  118. package/dist/mcp/tools/comments.js +21 -0
  119. package/dist/mcp/tools/comments.js.map +1 -0
  120. package/dist/mcp/tools/documents.d.ts +11 -0
  121. package/dist/mcp/tools/documents.d.ts.map +1 -0
  122. package/dist/mcp/tools/documents.js +44 -0
  123. package/dist/mcp/tools/documents.js.map +1 -0
  124. package/dist/mcp/tools/issues.d.ts +23 -0
  125. package/dist/mcp/tools/issues.d.ts.map +1 -0
  126. package/dist/mcp/tools/issues.js +141 -0
  127. package/dist/mcp/tools/issues.js.map +1 -0
  128. package/package.json +40 -0
  129. package/scripts/postinstall.js +81 -0
  130. package/scripts/preuninstall.js +175 -0
@@ -0,0 +1,126 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+ export function formatRelativeTime(dateStr) {
5
+ const diffMs = Date.now() - new Date(dateStr).getTime();
6
+ const diffSec = Math.floor(diffMs / 1000);
7
+ if (diffSec < 60)
8
+ return `${diffSec}s ago`;
9
+ if (diffSec < 3600)
10
+ return `${Math.floor(diffSec / 60)}m ago`;
11
+ return `${Math.floor(diffSec / 3600)}h ago`;
12
+ }
13
+ const CONDUCTOR_DIR = path.join(os.homedir(), '.conductor');
14
+ const DAEMON_PID_PATH = path.join(CONDUCTOR_DIR, 'daemon.pid');
15
+ const SYNC_QUEUE_PATH = path.join(CONDUCTOR_DIR, 'sync-queue.json');
16
+ export const DAEMON_LOG_PATH = path.join(CONDUCTOR_DIR, 'daemon.log');
17
+ export function isDaemonRunning() {
18
+ try {
19
+ const raw = fs.readFileSync(DAEMON_PID_PATH, 'utf8').trim();
20
+ const pid = parseInt(raw, 10);
21
+ if (isNaN(pid)) {
22
+ fs.unlinkSync(DAEMON_PID_PATH);
23
+ return false;
24
+ }
25
+ process.kill(pid, 0);
26
+ return true;
27
+ }
28
+ catch (err) {
29
+ // Process not found — clean up stale PID file
30
+ if (err.code === 'ESRCH') {
31
+ try {
32
+ fs.unlinkSync(DAEMON_PID_PATH);
33
+ }
34
+ catch { /* already gone */ }
35
+ }
36
+ return false;
37
+ }
38
+ }
39
+ export function getDaemonPid() {
40
+ try {
41
+ const raw = fs.readFileSync(DAEMON_PID_PATH, 'utf8').trim();
42
+ const pid = parseInt(raw, 10);
43
+ if (isNaN(pid))
44
+ return null;
45
+ process.kill(pid, 0);
46
+ return pid;
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ export function getQueueCount() {
53
+ try {
54
+ const raw = fs.readFileSync(SYNC_QUEUE_PATH, 'utf8');
55
+ const parsed = JSON.parse(raw);
56
+ if (Array.isArray(parsed))
57
+ return parsed.length;
58
+ return 0;
59
+ }
60
+ catch {
61
+ return 0;
62
+ }
63
+ }
64
+ export function getLogFileSizeMb() {
65
+ try {
66
+ const stat = fs.statSync(DAEMON_LOG_PATH);
67
+ return Math.round((stat.size / (1024 * 1024)) * 10) / 10;
68
+ }
69
+ catch {
70
+ return null;
71
+ }
72
+ }
73
+ export function registerStatus(program) {
74
+ program
75
+ .command('status')
76
+ .description('Show daemon and sync queue status')
77
+ .option('--json', 'Output status as JSON')
78
+ .addHelpText('after', `
79
+ Examples:
80
+ conductor status
81
+ conductor status --json`)
82
+ .action((options) => {
83
+ const running = isDaemonRunning();
84
+ const pid = getDaemonPid();
85
+ const queueSize = getQueueCount();
86
+ const logSizeMb = getLogFileSizeMb();
87
+ if (options.json) {
88
+ let uptime = null;
89
+ if (running && pid !== null) {
90
+ // Uptime is best-effort; daemon.log is not the source — use state file if needed
91
+ uptime = null;
92
+ }
93
+ const output = {
94
+ daemon: {
95
+ running,
96
+ pid: running ? pid : null,
97
+ uptime,
98
+ },
99
+ syncQueue: {
100
+ size: queueSize,
101
+ },
102
+ log: {
103
+ path: DAEMON_LOG_PATH,
104
+ sizeMb: logSizeMb,
105
+ },
106
+ };
107
+ process.stdout.write(JSON.stringify(output, null, 2) + '\n');
108
+ process.exit(0);
109
+ return;
110
+ }
111
+ if (running) {
112
+ console.log(`Daemon: running (PID ${pid})`);
113
+ }
114
+ else {
115
+ console.log('Daemon: not running');
116
+ }
117
+ console.log(`Sync queue: ${queueSize} item(s)`);
118
+ if (logSizeMb !== null) {
119
+ console.log(`Log: ${DAEMON_LOG_PATH} (${logSizeMb.toFixed(1)} MB)`);
120
+ }
121
+ else {
122
+ console.log(`Log: ${DAEMON_LOG_PATH} (not found)`);
123
+ }
124
+ });
125
+ }
126
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AAGxB,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAA;IACvD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACzC,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,OAAO,OAAO,CAAA;IAC1C,IAAI,OAAO,GAAG,IAAI;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,CAAA;IAC7D,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;AAC7C,CAAC;AAED,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAA;AAC3D,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;AAC9D,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAA;AACnE,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;AAErE,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;QAC3D,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;QAC7B,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACf,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,CAAA;YAC9B,OAAO,KAAK,CAAA;QACd,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;QACpB,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,8CAA8C;QAC9C,IAAK,GAA6B,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACpD,IAAI,CAAC;gBAAC,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,CAAA;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;QACrE,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;QAC3D,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;QAC7B,IAAI,KAAK,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA;QAC3B,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;QACpB,OAAO,GAAG,CAAA;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAA;QACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAA;QACzC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC,MAAM,CAAA;QAC/C,OAAO,CAAC,CAAA;IACV,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAA;IACV,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAA;QACzC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAA;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAgB;IAC7C,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,mCAAmC,CAAC;SAChD,MAAM,CAAC,QAAQ,EAAE,uBAAuB,CAAC;SACzC,WAAW,CAAC,OAAO,EAAE;;;0BAGA,CAAC;SACtB,MAAM,CAAC,CAAC,OAA2B,EAAE,EAAE;QACtC,MAAM,OAAO,GAAG,eAAe,EAAE,CAAA;QACjC,MAAM,GAAG,GAAG,YAAY,EAAE,CAAA;QAC1B,MAAM,SAAS,GAAG,aAAa,EAAE,CAAA;QACjC,MAAM,SAAS,GAAG,gBAAgB,EAAE,CAAA;QAEpC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,IAAI,MAAM,GAAkB,IAAI,CAAA;YAChC,IAAI,OAAO,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;gBAC5B,iFAAiF;gBACjF,MAAM,GAAG,IAAI,CAAA;YACf,CAAC;YACD,MAAM,MAAM,GAAG;gBACb,MAAM,EAAE;oBACN,OAAO;oBACP,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI;oBACzB,MAAM;iBACP;gBACD,SAAS,EAAE;oBACT,IAAI,EAAE,SAAS;iBAChB;gBACD,GAAG,EAAE;oBACH,IAAI,EAAE,eAAe;oBACrB,MAAM,EAAE,SAAS;iBAClB;aACF,CAAA;YACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;YAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACf,OAAM;QACR,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,GAAG,CAAC,wBAAwB,GAAG,GAAG,CAAC,CAAA;QAC7C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA;QACpC,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,eAAe,SAAS,UAAU,CAAC,CAAA;QAC/C,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,QAAQ,eAAe,KAAK,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QACrE,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,QAAQ,eAAe,cAAc,CAAC,CAAA;QACpD,CAAC;IACH,CAAC,CAAC,CAAA;AACN,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Command } from 'commander';
2
+ export declare const PID_FILE: string;
3
+ export declare function registerStop(program: Command): void;
4
+ //# sourceMappingURL=stop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.d.ts","sourceRoot":"","sources":["../../src/commands/stop.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAGnC,eAAO,MAAM,QAAQ,QAAyC,CAAA;AAE9D,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwBnD"}
@@ -0,0 +1,30 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+ const CONDUCTOR_DIR = path.join(os.homedir(), '.conductor');
5
+ export const PID_FILE = path.join(CONDUCTOR_DIR, 'daemon.pid');
6
+ export function registerStop(program) {
7
+ program
8
+ .command('stop')
9
+ .description('Stop the file watcher daemon')
10
+ .addHelpText('after', `
11
+ Examples:
12
+ conductor stop`)
13
+ .action(() => {
14
+ if (!fs.existsSync(PID_FILE)) {
15
+ console.log('Daemon is not running');
16
+ return;
17
+ }
18
+ const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim(), 10);
19
+ try {
20
+ process.kill(pid, 'SIGTERM');
21
+ fs.unlinkSync(PID_FILE);
22
+ console.log(`Daemon stopped (PID ${pid})`);
23
+ }
24
+ catch {
25
+ fs.unlinkSync(PID_FILE);
26
+ console.log('Daemon was not running (removed stale PID file)');
27
+ }
28
+ });
29
+ }
30
+ //# sourceMappingURL=stop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.js","sourceRoot":"","sources":["../../src/commands/stop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AAGxB,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAA;AAC3D,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;AAE9D,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC3C,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,8BAA8B,CAAC;SAC3C,WAAW,CAAC,OAAO,EAAE;;iBAET,CAAC;SACb,MAAM,CAAC,GAAG,EAAE;QACX,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;YACpC,OAAM;QACR,CAAC;QAED,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAA;QAElE,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAA;YAC5B,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;YACvB,OAAO,CAAC,GAAG,CAAC,uBAAuB,GAAG,GAAG,CAAC,CAAA;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;YACvB,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAA;QAChE,CAAC;IACH,CAAC,CAAC,CAAA;AACN,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { Config } from '../lib/config.js';
2
+ export type PollMode = 'idle' | 'active';
3
+ export interface PollConfig {
4
+ idleIntervalMs: number;
5
+ activeIntervalMs: number;
6
+ activeWindowMs: number;
7
+ maxConsecutiveErrors: number;
8
+ }
9
+ export type EventHandler = (events: DaemonEvent[]) => Promise<void>;
10
+ export interface DaemonEvent {
11
+ eventId: string;
12
+ type: string;
13
+ payload: Record<string, unknown>;
14
+ }
15
+ export declare function startPoller(getConfig: () => Config, onEvent: EventHandler, options?: Partial<PollConfig>): {
16
+ stop: () => void;
17
+ };
18
+ //# sourceMappingURL=poller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poller.d.ts","sourceRoot":"","sources":["../../src/daemon/poller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAGzC,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAA;AAExC,MAAM,WAAW,UAAU;IACzB,cAAc,EAAE,MAAM,CAAA;IACtB,gBAAgB,EAAE,MAAM,CAAA;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,oBAAoB,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAEnE,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC;AASD,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,MAAM,EACvB,OAAO,EAAE,YAAY,EACrB,OAAO,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAC5B;IAAE,IAAI,EAAE,MAAM,IAAI,CAAA;CAAE,CA+FtB"}
@@ -0,0 +1,96 @@
1
+ import { writeDaemonState, readDaemonState } from './state.js';
2
+ const DEFAULT_CONFIG = {
3
+ idleIntervalMs: 60000,
4
+ activeIntervalMs: 5000,
5
+ activeWindowMs: 300000,
6
+ maxConsecutiveErrors: 3,
7
+ };
8
+ export function startPoller(getConfig, onEvent, options) {
9
+ const pollConfig = { ...DEFAULT_CONFIG, ...options };
10
+ let stopped = false;
11
+ let timer = null;
12
+ let mode = 'idle';
13
+ let lastEventAt = null;
14
+ let consecutiveErrors = 0;
15
+ let eventsThisSession = 0;
16
+ async function poll() {
17
+ if (stopped)
18
+ return;
19
+ const config = getConfig();
20
+ const url = `${config.apiUrl}/api/v1/projects/${config.projectId}/daemon/events`;
21
+ try {
22
+ const response = await fetch(url, {
23
+ headers: {
24
+ Authorization: `Bearer ${config.apiKey}`,
25
+ 'Content-Type': 'application/json',
26
+ },
27
+ });
28
+ if (!response.ok) {
29
+ throw new Error(`Poll failed with status ${response.status}`);
30
+ }
31
+ const data = (await response.json());
32
+ const events = data.events ?? [];
33
+ consecutiveErrors = 0;
34
+ if (events.length > 0) {
35
+ mode = 'active';
36
+ lastEventAt = Date.now();
37
+ eventsThisSession += events.length;
38
+ await onEvent(events);
39
+ }
40
+ else {
41
+ // No events — determine mode based on activeWindowMs
42
+ if (lastEventAt !== null && Date.now() - lastEventAt < pollConfig.activeWindowMs) {
43
+ mode = 'active';
44
+ }
45
+ else {
46
+ mode = 'idle';
47
+ }
48
+ }
49
+ }
50
+ catch (err) {
51
+ consecutiveErrors++;
52
+ mode = 'idle';
53
+ if (consecutiveErrors >= pollConfig.maxConsecutiveErrors) {
54
+ console.warn('Poll endpoint returning errors, continuing at idle interval');
55
+ }
56
+ else {
57
+ console.error(`Poll error: ${err.message}`);
58
+ }
59
+ }
60
+ // Update daemon-state.json after every poll
61
+ const existingState = readDaemonState();
62
+ writeDaemonState({
63
+ pid: existingState?.pid ?? process.pid,
64
+ startedAt: existingState?.startedAt ?? new Date().toISOString(),
65
+ pollMode: mode,
66
+ lastPollAt: new Date().toISOString(),
67
+ consecutiveErrors,
68
+ eventsThisSession,
69
+ activeRuns: existingState?.activeRuns ?? [],
70
+ syncQueueSize: existingState?.syncQueueSize ?? 0,
71
+ });
72
+ if (!stopped) {
73
+ scheduleNext();
74
+ }
75
+ }
76
+ function scheduleNext() {
77
+ if (stopped)
78
+ return;
79
+ const intervalMs = mode === 'active' ? pollConfig.activeIntervalMs : pollConfig.idleIntervalMs;
80
+ timer = setTimeout(() => {
81
+ poll().catch(console.error);
82
+ }, intervalMs);
83
+ }
84
+ // Start the polling loop
85
+ scheduleNext();
86
+ return {
87
+ stop() {
88
+ stopped = true;
89
+ if (timer !== null) {
90
+ clearTimeout(timer);
91
+ timer = null;
92
+ }
93
+ },
94
+ };
95
+ }
96
+ //# sourceMappingURL=poller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poller.js","sourceRoot":"","sources":["../../src/daemon/poller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { Config } from '../lib/config.js';
2
+ import type { WorkflowTriggerEvent } from './runner.js';
3
+ /**
4
+ * PATCH /api/v1/workflow-runs/{runId}
5
+ * Updates the status of a WorkflowRun. Errors are swallowed and logged.
6
+ */
7
+ export declare function updateRunStatus(runId: string, status: 'SUCCESS' | 'FAILED', config: Config): Promise<void>;
8
+ /**
9
+ * POST /api/v1/projects/{projectId}/daemon/events/ack
10
+ * Marks a daemon event as processed. Errors are swallowed and logged.
11
+ */
12
+ export declare function acknowledgeEvent(projectId: string, eventId: string, config: Config): Promise<void>;
13
+ /**
14
+ * Finalises a workflow run after Docker container exit:
15
+ * 1. PATCH the WorkflowRun status to SUCCESS or FAILED
16
+ * 2. POST to the ack endpoint to mark the event as processed
17
+ *
18
+ * Used by the run queue orchestrator.
19
+ */
20
+ export declare function completeRun(event: WorkflowTriggerEvent, status: 'SUCCESS' | 'FAILED', config: Config): Promise<void>;
21
+ //# sourceMappingURL=run-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-lifecycle.d.ts","sourceRoot":"","sources":["../../src/daemon/run-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAIvD;;;GAGG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,SAAS,GAAG,QAAQ,EAC5B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAaf;AAID;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAaf;AAID;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,oBAAoB,EAC3B,MAAM,EAAE,SAAS,GAAG,QAAQ,EAC5B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAGf"}
@@ -0,0 +1,53 @@
1
+ // ─── updateRunStatus ──────────────────────────────────────────────────────────
2
+ /**
3
+ * PATCH /api/v1/workflow-runs/{runId}
4
+ * Updates the status of a WorkflowRun. Errors are swallowed and logged.
5
+ */
6
+ export async function updateRunStatus(runId, status, config) {
7
+ try {
8
+ await fetch(`${config.apiUrl}/api/v1/workflow-runs/${runId}`, {
9
+ method: 'PATCH',
10
+ headers: {
11
+ 'Content-Type': 'application/json',
12
+ Authorization: `Bearer ${config.apiKey}`,
13
+ },
14
+ body: JSON.stringify({ status }),
15
+ });
16
+ }
17
+ catch (err) {
18
+ console.error('[run-lifecycle] Failed to update run status:', err);
19
+ }
20
+ }
21
+ // ─── acknowledgeEvent ─────────────────────────────────────────────────────────
22
+ /**
23
+ * POST /api/v1/projects/{projectId}/daemon/events/ack
24
+ * Marks a daemon event as processed. Errors are swallowed and logged.
25
+ */
26
+ export async function acknowledgeEvent(projectId, eventId, config) {
27
+ try {
28
+ await fetch(`${config.apiUrl}/api/v1/projects/${projectId}/daemon/events/ack`, {
29
+ method: 'POST',
30
+ headers: {
31
+ 'Content-Type': 'application/json',
32
+ Authorization: `Bearer ${config.apiKey}`,
33
+ },
34
+ body: JSON.stringify({ eventIds: [eventId] }),
35
+ });
36
+ }
37
+ catch (err) {
38
+ console.error('[run-lifecycle] Failed to acknowledge event:', err);
39
+ }
40
+ }
41
+ // ─── completeRun ──────────────────────────────────────────────────────────────
42
+ /**
43
+ * Finalises a workflow run after Docker container exit:
44
+ * 1. PATCH the WorkflowRun status to SUCCESS or FAILED
45
+ * 2. POST to the ack endpoint to mark the event as processed
46
+ *
47
+ * Used by the run queue orchestrator.
48
+ */
49
+ export async function completeRun(event, status, config) {
50
+ await updateRunStatus(event.workflowRunId, status, config);
51
+ await acknowledgeEvent(event.projectId, event.eventId, config);
52
+ }
53
+ //# sourceMappingURL=run-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-lifecycle.js","sourceRoot":"","sources":["../../src/daemon/run-lifecycle.ts"],"names":[],"mappings":"AAGA,iFAAiF;AAEjF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAa,EACb,MAA4B,EAC5B,MAAc;IAEd,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,yBAAyB,KAAK,EAAE,EAAE;YAC5D,MAAM,EAAE,OAAO;YACf,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;aACzC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;SACjC,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,8CAA8C,EAAE,GAAG,CAAC,CAAA;IACpE,CAAC;AACH,CAAC;AAED,iFAAiF;AAEjF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,SAAiB,EACjB,OAAe,EACf,MAAc;IAEd,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,oBAAoB,SAAS,oBAAoB,EAAE;YAC7E,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;aACzC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;SAC9C,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,8CAA8C,EAAE,GAAG,CAAC,CAAA;IACpE,CAAC;AACH,CAAC;AAED,iFAAiF;AAEjF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAA2B,EAC3B,MAA4B,EAC5B,MAAc;IAEd,MAAM,eAAe,CAAC,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1D,MAAM,gBAAgB,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;AAChE,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { Config } from '../lib/config.js';
2
+ import { type WorkflowTriggerEvent } from './runner.js';
3
+ export declare class RunQueue {
4
+ private maxConcurrent;
5
+ private activeCount;
6
+ private queue;
7
+ constructor(maxConcurrent?: number);
8
+ enqueue(event: WorkflowTriggerEvent, getConfig: () => Config): void;
9
+ private processNext;
10
+ private runItem;
11
+ }
12
+ //# sourceMappingURL=run-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-queue.d.ts","sourceRoot":"","sources":["../../src/daemon/run-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAU,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAI/D,qBAAa,QAAQ;IACnB,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,WAAW,CAAY;IAC/B,OAAO,CAAC,KAAK,CAAsE;gBAEvE,aAAa,GAAE,MAAU;IAIrC,OAAO,CAAC,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,MAAM,GAAG,IAAI;IAKnE,OAAO,CAAC,WAAW;YAUL,OAAO;CAqCtB"}
@@ -0,0 +1,59 @@
1
+ import { runJob } from './runner.js';
2
+ import { completeRun } from './run-lifecycle.js';
3
+ import { readDaemonState, writeDaemonState } from './state.js';
4
+ export class RunQueue {
5
+ constructor(maxConcurrent = 1) {
6
+ this.activeCount = 0;
7
+ this.queue = [];
8
+ this.maxConcurrent = maxConcurrent;
9
+ }
10
+ enqueue(event, getConfig) {
11
+ this.queue.push({ event, getConfig });
12
+ this.processNext();
13
+ }
14
+ processNext() {
15
+ if (this.activeCount >= this.maxConcurrent || this.queue.length === 0)
16
+ return;
17
+ const item = this.queue.shift();
18
+ this.activeCount++;
19
+ this.runItem(item).finally(() => {
20
+ this.activeCount--;
21
+ this.processNext();
22
+ });
23
+ }
24
+ async runItem(item) {
25
+ const { event, getConfig } = item;
26
+ const config = getConfig();
27
+ const selfHostedJobs = event.jobs.filter((j) => j.runsOn === 'self-hosted');
28
+ for (const job of selfHostedJobs) {
29
+ // Add to activeRuns in daemon-state.json
30
+ const stateBefore = readDaemonState();
31
+ if (stateBefore) {
32
+ writeDaemonState({
33
+ ...stateBefore,
34
+ activeRuns: [
35
+ ...stateBefore.activeRuns,
36
+ {
37
+ runId: event.workflowRunId,
38
+ issueTitle: event.issueTitle,
39
+ jobName: job.id,
40
+ status: 'running',
41
+ startedAt: new Date().toISOString(),
42
+ },
43
+ ],
44
+ });
45
+ }
46
+ const status = await runJob(event, job, config);
47
+ await completeRun(event, status, config);
48
+ // Remove from activeRuns in daemon-state.json
49
+ const stateAfter = readDaemonState();
50
+ if (stateAfter) {
51
+ writeDaemonState({
52
+ ...stateAfter,
53
+ activeRuns: stateAfter.activeRuns.filter((r) => r.runId !== event.workflowRunId),
54
+ });
55
+ }
56
+ }
57
+ }
58
+ }
59
+ //# sourceMappingURL=run-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-queue.js","sourceRoot":"","sources":["../../src/daemon/run-queue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAA6B,MAAM,aAAa,CAAA;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAE9D,MAAM,OAAO,QAAQ;IAKnB,YAAY,gBAAwB,CAAC;QAH7B,gBAAW,GAAW,CAAC,CAAA;QACvB,UAAK,GAAoE,EAAE,CAAA;QAGjF,IAAI,CAAC,aAAa,GAAG,aAAa,CAAA;IACpC,CAAC;IAED,OAAO,CAAC,KAA2B,EAAE,SAAuB;QAC1D,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;QACrC,IAAI,CAAC,WAAW,EAAE,CAAA;IACpB,CAAC;IAEO,WAAW;QACjB,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAM;QAC7E,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAG,CAAA;QAChC,IAAI,CAAC,WAAW,EAAE,CAAA;QAClB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE;YAC9B,IAAI,CAAC,WAAW,EAAE,CAAA;YAClB,IAAI,CAAC,WAAW,EAAE,CAAA;QACpB,CAAC,CAAC,CAAA;IACJ,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,IAA8D;QAClF,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,IAAI,CAAA;QACjC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;QAC1B,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAA;QAE3E,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;YACjC,yCAAyC;YACzC,MAAM,WAAW,GAAG,eAAe,EAAE,CAAA;YACrC,IAAI,WAAW,EAAE,CAAC;gBAChB,gBAAgB,CAAC;oBACf,GAAG,WAAW;oBACd,UAAU,EAAE;wBACV,GAAG,WAAW,CAAC,UAAU;wBACzB;4BACE,KAAK,EAAE,KAAK,CAAC,aAAa;4BAC1B,UAAU,EAAE,KAAK,CAAC,UAAU;4BAC5B,OAAO,EAAE,GAAG,CAAC,EAAE;4BACf,MAAM,EAAE,SAAS;4BACjB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;yBACpC;qBACF;iBACF,CAAC,CAAA;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;YAC/C,MAAM,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;YAExC,8CAA8C;YAC9C,MAAM,UAAU,GAAG,eAAe,EAAE,CAAA;YACpC,IAAI,UAAU,EAAE,CAAC;gBACf,gBAAgB,CAAC;oBACf,GAAG,UAAU;oBACb,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,aAAa,CAAC;iBACjF,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,31 @@
1
+ import type { Config } from '../lib/config.js';
2
+ export interface JobConfig {
3
+ id: string;
4
+ runsOn: string;
5
+ container?: {
6
+ image: string;
7
+ };
8
+ steps?: Array<{
9
+ name: string;
10
+ run?: string;
11
+ }>;
12
+ env?: Record<string, string>;
13
+ }
14
+ export interface WorkflowTriggerEvent {
15
+ eventId: string;
16
+ type: string;
17
+ workflowRunId: string;
18
+ workflowId: string;
19
+ workflowName: string;
20
+ issueId: string;
21
+ issueTitle: string;
22
+ projectId: string;
23
+ trigger: {
24
+ type: string;
25
+ fromStatus?: string;
26
+ toStatus?: string;
27
+ };
28
+ jobs: JobConfig[];
29
+ }
30
+ export declare function runJob(event: WorkflowTriggerEvent, job: JobConfig, config: Config): Promise<'SUCCESS' | 'FAILED'>;
31
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/daemon/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAI9C,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7B,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAA;QACZ,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB,CAAA;IACD,IAAI,EAAE,SAAS,EAAE,CAAA;CAClB;AA4DD,wBAAsB,MAAM,CAC1B,KAAK,EAAE,oBAAoB,EAC3B,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,SAAS,GAAG,QAAQ,CAAC,CAqD/B"}
@@ -0,0 +1,91 @@
1
+ import { spawn } from 'child_process';
2
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
3
+ async function streamLogChunk(runId, chunk, config) {
4
+ try {
5
+ await fetch(`${config.apiUrl}/internal/workflow-runs/${runId}/log-chunk`, {
6
+ method: 'POST',
7
+ headers: {
8
+ 'Content-Type': 'application/json',
9
+ Authorization: `Bearer ${config.apiKey}`,
10
+ },
11
+ body: JSON.stringify({ chunk }),
12
+ });
13
+ }
14
+ catch (err) {
15
+ console.error('[runner] Failed to stream log chunk:', err);
16
+ }
17
+ }
18
+ /**
19
+ * Spawns a command and returns its exit code. Streams stdout/stderr lines to
20
+ * the log-chunk endpoint if a runId and config are provided.
21
+ */
22
+ function spawnAndWait(cmd, args, opts) {
23
+ return new Promise((resolve) => {
24
+ const proc = spawn(cmd, args);
25
+ proc.stdout?.on('data', (data) => {
26
+ const line = data.toString();
27
+ if (opts?.runId && opts?.config) {
28
+ void streamLogChunk(opts.runId, line, opts.config);
29
+ }
30
+ });
31
+ proc.stderr?.on('data', (data) => {
32
+ const line = data.toString();
33
+ if (opts?.runId && opts?.config) {
34
+ void streamLogChunk(opts.runId, line, opts.config);
35
+ }
36
+ });
37
+ proc.on('error', () => {
38
+ resolve(1);
39
+ });
40
+ proc.on('close', (code) => {
41
+ resolve(code ?? 1);
42
+ });
43
+ });
44
+ }
45
+ // ─── Main ────────────────────────────────────────────────────────────────────
46
+ export async function runJob(event, job, config) {
47
+ const { workflowRunId } = event;
48
+ // Step 1: Check Docker is available
49
+ const dockerInfoCode = await spawnAndWait('docker', ['info']);
50
+ if (dockerInfoCode !== 0) {
51
+ await streamLogChunk(workflowRunId, 'Docker not available on this host', config);
52
+ return 'FAILED';
53
+ }
54
+ // Step 2: Validate container image
55
+ if (!job.container?.image) {
56
+ await streamLogChunk(workflowRunId, 'No container image specified for this job', config);
57
+ return 'FAILED';
58
+ }
59
+ const image = job.container.image;
60
+ // Step 3: Pull image
61
+ const pullCode = await spawnAndWait('docker', ['pull', image]);
62
+ if (pullCode !== 0) {
63
+ await streamLogChunk(workflowRunId, `Failed to pull image: ${image}`, config);
64
+ return 'FAILED';
65
+ }
66
+ // Step 4: Build env args
67
+ const conductorEnv = {
68
+ CONDUCTOR_ISSUE_ID: event.issueId,
69
+ CONDUCTOR_PROJECT_ID: event.projectId,
70
+ CONDUCTOR_WORKFLOW_RUN_ID: event.workflowRunId,
71
+ CONDUCTOR_API_KEY: config.apiKey,
72
+ CONDUCTOR_API_URL: config.apiUrl,
73
+ };
74
+ const allEnv = {
75
+ ...conductorEnv,
76
+ ...(job.env ?? {}),
77
+ };
78
+ const envArgs = [];
79
+ for (const [key, value] of Object.entries(allEnv)) {
80
+ envArgs.push('-e', `${key}=${value}`);
81
+ }
82
+ // Step 5: Run container
83
+ const runArgs = ['run', '--rm', ...envArgs, image];
84
+ const runCode = await spawnAndWait('docker', runArgs, {
85
+ runId: workflowRunId,
86
+ config,
87
+ });
88
+ // Step 6: Return result
89
+ return runCode === 0 ? 'SUCCESS' : 'FAILED';
90
+ }
91
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/daemon/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export declare const STATE_FILE_PATH: string;
2
+ export interface ActiveRun {
3
+ runId: string;
4
+ issueTitle: string;
5
+ jobName: string;
6
+ status: 'running' | 'completed' | 'failed';
7
+ startedAt: string;
8
+ }
9
+ export interface DaemonState {
10
+ pid: number;
11
+ startedAt: string;
12
+ pollMode: 'idle' | 'active';
13
+ lastPollAt: string | null;
14
+ consecutiveErrors: number;
15
+ eventsThisSession: number;
16
+ activeRuns: ActiveRun[];
17
+ syncQueueSize: number;
18
+ }
19
+ export declare function writeDaemonState(state: DaemonState): void;
20
+ export declare function readDaemonState(): DaemonState | null;
21
+ export declare function deleteDaemonState(): void;
22
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/daemon/state.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,eAAe,QAAgD,CAAA;AAE5E,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAA;IAC1C,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAA;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,iBAAiB,EAAE,MAAM,CAAA;IACzB,iBAAiB,EAAE,MAAM,CAAA;IACzB,UAAU,EAAE,SAAS,EAAE,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAGzD;AAED,wBAAgB,eAAe,IAAI,WAAW,GAAG,IAAI,CAOpD;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAQxC"}