@gh-symphony/cli 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-DLZ2XHWY.js → chunk-2ZIPQ7ZS.js} +1 -1
- package/dist/{chunk-3SKN5L3I.js → chunk-5HQLPZR5.js} +15 -6
- package/dist/{chunk-FAU72YC2.js → chunk-A7EFL6EJ.js} +1 -1
- package/dist/{chunk-PLBG7TZA.js → chunk-GL55AKBI.js} +169 -33
- package/dist/{chunk-7Z7WYGDL.js → chunk-GTNWFVFU.js} +9 -5
- package/dist/{chunk-BOM2BYZQ.js → chunk-SMNIGNS3.js} +106 -11
- package/dist/{chunk-NRABQNAX.js → chunk-VHEGRYK7.js} +3 -2
- package/dist/{doctor-MD4MD6SZ.js → doctor-3IIM4UYS.js} +6 -6
- package/dist/index.js +7 -7
- package/dist/{repo-CK2IDMZF.js → repo-SLK4DDXH.js} +198 -13
- package/dist/{setup-KZ3U53PY.js → setup-G5VYN6FV.js} +33 -13
- package/dist/{upgrade-2WPPOUZL.js → upgrade-BXIHAENU.js} +2 -2
- package/dist/{version-Z2T42H5M.js → version-MC2KSPJB.js} +1 -1
- package/dist/worker-entry.js +5 -5
- package/dist/{workflow-7Y6GTV2C.js → workflow-4OFPSVZ3.js} +6 -6
- package/package.json +3 -3
|
@@ -517,9 +517,10 @@ var PROJECT_ITEMS_PAGE_QUERY = `
|
|
|
517
517
|
import { execFileSync, spawnSync } from "child_process";
|
|
518
518
|
var REQUIRED_GH_SCOPES = ["repo", "read:org", "project"];
|
|
519
519
|
var GhAuthError = class extends Error {
|
|
520
|
-
constructor(code, message) {
|
|
520
|
+
constructor(code, message, details = {}) {
|
|
521
521
|
super(message);
|
|
522
522
|
this.code = code;
|
|
523
|
+
this.details = details;
|
|
523
524
|
this.name = "GhAuthError";
|
|
524
525
|
}
|
|
525
526
|
};
|
|
@@ -527,7 +528,77 @@ function ghTokenReadErrorMessage() {
|
|
|
527
528
|
return "Failed to read a GitHub token from gh CLI. Run 'gh auth status' and try again.";
|
|
528
529
|
}
|
|
529
530
|
function missingGhScopesMessage(missing) {
|
|
530
|
-
return `Run 'gh auth refresh --scopes
|
|
531
|
+
return `Run 'gh auth refresh --scopes ${REQUIRED_GH_SCOPES.join(",")}'. Missing scopes: ${missing.join(", ")}`;
|
|
532
|
+
}
|
|
533
|
+
function parseMissingScopes(message) {
|
|
534
|
+
const matched = message.match(/Missing scopes:\s*([^.\n]+)/i);
|
|
535
|
+
if (!matched) {
|
|
536
|
+
return [];
|
|
537
|
+
}
|
|
538
|
+
return matched[1].split(",").map((scope) => scope.trim()).filter((scope) => scope.length > 0);
|
|
539
|
+
}
|
|
540
|
+
function formatGhAuthRemediation(error, opts) {
|
|
541
|
+
const retryCommand = opts?.retryCommand ?? "gh-symphony repo start";
|
|
542
|
+
switch (error.code) {
|
|
543
|
+
case "not_installed":
|
|
544
|
+
return {
|
|
545
|
+
title: "GitHub authentication is not available",
|
|
546
|
+
message: error.message,
|
|
547
|
+
hint: "Install gh CLI from https://cli.github.com or set GITHUB_GRAPHQL_TOKEN."
|
|
548
|
+
};
|
|
549
|
+
case "not_authenticated": {
|
|
550
|
+
const command = `gh auth login --scopes ${REQUIRED_GH_SCOPES.join(",")}`;
|
|
551
|
+
return {
|
|
552
|
+
title: "GitHub authentication is required",
|
|
553
|
+
message: error.message,
|
|
554
|
+
command,
|
|
555
|
+
hint: `Run '${command}', then re-run '${retryCommand}'.`
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
case "missing_scopes": {
|
|
559
|
+
if (error.details.source === "env") {
|
|
560
|
+
return {
|
|
561
|
+
title: "GitHub token is missing required scopes",
|
|
562
|
+
message: error.message,
|
|
563
|
+
hint: `Update GITHUB_GRAPHQL_TOKEN with a token that has ${REQUIRED_GH_SCOPES.join(",")} scopes, or unset it to use gh CLI auth, then re-run '${retryCommand}'.`
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
const currentSet = new Set(
|
|
567
|
+
(error.details.currentScopes ?? []).map((scope) => scope.toLowerCase())
|
|
568
|
+
);
|
|
569
|
+
const inferredMissing = currentSet.size > 0 ? REQUIRED_GH_SCOPES.filter((scope) => !currentSet.has(scope)) : [];
|
|
570
|
+
const missing = error.details.missingScopes?.length ? error.details.missingScopes : inferredMissing.length > 0 ? inferredMissing : parseMissingScopes(error.message);
|
|
571
|
+
const scopeArg = missing.length > 0 ? missing.join(",") : REQUIRED_GH_SCOPES.join(",");
|
|
572
|
+
const command = `gh auth refresh --scopes ${scopeArg}`;
|
|
573
|
+
return {
|
|
574
|
+
title: "GitHub token is missing required scopes",
|
|
575
|
+
message: error.message,
|
|
576
|
+
command,
|
|
577
|
+
hint: `Run '${command}', then re-run '${retryCommand}'.`
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
case "invalid_token":
|
|
581
|
+
case "token_failed": {
|
|
582
|
+
if (error.details.source === "env") {
|
|
583
|
+
return {
|
|
584
|
+
title: "GitHub token validation failed",
|
|
585
|
+
message: error.message,
|
|
586
|
+
hint: `Update or unset GITHUB_GRAPHQL_TOKEN, then re-run '${retryCommand}'.`
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
const command = `gh auth login --scopes ${REQUIRED_GH_SCOPES.join(",")}`;
|
|
590
|
+
return {
|
|
591
|
+
title: "GitHub token validation failed",
|
|
592
|
+
message: error.message,
|
|
593
|
+
command,
|
|
594
|
+
hint: `Run '${command}' to re-authenticate, then re-run '${retryCommand}'.`
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
default: {
|
|
598
|
+
const exhaustive = error.code;
|
|
599
|
+
throw new Error(`Unhandled GhAuthError code: ${exhaustive}`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
531
602
|
}
|
|
532
603
|
function classifyTokenValidationError(error, source) {
|
|
533
604
|
if (error instanceof GhAuthError) {
|
|
@@ -537,19 +608,25 @@ function classifyTokenValidationError(error, source) {
|
|
|
537
608
|
if (error.status === 401) {
|
|
538
609
|
return new GhAuthError(
|
|
539
610
|
source === "env" ? "invalid_token" : "token_failed",
|
|
540
|
-
source === "env" ? "GITHUB_GRAPHQL_TOKEN is invalid or expired." : ghTokenReadErrorMessage()
|
|
611
|
+
source === "env" ? "GITHUB_GRAPHQL_TOKEN is invalid or expired." : ghTokenReadErrorMessage(),
|
|
612
|
+
{ source }
|
|
541
613
|
);
|
|
542
614
|
}
|
|
543
615
|
const prefix = source === "env" ? "GITHUB_GRAPHQL_TOKEN could not be validated" : "gh CLI token could not be validated";
|
|
544
|
-
return new GhAuthError("token_failed", `${prefix}: ${error.message}
|
|
616
|
+
return new GhAuthError("token_failed", `${prefix}: ${error.message}`, {
|
|
617
|
+
source
|
|
618
|
+
});
|
|
545
619
|
}
|
|
546
620
|
if (error instanceof Error) {
|
|
547
621
|
const prefix = source === "env" ? "GITHUB_GRAPHQL_TOKEN could not be validated" : "gh CLI token could not be validated";
|
|
548
|
-
return new GhAuthError("token_failed", `${prefix}: ${error.message}
|
|
622
|
+
return new GhAuthError("token_failed", `${prefix}: ${error.message}`, {
|
|
623
|
+
source
|
|
624
|
+
});
|
|
549
625
|
}
|
|
550
626
|
return new GhAuthError(
|
|
551
627
|
"token_failed",
|
|
552
|
-
source === "env" ? "GITHUB_GRAPHQL_TOKEN could not be validated." : "gh CLI token could not be validated."
|
|
628
|
+
source === "env" ? "GITHUB_GRAPHQL_TOKEN could not be validated." : "gh CLI token could not be validated.",
|
|
629
|
+
{ source }
|
|
553
630
|
);
|
|
554
631
|
}
|
|
555
632
|
function getEnvGitHubToken() {
|
|
@@ -651,12 +728,22 @@ async function validateGitHubToken(token, source, opts) {
|
|
|
651
728
|
if (source === "env") {
|
|
652
729
|
throw new GhAuthError(
|
|
653
730
|
"missing_scopes",
|
|
654
|
-
`GITHUB_GRAPHQL_TOKEN is missing required scopes: ${scopeCheck.missing.join(", ")}
|
|
731
|
+
`GITHUB_GRAPHQL_TOKEN is missing required scopes: ${scopeCheck.missing.join(", ")}`,
|
|
732
|
+
{
|
|
733
|
+
missingScopes: [...scopeCheck.missing],
|
|
734
|
+
currentScopes: viewer.scopes,
|
|
735
|
+
source
|
|
736
|
+
}
|
|
655
737
|
);
|
|
656
738
|
}
|
|
657
739
|
throw new GhAuthError(
|
|
658
740
|
"missing_scopes",
|
|
659
|
-
missingGhScopesMessage(scopeCheck.missing)
|
|
741
|
+
missingGhScopesMessage(scopeCheck.missing),
|
|
742
|
+
{
|
|
743
|
+
missingScopes: [...scopeCheck.missing],
|
|
744
|
+
currentScopes: viewer.scopes,
|
|
745
|
+
source
|
|
746
|
+
}
|
|
660
747
|
);
|
|
661
748
|
}
|
|
662
749
|
return {
|
|
@@ -696,21 +783,28 @@ function ensureGhAuth(opts) {
|
|
|
696
783
|
if (!checkGhInstalled({ execImpl })) {
|
|
697
784
|
throw new GhAuthError(
|
|
698
785
|
"not_installed",
|
|
699
|
-
"gh CLI is not installed. Install it from https://cli.github.com or set GITHUB_GRAPHQL_TOKEN."
|
|
786
|
+
"gh CLI is not installed. Install it from https://cli.github.com or set GITHUB_GRAPHQL_TOKEN.",
|
|
787
|
+
{ source: "gh" }
|
|
700
788
|
);
|
|
701
789
|
}
|
|
702
790
|
const auth = checkGhAuthenticated({ spawnImpl });
|
|
703
791
|
if (!auth.authenticated) {
|
|
704
792
|
throw new GhAuthError(
|
|
705
793
|
"not_authenticated",
|
|
706
|
-
"Run 'gh auth login --scopes repo,read:org,project' or set GITHUB_GRAPHQL_TOKEN."
|
|
794
|
+
"Run 'gh auth login --scopes repo,read:org,project' or set GITHUB_GRAPHQL_TOKEN.",
|
|
795
|
+
{ source: "gh" }
|
|
707
796
|
);
|
|
708
797
|
}
|
|
709
798
|
const scopeCheck = checkGhScopes({ spawnImpl });
|
|
710
799
|
if (!scopeCheck.valid) {
|
|
711
800
|
throw new GhAuthError(
|
|
712
801
|
"missing_scopes",
|
|
713
|
-
missingGhScopesMessage(scopeCheck.missing)
|
|
802
|
+
missingGhScopesMessage(scopeCheck.missing),
|
|
803
|
+
{
|
|
804
|
+
missingScopes: [...scopeCheck.missing],
|
|
805
|
+
currentScopes: scopeCheck.scopes,
|
|
806
|
+
source: "gh"
|
|
807
|
+
}
|
|
714
808
|
);
|
|
715
809
|
}
|
|
716
810
|
const { token } = getGhTokenWithSource({
|
|
@@ -789,6 +883,7 @@ export {
|
|
|
789
883
|
getProjectDetail,
|
|
790
884
|
REQUIRED_GH_SCOPES,
|
|
791
885
|
GhAuthError,
|
|
886
|
+
formatGhAuthRemediation,
|
|
792
887
|
getEnvGitHubToken,
|
|
793
888
|
checkGhInstalled,
|
|
794
889
|
checkGhAuthenticated,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
resolveWorkflowRuntimeTimeouts,
|
|
31
31
|
safeReadDir,
|
|
32
32
|
scheduleRetryAt
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-5HQLPZR5.js";
|
|
34
34
|
import {
|
|
35
35
|
loadGlobalConfig,
|
|
36
36
|
loadProjectConfig
|
|
@@ -4156,7 +4156,8 @@ var OrchestratorService = class {
|
|
|
4156
4156
|
stateFieldName: "Status",
|
|
4157
4157
|
activeStates: ["Todo", "In Progress"],
|
|
4158
4158
|
terminalStates: ["Done"],
|
|
4159
|
-
blockerCheckStates: ["Todo"]
|
|
4159
|
+
blockerCheckStates: ["Todo"],
|
|
4160
|
+
planningStates: ["Todo"]
|
|
4160
4161
|
}
|
|
4161
4162
|
};
|
|
4162
4163
|
}
|
|
@@ -5,14 +5,14 @@ import {
|
|
|
5
5
|
parseIssueReference,
|
|
6
6
|
readGitHubProjectBinding,
|
|
7
7
|
renderIssueWorkflowPreview
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-GTNWFVFU.js";
|
|
9
|
+
import "./chunk-GL55AKBI.js";
|
|
10
10
|
import {
|
|
11
11
|
fetchGithubProjectIssueByRepositoryAndNumber,
|
|
12
12
|
fetchGithubProjectIssues,
|
|
13
13
|
inspectManagedProjectSelection
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-VHEGRYK7.js";
|
|
15
|
+
import "./chunk-A7EFL6EJ.js";
|
|
16
16
|
import {
|
|
17
17
|
resolveRuntimeRoot
|
|
18
18
|
} from "./chunk-RZ3WO7OV.js";
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
runGhAuthLogin,
|
|
32
32
|
runGhAuthRefresh,
|
|
33
33
|
validateGitHubToken
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-SMNIGNS3.js";
|
|
35
35
|
import {
|
|
36
36
|
isClaudeRuntimeCommand,
|
|
37
37
|
parseWorkflowMarkdown,
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
resolveClaudeCommandBinary,
|
|
41
41
|
resolveRuntimeCommandBinary,
|
|
42
42
|
runClaudePreflight
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-5HQLPZR5.js";
|
|
44
44
|
import {
|
|
45
45
|
configFilePath,
|
|
46
46
|
orchestratorLogPath,
|
package/dist/index.js
CHANGED
|
@@ -282,7 +282,7 @@ var HELP_SECTIONS = [
|
|
|
282
282
|
},
|
|
283
283
|
{
|
|
284
284
|
name: "repo start",
|
|
285
|
-
description: "Start the orchestrator
|
|
285
|
+
description: "Start the orchestrator after validating tracker authentication"
|
|
286
286
|
},
|
|
287
287
|
{
|
|
288
288
|
name: "repo start --daemon",
|
|
@@ -417,13 +417,13 @@ function createRemovedCommandHandler(message) {
|
|
|
417
417
|
|
|
418
418
|
// src/index.ts
|
|
419
419
|
var COMMANDS = {
|
|
420
|
-
workflow: () => import("./workflow-
|
|
421
|
-
setup: () => import("./setup-
|
|
422
|
-
doctor: () => import("./doctor-
|
|
423
|
-
upgrade: () => import("./upgrade-
|
|
424
|
-
repo: () => import("./repo-
|
|
420
|
+
workflow: () => import("./workflow-4OFPSVZ3.js"),
|
|
421
|
+
setup: () => import("./setup-G5VYN6FV.js"),
|
|
422
|
+
doctor: () => import("./doctor-3IIM4UYS.js"),
|
|
423
|
+
upgrade: () => import("./upgrade-BXIHAENU.js"),
|
|
424
|
+
repo: () => import("./repo-SLK4DDXH.js"),
|
|
425
425
|
config: () => import("./config-cmd-AOZVS6GU.js"),
|
|
426
|
-
version: () => import("./version-
|
|
426
|
+
version: () => import("./version-MC2KSPJB.js")
|
|
427
427
|
};
|
|
428
428
|
function addGlobalOptions(command) {
|
|
429
429
|
return command.option("--config <dir>", "Config directory").addOption(new Option("--config-dir <dir>").hideHelp()).option("-v, --verbose", "Enable verbose output").option("--json", "Output in JSON format").option("--no-color", "Disable color output");
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import {
|
|
18
18
|
initRepoRuntime,
|
|
19
19
|
parseRepoRuntimeFlags
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-2ZIPQ7ZS.js";
|
|
21
21
|
import {
|
|
22
22
|
OrchestratorService,
|
|
23
23
|
acquireProjectLock,
|
|
@@ -33,16 +33,22 @@ import {
|
|
|
33
33
|
resolveOrchestratorLogLevel,
|
|
34
34
|
resolveTrackerAdapter,
|
|
35
35
|
runCli
|
|
36
|
-
} from "./chunk-
|
|
37
|
-
import "./chunk-
|
|
36
|
+
} from "./chunk-VHEGRYK7.js";
|
|
37
|
+
import "./chunk-A7EFL6EJ.js";
|
|
38
38
|
import {
|
|
39
39
|
resolveRepoRuntimeRoot,
|
|
40
40
|
resolveRuntimeRoot
|
|
41
41
|
} from "./chunk-RZ3WO7OV.js";
|
|
42
42
|
import {
|
|
43
43
|
GhAuthError,
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
GitHubApiError,
|
|
45
|
+
GitHubScopeError,
|
|
46
|
+
formatGhAuthRemediation,
|
|
47
|
+
getGhToken,
|
|
48
|
+
resolveGitHubAuth,
|
|
49
|
+
runGhAuthLogin,
|
|
50
|
+
runGhAuthRefresh
|
|
51
|
+
} from "./chunk-SMNIGNS3.js";
|
|
46
52
|
import {
|
|
47
53
|
WorkflowConfigStore,
|
|
48
54
|
deriveIssueWorkspaceKeyFromIdentifier,
|
|
@@ -52,7 +58,7 @@ import {
|
|
|
52
58
|
parseRecentEvents,
|
|
53
59
|
readJsonFile,
|
|
54
60
|
safeReadDir
|
|
55
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-5HQLPZR5.js";
|
|
56
62
|
import {
|
|
57
63
|
daemonPidPath,
|
|
58
64
|
httpStatusPath,
|
|
@@ -765,6 +771,7 @@ import { writeFile, mkdir, readFile as readFile5, rm } from "fs/promises";
|
|
|
765
771
|
import { dirname as dirname2, join as join6 } from "path";
|
|
766
772
|
import { spawn } from "child_process";
|
|
767
773
|
import { createServer as createServer3 } from "http";
|
|
774
|
+
import * as p from "@clack/prompts";
|
|
768
775
|
|
|
769
776
|
// ../dashboard/src/store.ts
|
|
770
777
|
import { open } from "fs/promises";
|
|
@@ -1437,6 +1444,149 @@ function logLine(icon, msg) {
|
|
|
1437
1444
|
process.stdout.write(`${timestamp()} ${icon} ${msg}
|
|
1438
1445
|
`);
|
|
1439
1446
|
}
|
|
1447
|
+
var REPO_START_COMMAND = "gh-symphony repo start";
|
|
1448
|
+
function isInteractiveTerminal() {
|
|
1449
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
1450
|
+
}
|
|
1451
|
+
function displayGhAuthError(error) {
|
|
1452
|
+
const remediation = formatGhAuthRemediation(error, {
|
|
1453
|
+
retryCommand: REPO_START_COMMAND
|
|
1454
|
+
});
|
|
1455
|
+
process.stderr.write(`${remediation.title}: ${remediation.message}
|
|
1456
|
+
`);
|
|
1457
|
+
process.stderr.write(`${remediation.hint}
|
|
1458
|
+
`);
|
|
1459
|
+
}
|
|
1460
|
+
function formatAuthSource(source) {
|
|
1461
|
+
return source === "env" ? "GITHUB_GRAPHQL_TOKEN" : "gh CLI";
|
|
1462
|
+
}
|
|
1463
|
+
function displayGitHubAuthSuccess(auth) {
|
|
1464
|
+
process.stdout.write(
|
|
1465
|
+
`Authenticated via ${formatAuthSource(auth.source)} as ${auth.login}
|
|
1466
|
+
`
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
async function resolveRepoStartGitHubAuth(input) {
|
|
1470
|
+
try {
|
|
1471
|
+
const auth = await resolveGitHubAuth();
|
|
1472
|
+
process.env.GITHUB_GRAPHQL_TOKEN = auth.token;
|
|
1473
|
+
displayGitHubAuthSuccess(auth);
|
|
1474
|
+
return { ok: true, githubAuthSource: auth.source };
|
|
1475
|
+
} catch (error) {
|
|
1476
|
+
if (!(error instanceof GhAuthError)) {
|
|
1477
|
+
throw error;
|
|
1478
|
+
}
|
|
1479
|
+
displayGhAuthError(error);
|
|
1480
|
+
const remediation = formatGhAuthRemediation(error, {
|
|
1481
|
+
retryCommand: REPO_START_COMMAND
|
|
1482
|
+
});
|
|
1483
|
+
const canRemediate = input.allowInteractiveRemediation && isInteractiveTerminal() && remediation.command !== void 0 && error.details.source !== "env";
|
|
1484
|
+
if (!canRemediate) {
|
|
1485
|
+
process.exitCode = 1;
|
|
1486
|
+
return { ok: false };
|
|
1487
|
+
}
|
|
1488
|
+
const shouldRun = await p.confirm({
|
|
1489
|
+
message: `Run '${remediation.command}' now?`,
|
|
1490
|
+
initialValue: true
|
|
1491
|
+
});
|
|
1492
|
+
if (p.isCancel(shouldRun) || shouldRun !== true) {
|
|
1493
|
+
process.exitCode = 1;
|
|
1494
|
+
return { ok: false };
|
|
1495
|
+
}
|
|
1496
|
+
const result = error.code === "missing_scopes" ? runGhAuthRefresh({ interactive: true }) : runGhAuthLogin({ interactive: true });
|
|
1497
|
+
process.stderr.write(`${result.summary}
|
|
1498
|
+
`);
|
|
1499
|
+
if (result.status !== "applied") {
|
|
1500
|
+
process.exitCode = 1;
|
|
1501
|
+
return { ok: false };
|
|
1502
|
+
}
|
|
1503
|
+
try {
|
|
1504
|
+
const auth = await resolveGitHubAuth();
|
|
1505
|
+
process.env.GITHUB_GRAPHQL_TOKEN = auth.token;
|
|
1506
|
+
displayGitHubAuthSuccess(auth);
|
|
1507
|
+
return { ok: true, githubAuthSource: auth.source };
|
|
1508
|
+
} catch (retryError) {
|
|
1509
|
+
if (retryError instanceof GhAuthError) {
|
|
1510
|
+
displayGhAuthError(retryError);
|
|
1511
|
+
process.exitCode = 1;
|
|
1512
|
+
return { ok: false };
|
|
1513
|
+
}
|
|
1514
|
+
throw retryError;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
async function preflightRepoStartAuth(projectConfig, input) {
|
|
1519
|
+
if (projectConfig.tracker.adapter === "github-project") {
|
|
1520
|
+
return resolveRepoStartGitHubAuth({
|
|
1521
|
+
allowInteractiveRemediation: !input.daemon
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
if (projectConfig.tracker.adapter === "linear") {
|
|
1525
|
+
if (process.env.LINEAR_API_KEY?.trim()) {
|
|
1526
|
+
return { ok: true };
|
|
1527
|
+
}
|
|
1528
|
+
process.stderr.write(
|
|
1529
|
+
"Linear authentication is required. Set LINEAR_API_KEY in the environment before running 'gh-symphony repo start'.\n"
|
|
1530
|
+
);
|
|
1531
|
+
process.exitCode = 1;
|
|
1532
|
+
return { ok: false };
|
|
1533
|
+
}
|
|
1534
|
+
return { ok: true };
|
|
1535
|
+
}
|
|
1536
|
+
function isGitHubAuthRuntimeError(error) {
|
|
1537
|
+
if (error instanceof GitHubScopeError) {
|
|
1538
|
+
return true;
|
|
1539
|
+
}
|
|
1540
|
+
if (error instanceof GhAuthError) {
|
|
1541
|
+
return error.code === "missing_scopes" || error.code === "invalid_token";
|
|
1542
|
+
}
|
|
1543
|
+
if (error instanceof GitHubApiError) {
|
|
1544
|
+
return error.status === 401;
|
|
1545
|
+
}
|
|
1546
|
+
if (error instanceof Error) {
|
|
1547
|
+
const maybeStatus = error.status;
|
|
1548
|
+
if (maybeStatus === 401) {
|
|
1549
|
+
return true;
|
|
1550
|
+
}
|
|
1551
|
+
const message = error.message.toLowerCase();
|
|
1552
|
+
return message.includes("missing required github scopes") || message.includes("missing required scopes") || message.includes("missing required scope") || message.includes("missing_scopes") || message.includes("bad credentials") || message.includes("invalid token") || message.includes("authentication failed") || message.includes("status 401") || message.includes("401 unauthorized");
|
|
1553
|
+
}
|
|
1554
|
+
return false;
|
|
1555
|
+
}
|
|
1556
|
+
function ghRuntimeErrorToAuthError(error, source) {
|
|
1557
|
+
if (error instanceof GhAuthError) {
|
|
1558
|
+
return error;
|
|
1559
|
+
}
|
|
1560
|
+
if (error instanceof GitHubScopeError) {
|
|
1561
|
+
return new GhAuthError(
|
|
1562
|
+
"missing_scopes",
|
|
1563
|
+
`GitHub token is missing required scopes: ${error.requiredScopes.join(", ")}`,
|
|
1564
|
+
{
|
|
1565
|
+
missingScopes: [...error.requiredScopes],
|
|
1566
|
+
currentScopes: [...error.currentScopes],
|
|
1567
|
+
source
|
|
1568
|
+
}
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
if (error.message.toLowerCase().includes("missing required github scopes") || error.message.toLowerCase().includes("missing required scopes") || error.message.toLowerCase().includes("missing_scopes")) {
|
|
1572
|
+
return new GhAuthError("missing_scopes", error.message, { source });
|
|
1573
|
+
}
|
|
1574
|
+
return new GhAuthError(
|
|
1575
|
+
"invalid_token",
|
|
1576
|
+
error.message || "GitHub token validation failed.",
|
|
1577
|
+
{ source }
|
|
1578
|
+
);
|
|
1579
|
+
}
|
|
1580
|
+
function displayRuntimeAuthShutdown(error, source) {
|
|
1581
|
+
const authError = ghRuntimeErrorToAuthError(error, source);
|
|
1582
|
+
displayGhAuthError(authError);
|
|
1583
|
+
process.stderr.write(
|
|
1584
|
+
"Stopping repo start because GitHub authentication can no longer be validated.\n"
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
function shouldElevateGitHubAuthRuntimeError(projectConfig, error) {
|
|
1588
|
+
return projectConfig.tracker.adapter === "github-project" && isGitHubAuthRuntimeError(error);
|
|
1589
|
+
}
|
|
1440
1590
|
var DEFAULT_HTTP_PORT = 4680;
|
|
1441
1591
|
var HTTP_HOST = "0.0.0.0";
|
|
1442
1592
|
function parseStartArgs(args) {
|
|
@@ -1762,6 +1912,12 @@ var handler5 = async (args, options) => {
|
|
|
1762
1912
|
process.exitCode = 2;
|
|
1763
1913
|
return;
|
|
1764
1914
|
}
|
|
1915
|
+
const authPreflight = await preflightRepoStartAuth(projectConfig, {
|
|
1916
|
+
daemon: parsed.daemon
|
|
1917
|
+
});
|
|
1918
|
+
if (!authPreflight.ok) {
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1765
1921
|
if (parsed.daemon) {
|
|
1766
1922
|
await startDaemon(
|
|
1767
1923
|
options,
|
|
@@ -1773,12 +1929,6 @@ var handler5 = async (args, options) => {
|
|
|
1773
1929
|
);
|
|
1774
1930
|
return;
|
|
1775
1931
|
}
|
|
1776
|
-
if (!process.env.GITHUB_GRAPHQL_TOKEN) {
|
|
1777
|
-
try {
|
|
1778
|
-
process.env.GITHUB_GRAPHQL_TOKEN = getGhToken();
|
|
1779
|
-
} catch {
|
|
1780
|
-
}
|
|
1781
|
-
}
|
|
1782
1932
|
let projectLock = null;
|
|
1783
1933
|
try {
|
|
1784
1934
|
projectLock = await acquireProjectLock({
|
|
@@ -1789,11 +1939,29 @@ var handler5 = async (args, options) => {
|
|
|
1789
1939
|
const store = createStore(runtimeRoot);
|
|
1790
1940
|
let prevSnapshot = null;
|
|
1791
1941
|
let isFirst = true;
|
|
1942
|
+
let requestShutdown = null;
|
|
1943
|
+
let authShutdownRequested = false;
|
|
1792
1944
|
const service = new OrchestratorService(store, projectConfig, {
|
|
1793
1945
|
logLevel,
|
|
1794
1946
|
assignedOnly: parsed.assignedOnly,
|
|
1795
1947
|
onTick: async (snapshot) => {
|
|
1796
1948
|
try {
|
|
1949
|
+
if (authShutdownRequested) {
|
|
1950
|
+
return;
|
|
1951
|
+
}
|
|
1952
|
+
if (projectConfig.tracker.adapter === "github-project" && snapshot.lastError) {
|
|
1953
|
+
const runtimeError = new Error(snapshot.lastError);
|
|
1954
|
+
if (isGitHubAuthRuntimeError(runtimeError)) {
|
|
1955
|
+
authShutdownRequested = true;
|
|
1956
|
+
displayRuntimeAuthShutdown(
|
|
1957
|
+
runtimeError,
|
|
1958
|
+
authPreflight.githubAuthSource
|
|
1959
|
+
);
|
|
1960
|
+
process.exitCode = 1;
|
|
1961
|
+
requestShutdown?.();
|
|
1962
|
+
return;
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1797
1965
|
logTickResult(snapshot, prevSnapshot, isFirst);
|
|
1798
1966
|
if (!isFirst) {
|
|
1799
1967
|
const currentRunIds = new Set(
|
|
@@ -1813,6 +1981,13 @@ var handler5 = async (args, options) => {
|
|
|
1813
1981
|
prevSnapshot = snapshot;
|
|
1814
1982
|
isFirst = false;
|
|
1815
1983
|
} catch (error) {
|
|
1984
|
+
if (shouldElevateGitHubAuthRuntimeError(projectConfig, error)) {
|
|
1985
|
+
authShutdownRequested = true;
|
|
1986
|
+
displayRuntimeAuthShutdown(error, authPreflight.githubAuthSource);
|
|
1987
|
+
process.exitCode = 1;
|
|
1988
|
+
requestShutdown?.();
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1816
1991
|
logLine(
|
|
1817
1992
|
red("\u2717"),
|
|
1818
1993
|
red(
|
|
@@ -1850,6 +2025,9 @@ var handler5 = async (args, options) => {
|
|
|
1850
2025
|
const handleSigterm = () => {
|
|
1851
2026
|
void shutdown();
|
|
1852
2027
|
};
|
|
2028
|
+
requestShutdown = () => {
|
|
2029
|
+
void shutdown();
|
|
2030
|
+
};
|
|
1853
2031
|
process.on("SIGINT", handleSigint);
|
|
1854
2032
|
process.on("SIGTERM", handleSigterm);
|
|
1855
2033
|
try {
|
|
@@ -1923,6 +2101,13 @@ var handler5 = async (args, options) => {
|
|
|
1923
2101
|
if (shuttingDown) {
|
|
1924
2102
|
break;
|
|
1925
2103
|
}
|
|
2104
|
+
if (shouldElevateGitHubAuthRuntimeError(projectConfig, error)) {
|
|
2105
|
+
authShutdownRequested = true;
|
|
2106
|
+
displayRuntimeAuthShutdown(error, authPreflight.githubAuthSource);
|
|
2107
|
+
process.exitCode = 1;
|
|
2108
|
+
await shutdown();
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
1926
2111
|
logLine(
|
|
1927
2112
|
red("\u2717"),
|
|
1928
2113
|
red(
|
|
@@ -1998,7 +2183,7 @@ async function shutdownForegroundOrchestrator(input) {
|
|
|
1998
2183
|
`Failed to release project lock: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1999
2184
|
);
|
|
2000
2185
|
}
|
|
2001
|
-
return (input.exit ?? process.exit)(0);
|
|
2186
|
+
return (input.exit ?? process.exit)(process.exitCode ?? 0);
|
|
2002
2187
|
}
|
|
2003
2188
|
function hasConfiguredRepository(config) {
|
|
2004
2189
|
return Boolean(config.repository?.owner && config.repository.name);
|