@gh-symphony/cli 0.0.21 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -0
- package/dist/{chunk-SXGT7LOF.js → chunk-2TSM3INR.js} +26 -1
- package/dist/{chunk-A67CMOYE.js → chunk-2UW7NQLX.js} +1 -1
- package/dist/{chunk-MVRF7BES.js → chunk-36KYEDEO.js} +10 -1
- package/dist/{chunk-C7G7RJ4G.js → chunk-DDL4BWSL.js} +1 -1
- package/dist/{chunk-XN5ABWZ6.js → chunk-DFLXHNYQ.js} +26 -30
- package/dist/{chunk-KY6WKH66.js → chunk-E7HYEEZD.js} +70 -52
- package/dist/{chunk-QEONJ5DZ.js → chunk-EEQQWTXS.js} +1288 -92
- package/dist/chunk-GDE6FYN4.js +26 -0
- package/dist/{chunk-Y6TYJMNT.js → chunk-GSX2FV3M.js} +10 -16
- package/dist/{chunk-JN3TQVFV.js → chunk-HMLBBZNY.js} +11 -2
- package/dist/{chunk-5NV3LSAJ.js → chunk-IWFX2FMA.js} +5 -1
- package/dist/{chunk-MYVJ6HK4.js → chunk-PUDXVBSN.js} +706 -376
- package/dist/{chunk-ROGRTUFI.js → chunk-QIRE2VXS.js} +14 -3
- package/dist/{chunk-S6VIK4FF.js → chunk-ZHOKYUO3.js} +337 -13
- package/dist/{config-cmd-DNXNL26Z.js → config-cmd-Z3A7V6NC.js} +1 -1
- package/dist/{doctor-4HBRICHP.js → doctor-EJUMPBMW.js} +4 -4
- package/dist/index.js +88 -21
- package/dist/{init-HZ3JEDGQ.js → init-54HMKNYI.js} +3 -3
- package/dist/{logs-6JKKYDGJ.js → logs-GTZ4U5JE.js} +2 -2
- package/dist/project-RMYMZSFV.js +25 -0
- package/dist/{recover-L3MJHHDA.js → recover-LTLKMTRX.js} +7 -7
- package/dist/repo-WI7GF6XQ.js +749 -0
- package/dist/{run-XJQ6BF7U.js → run-IHN3ZL35.js} +21 -9
- package/dist/{setup-B2SVLW2R.js → setup-TZJSM3QV.js} +14 -13
- package/dist/start-RTAHQMR2.js +19 -0
- package/dist/status-F4D52OVK.js +12 -0
- package/dist/stop-MDKMJPVR.js +10 -0
- package/dist/{upgrade-OJXPZRYE.js → upgrade-O33S2SJK.js} +2 -2
- package/dist/{version-TBDCTKDO.js → version-CW54Q7BK.js} +1 -1
- package/dist/worker-entry.js +369 -13
- package/dist/{workflow-BLJH2HC3.js → workflow-L3KT6HB7.js} +5 -5
- package/package.json +3 -3
- package/dist/project-25NQ4J4Y.js +0 -24
- package/dist/repo-TDCWQR6P.js +0 -379
- package/dist/start-I2CC7BLW.js +0 -18
- package/dist/status-QSCFVGRQ.js +0 -11
- package/dist/stop-7MFCBQVW.js +0 -9
package/README.md
CHANGED
|
@@ -181,6 +181,7 @@ gh-symphony doctor # Validate local prerequisites, auth, confi
|
|
|
181
181
|
gh-symphony doctor --fix # Apply safe fixes and guide/launch follow-up recovery commands
|
|
182
182
|
gh-symphony project list # List all configured projects
|
|
183
183
|
gh-symphony project remove <id> # Remove a project
|
|
184
|
+
gh-symphony project explain owner/repo#123 # Explain why one issue is not dispatching
|
|
184
185
|
gh-symphony repo add owner/name # Validate and save a repo target manually
|
|
185
186
|
gh-symphony repo sync # Add newly linked repositories from the GitHub Project
|
|
186
187
|
gh-symphony repo sync --dry-run # Preview linked repository drift
|
|
@@ -201,6 +202,41 @@ removed, unchanged, and final repository sets.
|
|
|
201
202
|
|
|
202
203
|
For empty projects, use `gh-symphony repo add owner/name` after setup to seed the local repository list without re-running the whole wizard.
|
|
203
204
|
|
|
205
|
+
### Why Is My Issue Not Running?
|
|
206
|
+
|
|
207
|
+
Use `gh-symphony project explain <owner/repo#number>` before digging through
|
|
208
|
+
logs manually:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
gh-symphony project explain owner/repo#123
|
|
212
|
+
gh-symphony project explain owner/repo#123 --json
|
|
213
|
+
gh-symphony project explain owner/repo#123 --workflow ./WORKFLOW.md
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
The command checks project repository linkage, GitHub Project item presence,
|
|
217
|
+
`WORKFLOW.md` active / wait / terminal state mapping, blocker state, existing
|
|
218
|
+
run / retry / convergence ownership, and project or per-state concurrency
|
|
219
|
+
limits.
|
|
220
|
+
|
|
221
|
+
If the project has no previous local run snapshot and the repository path is
|
|
222
|
+
not stored in the managed project config, pass `--workflow` so the command
|
|
223
|
+
does not guess from the current shell directory.
|
|
224
|
+
|
|
225
|
+
```text
|
|
226
|
+
Issue dispatch explanation: owner/repo#123
|
|
227
|
+
Not dispatchable: Issue has 1 unresolved blocker.
|
|
228
|
+
|
|
229
|
+
Checks:
|
|
230
|
+
✓ Repository owner/repo is linked to the active managed project.
|
|
231
|
+
✓ Issue is present in the bound GitHub Project item set.
|
|
232
|
+
✓ Project state "Todo" maps to an active state in WORKFLOW.md.
|
|
233
|
+
✗ Issue has 1 unresolved blocker.
|
|
234
|
+
Hint: Move blocker issues to a terminal state or update the blocker relationship in GitHub.
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The remediation hints point to existing commands such as `workflow preview`,
|
|
238
|
+
`doctor`, `project status`, and `logs --issue`.
|
|
239
|
+
|
|
204
240
|
## 4. Run the Orchestrator
|
|
205
241
|
|
|
206
242
|
### Foreground
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_WORKFLOW_LIFECYCLE
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-EEQQWTXS.js";
|
|
5
5
|
|
|
6
6
|
// ../tracker-github/src/adapter.ts
|
|
7
7
|
import { createHash } from "crypto";
|
|
@@ -951,6 +951,19 @@ var githubProjectTrackerAdapter = {
|
|
|
951
951
|
};
|
|
952
952
|
}
|
|
953
953
|
};
|
|
954
|
+
async function findGithubProjectIssue(project, identifier, dependencies = {}) {
|
|
955
|
+
const parsed = parseIssueIdentifier(identifier);
|
|
956
|
+
if (!parsed) {
|
|
957
|
+
return null;
|
|
958
|
+
}
|
|
959
|
+
const trackerConfig = resolveGitHubTrackerConfig(project, dependencies);
|
|
960
|
+
return fetchGithubProjectIssueByRepositoryAndNumber(
|
|
961
|
+
trackerConfig,
|
|
962
|
+
{ owner: parsed.owner, name: parsed.name },
|
|
963
|
+
parsed.number,
|
|
964
|
+
dependencies.fetchImpl
|
|
965
|
+
);
|
|
966
|
+
}
|
|
954
967
|
async function listProjectIssues(project, dependencies = {}) {
|
|
955
968
|
const trackerConfig = resolveGitHubTrackerConfig(project, dependencies);
|
|
956
969
|
const loadProjectIssues = () => fetchGithubProjectIssues(trackerConfig, dependencies.fetchImpl);
|
|
@@ -1053,8 +1066,20 @@ function parseIssueNumber(identifier) {
|
|
|
1053
1066
|
const match = identifier.match(/#(\d+)$/);
|
|
1054
1067
|
return match ? Number.parseInt(match[1] ?? "0", 10) : 0;
|
|
1055
1068
|
}
|
|
1069
|
+
function parseIssueIdentifier(identifier) {
|
|
1070
|
+
const match = identifier.match(/^([^/\s#]+)\/([^/\s#]+)#(\d+)$/);
|
|
1071
|
+
if (!match) {
|
|
1072
|
+
return null;
|
|
1073
|
+
}
|
|
1074
|
+
return {
|
|
1075
|
+
owner: match[1],
|
|
1076
|
+
name: match[2],
|
|
1077
|
+
number: Number.parseInt(match[3], 10)
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1056
1080
|
|
|
1057
1081
|
export {
|
|
1058
1082
|
fetchGithubProjectIssueByRepositoryAndNumber,
|
|
1083
|
+
findGithubProjectIssue,
|
|
1059
1084
|
resolveTrackerAdapter
|
|
1060
1085
|
};
|
|
@@ -51,6 +51,14 @@ function showCursor() {
|
|
|
51
51
|
return "\x1B[?25h";
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
// src/format/repository.ts
|
|
55
|
+
function formatRepositoryDisplay(snapshot, fallback = "repository") {
|
|
56
|
+
if (snapshot.repository) {
|
|
57
|
+
return `${snapshot.repository.owner}/${snapshot.repository.name}`;
|
|
58
|
+
}
|
|
59
|
+
return snapshot.slug ?? fallback;
|
|
60
|
+
}
|
|
61
|
+
|
|
54
62
|
export {
|
|
55
63
|
bold,
|
|
56
64
|
dim,
|
|
@@ -64,5 +72,6 @@ export {
|
|
|
64
72
|
setNoColor,
|
|
65
73
|
clearScreen,
|
|
66
74
|
hideCursor,
|
|
67
|
-
showCursor
|
|
75
|
+
showCursor,
|
|
76
|
+
formatRepositoryDisplay
|
|
68
77
|
};
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
clearScreen,
|
|
6
6
|
cyan,
|
|
7
7
|
dim,
|
|
8
|
+
formatRepositoryDisplay,
|
|
8
9
|
green,
|
|
9
10
|
hideCursor,
|
|
10
11
|
magenta,
|
|
@@ -12,14 +13,17 @@ import {
|
|
|
12
13
|
showCursor,
|
|
13
14
|
stripAnsi,
|
|
14
15
|
yellow
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-36KYEDEO.js";
|
|
16
17
|
import {
|
|
17
18
|
resolveRuntimeRoot
|
|
18
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-IWFX2FMA.js";
|
|
20
|
+
import {
|
|
21
|
+
rejectRemovedProjectId
|
|
22
|
+
} from "./chunk-GDE6FYN4.js";
|
|
19
23
|
import {
|
|
20
24
|
handleMissingManagedProjectConfig,
|
|
21
25
|
resolveManagedProjectConfig
|
|
22
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-DDL4BWSL.js";
|
|
23
27
|
|
|
24
28
|
// src/commands/status.ts
|
|
25
29
|
import { readFile } from "fs/promises";
|
|
@@ -212,7 +216,9 @@ function renderDashboard(snapshots, options) {
|
|
|
212
216
|
const hasActiveRuns = snap.activeRuns.length > 0;
|
|
213
217
|
const hasRetries = snap.retryQueue.length > 0;
|
|
214
218
|
if (!hasActiveRuns && !hasRetries) continue;
|
|
215
|
-
lines.push(
|
|
219
|
+
lines.push(
|
|
220
|
+
sectionDivider(formatRepositoryDisplay(snap), width, c)
|
|
221
|
+
);
|
|
216
222
|
if (hasActiveRuns) {
|
|
217
223
|
lines.push(tableHeaderRow(c));
|
|
218
224
|
for (const rawRun of snap.activeRuns) {
|
|
@@ -280,7 +286,7 @@ function resolveProjectTokenDelta(snapshot) {
|
|
|
280
286
|
function renderLegacyStatus(snapshot, noColor) {
|
|
281
287
|
const apply = noColor ? (s) => stripAnsi(s) : (s) => s;
|
|
282
288
|
const lines = [];
|
|
283
|
-
const headerTitle = `gh-symphony \u2219 ${snapshot
|
|
289
|
+
const headerTitle = `gh-symphony \u2219 ${formatRepositoryDisplay(snapshot)}`;
|
|
284
290
|
const headerWidth = 45;
|
|
285
291
|
const headerPadding = Math.max(
|
|
286
292
|
0,
|
|
@@ -363,16 +369,6 @@ function parseStatusArgs(args) {
|
|
|
363
369
|
parsed.watch = true;
|
|
364
370
|
continue;
|
|
365
371
|
}
|
|
366
|
-
if (arg === "--project" || arg === "--project-id") {
|
|
367
|
-
const value = args[i + 1];
|
|
368
|
-
if (!value || value.startsWith("-")) {
|
|
369
|
-
parsed.error = `Option '${arg}' argument missing`;
|
|
370
|
-
return parsed;
|
|
371
|
-
}
|
|
372
|
-
parsed.projectId = value;
|
|
373
|
-
i += 1;
|
|
374
|
-
continue;
|
|
375
|
-
}
|
|
376
372
|
if (arg?.startsWith("-")) {
|
|
377
373
|
parsed.error = `Unknown option '${arg}'`;
|
|
378
374
|
return parsed;
|
|
@@ -381,33 +377,33 @@ function parseStatusArgs(args) {
|
|
|
381
377
|
return parsed;
|
|
382
378
|
}
|
|
383
379
|
async function readStatusSnapshot(runtimeRoot, projectId) {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
"
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
} catch {
|
|
394
|
-
return null;
|
|
380
|
+
for (const statusPath of [
|
|
381
|
+
join(runtimeRoot, "status.json"),
|
|
382
|
+
join(runtimeRoot, "projects", projectId, "status.json")
|
|
383
|
+
]) {
|
|
384
|
+
try {
|
|
385
|
+
const content = await readFile(statusPath, "utf-8");
|
|
386
|
+
return JSON.parse(content);
|
|
387
|
+
} catch {
|
|
388
|
+
}
|
|
395
389
|
}
|
|
390
|
+
return null;
|
|
396
391
|
}
|
|
397
392
|
var handler = async (args, options) => {
|
|
393
|
+
if (rejectRemovedProjectId(args)) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
398
396
|
const parsed = parseStatusArgs(args);
|
|
399
397
|
if (parsed.error) {
|
|
400
398
|
process.stderr.write(`${parsed.error}
|
|
401
399
|
`);
|
|
402
|
-
process.stderr.write(
|
|
403
|
-
"Usage: gh-symphony status [--project-id <project-id>] [--watch]\n"
|
|
404
|
-
);
|
|
400
|
+
process.stderr.write("Usage: gh-symphony status [--watch]\n");
|
|
405
401
|
process.exitCode = 2;
|
|
406
402
|
return;
|
|
407
403
|
}
|
|
408
404
|
const projectConfig = await resolveManagedProjectConfig({
|
|
409
405
|
configDir: options.configDir,
|
|
410
|
-
requestedProjectId:
|
|
406
|
+
requestedProjectId: void 0
|
|
411
407
|
});
|
|
412
408
|
if (!projectConfig) {
|
|
413
409
|
handleMissingManagedProjectConfig();
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
createStore,
|
|
6
6
|
releaseProjectLock,
|
|
7
7
|
resolveOrchestratorLogLevel
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PUDXVBSN.js";
|
|
9
9
|
import {
|
|
10
10
|
getGhToken
|
|
11
11
|
} from "./chunk-C67H3OUL.js";
|
|
@@ -17,29 +17,33 @@ import {
|
|
|
17
17
|
parseRecentEvents,
|
|
18
18
|
readJsonFile,
|
|
19
19
|
safeReadDir
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-EEQQWTXS.js";
|
|
21
21
|
import {
|
|
22
22
|
bold,
|
|
23
23
|
cyan,
|
|
24
24
|
dim,
|
|
25
|
+
formatRepositoryDisplay,
|
|
25
26
|
green,
|
|
26
27
|
red,
|
|
27
28
|
setNoColor,
|
|
28
29
|
yellow
|
|
29
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-36KYEDEO.js";
|
|
30
31
|
import {
|
|
31
32
|
resolveRuntimeRoot
|
|
32
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-IWFX2FMA.js";
|
|
34
|
+
import {
|
|
35
|
+
rejectRemovedProjectId
|
|
36
|
+
} from "./chunk-GDE6FYN4.js";
|
|
33
37
|
import {
|
|
34
38
|
handleMissingManagedProjectConfig,
|
|
35
39
|
resolveManagedProjectConfig
|
|
36
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-DDL4BWSL.js";
|
|
37
41
|
import {
|
|
38
42
|
daemonPidPath,
|
|
39
43
|
httpStatusPath,
|
|
40
44
|
orchestratorLogPath,
|
|
41
45
|
writeJsonFile
|
|
42
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-QIRE2VXS.js";
|
|
43
47
|
|
|
44
48
|
// src/commands/start.ts
|
|
45
49
|
import { writeFile, mkdir, readFile as readFile2, rm } from "fs/promises";
|
|
@@ -55,24 +59,30 @@ var RECENT_EVENT_CHUNK_SIZE = 4096;
|
|
|
55
59
|
var MAX_RECENT_EVENT_SCAN_BYTES = 64 * 1024;
|
|
56
60
|
var RUN_RECORD_LOAD_CONCURRENCY = 8;
|
|
57
61
|
var DashboardFsReader = class {
|
|
58
|
-
constructor(runtimeRoot
|
|
62
|
+
constructor(runtimeRoot) {
|
|
59
63
|
this.runtimeRoot = runtimeRoot;
|
|
60
|
-
this.projectId = projectId;
|
|
61
|
-
assertValidDashboardProjectId(projectId);
|
|
62
64
|
this.resolvedRuntimeRoot = resolve(runtimeRoot);
|
|
63
65
|
}
|
|
64
66
|
resolvedRuntimeRoot;
|
|
65
67
|
projectDir() {
|
|
66
|
-
return
|
|
68
|
+
return this.resolvedRuntimeRoot;
|
|
67
69
|
}
|
|
68
70
|
runDir(runId) {
|
|
69
71
|
assertValidDashboardRunId(runId);
|
|
70
|
-
return join(this.
|
|
72
|
+
return join(this.resolvedRuntimeRoot, "runs", runId);
|
|
71
73
|
}
|
|
72
74
|
async loadProjectStatus() {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
const snapshot = await readJsonFile(join(this.projectDir(), "status.json"));
|
|
76
|
+
if (!snapshot) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const status = { ...snapshot };
|
|
80
|
+
delete status.projectId;
|
|
81
|
+
delete status.slug;
|
|
82
|
+
if (!isRepositoryRef(status.repository)) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return status;
|
|
76
86
|
}
|
|
77
87
|
async loadProjectState() {
|
|
78
88
|
const snapshot = await this.loadProjectStatus();
|
|
@@ -216,7 +226,6 @@ async function statusForIssue(reader, issueIdentifier) {
|
|
|
216
226
|
const currentRunCandidate = issueRecord.currentRunId ? await reader.loadRun(issueRecord.currentRunId) : null;
|
|
217
227
|
const currentRun = isMatchingIssueRun(
|
|
218
228
|
currentRunCandidate,
|
|
219
|
-
reader.projectId,
|
|
220
229
|
issueRecord.issueId,
|
|
221
230
|
issueIdentifier
|
|
222
231
|
) ? currentRunCandidate : null;
|
|
@@ -300,13 +309,6 @@ function findLatestRunForIssue(matchingRuns) {
|
|
|
300
309
|
);
|
|
301
310
|
return sortedRuns[0] ?? null;
|
|
302
311
|
}
|
|
303
|
-
function assertValidDashboardProjectId(projectId) {
|
|
304
|
-
if (projectId.length === 0 || projectId === "." || projectId === ".." || projectId.includes("/") || projectId.includes("\\")) {
|
|
305
|
-
throw new Error(
|
|
306
|
-
`Invalid project ID "${projectId}". Project IDs must not contain path separators or traversal segments.`
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
312
|
function assertValidDashboardRunId(runId) {
|
|
311
313
|
if (runId.length === 0 || runId === "." || runId === ".." || runId.includes("/") || runId.includes("\\")) {
|
|
312
314
|
throw new Error(
|
|
@@ -314,6 +316,13 @@ function assertValidDashboardRunId(runId) {
|
|
|
314
316
|
);
|
|
315
317
|
}
|
|
316
318
|
}
|
|
319
|
+
function isRepositoryRef(value) {
|
|
320
|
+
if (!value || typeof value !== "object") {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
const repository = value;
|
|
324
|
+
return typeof repository.owner === "string" && repository.owner.length > 0 && typeof repository.name === "string" && repository.name.length > 0 && typeof repository.cloneUrl === "string";
|
|
325
|
+
}
|
|
317
326
|
async function mapWithConcurrency(items, concurrency, mapper) {
|
|
318
327
|
const results = new Array(items.length);
|
|
319
328
|
let nextIndex = 0;
|
|
@@ -507,7 +516,7 @@ function createControlPlaneHandler(options) {
|
|
|
507
516
|
};
|
|
508
517
|
}
|
|
509
518
|
async function startControlPlaneServer(options) {
|
|
510
|
-
const reader = new DashboardFsReader(options.runtimeRoot
|
|
519
|
+
const reader = new DashboardFsReader(options.runtimeRoot);
|
|
511
520
|
const handler2 = createControlPlaneHandler({
|
|
512
521
|
reader,
|
|
513
522
|
onRefreshRequest: options.onRefreshRequest
|
|
@@ -721,16 +730,6 @@ function parseStartArgs(args) {
|
|
|
721
730
|
i += 1;
|
|
722
731
|
continue;
|
|
723
732
|
}
|
|
724
|
-
if (arg === "--project" || arg === "--project-id") {
|
|
725
|
-
const value = args[i + 1];
|
|
726
|
-
if (!value || value.startsWith("-")) {
|
|
727
|
-
parsed.error = `Option '${arg}' argument missing`;
|
|
728
|
-
return parsed;
|
|
729
|
-
}
|
|
730
|
-
parsed.projectId = value;
|
|
731
|
-
i += 1;
|
|
732
|
-
continue;
|
|
733
|
-
}
|
|
734
733
|
if (arg === "--log-level") {
|
|
735
734
|
const value = args[i + 1];
|
|
736
735
|
if (!value || value.startsWith("-")) {
|
|
@@ -756,7 +755,9 @@ function logTickResult(snapshot, prevSnapshot, isFirst) {
|
|
|
756
755
|
const healthColor = snapshot.health === "degraded" ? red : snapshot.health === "running" ? green : cyan;
|
|
757
756
|
logLine(
|
|
758
757
|
green("\u25CF"),
|
|
759
|
-
`
|
|
758
|
+
`Repository ${bold(formatRepositoryDisplay(snapshot))} connected ${dim(
|
|
759
|
+
"("
|
|
760
|
+
)}${healthColor(snapshot.health)}${dim(")")}`
|
|
760
761
|
);
|
|
761
762
|
if (snapshot.summary.activeRuns > 0) {
|
|
762
763
|
logLine(cyan("\u25B8"), `${snapshot.summary.activeRuns} active run(s)`);
|
|
@@ -885,7 +886,7 @@ async function removeHttpBindingState(configDir, projectId) {
|
|
|
885
886
|
await rm(httpStatusPath(configDir, projectId), { force: true });
|
|
886
887
|
}
|
|
887
888
|
async function startHttpServer(input) {
|
|
888
|
-
const reader = new DashboardFsReader(input.runtimeRoot
|
|
889
|
+
const reader = new DashboardFsReader(input.runtimeRoot);
|
|
889
890
|
for (let port = input.initialPort; port <= 65535; port += 1) {
|
|
890
891
|
const server = createServer3((request, response) => {
|
|
891
892
|
void (async () => {
|
|
@@ -955,6 +956,9 @@ var handler = async (args, options) => {
|
|
|
955
956
|
setNoColor(options.noColor);
|
|
956
957
|
let parsed;
|
|
957
958
|
try {
|
|
959
|
+
if (rejectRemovedProjectId(args)) {
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
958
962
|
parsed = parseStartArgs(args);
|
|
959
963
|
} catch (error) {
|
|
960
964
|
process.stderr.write(
|
|
@@ -968,24 +972,33 @@ var handler = async (args, options) => {
|
|
|
968
972
|
process.stderr.write(`${parsed.error}
|
|
969
973
|
`);
|
|
970
974
|
process.stderr.write(
|
|
971
|
-
"Usage: gh-symphony start
|
|
975
|
+
"Usage: gh-symphony start [--daemon] [--once] [--http [port]] [--web [port]]\n"
|
|
972
976
|
);
|
|
973
977
|
process.exitCode = 2;
|
|
974
978
|
return;
|
|
975
979
|
}
|
|
976
980
|
if (parsed.daemon && parsed.once) {
|
|
977
|
-
process.stderr.write(
|
|
981
|
+
process.stderr.write(
|
|
982
|
+
"Options '--daemon' and '--once' cannot be used together\n"
|
|
983
|
+
);
|
|
978
984
|
process.exitCode = 2;
|
|
979
985
|
return;
|
|
980
986
|
}
|
|
981
987
|
const projectConfig = await resolveManagedProjectConfig({
|
|
982
988
|
configDir: options.configDir,
|
|
983
|
-
requestedProjectId:
|
|
989
|
+
requestedProjectId: void 0
|
|
984
990
|
});
|
|
985
991
|
if (!projectConfig) {
|
|
986
992
|
handleMissingManagedProjectConfig();
|
|
987
993
|
return;
|
|
988
994
|
}
|
|
995
|
+
if (!hasConfiguredRepository(projectConfig)) {
|
|
996
|
+
process.stderr.write(
|
|
997
|
+
"No repository is configured in this project. Run 'gh-symphony repo add owner/name' first.\n"
|
|
998
|
+
);
|
|
999
|
+
process.exitCode = 1;
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
989
1002
|
const runtimeRoot = resolveRuntimeRoot(options.configDir);
|
|
990
1003
|
const projectId = projectConfig.projectId;
|
|
991
1004
|
let logLevel;
|
|
@@ -1094,7 +1107,6 @@ var handler = async (args, options) => {
|
|
|
1094
1107
|
host: HTTP_HOST,
|
|
1095
1108
|
port: parsed.webPort,
|
|
1096
1109
|
runtimeRoot,
|
|
1097
|
-
projectId,
|
|
1098
1110
|
onRefreshRequest: () => service.requestReconcile()
|
|
1099
1111
|
}) : parsed.httpPort !== void 0 ? await startHttpServer({
|
|
1100
1112
|
runtimeRoot,
|
|
@@ -1238,19 +1250,27 @@ async function shutdownForegroundOrchestrator(input) {
|
|
|
1238
1250
|
}
|
|
1239
1251
|
return (input.exit ?? process.exit)(0);
|
|
1240
1252
|
}
|
|
1253
|
+
function hasConfiguredRepository(config) {
|
|
1254
|
+
return Boolean(config.repository?.owner && config.repository.name);
|
|
1255
|
+
}
|
|
1241
1256
|
async function tailWorkerLog(runtimeRoot, projectId, runId, issueIdentifier) {
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1257
|
+
for (const logPath of [
|
|
1258
|
+
join3(runtimeRoot, "runs", runId, "worker.log"),
|
|
1259
|
+
join3(runtimeRoot, "projects", projectId, "runs", runId, "worker.log")
|
|
1260
|
+
]) {
|
|
1261
|
+
try {
|
|
1262
|
+
const content = await readFile2(logPath, "utf8");
|
|
1263
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
1264
|
+
if (lines.length === 0) return;
|
|
1265
|
+
const tail = lines.slice(-30);
|
|
1266
|
+
logLine(red("\u2717"), red(`Worker stderr (${issueIdentifier}):`));
|
|
1267
|
+
for (const line of tail) {
|
|
1268
|
+
process.stdout.write(` ${dim(line)}
|
|
1251
1269
|
`);
|
|
1270
|
+
}
|
|
1271
|
+
return;
|
|
1272
|
+
} catch {
|
|
1252
1273
|
}
|
|
1253
|
-
} catch {
|
|
1254
1274
|
}
|
|
1255
1275
|
}
|
|
1256
1276
|
var start_default = handler;
|
|
@@ -1264,8 +1284,6 @@ async function startDaemon(options, projectId, logLevel, httpPort, webPort) {
|
|
|
1264
1284
|
[
|
|
1265
1285
|
process.argv[1],
|
|
1266
1286
|
"start",
|
|
1267
|
-
"--project",
|
|
1268
|
-
projectId,
|
|
1269
1287
|
...httpPort !== void 0 ? ["--http", String(httpPort)] : [],
|
|
1270
1288
|
...webPort !== void 0 ? ["--web", String(webPort)] : [],
|
|
1271
1289
|
...logLevel ? ["--log-level", logLevel] : []
|
|
@@ -1289,7 +1307,7 @@ async function startDaemon(options, projectId, logLevel, httpPort, webPort) {
|
|
|
1289
1307
|
process.stdout.write(
|
|
1290
1308
|
`Orchestrator started in background (PID: ${child.pid}).
|
|
1291
1309
|
Logs: ${logPath}
|
|
1292
|
-
Stop with: gh-symphony
|
|
1310
|
+
Stop with: gh-symphony repo stop
|
|
1293
1311
|
`
|
|
1294
1312
|
);
|
|
1295
1313
|
}
|