agent-control-plane 0.2.0 → 0.4.9
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 +69 -19
- package/assets/workflow-catalog.json +1 -1
- package/bin/pr-risk.sh +22 -7
- package/bin/sync-pr-labels.sh +1 -1
- package/hooks/heartbeat-hooks.sh +125 -12
- package/hooks/issue-reconcile-hooks.sh +1 -1
- package/hooks/pr-reconcile-hooks.sh +1 -1
- package/npm/bin/agent-control-plane.js +296 -61
- package/package.json +11 -7
- package/tools/bin/agent-github-update-labels +36 -2
- package/tools/bin/agent-project-catch-up-merged-prs +4 -2
- package/tools/bin/agent-project-cleanup-session +49 -5
- package/tools/bin/agent-project-heartbeat-loop +119 -1471
- package/tools/bin/agent-project-publish-issue-pr +6 -3
- package/tools/bin/agent-project-reconcile-issue-session +78 -106
- package/tools/bin/agent-project-reconcile-pr-session +166 -143
- package/tools/bin/agent-project-retry-state +18 -7
- package/tools/bin/agent-project-run-claude-session +10 -0
- package/tools/bin/agent-project-run-codex-resilient +99 -14
- package/tools/bin/agent-project-run-codex-session +16 -5
- package/tools/bin/agent-project-run-kilo-session +10 -0
- package/tools/bin/agent-project-run-openclaw-session +10 -0
- package/tools/bin/agent-project-run-opencode-session +10 -0
- package/tools/bin/agent-project-sync-source-repo-main +163 -0
- package/tools/bin/agent-project-worker-status +10 -7
- package/tools/bin/cleanup-worktree.sh +6 -1
- package/tools/bin/flow-config-lib.sh +1257 -34
- package/tools/bin/flow-resident-worker-lib.sh +119 -1
- package/tools/bin/flow-shell-lib.sh +56 -0
- package/tools/bin/github-core-rate-limit-state.sh +77 -0
- package/tools/bin/github-write-outbox.sh +470 -0
- package/tools/bin/heartbeat-loop-cache-lib.sh +164 -0
- package/tools/bin/heartbeat-loop-counting-lib.sh +306 -0
- package/tools/bin/heartbeat-loop-pr-strategy-lib.sh +199 -0
- package/tools/bin/heartbeat-loop-scheduling-lib.sh +506 -0
- package/tools/bin/heartbeat-loop-worker-lib.sh +319 -0
- package/tools/bin/heartbeat-recovery-preflight.sh +12 -1
- package/tools/bin/heartbeat-safe-auto.sh +56 -3
- package/tools/bin/install-project-launchd.sh +17 -2
- package/tools/bin/project-init.sh +21 -1
- package/tools/bin/project-launchd-bootstrap.sh +16 -9
- package/tools/bin/project-runtimectl.sh +46 -2
- package/tools/bin/reconcile-bootstrap-lib.sh +113 -0
- package/tools/bin/resident-issue-controller-lib.sh +448 -0
- package/tools/bin/scaffold-profile.sh +61 -3
- package/tools/bin/start-pr-fix-worker.sh +47 -10
- package/tools/bin/start-resident-issue-loop.sh +28 -439
- package/tools/dashboard/app.js +37 -1
- package/tools/dashboard/dashboard_snapshot.py +65 -26
- package/tools/templates/pr-fix-template.md +3 -1
- package/tools/templates/pr-merge-repair-template.md +2 -1
- package/SKILL.md +0 -149
- package/references/architecture.md +0 -217
- package/references/commands.md +0 -128
- package/references/control-plane-map.md +0 -124
- package/references/docs-map.md +0 -73
- package/references/release-checklist.md +0 -65
- package/references/repo-map.md +0 -36
- package/tools/bin/split-retained-slice.sh +0 -124
|
@@ -45,11 +45,17 @@ Usage:
|
|
|
45
45
|
|
|
46
46
|
Guided install/bootstrap flow for one repo profile. It can detect the current
|
|
47
47
|
repo, suggest sane runtime paths, sync ACP into ~/.agent-runtime, scaffold one
|
|
48
|
-
profile, run doctor checks, and optionally start the runtime.
|
|
48
|
+
profile, run doctor checks, and optionally start the runtime. ACP can run
|
|
49
|
+
GitHub-first or local-first with Gitea as the working forge.
|
|
49
50
|
|
|
50
51
|
Options:
|
|
51
52
|
--profile-id <id> Profile id to create or refresh
|
|
52
|
-
--repo-slug <owner/repo>
|
|
53
|
+
--repo-slug <owner/repo> Forge repo slug
|
|
54
|
+
--forge-provider <provider> One of: github, gitea
|
|
55
|
+
--gitea-base-url <url> Base URL for a local/self-hosted Gitea instance
|
|
56
|
+
--gitea-token <token> Gitea API token written to profile runtime.env
|
|
57
|
+
--gitea-username <user> Gitea username written to profile runtime.env
|
|
58
|
+
--gitea-password <pass> Gitea password written to profile runtime.env
|
|
53
59
|
--repo-root <path> Local checkout to manage (defaults to current git root)
|
|
54
60
|
--agent-root <path> ACP-managed runtime root for this profile
|
|
55
61
|
--agent-repo-root <path> Clean ACP-managed anchor repo root
|
|
@@ -380,6 +386,11 @@ function parseSetupArgs(args) {
|
|
|
380
386
|
const options = {
|
|
381
387
|
profileId: "",
|
|
382
388
|
repoSlug: "",
|
|
389
|
+
forgeProvider: "",
|
|
390
|
+
giteaBaseUrl: "",
|
|
391
|
+
giteaToken: "",
|
|
392
|
+
giteaUsername: "",
|
|
393
|
+
giteaPassword: "",
|
|
383
394
|
repoRoot: "",
|
|
384
395
|
agentRoot: "",
|
|
385
396
|
agentRepoRoot: "",
|
|
@@ -415,6 +426,21 @@ function parseSetupArgs(args) {
|
|
|
415
426
|
case "--repo-slug":
|
|
416
427
|
options.repoSlug = args[++index] || "";
|
|
417
428
|
break;
|
|
429
|
+
case "--forge-provider":
|
|
430
|
+
options.forgeProvider = args[++index] || "";
|
|
431
|
+
break;
|
|
432
|
+
case "--gitea-base-url":
|
|
433
|
+
options.giteaBaseUrl = args[++index] || "";
|
|
434
|
+
break;
|
|
435
|
+
case "--gitea-token":
|
|
436
|
+
options.giteaToken = args[++index] || "";
|
|
437
|
+
break;
|
|
438
|
+
case "--gitea-username":
|
|
439
|
+
options.giteaUsername = args[++index] || "";
|
|
440
|
+
break;
|
|
441
|
+
case "--gitea-password":
|
|
442
|
+
options.giteaPassword = args[++index] || "";
|
|
443
|
+
break;
|
|
418
444
|
case "--repo-root":
|
|
419
445
|
options.repoRoot = args[++index] || "";
|
|
420
446
|
break;
|
|
@@ -581,7 +607,7 @@ Agent schedule: every 4h
|
|
|
581
607
|
|
|
582
608
|
- Run \`pnpm typecheck\` (or the repo-equivalent lint/typecheck) after code changes and record verification.
|
|
583
609
|
`,
|
|
584
|
-
labels: ["agent-
|
|
610
|
+
labels: ["agent-keep-open"]
|
|
585
611
|
},
|
|
586
612
|
{
|
|
587
613
|
key: "test-coverage",
|
|
@@ -597,7 +623,7 @@ Agent schedule: every 6h
|
|
|
597
623
|
|
|
598
624
|
- Run the narrowest relevant test command after code changes and record verification.
|
|
599
625
|
`,
|
|
600
|
-
labels: ["agent-
|
|
626
|
+
labels: ["agent-keep-open"]
|
|
601
627
|
},
|
|
602
628
|
{
|
|
603
629
|
key: "documentation",
|
|
@@ -613,7 +639,7 @@ Agent schedule: every 8h
|
|
|
613
639
|
|
|
614
640
|
- Run any doc-build or link-check command after changes and record verification.
|
|
615
641
|
`,
|
|
616
|
-
labels: ["agent-
|
|
642
|
+
labels: ["agent-keep-open"]
|
|
617
643
|
},
|
|
618
644
|
{
|
|
619
645
|
key: "dependency-audit",
|
|
@@ -629,7 +655,7 @@ Agent schedule: every 12h
|
|
|
629
655
|
|
|
630
656
|
- Run \`pnpm audit\` and \`pnpm install --frozen-lockfile\` after changes and record verification.
|
|
631
657
|
`,
|
|
632
|
-
labels: ["agent-
|
|
658
|
+
labels: ["agent-keep-open"]
|
|
633
659
|
},
|
|
634
660
|
{
|
|
635
661
|
key: "refactor",
|
|
@@ -645,7 +671,7 @@ Agent schedule: every 8h
|
|
|
645
671
|
|
|
646
672
|
- Run the narrowest relevant test command after refactoring and record verification.
|
|
647
673
|
`,
|
|
648
|
-
labels: ["agent-
|
|
674
|
+
labels: ["agent-keep-open"]
|
|
649
675
|
}
|
|
650
676
|
];
|
|
651
677
|
|
|
@@ -656,8 +682,8 @@ async function maybeCreateStarterIssues(options, config, prereq) {
|
|
|
656
682
|
return result;
|
|
657
683
|
}
|
|
658
684
|
|
|
659
|
-
if (!prereq.
|
|
660
|
-
result.reason =
|
|
685
|
+
if (!prereq.forgeAuthOk) {
|
|
686
|
+
result.reason = `${config.forge.provider}-auth-not-ready`;
|
|
661
687
|
return result;
|
|
662
688
|
}
|
|
663
689
|
|
|
@@ -701,24 +727,28 @@ async function maybeCreateStarterIssues(options, config, prereq) {
|
|
|
701
727
|
// Ensure labels exist
|
|
702
728
|
console.log("\nCreating labels and issues...");
|
|
703
729
|
const requiredLabels = [
|
|
704
|
-
{ name: "agent-ready", color: "0E8A16", description: "Ready for agent automation" },
|
|
705
730
|
{ name: "agent-keep-open", color: "D4C5F9", description: "Recurring issue — agent works on this continuously" }
|
|
706
731
|
];
|
|
707
|
-
|
|
708
|
-
|
|
732
|
+
if (config.forge.provider === "gitea") {
|
|
733
|
+
for (const label of requiredLabels) {
|
|
734
|
+
runForgeFlowConfigCommand(config, [
|
|
735
|
+
"flow_github_label_create",
|
|
736
|
+
shellQuote(config.repoSlug),
|
|
737
|
+
shellQuote(label.name),
|
|
738
|
+
shellQuote(label.description),
|
|
739
|
+
shellQuote(label.color)
|
|
740
|
+
]);
|
|
741
|
+
}
|
|
742
|
+
} else {
|
|
743
|
+
for (const label of requiredLabels) {
|
|
744
|
+
spawnSync("gh", ["label", "create", label.name, "--repo", config.repoSlug, "--description", label.description, "--color", label.color, "--force"], { stdio: "pipe", timeout: 15000 });
|
|
745
|
+
}
|
|
709
746
|
}
|
|
710
747
|
|
|
711
748
|
for (const { issue } of toCreate) {
|
|
712
|
-
const
|
|
713
|
-
|
|
714
|
-
"
|
|
715
|
-
"--title", issue.title,
|
|
716
|
-
"--body", issue.body,
|
|
717
|
-
"--label", issue.labels.join(",")
|
|
718
|
-
];
|
|
719
|
-
const ghResult = spawnSync("gh", ghArgs, { encoding: "utf8", stdio: "pipe", timeout: 30000 });
|
|
720
|
-
if (ghResult.status === 0) {
|
|
721
|
-
const url = (ghResult.stdout || "").trim();
|
|
749
|
+
const createResult = createStarterIssueOnForge(config, issue);
|
|
750
|
+
if (createResult.status === 0) {
|
|
751
|
+
const url = (createResult.stdout || "").trim();
|
|
722
752
|
result.created.push({ key: issue.key, title: issue.title, url });
|
|
723
753
|
console.log(` Created: ${issue.title}`);
|
|
724
754
|
if (url) {
|
|
@@ -726,7 +756,7 @@ async function maybeCreateStarterIssues(options, config, prereq) {
|
|
|
726
756
|
}
|
|
727
757
|
} else {
|
|
728
758
|
console.log(` Failed to create: ${issue.title}`);
|
|
729
|
-
const stderr = (
|
|
759
|
+
const stderr = (createResult.stderr || "").trim();
|
|
730
760
|
if (stderr) {
|
|
731
761
|
console.log(` ${stderr.split("\n")[0]}`);
|
|
732
762
|
}
|
|
@@ -739,6 +769,87 @@ async function maybeCreateStarterIssues(options, config, prereq) {
|
|
|
739
769
|
return result;
|
|
740
770
|
}
|
|
741
771
|
|
|
772
|
+
function buildForgeCommandEnv(config) {
|
|
773
|
+
const env = { ...process.env };
|
|
774
|
+
env.ACP_FORGE_PROVIDER = config.forge.provider;
|
|
775
|
+
env.F_LOSNING_FORGE_PROVIDER = config.forge.provider;
|
|
776
|
+
if (config.forge.provider === "gitea") {
|
|
777
|
+
if (config.forge.giteaBaseUrl) {
|
|
778
|
+
env.ACP_GITEA_BASE_URL = config.forge.giteaBaseUrl;
|
|
779
|
+
env.GITEA_BASE_URL = config.forge.giteaBaseUrl;
|
|
780
|
+
}
|
|
781
|
+
if (config.forge.giteaToken) {
|
|
782
|
+
env.ACP_GITEA_TOKEN = config.forge.giteaToken;
|
|
783
|
+
env.GITEA_TOKEN = config.forge.giteaToken;
|
|
784
|
+
}
|
|
785
|
+
if (config.forge.giteaUsername) {
|
|
786
|
+
env.ACP_GITEA_USERNAME = config.forge.giteaUsername;
|
|
787
|
+
env.GITEA_USERNAME = config.forge.giteaUsername;
|
|
788
|
+
}
|
|
789
|
+
if (config.forge.giteaPassword) {
|
|
790
|
+
env.ACP_GITEA_PASSWORD = config.forge.giteaPassword;
|
|
791
|
+
env.GITEA_PASSWORD = config.forge.giteaPassword;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
return env;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function runForgeFlowConfigCommand(config, commandParts, extraEnv = {}) {
|
|
798
|
+
const flowConfigLib = path.join(packageRoot, "tools", "bin", "flow-config-lib.sh");
|
|
799
|
+
const script = `set -euo pipefail\nsource ${shellQuote(flowConfigLib)}\n${commandParts.join(" ")}\n`;
|
|
800
|
+
return spawnSync("bash", ["-lc", script], {
|
|
801
|
+
encoding: "utf8",
|
|
802
|
+
stdio: "pipe",
|
|
803
|
+
timeout: 30000,
|
|
804
|
+
env: {
|
|
805
|
+
...buildForgeCommandEnv(config),
|
|
806
|
+
...extraEnv
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function createStarterIssueOnForge(config, issue) {
|
|
812
|
+
if (config.forge.provider !== "gitea") {
|
|
813
|
+
const ghArgs = [
|
|
814
|
+
"issue", "create",
|
|
815
|
+
"--repo", config.repoSlug,
|
|
816
|
+
"--title", issue.title,
|
|
817
|
+
"--body", issue.body,
|
|
818
|
+
"--label", issue.labels.join(",")
|
|
819
|
+
];
|
|
820
|
+
return spawnSync("gh", ghArgs, { encoding: "utf8", stdio: "pipe", timeout: 30000 });
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
const bodyFile = fs.mkdtempSync(path.join(os.tmpdir(), "acp-starter-issue-"));
|
|
824
|
+
const bodyPath = path.join(bodyFile, "body.md");
|
|
825
|
+
fs.writeFileSync(bodyPath, issue.body);
|
|
826
|
+
try {
|
|
827
|
+
const createResult = runForgeFlowConfigCommand(
|
|
828
|
+
config,
|
|
829
|
+
["flow_github_issue_create", shellQuote(config.repoSlug), "$ACP_ISSUE_TITLE", "$ACP_ISSUE_BODY_FILE"],
|
|
830
|
+
{
|
|
831
|
+
ACP_ISSUE_TITLE: issue.title,
|
|
832
|
+
ACP_ISSUE_BODY_FILE: bodyPath
|
|
833
|
+
}
|
|
834
|
+
);
|
|
835
|
+
const issueUrl = (createResult.stdout || "").trim();
|
|
836
|
+
if (createResult.status === 0 && issueUrl && issue.labels.length > 0) {
|
|
837
|
+
const issueNumber = issueUrl.split("/").pop();
|
|
838
|
+
for (const labelName of issue.labels) {
|
|
839
|
+
spawnSync("bash", [path.join(packageRoot, "tools", "bin", "agent-github-update-labels"), "--repo-slug", config.repoSlug, "--number", issueNumber, "--add", labelName], {
|
|
840
|
+
encoding: "utf8",
|
|
841
|
+
stdio: "pipe",
|
|
842
|
+
timeout: 30000,
|
|
843
|
+
env: buildForgeCommandEnv(config)
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
return createResult;
|
|
848
|
+
} finally {
|
|
849
|
+
fs.rmSync(bodyFile, { recursive: true, force: true });
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
742
853
|
function buildSetupPaths(platformHome, repoRoot, profileId, overrides) {
|
|
743
854
|
const agentRoot = path.resolve(overrides.agentRoot || path.join(platformHome, "projects", profileId));
|
|
744
855
|
const repoRootResolved = path.resolve(repoRoot);
|
|
@@ -753,12 +864,27 @@ function buildSetupPaths(platformHome, repoRoot, profileId, overrides) {
|
|
|
753
864
|
};
|
|
754
865
|
}
|
|
755
866
|
|
|
756
|
-
function collectPrereqStatus(codingWorker) {
|
|
757
|
-
const
|
|
867
|
+
function collectPrereqStatus(codingWorker, forge) {
|
|
868
|
+
const forgeProvider = (forge && forge.provider) || "github";
|
|
869
|
+
const requiredTools = ["bash", "git", "jq", "python3", "tmux"];
|
|
870
|
+
if (forgeProvider !== "gitea") {
|
|
871
|
+
requiredTools.push("gh");
|
|
872
|
+
}
|
|
758
873
|
const missingRequired = requiredTools.filter((tool) => !commandExists(tool));
|
|
759
874
|
const workerCommand = codingWorker;
|
|
760
875
|
const workerAvailable = commandExists(workerCommand);
|
|
761
876
|
const ghAuthResult = commandExists("gh") ? runCapture("gh", ["auth", "status"]) : { status: 1, stdout: "", stderr: "" };
|
|
877
|
+
const giteaAuthOk = forgeProvider !== "gitea"
|
|
878
|
+
? false
|
|
879
|
+
: Boolean(
|
|
880
|
+
forge &&
|
|
881
|
+
forge.giteaBaseUrl &&
|
|
882
|
+
(
|
|
883
|
+
forge.giteaToken ||
|
|
884
|
+
(forge.giteaUsername && forge.giteaPassword)
|
|
885
|
+
)
|
|
886
|
+
);
|
|
887
|
+
const forgeAuthOk = forgeProvider === "gitea" ? giteaAuthOk : ghAuthResult.status === 0;
|
|
762
888
|
|
|
763
889
|
return {
|
|
764
890
|
missingRequired,
|
|
@@ -766,6 +892,8 @@ function collectPrereqStatus(codingWorker) {
|
|
|
766
892
|
workerCommand,
|
|
767
893
|
workerAvailable,
|
|
768
894
|
ghAuthOk: ghAuthResult.status === 0,
|
|
895
|
+
forgeProvider,
|
|
896
|
+
forgeAuthOk,
|
|
769
897
|
ghAuthOutput: `${ghAuthResult.stdout}${ghAuthResult.stderr}`.trim()
|
|
770
898
|
};
|
|
771
899
|
}
|
|
@@ -774,6 +902,9 @@ function detectPackageManager() {
|
|
|
774
902
|
if (commandExists("brew")) {
|
|
775
903
|
return { name: "brew" };
|
|
776
904
|
}
|
|
905
|
+
if (commandExists("apt")) {
|
|
906
|
+
return { name: "apt" };
|
|
907
|
+
}
|
|
777
908
|
if (commandExists("apt-get")) {
|
|
778
909
|
return { name: "apt-get" };
|
|
779
910
|
}
|
|
@@ -789,6 +920,9 @@ function detectPackageManager() {
|
|
|
789
920
|
if (commandExists("zypper")) {
|
|
790
921
|
return { name: "zypper" };
|
|
791
922
|
}
|
|
923
|
+
if (commandExists("apk")) {
|
|
924
|
+
return { name: "apk" };
|
|
925
|
+
}
|
|
792
926
|
return null;
|
|
793
927
|
}
|
|
794
928
|
|
|
@@ -812,6 +946,24 @@ function dependencyPackageMap(managerName) {
|
|
|
812
946
|
python3: "python3",
|
|
813
947
|
tmux: "tmux"
|
|
814
948
|
};
|
|
949
|
+
case "apt":
|
|
950
|
+
return {
|
|
951
|
+
bash: "bash",
|
|
952
|
+
git: "git",
|
|
953
|
+
gh: "gh",
|
|
954
|
+
jq: "jq",
|
|
955
|
+
python3: "python3",
|
|
956
|
+
tmux: "tmux"
|
|
957
|
+
};
|
|
958
|
+
case "apk":
|
|
959
|
+
return {
|
|
960
|
+
bash: "bash",
|
|
961
|
+
git: "git",
|
|
962
|
+
gh: "gh",
|
|
963
|
+
jq: "jq",
|
|
964
|
+
python3: "python3",
|
|
965
|
+
tmux: "tmux"
|
|
966
|
+
};
|
|
815
967
|
case "dnf":
|
|
816
968
|
case "yum":
|
|
817
969
|
return {
|
|
@@ -870,6 +1022,10 @@ function buildDependencyInstallPlan(missingTools) {
|
|
|
870
1022
|
commands.push([...prefix, "apt-get", "update"]);
|
|
871
1023
|
commands.push([...prefix, "apt-get", "install", "-y", ...packages]);
|
|
872
1024
|
break;
|
|
1025
|
+
case "apt":
|
|
1026
|
+
commands.push([...prefix, "apt", "update"]);
|
|
1027
|
+
commands.push([...prefix, "apt", "install", "-y", ...packages]);
|
|
1028
|
+
break;
|
|
873
1029
|
case "dnf":
|
|
874
1030
|
commands.push([...prefix, "dnf", "install", "-y", ...packages]);
|
|
875
1031
|
break;
|
|
@@ -882,6 +1038,9 @@ function buildDependencyInstallPlan(missingTools) {
|
|
|
882
1038
|
case "zypper":
|
|
883
1039
|
commands.push([...prefix, "zypper", "install", "-y", ...packages]);
|
|
884
1040
|
break;
|
|
1041
|
+
case "apk":
|
|
1042
|
+
commands.push([...prefix, "apk", "add", "--no-cache", ...packages]);
|
|
1043
|
+
break;
|
|
885
1044
|
default:
|
|
886
1045
|
return null;
|
|
887
1046
|
}
|
|
@@ -923,7 +1082,7 @@ async function maybeInstallMissingDependencies(options, prereq) {
|
|
|
923
1082
|
if (!plan) {
|
|
924
1083
|
console.log("\nACP found missing core dependencies but cannot install them automatically on this machine.");
|
|
925
1084
|
console.log(`- missing tools: ${prereq.missingRequired.join(", ")}`);
|
|
926
|
-
console.log("- supported auto-install package managers today: brew, apt-get, dnf, yum, pacman, zypper");
|
|
1085
|
+
console.log("- supported auto-install package managers today: brew, apt, apt-get, dnf, yum, pacman, zypper, apk");
|
|
927
1086
|
return {
|
|
928
1087
|
status: "unavailable",
|
|
929
1088
|
reason: "no-supported-package-manager",
|
|
@@ -990,6 +1149,9 @@ async function maybeInstallMissingDependencies(options, prereq) {
|
|
|
990
1149
|
}
|
|
991
1150
|
|
|
992
1151
|
async function maybeRunGithubAuthLogin(options, prereq) {
|
|
1152
|
+
if (prereq.forgeProvider === "gitea") {
|
|
1153
|
+
return { status: prereq.forgeAuthOk ? "not-needed" : "skipped", reason: prereq.forgeAuthOk ? "" : "gitea-auth-not-ready" };
|
|
1154
|
+
}
|
|
993
1155
|
if (prereq.ghAuthOk) {
|
|
994
1156
|
return { status: "not-needed", reason: "" };
|
|
995
1157
|
}
|
|
@@ -1326,7 +1488,7 @@ function printPrereqSummary(prereq) {
|
|
|
1326
1488
|
console.log("\nPrerequisite check");
|
|
1327
1489
|
console.log(`- core tools: ${prereq.coreToolsOk ? "ok" : `missing ${prereq.missingRequired.join(", ")}`}`);
|
|
1328
1490
|
console.log(`- worker backend (${prereq.workerCommand}): ${prereq.workerAvailable ? "found" : "missing on PATH"}`);
|
|
1329
|
-
console.log(`- GitHub auth: ${prereq.
|
|
1491
|
+
console.log(`- ${prereq.forgeProvider === "gitea" ? "Gitea auth" : "GitHub auth"}: ${prereq.forgeAuthOk ? "ok" : "not ready"}`);
|
|
1330
1492
|
console.log(`- backend note: ${backendReadinessHint(prereq.workerCommand)}`);
|
|
1331
1493
|
}
|
|
1332
1494
|
|
|
@@ -1434,7 +1596,11 @@ function buildScopedContext(context, profileId) {
|
|
|
1434
1596
|
function renderSetupSummary(config) {
|
|
1435
1597
|
console.log("\nSetup plan");
|
|
1436
1598
|
console.log(`- profile id: ${config.profileId}`);
|
|
1599
|
+
console.log(`- forge provider: ${config.forge.provider}`);
|
|
1437
1600
|
console.log(`- repo slug: ${config.repoSlug}`);
|
|
1601
|
+
if (config.forge.provider === "gitea") {
|
|
1602
|
+
console.log(`- gitea base url: ${config.forge.giteaBaseUrl || "(not set)"}`);
|
|
1603
|
+
}
|
|
1438
1604
|
console.log(`- repo root: ${config.paths.repoRoot}`);
|
|
1439
1605
|
console.log(`- agent root: ${config.paths.agentRoot}`);
|
|
1440
1606
|
console.log(`- agent repo root: ${config.paths.agentRepoRoot}`);
|
|
@@ -1442,6 +1608,10 @@ function renderSetupSummary(config) {
|
|
|
1442
1608
|
console.log(`- coding worker: ${config.codingWorker}`);
|
|
1443
1609
|
}
|
|
1444
1610
|
|
|
1611
|
+
function forgeAuthLabel(config) {
|
|
1612
|
+
return config.forge.provider === "gitea" ? "Gitea auth" : "GitHub auth";
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1445
1615
|
function planStatusWithReason(status, reason = "") {
|
|
1446
1616
|
return {
|
|
1447
1617
|
status,
|
|
@@ -1483,8 +1653,10 @@ function buildSetupDryRunPlan(options, context, config) {
|
|
|
1483
1653
|
}
|
|
1484
1654
|
|
|
1485
1655
|
let githubAuthAction = planStatusWithReason("not-needed");
|
|
1486
|
-
if (!prereq.
|
|
1487
|
-
if (
|
|
1656
|
+
if (!prereq.forgeAuthOk) {
|
|
1657
|
+
if (config.forge.provider === "gitea") {
|
|
1658
|
+
githubAuthAction = planStatusWithReason("blocked", "gitea-auth-not-ready");
|
|
1659
|
+
} else if (!commandExists("gh")) {
|
|
1488
1660
|
githubAuthAction = planStatusWithReason("blocked", "gh-missing");
|
|
1489
1661
|
} else if (options.ghAuthLogin === false) {
|
|
1490
1662
|
githubAuthAction = planStatusWithReason("skipped", "disabled");
|
|
@@ -1503,8 +1675,8 @@ function buildSetupDryRunPlan(options, context, config) {
|
|
|
1503
1675
|
runtimeStartAction = planStatusWithReason("blocked", `missing-worker:${prereq.workerCommand}`);
|
|
1504
1676
|
} else if (anchorSync.status !== "ok") {
|
|
1505
1677
|
runtimeStartAction = planStatusWithReason("blocked", `anchor-sync-${anchorSync.reason}`);
|
|
1506
|
-
} else if (!prereq.
|
|
1507
|
-
runtimeStartAction = planStatusWithReason("blocked",
|
|
1678
|
+
} else if (!prereq.forgeAuthOk) {
|
|
1679
|
+
runtimeStartAction = planStatusWithReason("blocked", `${config.forge.provider}-auth-not-ready`);
|
|
1508
1680
|
} else {
|
|
1509
1681
|
runtimeStartAction = planStatusWithReason("would-run");
|
|
1510
1682
|
}
|
|
@@ -1556,9 +1728,13 @@ function printSetupDryRunPlan(context, config, plan) {
|
|
|
1556
1728
|
if (plan.workerAction.status !== "not-needed" && plan.workerInstallPlan && plan.workerInstallPlan.commands.length > 0) {
|
|
1557
1729
|
console.log(` command preview: ${plan.workerInstallPlan.commands.map(formatCommand).join(" && ")}`);
|
|
1558
1730
|
}
|
|
1559
|
-
console.log(`-
|
|
1731
|
+
console.log(`- ${forgeAuthLabel(config)} step: ${plan.githubAuthAction.status}${plan.githubAuthAction.reason ? ` (${plan.githubAuthAction.reason})` : ""}`);
|
|
1560
1732
|
if (plan.githubAuthAction.status !== "not-needed") {
|
|
1561
|
-
|
|
1733
|
+
if (config.forge.provider === "github") {
|
|
1734
|
+
console.log(` command preview: gh auth login`);
|
|
1735
|
+
} else {
|
|
1736
|
+
console.log(` next step: pass --gitea-base-url plus --gitea-token or --gitea-username/--gitea-password`);
|
|
1737
|
+
}
|
|
1562
1738
|
}
|
|
1563
1739
|
console.log(`- runtime start: ${plan.runtimeStartAction.status}${plan.runtimeStartAction.reason ? ` (${plan.runtimeStartAction.reason})` : ""}`);
|
|
1564
1740
|
if (process.platform === "darwin") {
|
|
@@ -1572,6 +1748,7 @@ function buildSetupResultPayload(params) {
|
|
|
1572
1748
|
setupMode: params.setupMode,
|
|
1573
1749
|
profileId: params.profileId,
|
|
1574
1750
|
repoSlug: params.repoSlug,
|
|
1751
|
+
forge: params.forge,
|
|
1575
1752
|
codingWorker: params.codingWorker,
|
|
1576
1753
|
profileExists: params.profileExists,
|
|
1577
1754
|
paths: {
|
|
@@ -1648,8 +1825,8 @@ function collectFinalSetupIssues(config, prereq, doctorKv, runtimeStartStatus) {
|
|
|
1648
1825
|
if (!prereq.workerAvailable) {
|
|
1649
1826
|
issues.push(`missing worker backend on PATH: ${prereq.workerCommand}`);
|
|
1650
1827
|
}
|
|
1651
|
-
if (!prereq.
|
|
1652
|
-
issues.push("GitHub CLI is not authenticated");
|
|
1828
|
+
if (!prereq.forgeAuthOk) {
|
|
1829
|
+
issues.push(config.forge.provider === "gitea" ? "Gitea auth is not configured" : "GitHub CLI is not authenticated");
|
|
1653
1830
|
}
|
|
1654
1831
|
if ((doctorKv.DOCTOR_STATUS || "") !== "ok") {
|
|
1655
1832
|
issues.push(`doctor status is ${doctorKv.DOCTOR_STATUS || "unknown"}`);
|
|
@@ -1692,8 +1869,12 @@ async function maybeRunFinalSetupFixups(options, scopedContext, config, currentS
|
|
|
1692
1869
|
else if (worker === "claude") console.log(" Fix: npm install -g @anthropic-ai/claude-code && claude auth login");
|
|
1693
1870
|
else console.log(` Fix: install ${worker} and add it to PATH`);
|
|
1694
1871
|
}
|
|
1695
|
-
if (!currentState.prereq.
|
|
1696
|
-
|
|
1872
|
+
if (!currentState.prereq.forgeAuthOk) {
|
|
1873
|
+
if (config.forge.provider === "gitea") {
|
|
1874
|
+
console.log(" Fix: pass --gitea-base-url plus --gitea-token or --gitea-username/--gitea-password");
|
|
1875
|
+
} else {
|
|
1876
|
+
console.log(" Fix: run gh auth login");
|
|
1877
|
+
}
|
|
1697
1878
|
}
|
|
1698
1879
|
|
|
1699
1880
|
if (!options.interactive) {
|
|
@@ -1736,19 +1917,19 @@ async function maybeRunFinalSetupFixups(options, scopedContext, config, currentS
|
|
|
1736
1917
|
if (!prereq.coreToolsOk) {
|
|
1737
1918
|
actions.push("install-core-tools");
|
|
1738
1919
|
dependencyInstall = await maybeInstallMissingDependencies({ ...options, installMissingDeps: true, interactive: false }, prereq);
|
|
1739
|
-
prereq = collectPrereqStatus(config.codingWorker);
|
|
1920
|
+
prereq = collectPrereqStatus(config.codingWorker, config.forge);
|
|
1740
1921
|
}
|
|
1741
1922
|
|
|
1742
1923
|
if (!prereq.workerAvailable) {
|
|
1743
1924
|
actions.push("install-worker-backend");
|
|
1744
1925
|
workerSetupStep = await maybeShowWorkerSetupGuide({ ...options, installMissingBackend: true, interactive: options.interactive }, prereq);
|
|
1745
|
-
prereq = collectPrereqStatus(config.codingWorker);
|
|
1926
|
+
prereq = collectPrereqStatus(config.codingWorker, config.forge);
|
|
1746
1927
|
}
|
|
1747
1928
|
|
|
1748
|
-
if (!prereq.
|
|
1929
|
+
if (!prereq.forgeAuthOk) {
|
|
1749
1930
|
actions.push("github-auth-login");
|
|
1750
1931
|
githubAuthStep = await maybeRunGithubAuthLogin({ ...options, ghAuthLogin: true, interactive: false }, prereq);
|
|
1751
|
-
prereq = collectPrereqStatus(config.codingWorker);
|
|
1932
|
+
prereq = collectPrereqStatus(config.codingWorker, config.forge);
|
|
1752
1933
|
}
|
|
1753
1934
|
|
|
1754
1935
|
if ((doctorKv.DOCTOR_STATUS || "") !== "ok") {
|
|
@@ -1768,9 +1949,9 @@ async function maybeRunFinalSetupFixups(options, scopedContext, config, currentS
|
|
|
1768
1949
|
} else if (anchorSync && anchorSync.status !== "ok") {
|
|
1769
1950
|
runtimeStartStatus = "skipped";
|
|
1770
1951
|
runtimeStartReason = `anchor-sync-${anchorSync.reason}`;
|
|
1771
|
-
} else if (!prereq.
|
|
1952
|
+
} else if (!prereq.forgeAuthOk) {
|
|
1772
1953
|
runtimeStartStatus = "skipped";
|
|
1773
|
-
runtimeStartReason =
|
|
1954
|
+
runtimeStartReason = `${config.forge.provider}-auth-not-ready`;
|
|
1774
1955
|
} else if ((doctorKv.DOCTOR_STATUS || "") !== "ok") {
|
|
1775
1956
|
runtimeStartStatus = "skipped";
|
|
1776
1957
|
runtimeStartReason = `doctor-${doctorKv.DOCTOR_STATUS || "not-ok"}`;
|
|
@@ -1821,16 +2002,29 @@ async function collectSetupConfig(options, context) {
|
|
|
1821
2002
|
const detectedRepoSlug = options.repoSlug || detectRepoSlug(detectedRepoRoot);
|
|
1822
2003
|
const suggestedProfileId = options.profileId || sanitizeProfileId((detectedRepoSlug.split("/").pop() || path.basename(detectedRepoRoot)));
|
|
1823
2004
|
const suggestedWorker = options.codingWorker || detectPreferredWorker();
|
|
2005
|
+
const detectedForgeProvider = options.forgeProvider || process.env.ACP_FORGE_PROVIDER || "github";
|
|
2006
|
+
const defaultGiteaBaseUrl = options.giteaBaseUrl || process.env.ACP_GITEA_BASE_URL || process.env.GITEA_BASE_URL || "http://127.0.0.1:3000";
|
|
1824
2007
|
|
|
1825
2008
|
let repoRoot = detectedRepoRoot;
|
|
1826
2009
|
let repoSlug = detectedRepoSlug;
|
|
1827
2010
|
let profileId = suggestedProfileId;
|
|
1828
2011
|
let codingWorker = suggestedWorker;
|
|
1829
|
-
|
|
1830
|
-
|
|
2012
|
+
let forgeProvider = detectedForgeProvider;
|
|
2013
|
+
let giteaBaseUrl = defaultGiteaBaseUrl;
|
|
2014
|
+
let giteaToken = options.giteaToken || process.env.ACP_GITEA_TOKEN || process.env.GITEA_TOKEN || "";
|
|
2015
|
+
let giteaUsername = options.giteaUsername || process.env.ACP_GITEA_USERNAME || process.env.GITEA_USERNAME || "";
|
|
2016
|
+
let giteaPassword = options.giteaPassword || process.env.ACP_GITEA_PASSWORD || process.env.GITEA_PASSWORD || "";
|
|
2017
|
+
|
|
2018
|
+
const detectedRepoRootExists = fs.existsSync(detectedRepoRoot);
|
|
2019
|
+
if (!detectedRepoRootExists && !options.allowMissingRepo) {
|
|
1831
2020
|
throw new Error(`setup repo root does not exist: ${detectedRepoRoot}`);
|
|
1832
2021
|
}
|
|
1833
2022
|
|
|
2023
|
+
if (options.allowMissingRepo && !detectedRepoRootExists) {
|
|
2024
|
+
console.log(`\nSource repo root not found yet: ${detectedRepoRoot}`);
|
|
2025
|
+
console.log("- continuing because --allow-missing-repo was set; profile adopt will run in missing-repo mode.");
|
|
2026
|
+
}
|
|
2027
|
+
|
|
1834
2028
|
if (!options.interactive) {
|
|
1835
2029
|
if (!repoSlug) {
|
|
1836
2030
|
throw new Error("setup could not detect --repo-slug automatically; pass --repo-slug <owner/repo> or run interactively inside a git checkout with origin set");
|
|
@@ -1842,7 +2036,22 @@ async function collectSetupConfig(options, context) {
|
|
|
1842
2036
|
printWizardStep(1, 4, "Project details");
|
|
1843
2037
|
|
|
1844
2038
|
repoRoot = path.resolve(await promptText(rl, "Local repo root", detectedRepoRoot));
|
|
1845
|
-
|
|
2039
|
+
let forgeInput = forgeProvider;
|
|
2040
|
+
while (!["github", "gitea"].includes(forgeInput)) {
|
|
2041
|
+
forgeInput = await promptText(rl, "Forge provider (github / gitea)", forgeProvider || "github");
|
|
2042
|
+
}
|
|
2043
|
+
forgeProvider = forgeInput;
|
|
2044
|
+
repoSlug = await promptText(rl, "Forge repo slug", repoSlug || "");
|
|
2045
|
+
if (forgeProvider === "gitea") {
|
|
2046
|
+
giteaBaseUrl = await promptText(rl, "Gitea base URL", giteaBaseUrl);
|
|
2047
|
+
giteaToken = await promptText(rl, "Gitea token (Enter to skip)", giteaToken);
|
|
2048
|
+
if (!giteaToken) {
|
|
2049
|
+
giteaUsername = await promptText(rl, "Gitea username (Enter to skip)", giteaUsername);
|
|
2050
|
+
if (giteaUsername) {
|
|
2051
|
+
giteaPassword = await promptText(rl, "Gitea password (Enter to skip)", giteaPassword);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
1846
2055
|
profileId = sanitizeProfileId(await promptText(rl, "Profile id", profileId));
|
|
1847
2056
|
|
|
1848
2057
|
let workerInput = codingWorker;
|
|
@@ -1860,13 +2069,21 @@ async function collectSetupConfig(options, context) {
|
|
|
1860
2069
|
}
|
|
1861
2070
|
|
|
1862
2071
|
const paths = buildSetupPaths(context.platformHome, repoRoot, profileId, options);
|
|
1863
|
-
const
|
|
2072
|
+
const forge = {
|
|
2073
|
+
provider: forgeProvider,
|
|
2074
|
+
giteaBaseUrl,
|
|
2075
|
+
giteaToken,
|
|
2076
|
+
giteaUsername,
|
|
2077
|
+
giteaPassword
|
|
2078
|
+
};
|
|
2079
|
+
const prereq = collectPrereqStatus(codingWorker, forge);
|
|
1864
2080
|
const config = {
|
|
1865
2081
|
profileId,
|
|
1866
2082
|
repoSlug,
|
|
1867
2083
|
repoRoot,
|
|
1868
2084
|
codingWorker,
|
|
1869
2085
|
paths,
|
|
2086
|
+
forge,
|
|
1870
2087
|
prereq
|
|
1871
2088
|
};
|
|
1872
2089
|
|
|
@@ -1879,7 +2096,7 @@ async function collectSetupConfig(options, context) {
|
|
|
1879
2096
|
printPrereqSummary(prereq);
|
|
1880
2097
|
const rl = createPromptInterface();
|
|
1881
2098
|
try {
|
|
1882
|
-
if (!prereq.coreToolsOk || !prereq.workerAvailable || !prereq.
|
|
2099
|
+
if (!prereq.coreToolsOk || !prereq.workerAvailable || !prereq.forgeAuthOk) {
|
|
1883
2100
|
console.log("\nACP can still scaffold the profile now, but runtime start may be skipped until these checks are green.");
|
|
1884
2101
|
}
|
|
1885
2102
|
const shouldContinue = await promptYesNo(rl, "Continue with these values", true);
|
|
@@ -2026,7 +2243,7 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
2026
2243
|
workerBackendInstallExample: plan.workerGuide.installExamples[0] || "",
|
|
2027
2244
|
workerBackendAuthExample: plan.workerGuide.authExamples[0] || "",
|
|
2028
2245
|
workerBackendVerifyExample: plan.workerGuide.verifyExamples[0] || "",
|
|
2029
|
-
githubAuthStatus: plan.prereq.
|
|
2246
|
+
githubAuthStatus: plan.prereq.forgeAuthOk ? "ok" : "not-ready",
|
|
2030
2247
|
githubAuthStepStatus: plan.githubAuthAction.status,
|
|
2031
2248
|
githubAuthStepReason: plan.githubAuthAction.reason || "",
|
|
2032
2249
|
dependencyInstallStatus: plan.dependencyAction.status,
|
|
@@ -2050,6 +2267,7 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
2050
2267
|
console.log(`SETUP_STATUS=dry-run`);
|
|
2051
2268
|
console.log(`SETUP_MODE=dry-run`);
|
|
2052
2269
|
console.log(`PROFILE_ID=${config.profileId}`);
|
|
2270
|
+
console.log(`FORGE_PROVIDER=${config.forge.provider}`);
|
|
2053
2271
|
console.log(`REPO_SLUG=${config.repoSlug}`);
|
|
2054
2272
|
console.log(`REPO_ROOT=${config.paths.repoRoot}`);
|
|
2055
2273
|
console.log(`AGENT_ROOT=${config.paths.agentRoot}`);
|
|
@@ -2068,7 +2286,7 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
2068
2286
|
}
|
|
2069
2287
|
console.log(`WORKER_BACKEND_COMMAND=${plan.prereq.workerCommand}`);
|
|
2070
2288
|
console.log(`WORKER_BACKEND_STATUS=${plan.prereq.workerAvailable ? "ok" : "missing"}`);
|
|
2071
|
-
console.log(`GITHUB_AUTH_STATUS=${plan.prereq.
|
|
2289
|
+
console.log(`GITHUB_AUTH_STATUS=${plan.prereq.forgeAuthOk ? "ok" : "not-ready"}`);
|
|
2072
2290
|
console.log(`DEPENDENCY_INSTALL_STATUS=${plan.dependencyAction.status}`);
|
|
2073
2291
|
if (plan.dependencyAction.reason) {
|
|
2074
2292
|
console.log(`DEPENDENCY_INSTALL_REASON=${plan.dependencyAction.reason}`);
|
|
@@ -2134,16 +2352,16 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
2134
2352
|
console.error("dependency installation failed");
|
|
2135
2353
|
return 1;
|
|
2136
2354
|
}
|
|
2137
|
-
prereq = collectPrereqStatus(config.codingWorker);
|
|
2355
|
+
prereq = collectPrereqStatus(config.codingWorker, config.forge);
|
|
2138
2356
|
|
|
2139
2357
|
let githubAuthStep = await maybeRunGithubAuthLogin(options, prereq);
|
|
2140
2358
|
if (githubAuthStep.status === "failed") {
|
|
2141
|
-
console.error("GitHub authentication failed");
|
|
2359
|
+
console.error(config.forge.provider === "gitea" ? "Gitea authentication failed" : "GitHub authentication failed");
|
|
2142
2360
|
return 1;
|
|
2143
2361
|
}
|
|
2144
|
-
prereq = collectPrereqStatus(config.codingWorker);
|
|
2362
|
+
prereq = collectPrereqStatus(config.codingWorker, config.forge);
|
|
2145
2363
|
let workerSetupStep = await maybeShowWorkerSetupGuide(options, prereq);
|
|
2146
|
-
prereq = collectPrereqStatus(config.codingWorker);
|
|
2364
|
+
prereq = collectPrereqStatus(config.codingWorker, config.forge);
|
|
2147
2365
|
|
|
2148
2366
|
// Check OpenRouter API key when openclaw or pi is selected
|
|
2149
2367
|
if ((config.codingWorker === "openclaw" || config.codingWorker === "pi") && !process.env.OPENROUTER_API_KEY) {
|
|
@@ -2215,6 +2433,7 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
2215
2433
|
const initArgs = [
|
|
2216
2434
|
"--profile-id", config.profileId,
|
|
2217
2435
|
"--repo-slug", config.repoSlug,
|
|
2436
|
+
"--forge-provider", config.forge.provider,
|
|
2218
2437
|
"--repo-root", config.paths.repoRoot,
|
|
2219
2438
|
"--agent-root", config.paths.agentRoot,
|
|
2220
2439
|
"--agent-repo-root", config.paths.agentRepoRoot,
|
|
@@ -2224,6 +2443,12 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
2224
2443
|
"--source-repo-root", config.paths.sourceRepoRoot,
|
|
2225
2444
|
"--coding-worker", config.codingWorker
|
|
2226
2445
|
];
|
|
2446
|
+
if (config.forge.provider === "gitea") {
|
|
2447
|
+
if (config.forge.giteaBaseUrl) initArgs.push("--gitea-base-url", config.forge.giteaBaseUrl);
|
|
2448
|
+
if (config.forge.giteaToken) initArgs.push("--gitea-token", config.forge.giteaToken);
|
|
2449
|
+
if (config.forge.giteaUsername) initArgs.push("--gitea-username", config.forge.giteaUsername);
|
|
2450
|
+
if (config.forge.giteaPassword) initArgs.push("--gitea-password", config.forge.giteaPassword);
|
|
2451
|
+
}
|
|
2227
2452
|
if (options.force) {
|
|
2228
2453
|
initArgs.push("--force");
|
|
2229
2454
|
}
|
|
@@ -2257,9 +2482,13 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
2257
2482
|
} else if (anchorSync.status !== "ok") {
|
|
2258
2483
|
runtimeStartReason = `anchor-sync-${anchorSync.reason}`;
|
|
2259
2484
|
console.log("runtime start skipped: ACP deferred anchor repo sync for this setup run.");
|
|
2260
|
-
} else if (!prereq.
|
|
2261
|
-
runtimeStartReason =
|
|
2262
|
-
|
|
2485
|
+
} else if (!prereq.forgeAuthOk) {
|
|
2486
|
+
runtimeStartReason = `${config.forge.provider}-auth-not-ready`;
|
|
2487
|
+
if (config.forge.provider === "gitea") {
|
|
2488
|
+
console.log("runtime start skipped: Gitea auth is not configured yet. Pass --gitea-base-url and --gitea-token (or username/password), then re-run setup or start the runtime afterwards.");
|
|
2489
|
+
} else {
|
|
2490
|
+
console.log("runtime start skipped: GitHub CLI is not authenticated yet. Run `gh auth login` and start the runtime afterwards.");
|
|
2491
|
+
}
|
|
2263
2492
|
} else {
|
|
2264
2493
|
runSetupStep(scopedContext, "Start the runtime", "tools/bin/project-runtimectl.sh", ["start", "--profile-id", config.profileId], { useRuntimeCopy: true });
|
|
2265
2494
|
const runtimeStatusOutput = runSetupStep(scopedContext, "Read back runtime status", "tools/bin/project-runtimectl.sh", ["status", "--profile-id", config.profileId], { useRuntimeCopy: true });
|
|
@@ -2365,6 +2594,7 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
2365
2594
|
setupMode: "run",
|
|
2366
2595
|
profileId: config.profileId,
|
|
2367
2596
|
repoSlug: config.repoSlug,
|
|
2597
|
+
forge: config.forge,
|
|
2368
2598
|
codingWorker: config.codingWorker,
|
|
2369
2599
|
profileExists: true,
|
|
2370
2600
|
repoRoot: config.paths.repoRoot,
|
|
@@ -2389,7 +2619,7 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
2389
2619
|
workerBackendInstallExample: workerSetupStep.guide.installExamples[0] || "",
|
|
2390
2620
|
workerBackendAuthExample: workerSetupStep.guide.authExamples[0] || "",
|
|
2391
2621
|
workerBackendVerifyExample: workerSetupStep.guide.verifyExamples[0] || "",
|
|
2392
|
-
githubAuthStatus: prereq.
|
|
2622
|
+
githubAuthStatus: prereq.forgeAuthOk ? "ok" : "not-ready",
|
|
2393
2623
|
githubAuthStepStatus: githubAuthStep.status,
|
|
2394
2624
|
githubAuthStepReason: githubAuthStep.reason || "",
|
|
2395
2625
|
dependencyInstallStatus: dependencyInstall.status,
|
|
@@ -2420,7 +2650,10 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
2420
2650
|
console.log(` Runtime : ${context.runtimeHome}`);
|
|
2421
2651
|
|
|
2422
2652
|
const pendingItems = [];
|
|
2423
|
-
if (!prereq.
|
|
2653
|
+
if (!prereq.forgeAuthOk) {
|
|
2654
|
+
if (config.forge.provider === "gitea") pendingItems.push("Gitea auth not configured — rerun setup with --gitea-base-url and --gitea-token");
|
|
2655
|
+
else pendingItems.push("GitHub CLI not authenticated — run: gh auth login");
|
|
2656
|
+
}
|
|
2424
2657
|
if (!prereq.workerAvailable) pendingItems.push(`${config.codingWorker} not found on PATH — install it before starting`);
|
|
2425
2658
|
if (config.codingWorker === "openclaw" && !process.env.OPENROUTER_API_KEY) {
|
|
2426
2659
|
pendingItems.push("OPENROUTER_API_KEY not set — required for openclaw workers");
|
|
@@ -2459,7 +2692,7 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
2459
2692
|
}
|
|
2460
2693
|
} else {
|
|
2461
2694
|
console.log("\n Getting started:");
|
|
2462
|
-
console.log(` 1.
|
|
2695
|
+
console.log(` 1. Leave a normal issue open in ${config.repoSlug}, or add 'agent-keep-open' for a recurring issue`);
|
|
2463
2696
|
console.log(" 2. ACP picks it up automatically, assigns a worker, and opens a PR");
|
|
2464
2697
|
console.log(" 3. Watch progress in the dashboard or with 'runtime status'");
|
|
2465
2698
|
}
|
|
@@ -2472,11 +2705,13 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
2472
2705
|
// Machine-readable KV output for non-interactive / scripted runs
|
|
2473
2706
|
console.log("\nSetup complete.");
|
|
2474
2707
|
console.log(`- profile: ${config.profileId}`);
|
|
2708
|
+
console.log(`- forge provider: ${config.forge.provider}`);
|
|
2475
2709
|
console.log(`- repo: ${config.repoSlug}`);
|
|
2476
2710
|
console.log(`- runtime home: ${context.runtimeHome}`);
|
|
2477
2711
|
|
|
2478
2712
|
console.log(`SETUP_STATUS=ok`);
|
|
2479
2713
|
console.log(`PROFILE_ID=${config.profileId}`);
|
|
2714
|
+
console.log(`FORGE_PROVIDER=${config.forge.provider}`);
|
|
2480
2715
|
console.log(`REPO_SLUG=${config.repoSlug}`);
|
|
2481
2716
|
console.log(`REPO_ROOT=${config.paths.repoRoot}`);
|
|
2482
2717
|
console.log(`AGENT_ROOT=${config.paths.agentRoot}`);
|
|
@@ -2521,7 +2756,7 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
2521
2756
|
if (workerSetupStep.guide.verifyExamples[0]) {
|
|
2522
2757
|
console.log(`WORKER_BACKEND_VERIFY_EXAMPLE=${workerSetupStep.guide.verifyExamples[0]}`);
|
|
2523
2758
|
}
|
|
2524
|
-
console.log(`GITHUB_AUTH_STATUS=${prereq.
|
|
2759
|
+
console.log(`GITHUB_AUTH_STATUS=${prereq.forgeAuthOk ? "ok" : "not-ready"}`);
|
|
2525
2760
|
console.log(`FINAL_FIXUP_STATUS=${finalFixup.status}`);
|
|
2526
2761
|
console.log(`FINAL_FIXUP_ACTIONS=${finalFixup.actions.join(",")}`);
|
|
2527
2762
|
console.log(`DEPENDENCY_INSTALL_STATUS=${dependencyInstall.status}`);
|