@mndrk/agx 1.4.18 → 1.4.19

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 (82) hide show
  1. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/BUILD_ID +1 -1
  2. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/app-build-manifest.json +51 -51
  3. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/app-path-routes-manifest.json +10 -10
  4. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/build-manifest.json +2 -2
  5. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/prerender-manifest.json +22 -22
  6. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  7. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/_not-found.html +1 -1
  8. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/_not-found.rsc +2 -2
  9. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/audit/route_client-reference-manifest.js +1 -1
  10. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/auth/[...nextauth]/route_client-reference-manifest.js +1 -1
  11. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/auth/daemon-secret/route_client-reference-manifest.js +1 -1
  12. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/auth/device/code/route_client-reference-manifest.js +1 -1
  13. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/auth/device/token/route_client-reference-manifest.js +1 -1
  14. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/auth/refresh/route_client-reference-manifest.js +1 -1
  15. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
  16. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/learnings/route_client-reference-manifest.js +1 -1
  17. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/logs/stream/route_client-reference-manifest.js +1 -1
  18. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/orchestrator/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  19. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/orchestrator/tasks/[taskId]/signal/route_client-reference-manifest.js +1 -1
  20. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/orchestrator/tasks/[taskId]/start/route_client-reference-manifest.js +1 -1
  21. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/orchestrator/tasks/[taskId]/status/route_client-reference-manifest.js +1 -1
  22. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/projects/[id]/route_client-reference-manifest.js +1 -1
  23. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  24. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/providers/route_client-reference-manifest.js +1 -1
  25. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/queue/complete/route_client-reference-manifest.js +1 -1
  26. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/queue/route_client-reference-manifest.js +1 -1
  27. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/stage-prompts/route_client-reference-manifest.js +1 -1
  28. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/[id]/comments/[commentId]/route_client-reference-manifest.js +1 -1
  29. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/[id]/comments/route_client-reference-manifest.js +1 -1
  30. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/[id]/heartbeat/route_client-reference-manifest.js +1 -1
  31. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/[id]/history/route_client-reference-manifest.js +1 -1
  32. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/[id]/logs/route_client-reference-manifest.js +1 -1
  33. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/[id]/route_client-reference-manifest.js +1 -1
  34. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/route_client-reference-manifest.js +1 -1
  35. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/stream/route_client-reference-manifest.js +1 -1
  36. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/user-settings/route_client-reference-manifest.js +1 -1
  37. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/workflows/[id]/nodes/route_client-reference-manifest.js +1 -1
  38. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/workflows/[id]/route_client-reference-manifest.js +1 -1
  39. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
  40. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/auth/callback/route_client-reference-manifest.js +1 -1
  41. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/auth/device/page.js +5 -5
  42. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/auth/device/page_client-reference-manifest.js +1 -1
  43. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/auth/device.html +1 -1
  44. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/auth/device.rsc +3 -3
  45. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/dashboard/page_client-reference-manifest.js +1 -1
  46. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/dashboard.html +1 -1
  47. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/dashboard.rsc +3 -3
  48. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/index.html +1 -1
  49. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/index.rsc +2 -2
  50. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/login/page_client-reference-manifest.js +1 -1
  51. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/login.html +1 -1
  52. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/login.rsc +2 -2
  53. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/page_client-reference-manifest.js +1 -1
  54. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  55. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/projects/[slug]/tasks/page_client-reference-manifest.js +1 -1
  56. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/projects/[slug]/workflow/page_client-reference-manifest.js +1 -1
  57. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  58. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/projects.html +1 -1
  59. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/projects.rsc +2 -2
  60. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  61. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/settings.html +1 -1
  62. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/settings.rsc +2 -2
  63. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app-paths-manifest.json +10 -10
  64. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/chunks/2298.js +1 -1
  65. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/middleware-manifest.json +5 -5
  66. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/pages/404.html +1 -1
  67. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/pages/500.html +1 -1
  68. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/server-reference-manifest.js +1 -1
  69. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/server-reference-manifest.json +1 -1
  70. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/static/chunks/2456-5577fa071bb78cca.js +1 -0
  71. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/static/chunks/app/auth/device/{page-f7a2d0670aaf8314.js → page-a334052961b047e9.js} +1 -1
  72. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/static/css/97623b2fc4a523a7.css +1 -0
  73. package/index.js +6 -6
  74. package/lib/cli/configStore.js +38 -0
  75. package/lib/cli/daemon.js +785 -0
  76. package/lib/cli/runCli.js +11 -4
  77. package/lib/cli/util.js +104 -0
  78. package/package.json +1 -1
  79. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/static/chunks/2456-9f4a875ea5d68f26.js +0 -1
  80. package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/static/css/f4bb6b8083291d60.css +0 -1
  81. /package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/static/{6tGMyW64n2NpGvLihl5xS → iDPijmmtGz8j8COcWytVz}/_buildManifest.js +0 -0
  82. /package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/static/{6tGMyW64n2NpGvLihl5xS → iDPijmmtGz8j8COcWytVz}/_ssgManifest.js +0 -0
@@ -0,0 +1,785 @@
1
+ /* eslint-disable no-console */
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+ const path = require('path');
7
+ const { spawn, spawnSync } = require('child_process');
8
+
9
+ const { c } = require('../ui/colors');
10
+ const { CONFIG_DIR, DAEMON_PID_FILE, DAEMON_LOG_FILE, DAEMON_STATE_FILE, TASK_LOGS_DIR, BOARD_PID_FILE, BOARD_LOG_FILE, BOARD_ENV_FILE } = require('../config/paths');
11
+ const { prompt } = require('./configStore');
12
+ const { sleep } = require('./util');
13
+
14
+ // Embedded orchestrator worker (pg-boss) runtime (optional). Keep legacy filenames for backward compatibility.
15
+ const WORKER_PID_FILE = path.join(CONFIG_DIR, 'orchestrator-worker.pid');
16
+ const WORKER_LOG_FILE = path.join(CONFIG_DIR, 'orchestrator-worker.log');
17
+
18
+ // Bundled runtime roots (standalone build tracing preserves an absolute-path-like subtree).
19
+ const PACKAGED_AGX_CLOUD_ROOT = path.join(__dirname, 'cloud-runtime', 'standalone');
20
+ // Legacy default path (only correct if the standalone build was traced under `/Users/<name>`).
21
+ const PACKAGED_AGX_CLOUD_DIR = path.join(PACKAGED_AGX_CLOUD_ROOT, 'Projects', 'Agents', 'agx-cloud');
22
+ const LOCAL_AGX_CLOUD_DIR = path.resolve(__dirname, '..', 'agx-cloud');
23
+
24
+ let cachedPackagedAgxCloudDir;
25
+ function resolvePackagedAgxCloudDir() {
26
+ if (cachedPackagedAgxCloudDir !== undefined) return cachedPackagedAgxCloudDir;
27
+
28
+ const hasFile = (dir, rel) => {
29
+ try { return fs.existsSync(path.join(dir, rel)); } catch { return false; }
30
+ };
31
+
32
+ const isStandaloneAppDir = (dir) => {
33
+ if (!hasFile(dir, 'server.js')) return false;
34
+ if (!hasFile(dir, 'package.json')) return false;
35
+ if (hasFile(dir, path.join('.next', 'BUILD_ID'))) return true;
36
+ if (hasFile(dir, path.join('.next', 'package.json'))) return true;
37
+ return false;
38
+ };
39
+
40
+ if (isStandaloneAppDir(PACKAGED_AGX_CLOUD_DIR)) {
41
+ cachedPackagedAgxCloudDir = PACKAGED_AGX_CLOUD_DIR;
42
+ return cachedPackagedAgxCloudDir;
43
+ }
44
+
45
+ const maxDepth = 8;
46
+ const stack = [{ dir: PACKAGED_AGX_CLOUD_ROOT, depth: 0 }];
47
+ while (stack.length) {
48
+ const { dir, depth } = stack.pop();
49
+ if (depth > maxDepth) continue;
50
+ let entries;
51
+ try {
52
+ entries = fs.readdirSync(dir, { withFileTypes: true });
53
+ } catch {
54
+ continue;
55
+ }
56
+ if (isStandaloneAppDir(dir)) {
57
+ cachedPackagedAgxCloudDir = dir;
58
+ return cachedPackagedAgxCloudDir;
59
+ }
60
+ for (const e of entries) {
61
+ if (!e.isDirectory()) continue;
62
+ if (e.name === '.git') continue;
63
+ stack.push({ dir: path.join(dir, e.name), depth: depth + 1 });
64
+ }
65
+ }
66
+
67
+ cachedPackagedAgxCloudDir = null;
68
+ return cachedPackagedAgxCloudDir;
69
+ }
70
+
71
+ function getTaskLogPath(taskName) {
72
+ if (!fs.existsSync(TASK_LOGS_DIR)) {
73
+ fs.mkdirSync(TASK_LOGS_DIR, { recursive: true });
74
+ }
75
+ return path.join(TASK_LOGS_DIR, `${taskName}.log`);
76
+ }
77
+
78
+ function isPidAlive(pid) {
79
+ try {
80
+ process.kill(pid, 0);
81
+ return true;
82
+ } catch {
83
+ return false;
84
+ }
85
+ }
86
+
87
+ function isDaemonRunning() {
88
+ try {
89
+ if (!fs.existsSync(DAEMON_PID_FILE)) return false;
90
+ const pid = parseInt(fs.readFileSync(DAEMON_PID_FILE, 'utf8').trim(), 10);
91
+ if (!pid) return false;
92
+ process.kill(pid, 0);
93
+ return pid;
94
+ } catch {
95
+ return false;
96
+ }
97
+ }
98
+
99
+ async function stopDaemonProcessTree(pid, timeoutMs = 5000) {
100
+ const deadline = Date.now() + timeoutMs;
101
+
102
+ const killTree = (signal) => {
103
+ try {
104
+ process.kill(-pid, signal);
105
+ return true;
106
+ } catch (err) {
107
+ if (err.code === 'ESRCH') return false;
108
+ process.kill(pid, signal);
109
+ return true;
110
+ }
111
+ };
112
+
113
+ killTree('SIGTERM');
114
+ while (isPidAlive(pid) && Date.now() < deadline) {
115
+ await sleep(100);
116
+ }
117
+
118
+ if (!isPidAlive(pid)) return true;
119
+
120
+ killTree('SIGKILL');
121
+ await sleep(150);
122
+ return !isPidAlive(pid);
123
+ }
124
+
125
+ function resolveEmbeddedWorkerProjectDir() {
126
+ const override = process.env.AGX_CLOUD_WORKER_DIR;
127
+ if (override && typeof override === 'string') {
128
+ const resolved = path.resolve(override);
129
+ try {
130
+ if (fs.existsSync(path.join(resolved, 'package.json'))) return resolved;
131
+ } catch { }
132
+ }
133
+
134
+ const hasFile = (dir, rel) => {
135
+ try { return fs.existsSync(path.join(dir, rel)); } catch { return false; }
136
+ };
137
+
138
+ const scoreCandidate = (dir) => {
139
+ if (!dir) return -Infinity;
140
+ if (!hasFile(dir, 'package.json')) return -Infinity;
141
+
142
+ const hasWorkerEntrypoint =
143
+ hasFile(dir, path.join('worker', 'index.ts')) ||
144
+ hasFile(dir, path.join('worker', 'index.js')) ||
145
+ hasFile(dir, path.join('worker', 'index.mjs'));
146
+
147
+ const hasTsx =
148
+ hasFile(dir, path.join('node_modules', '.bin', 'tsx')) ||
149
+ hasFile(dir, path.join('node_modules', 'tsx', 'dist', 'cli.mjs'));
150
+
151
+ let score = 0;
152
+ if (hasWorkerEntrypoint) score += 10;
153
+ if (hasTsx) score += 3;
154
+ if (path.resolve(dir) === path.resolve(LOCAL_AGX_CLOUD_DIR)) score += 5;
155
+ if (path.resolve(dir) === path.resolve(PACKAGED_AGX_CLOUD_DIR) && !hasWorkerEntrypoint) score -= 5;
156
+ return score;
157
+ };
158
+
159
+ const CWD_AGX_CLOUD_DIR = path.resolve(process.cwd(), '..', 'agx-cloud');
160
+
161
+ const packaged = resolvePackagedAgxCloudDir();
162
+ const candidates = [CWD_AGX_CLOUD_DIR, LOCAL_AGX_CLOUD_DIR, packaged, PACKAGED_AGX_CLOUD_DIR].filter(Boolean);
163
+ let best = null;
164
+ let bestScore = -Infinity;
165
+ for (const candidate of candidates) {
166
+ const s = scoreCandidate(candidate);
167
+ if (s > bestScore) {
168
+ bestScore = s;
169
+ best = candidate;
170
+ }
171
+ }
172
+
173
+ if (!best || bestScore < 1) return null;
174
+ return best;
175
+ }
176
+
177
+ function isTemporalWorkerRunning() {
178
+ const readPid = (pidFile) => {
179
+ try {
180
+ if (!fs.existsSync(pidFile)) return null;
181
+ const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
182
+ if (!pid) return null;
183
+ process.kill(pid, 0);
184
+ return pid;
185
+ } catch {
186
+ return null;
187
+ }
188
+ };
189
+
190
+ return readPid(WORKER_PID_FILE) || false;
191
+ }
192
+
193
+ function pickEmbeddedWorkerNpmScript(projectDir) {
194
+ try {
195
+ const pkgPath = path.join(projectDir, 'package.json');
196
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
197
+ const scripts = pkg?.scripts || {};
198
+ if (scripts['daemon:worker']) return 'daemon:worker';
199
+ if (scripts.worker) return 'worker';
200
+ } catch { }
201
+ return 'worker';
202
+ }
203
+
204
+ function loadEnvFile(envPath) {
205
+ try {
206
+ if (!envPath || !fs.existsSync(envPath)) return {};
207
+ const raw = fs.readFileSync(envPath, 'utf8');
208
+ const env = {};
209
+ for (const line of raw.split('\n')) {
210
+ const trimmed = line.trim();
211
+ if (!trimmed || trimmed.startsWith('#')) continue;
212
+ const idx = trimmed.indexOf('=');
213
+ if (idx === -1) continue;
214
+ const key = trimmed.slice(0, idx).trim();
215
+ let value = trimmed.slice(idx + 1).trim();
216
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
217
+ value = value.slice(1, -1);
218
+ }
219
+ if (!key) continue;
220
+ env[key] = value;
221
+ }
222
+ return env;
223
+ } catch {
224
+ return {};
225
+ }
226
+ }
227
+
228
+ function loadBoardEnv() {
229
+ return loadEnvFile(path.join(CONFIG_DIR, 'board.env'));
230
+ }
231
+
232
+ function isBoardRunning() {
233
+ try {
234
+ if (!fs.existsSync(BOARD_PID_FILE)) return false;
235
+ const pid = parseInt(fs.readFileSync(BOARD_PID_FILE, 'utf8').trim(), 10);
236
+ if (!pid) return false;
237
+ process.kill(pid, 0);
238
+ return pid;
239
+ } catch {
240
+ return false;
241
+ }
242
+ }
243
+
244
+ function resolveBoardDir() {
245
+ const override = process.env.AGX_CLOUD_WORKER_DIR;
246
+ if (override && typeof override === 'string') {
247
+ const resolved = path.resolve(override);
248
+ try {
249
+ if (fs.existsSync(path.join(resolved, 'package.json'))) return { dir: resolved, mode: 'dev' };
250
+ } catch { }
251
+ }
252
+
253
+ const hasFile = (dir, rel) => {
254
+ try { return fs.existsSync(path.join(dir, rel)); } catch { return false; }
255
+ };
256
+
257
+ const CWD_AGX_CLOUD_DIR = path.resolve(process.cwd(), '..', 'agx-cloud');
258
+ for (const dir of [CWD_AGX_CLOUD_DIR, LOCAL_AGX_CLOUD_DIR]) {
259
+ if (hasFile(dir, 'package.json')) {
260
+ try {
261
+ const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'));
262
+ if (pkg?.scripts?.dev) return { dir, mode: 'dev' };
263
+ } catch { }
264
+ }
265
+ }
266
+
267
+ const packaged = resolvePackagedAgxCloudDir();
268
+ if (packaged && hasFile(packaged, 'server.js')) {
269
+ return { dir: packaged, mode: 'bundled' };
270
+ }
271
+
272
+ return null;
273
+ }
274
+
275
+ function getBoardPort() {
276
+ const apiUrl = process.env.AGX_CLOUD_URL || process.env.AGX_BOARD_URL || 'http://localhost:41741';
277
+ try {
278
+ const u = new URL(apiUrl);
279
+ return parseInt(u.port, 10) || (u.protocol === 'https:' ? 443 : 80);
280
+ } catch {
281
+ return 41741;
282
+ }
283
+ }
284
+
285
+ async function probeBoardHealth(port, timeoutMs = 1500) {
286
+ try {
287
+ const controller = new AbortController();
288
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
289
+ const res = await fetch(`http://localhost:${port}/api/tasks`, {
290
+ signal: controller.signal,
291
+ headers: { 'x-user-id': '' },
292
+ });
293
+ clearTimeout(timer);
294
+ return res.ok || res.status === 401;
295
+ } catch {
296
+ return false;
297
+ }
298
+ }
299
+
300
+ const DOCKER_POSTGRES_CONTAINER = 'agx-postgres';
301
+ const DOCKER_DEFAULT_DB_URL = 'postgresql://agx:agx@localhost:55432/agx';
302
+
303
+ function isDockerPostgresRunning() {
304
+ try {
305
+ const result = spawnSync('docker', ['inspect', '-f', '{{.State.Running}}', DOCKER_POSTGRES_CONTAINER], { timeout: 3000 });
306
+ return result.stdout && result.stdout.toString().trim() === 'true';
307
+ } catch {
308
+ return false;
309
+ }
310
+ }
311
+
312
+ function dockerExecPsql({ sql, timeoutMs = 60000 }) {
313
+ return spawnSync('docker', [
314
+ 'exec', '-i', DOCKER_POSTGRES_CONTAINER,
315
+ 'psql', '-U', 'agx', '-d', 'agx',
316
+ ], { input: sql, timeout: timeoutMs });
317
+ }
318
+
319
+ function dockerHasRelation(qualifiedName) {
320
+ const safe = String(qualifiedName).replace(/'/g, "''");
321
+ const res = dockerExecPsql({ sql: `select to_regclass('${safe}') as rel;\\n`, timeoutMs: 10000 });
322
+ if (res.status !== 0) return false;
323
+ const out = (res.stdout || Buffer.from('')).toString('utf8');
324
+ return out.includes(qualifiedName.split('.').pop()) && !out.includes('null');
325
+ }
326
+
327
+ function ensureDockerSchemaInitialized() {
328
+ if (!isDockerPostgresRunning()) return;
329
+ const needsProjects = !dockerHasRelation('public.projects') || !dockerHasRelation('public.project_repos');
330
+ if (!needsProjects) return;
331
+
332
+ const initSqlPath = path.join(__dirname, 'templates', 'stack', 'postgres', 'init', '001_agx_board_schema.sql');
333
+ if (!fs.existsSync(initSqlPath)) return;
334
+
335
+ console.log(`${c.dim}Initializing database schema...${c.reset}`);
336
+ const initSql = fs.readFileSync(initSqlPath, 'utf8');
337
+ const psqlResult = dockerExecPsql({ sql: initSql, timeoutMs: 60000 });
338
+ if (psqlResult.status !== 0) {
339
+ const stderr = (psqlResult.stderr || Buffer.from('')).toString('utf8').trim();
340
+ console.log(`${c.yellow}Schema init returned non-zero${c.reset}${stderr ? `: ${stderr}` : ''}`);
341
+ }
342
+ }
343
+
344
+ function ensureSchemaInitialized(dbUrl) {
345
+ const initSqlPath = path.join(__dirname, 'templates', 'stack', 'postgres', 'init', '001_agx_board_schema.sql');
346
+ if (!fs.existsSync(initSqlPath)) return;
347
+
348
+ const initSql = fs.readFileSync(initSqlPath, 'utf8');
349
+
350
+ if (dbUrl === DOCKER_DEFAULT_DB_URL) {
351
+ if (!isDockerPostgresRunning()) return;
352
+ console.log(`${c.dim}Initializing database schema...${c.reset}`);
353
+ const psqlResult = dockerExecPsql({ sql: initSql, timeoutMs: 60000 });
354
+ if (psqlResult.status !== 0) {
355
+ const stderr = (psqlResult.stderr || Buffer.from('')).toString('utf8').trim();
356
+ console.log(`${c.yellow}Schema init returned non-zero${c.reset}${stderr ? `: ${stderr}` : ''}`);
357
+ }
358
+ return;
359
+ }
360
+
361
+ console.log(`${c.dim}Initializing database schema...${c.reset}`);
362
+ const psqlResult = spawnSync('psql', [dbUrl], { input: initSql, timeout: 60000 });
363
+ if (psqlResult.status !== 0) {
364
+ const stderr = (psqlResult.stderr || Buffer.from('')).toString('utf8').trim();
365
+ console.log(`${c.yellow}Schema init returned non-zero${c.reset}${stderr ? `: ${stderr}` : ''}`);
366
+ }
367
+ }
368
+
369
+ async function ensurePostgresReady() {
370
+ const boardEnv = loadBoardEnv();
371
+ if (boardEnv.DATABASE_URL) {
372
+ try {
373
+ const dbUrl = new URL(boardEnv.DATABASE_URL);
374
+ const host = dbUrl.hostname;
375
+ const port = dbUrl.port || '5432';
376
+ const result = spawnSync('pg_isready', ['-h', host, '-p', port], { timeout: 3000 });
377
+ if (result.status === 0) {
378
+ ensureSchemaInitialized(boardEnv.DATABASE_URL);
379
+ return boardEnv.DATABASE_URL;
380
+ }
381
+ } catch { }
382
+ }
383
+
384
+ if (isDockerPostgresRunning()) {
385
+ const dbUrl = DOCKER_DEFAULT_DB_URL;
386
+ saveBoardEnvValue('DATABASE_URL', dbUrl);
387
+ ensureSchemaInitialized(dbUrl);
388
+ return dbUrl;
389
+ }
390
+
391
+ console.log(`\\n${c.yellow}Postgres is required for the agx board server.${c.reset}`);
392
+ console.log(` ${c.bold}1${c.reset}) Enter a custom DATABASE_URL`);
393
+ console.log(` ${c.bold}2${c.reset}) Auto-start postgres via Docker`);
394
+
395
+ const answer = await prompt(`\\n${c.cyan}Choice [2]:${c.reset} `);
396
+ const choice = answer.trim() || '2';
397
+
398
+ if (choice === '1') {
399
+ const dbUrl = await prompt(`${c.cyan}DATABASE_URL:${c.reset} `);
400
+ if (!dbUrl) {
401
+ console.error(`${c.red}No DATABASE_URL provided.${c.reset}`);
402
+ process.exit(1);
403
+ }
404
+ saveBoardEnvValue('DATABASE_URL', dbUrl);
405
+ return dbUrl;
406
+ }
407
+
408
+ console.log(`${c.dim}Starting postgres via Docker...${c.reset}`);
409
+ const dockerResult = spawnSync('docker', [
410
+ 'run', '-d',
411
+ '--name', 'agx-postgres',
412
+ '-e', 'POSTGRES_DB=agx',
413
+ '-e', 'POSTGRES_USER=agx',
414
+ '-e', 'POSTGRES_PASSWORD=agx',
415
+ '-p', '55432:5432',
416
+ '-v', 'agx_pg_data:/var/lib/postgresql/data',
417
+ 'postgres:16-alpine',
418
+ ], { stdio: 'pipe', timeout: 60000 });
419
+
420
+ if (dockerResult.status !== 0) {
421
+ const stderr = dockerResult.stderr ? dockerResult.stderr.toString() : '';
422
+ if (stderr.includes('already in use')) {
423
+ spawnSync('docker', ['start', 'agx-postgres'], { timeout: 10000 });
424
+ } else {
425
+ console.error(`${c.red}Failed to start postgres:${c.reset} ${stderr}`);
426
+ process.exit(1);
427
+ }
428
+ }
429
+
430
+ console.log(`${c.dim}Waiting for postgres to be ready...${c.reset}`);
431
+ const deadline = Date.now() + 30000;
432
+ while (Date.now() < deadline) {
433
+ const check = spawnSync('docker', ['exec', 'agx-postgres', 'pg_isready', '-U', 'agx'], { timeout: 3000 });
434
+ if (check.status === 0) break;
435
+ await sleep(1000);
436
+ }
437
+
438
+ const dbUrl = DOCKER_DEFAULT_DB_URL;
439
+ saveBoardEnvValue('DATABASE_URL', dbUrl);
440
+ ensureSchemaInitialized(dbUrl);
441
+
442
+ console.log(`${c.green}✓${c.reset} Postgres ready`);
443
+ return dbUrl;
444
+ }
445
+
446
+ function saveBoardEnvValue(key, value) {
447
+ if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true });
448
+ const existing = loadBoardEnv();
449
+ existing[key] = value;
450
+ const content = Object.entries(existing).map(([k, v]) => `${k}=${v}`).join('\\n') + '\\n';
451
+ fs.writeFileSync(BOARD_ENV_FILE, content);
452
+ }
453
+
454
+ let _boardEnsured = false;
455
+ async function ensureBoardRunning() {
456
+ if (_boardEnsured) return;
457
+ _boardEnsured = true;
458
+
459
+ const apiUrl = process.env.AGX_CLOUD_URL || process.env.AGX_BOARD_URL || 'http://localhost:41741';
460
+ try {
461
+ const u = new URL(apiUrl);
462
+ if (u.hostname !== 'localhost' && u.hostname !== '127.0.0.1' && u.hostname !== '0.0.0.0' && u.hostname !== '::1') return;
463
+ } catch { return; }
464
+
465
+ const port = getBoardPort();
466
+ if (await probeBoardHealth(port)) return;
467
+
468
+ const existingPid = isBoardRunning();
469
+ if (existingPid && await probeBoardHealth(port)) return;
470
+
471
+ if (existingPid) {
472
+ try { fs.unlinkSync(BOARD_PID_FILE); } catch { }
473
+ }
474
+
475
+ console.log(`${c.dim}Board server not reachable at localhost:${port}, starting...${c.reset}`);
476
+ const dbUrl = await ensurePostgresReady();
477
+ ensureDockerSchemaInitialized();
478
+
479
+ const boardInfo = resolveBoardDir();
480
+ if (!boardInfo) {
481
+ console.error(`${c.red}Board runtime not found.${c.reset} Ensure agx-cloud is at ${LOCAL_AGX_CLOUD_DIR} or build standalone runtime.`);
482
+ console.log(`${c.dim}Tip: set AGX_CLOUD_WORKER_DIR=/path/to/agx-cloud${c.reset}`);
483
+ _boardEnsured = false;
484
+ return;
485
+ }
486
+
487
+ if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true });
488
+
489
+ let logFd;
490
+ try {
491
+ logFd = fs.openSync(BOARD_LOG_FILE, 'a');
492
+ } catch (err) {
493
+ console.error(`${c.red}Unable to open board log:${c.reset} ${err.message}`);
494
+ _boardEnsured = false;
495
+ return;
496
+ }
497
+
498
+ const boardEnv = {
499
+ ...process.env,
500
+ DATABASE_URL: dbUrl,
501
+ PORT: String(port),
502
+ AGX_BOARD_DISABLE_AUTH: '1',
503
+ };
504
+
505
+ saveBoardEnvValue('DATABASE_URL', dbUrl);
506
+ saveBoardEnvValue('PORT', String(port));
507
+
508
+ let proc;
509
+ try {
510
+ if (boardInfo.mode === 'bundled') {
511
+ proc = spawn('node', ['server.js'], {
512
+ cwd: boardInfo.dir,
513
+ detached: true,
514
+ stdio: ['ignore', logFd, logFd],
515
+ env: boardEnv,
516
+ });
517
+ } else {
518
+ proc = spawn('npm', ['run', 'dev'], {
519
+ cwd: boardInfo.dir,
520
+ detached: true,
521
+ stdio: ['ignore', logFd, logFd],
522
+ env: boardEnv,
523
+ });
524
+ }
525
+ } catch (err) {
526
+ fs.closeSync(logFd);
527
+ console.error(`${c.red}Failed to start board server:${c.reset} ${err.message}`);
528
+ _boardEnsured = false;
529
+ return;
530
+ }
531
+
532
+ fs.closeSync(logFd);
533
+ proc.unref();
534
+ fs.writeFileSync(BOARD_PID_FILE, String(proc.pid));
535
+
536
+ console.log(`${c.dim}Waiting for board server (pid ${proc.pid})...${c.reset}`);
537
+ const deadline = Date.now() + 15000;
538
+ let ready = false;
539
+ while (Date.now() < deadline) {
540
+ if (await probeBoardHealth(port)) {
541
+ ready = true;
542
+ break;
543
+ }
544
+ await sleep(500);
545
+ }
546
+
547
+ if (ready) {
548
+ console.log(`${c.green}✓${c.reset} Board server started (pid ${proc.pid}, port ${port})`);
549
+ console.log(`${c.dim} Logs: ${BOARD_LOG_FILE}${c.reset}`);
550
+ } else {
551
+ console.log(`${c.yellow}Board server started but not yet responding — check ${BOARD_LOG_FILE}${c.reset}`);
552
+ }
553
+ }
554
+
555
+ async function stopBoard() {
556
+ const pid = isBoardRunning();
557
+ if (!pid) {
558
+ console.log(`${c.yellow}Board server not running${c.reset}`);
559
+ return false;
560
+ }
561
+
562
+ try {
563
+ const stopped = await stopDaemonProcessTree(pid);
564
+ if (fs.existsSync(BOARD_PID_FILE)) fs.unlinkSync(BOARD_PID_FILE);
565
+
566
+ if (!stopped) {
567
+ console.error(`${c.red}Failed to stop board server:${c.reset} pid ${pid} still running`);
568
+ return false;
569
+ }
570
+
571
+ console.log(`${c.green}✓${c.reset} Board server stopped (pid ${pid})`);
572
+ return true;
573
+ } catch (err) {
574
+ console.error(`${c.red}Failed to stop board server:${c.reset} ${err.message}`);
575
+ return false;
576
+ }
577
+ }
578
+
579
+ function startTemporalWorker() {
580
+ const existingPid = isTemporalWorkerRunning();
581
+ if (existingPid) {
582
+ console.log(`${c.dim}Orchestrator worker already running (pid ${existingPid})${c.reset}`);
583
+ return existingPid;
584
+ }
585
+
586
+ const projectDir = resolveEmbeddedWorkerProjectDir();
587
+ if (!projectDir) {
588
+ console.log(
589
+ `${c.yellow}Local board runtime not found.${c.reset} ` +
590
+ `AGX couldn't locate an agx-cloud checkout/runtime for the embedded worker.\\n` +
591
+ `${c.dim}Looked for:${c.reset} ../agx-cloud (from current directory), ${LOCAL_AGX_CLOUD_DIR}, ${PACKAGED_AGX_CLOUD_DIR}\\n` +
592
+ `${c.dim}Fix:${c.reset} set AGX_CLOUD_WORKER_DIR=/path/to/agx-cloud, then run: (cd "$AGX_CLOUD_WORKER_DIR" && npm install && npm run build)`
593
+ );
594
+ return null;
595
+ }
596
+
597
+ const workerEntrypoints = [
598
+ path.join(projectDir, 'worker', 'index.ts'),
599
+ path.join(projectDir, 'worker', 'index.js'),
600
+ path.join(projectDir, 'worker', 'index.mjs'),
601
+ ];
602
+ if (!workerEntrypoints.some((p) => {
603
+ try { return fs.existsSync(p); } catch { return false; }
604
+ })) {
605
+ console.log(`${c.red}Orchestrator worker entrypoint not found in:${c.reset} ${projectDir}`);
606
+ console.log(`${c.dim}Expected one of:${c.reset} ${workerEntrypoints.join(', ')}`);
607
+ console.log(`${c.dim}Tip:${c.reset} set AGX_CLOUD_WORKER_DIR=/path/to/agx-cloud`);
608
+ return null;
609
+ }
610
+
611
+ if (!fs.existsSync(CONFIG_DIR)) {
612
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
613
+ }
614
+
615
+ let logFd;
616
+ try {
617
+ logFd = fs.openSync(WORKER_LOG_FILE, 'a');
618
+ } catch (err) {
619
+ console.error(`${c.red}Unable to open orchestrator worker log:${c.reset} ${err.message}`);
620
+ return null;
621
+ }
622
+
623
+ const script = pickEmbeddedWorkerNpmScript(projectDir);
624
+ let worker;
625
+ try {
626
+ const boardEnv = loadBoardEnv();
627
+ if (!boardEnv.DATABASE_URL) {
628
+ console.log(`${c.yellow}Orchestrator worker not started${c.reset} (missing DATABASE_URL).`);
629
+ console.log(`${c.dim}Start the board first so ~/.agx/board.env is populated:${c.reset} agx board start`);
630
+ fs.closeSync(logFd);
631
+ return null;
632
+ }
633
+ worker = spawn('npm', ['run', script], {
634
+ cwd: projectDir,
635
+ detached: true,
636
+ stdio: ['ignore', logFd, logFd],
637
+ env: { ...process.env, ...boardEnv },
638
+ });
639
+ } catch (err) {
640
+ fs.closeSync(logFd);
641
+ console.error(`${c.red}Failed to start orchestrator worker:${c.reset} ${err.message}`);
642
+ return null;
643
+ }
644
+
645
+ fs.closeSync(logFd);
646
+ worker.unref();
647
+ fs.writeFileSync(WORKER_PID_FILE, String(worker.pid));
648
+
649
+ console.log(`${c.green}✓${c.reset} Orchestrator worker started (pid ${worker.pid})`);
650
+ console.log(`${c.dim} Logs: ${WORKER_LOG_FILE}${c.reset}`);
651
+ return worker.pid;
652
+ }
653
+
654
+ async function ensureTemporalWorkerRunning() {
655
+ try {
656
+ const boardEnv = loadBoardEnv();
657
+ if (!boardEnv.DATABASE_URL) {
658
+ await ensureBoardRunning();
659
+ }
660
+ } catch { }
661
+ return startTemporalWorker();
662
+ }
663
+
664
+ async function stopTemporalWorker() {
665
+ const pid = isTemporalWorkerRunning();
666
+ if (!pid) {
667
+ console.log(`${c.yellow}Orchestrator worker not running${c.reset}`);
668
+ return false;
669
+ }
670
+
671
+ try {
672
+ const stopped = await stopDaemonProcessTree(pid);
673
+ if (!stopped) {
674
+ console.error(`${c.red}Failed to stop orchestrator worker process tree:${c.reset} pid ${pid} is still running`);
675
+ return false;
676
+ }
677
+
678
+ if (fs.existsSync(WORKER_PID_FILE)) fs.unlinkSync(WORKER_PID_FILE);
679
+
680
+ console.log(`${c.green}✓${c.reset} Orchestrator worker stopped (pid ${pid})`);
681
+ return true;
682
+ } catch (err) {
683
+ console.error(`${c.red}Failed to stop orchestrator worker:${c.reset} ${err.message}`);
684
+ return false;
685
+ }
686
+ }
687
+
688
+ function startDaemon(options = {}) {
689
+ const existingPid = isDaemonRunning();
690
+ if (existingPid) {
691
+ console.log(`${c.dim}Daemon already running (pid ${existingPid})${c.reset}`);
692
+ return existingPid;
693
+ }
694
+
695
+ const agxDir = path.dirname(DAEMON_PID_FILE);
696
+ if (!fs.existsSync(agxDir)) {
697
+ fs.mkdirSync(agxDir, { recursive: true });
698
+ }
699
+
700
+ const agxPath = process.argv[1];
701
+ const daemonArgs = [agxPath, 'daemon', 'run'];
702
+ if (options.maxWorkers && Number.isFinite(options.maxWorkers) && options.maxWorkers > 0) {
703
+ daemonArgs.push('--workers', String(options.maxWorkers));
704
+ }
705
+
706
+ const daemon = spawn(process.execPath, daemonArgs, {
707
+ detached: true,
708
+ stdio: ['ignore',
709
+ fs.openSync(DAEMON_LOG_FILE, 'a'),
710
+ fs.openSync(DAEMON_LOG_FILE, 'a')
711
+ ],
712
+ env: {
713
+ ...process.env,
714
+ AGX_DAEMON: '1',
715
+ ...(options.maxWorkers ? { AGX_DAEMON_MAX_CONCURRENT: String(options.maxWorkers) } : {}),
716
+ }
717
+ });
718
+
719
+ daemon.unref();
720
+ fs.writeFileSync(DAEMON_PID_FILE, String(daemon.pid));
721
+
722
+ console.log(`${c.green}✓${c.reset} Daemon started (pid ${daemon.pid})`);
723
+ console.log(`${c.dim} Logs: ${DAEMON_LOG_FILE}${c.reset}`);
724
+ console.log(`${c.dim} Execution workers: ${options.maxWorkers || 1}${c.reset}`);
725
+ console.log(`${c.dim} Configure workers: agx daemon start -w 4${c.reset}`);
726
+ console.log(`${c.dim} Run in foreground: agx daemon${c.reset}`);
727
+
728
+ void ensureTemporalWorkerRunning();
729
+
730
+ return daemon.pid;
731
+ }
732
+
733
+ async function stopDaemon() {
734
+ const pid = isDaemonRunning();
735
+ let daemonStopped = false;
736
+
737
+ if (!pid) {
738
+ console.log(`${c.yellow}Daemon not running${c.reset}`);
739
+ } else {
740
+ try {
741
+ const stopped = await stopDaemonProcessTree(pid);
742
+ if (fs.existsSync(DAEMON_PID_FILE)) {
743
+ fs.unlinkSync(DAEMON_PID_FILE);
744
+ }
745
+ if (!stopped) {
746
+ console.error(`${c.red}Failed to stop daemon process tree:${c.reset} pid ${pid} is still running`);
747
+ } else {
748
+ console.log(`${c.green}✓${c.reset} Daemon stopped (pid ${pid})`);
749
+ daemonStopped = true;
750
+ }
751
+ } catch (err) {
752
+ console.error(`${c.red}Failed to stop daemon:${c.reset} ${err.message}`);
753
+ }
754
+ }
755
+
756
+ const temporalStopped = await stopTemporalWorker();
757
+ const boardStopped = await stopBoard();
758
+ return daemonStopped || temporalStopped || boardStopped;
759
+ }
760
+
761
+ module.exports = {
762
+ // Paths/constants that other CLI pieces display.
763
+ DAEMON_PID_FILE,
764
+ DAEMON_LOG_FILE,
765
+ DAEMON_STATE_FILE,
766
+ BOARD_PID_FILE,
767
+ BOARD_LOG_FILE,
768
+ BOARD_ENV_FILE,
769
+ WORKER_PID_FILE,
770
+ WORKER_LOG_FILE,
771
+
772
+ // Helpers
773
+ getTaskLogPath,
774
+ isDaemonRunning,
775
+ isBoardRunning,
776
+ stopDaemonProcessTree,
777
+ ensureBoardRunning,
778
+ stopBoard,
779
+ ensureTemporalWorkerRunning,
780
+ stopTemporalWorker,
781
+ startDaemon,
782
+ stopDaemon,
783
+ resolveBoardDir,
784
+ resolvePackagedAgxCloudDir,
785
+ };