@loucompanion/forge-bridge 0.1.1-dev.03b8ba168599 → 0.1.1-dev.0b87deed581c
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/bridge/bridge.service.d.ts +1 -0
- package/dist/bridge/bridge.service.js +213 -25
- package/dist/bridge/bridge.types.d.ts +13 -2
- package/dist/cli/cli.service.d.ts +4 -0
- package/dist/cli/cli.service.js +245 -10
- package/dist/cli/local-config.d.ts +1 -0
- package/dist/cli/local-config.js +36 -4
- package/dist/config.js +1 -1
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +1 -0
- package/dist/session/internals.d.ts +3 -1
- package/dist/session/internals.js +1 -0
- package/dist/session/phase-completion.d.ts +126 -0
- package/dist/session/phase-completion.js +3225 -0
- package/dist/session/provider-cli/provider-cli.base.d.ts +4 -3
- package/dist/session/provider-cli/provider-cli.service.d.ts +23 -5
- package/dist/session/provider-cli/provider-cli.service.js +2363 -235
- package/dist/session/provider-cli/provider-cli.types.d.ts +6 -0
- package/dist/session/provider-cli/provider-usage.d.ts +25 -0
- package/dist/session/provider-cli/provider-usage.js +64 -0
- package/dist/session/qa-sidecar-validation.d.ts +3 -0
- package/dist/session/qa-sidecar-validation.js +44 -0
- package/dist/session/session.service.js +56 -16
- package/dist/session/session.types.d.ts +132 -0
- package/dist/session/session.types.js +261 -2
- package/dist/shared/cursor-agent/cursor-agent.launch.d.ts +9 -0
- package/dist/shared/cursor-agent/cursor-agent.launch.js +66 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worktree/artifact-injector.d.ts +2 -1
- package/dist/worktree/artifact-injector.js +125 -9
- package/dist/worktree/worktree.service.d.ts +1 -0
- package/dist/worktree/worktree.service.js +132 -6
- package/dist/worktree/worktree.types.d.ts +8 -1
- package/package.json +8 -3
package/dist/cli/cli.service.js
CHANGED
|
@@ -4,10 +4,13 @@ import os from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { createInterface } from "node:readline/promises";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
|
+
import { resolveCursorAgentDirectLaunch } from "../shared/cursor-agent/cursor-agent.launch.js";
|
|
7
8
|
import { redactSecrets, redactText } from "../shared/redaction/redaction.js";
|
|
8
9
|
import { defaultBridgeServiceConfig, ForgeBridgeService } from "../bridge/bridge.service.js";
|
|
10
|
+
import { forgeBridgeVersion } from "../version.js";
|
|
9
11
|
import { addRepoMapping, applyLocalConfigDefaults, providerStatusesFor, readLocalConfigSync, removeRepoMapping, resolveLocalConfigPath, writeLocalConfig } from "./local-config.js";
|
|
10
12
|
import { BridgeSessionService } from "../session/session.service.js";
|
|
13
|
+
import { parseCompletionMcpArgs, runCompletionMcpServer } from "../session/phase-completion.js";
|
|
11
14
|
import { failureResult, parseRunOnceDispatchPacket } from "../session/session.types.js";
|
|
12
15
|
const apiKeySecretGrantEnv = "FORGE_API_KEY_SECRET_GRANT";
|
|
13
16
|
const execFileAsync = promisify(execFile);
|
|
@@ -21,6 +24,22 @@ export class ForgeBridgeCliService {
|
|
|
21
24
|
this.pairRunner = pairRunner;
|
|
22
25
|
}
|
|
23
26
|
async run(argv, io = { stdout: process.stdout, stderr: process.stderr }) {
|
|
27
|
+
if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
|
|
28
|
+
io.stdout.write(helpText());
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
if (argv[0] === "help") {
|
|
32
|
+
io.stdout.write(argv[1] ? commandHelpText(argv[1]) : helpText());
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
if (argv[1] === "--help" || argv[1] === "-h") {
|
|
36
|
+
io.stdout.write(commandHelpText(argv[0]));
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
if (argv[0] === "version" || argv[0] === "--version" || argv[0] === "-v") {
|
|
40
|
+
io.stdout.write(`forge-bridge ${defaultBridgeServiceConfig().version ?? forgeBridgeVersion}\n`);
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
24
43
|
const args = parseArgs(argv);
|
|
25
44
|
if (!args.ok) {
|
|
26
45
|
io.stderr.write(`${args.error}\n`);
|
|
@@ -42,6 +61,7 @@ export class ForgeBridgeCliService {
|
|
|
42
61
|
process.once("SIGINT", stop);
|
|
43
62
|
process.once("SIGTERM", stop);
|
|
44
63
|
try {
|
|
64
|
+
io.stdout.write(connectStartupText(config));
|
|
45
65
|
await this.bridgeFactory(config).run(controller.signal);
|
|
46
66
|
return 0;
|
|
47
67
|
}
|
|
@@ -102,6 +122,15 @@ export class ForgeBridgeCliService {
|
|
|
102
122
|
if (args.command === "cleanup") {
|
|
103
123
|
return await cleanupRepos(args.config, args.mode, args.force, io);
|
|
104
124
|
}
|
|
125
|
+
if (args.command === "mcp") {
|
|
126
|
+
const parsed = parseCompletionMcpArgs(args.argv);
|
|
127
|
+
if (!parsed.ok) {
|
|
128
|
+
io.stderr.write(`${parsed.error}\n`);
|
|
129
|
+
return 2;
|
|
130
|
+
}
|
|
131
|
+
await runCompletionMcpServer(parsed.config);
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
105
134
|
let redactionSecrets = readEnvSecretValuesForRedaction();
|
|
106
135
|
try {
|
|
107
136
|
const dispatchJson = await fs.readFile(args.dispatchPath, "utf8");
|
|
@@ -165,19 +194,23 @@ function parseEnvSecretGrant(raw) {
|
|
|
165
194
|
};
|
|
166
195
|
}
|
|
167
196
|
function exactSecrets(dispatch) {
|
|
168
|
-
return dispatch.apiKeySecretGrant?.secret
|
|
197
|
+
return exactSecretValues(dispatch.apiKeySecretGrant?.secret, process.env.FORGE_DEVICE_TOKEN);
|
|
169
198
|
}
|
|
170
199
|
function readEnvSecretValuesForRedaction() {
|
|
200
|
+
const secrets = [];
|
|
171
201
|
const raw = process.env[apiKeySecretGrantEnv];
|
|
172
|
-
if (
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
return [];
|
|
202
|
+
if (raw) {
|
|
203
|
+
try {
|
|
204
|
+
secrets.push(parseEnvSecretGrant(raw).secret);
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// Ignore malformed grant env here; command handling will report the parse error.
|
|
208
|
+
}
|
|
180
209
|
}
|
|
210
|
+
return exactSecretValues(...secrets, process.env.FORGE_DEVICE_TOKEN);
|
|
211
|
+
}
|
|
212
|
+
function exactSecretValues(...values) {
|
|
213
|
+
return values.filter((value) => typeof value === "string" && value.length > 0);
|
|
181
214
|
}
|
|
182
215
|
export function parseArgs(argv) {
|
|
183
216
|
if (argv[0] === "connect") {
|
|
@@ -214,6 +247,9 @@ export function parseArgs(argv) {
|
|
|
214
247
|
if (argv[0] === "cleanup") {
|
|
215
248
|
return parseCleanupArgs(argv);
|
|
216
249
|
}
|
|
250
|
+
if (argv[0] === "mcp") {
|
|
251
|
+
return { ok: true, command: "mcp", argv };
|
|
252
|
+
}
|
|
217
253
|
if (argv[0] !== "run-once") {
|
|
218
254
|
return {
|
|
219
255
|
ok: false,
|
|
@@ -460,6 +496,7 @@ function pairedConfig(current, args, claim) {
|
|
|
460
496
|
supportsManagedWorktrees: current.supportsManagedWorktrees ?? true,
|
|
461
497
|
supportsManagedForgeArtifacts: current.supportsManagedForgeArtifacts ?? true,
|
|
462
498
|
supportsRunnerAuthoredPhaseArtifacts: current.supportsRunnerAuthoredPhaseArtifacts ?? true,
|
|
499
|
+
supportsDraftRunOnce: current.supportsDraftRunOnce ?? true,
|
|
463
500
|
repoMappings: current.repoMappings ?? []
|
|
464
501
|
};
|
|
465
502
|
}
|
|
@@ -508,7 +545,199 @@ function normalizeServerUrl(serverUrl) {
|
|
|
508
545
|
return serverUrl.endsWith("/") ? serverUrl : `${serverUrl}/`;
|
|
509
546
|
}
|
|
510
547
|
function usage() {
|
|
511
|
-
return
|
|
548
|
+
return helpText();
|
|
549
|
+
}
|
|
550
|
+
function helpText() {
|
|
551
|
+
const version = defaultBridgeServiceConfig().version ?? forgeBridgeVersion;
|
|
552
|
+
return [
|
|
553
|
+
`🛠 Forge Bridge ${version} — Local provider runners for Forge.`,
|
|
554
|
+
"",
|
|
555
|
+
"Usage: forge-bridge [options] [command]",
|
|
556
|
+
"",
|
|
557
|
+
"Options:",
|
|
558
|
+
" -h, --help Display help for command",
|
|
559
|
+
" -v, --version Output the version number",
|
|
560
|
+
"",
|
|
561
|
+
"Commands:",
|
|
562
|
+
" Hint: commands suffixed with * have detailed help. Run <command> --help for details.",
|
|
563
|
+
" connect * Start the runner bridge and wait for Forge dispatches",
|
|
564
|
+
" pair * Pair this machine with Forge and save runner credentials",
|
|
565
|
+
" status * Show config, runner identity, repos, provider CLIs, and updates",
|
|
566
|
+
" add-repo * Map a Forge repo label/name to a local checkout path",
|
|
567
|
+
" remove-repo * Remove a saved repo mapping",
|
|
568
|
+
" cleanup * Prune managed bridge worktrees",
|
|
569
|
+
" version * Print the installed bridge package version",
|
|
570
|
+
" help * Display help for command",
|
|
571
|
+
" run-once * Internal: run one dispatch packet from JSON",
|
|
572
|
+
" mcp * Internal: run the phase-completion MCP server",
|
|
573
|
+
"",
|
|
574
|
+
"Examples:",
|
|
575
|
+
" npm install -g @loucompanion/forge-bridge@next",
|
|
576
|
+
" forge-bridge pair 1234-5678 --server https://srv1493013.tail66073d.ts.net:8443 --repo-root \"%USERPROFILE%\\ForgeRepos\"",
|
|
577
|
+
" forge-bridge connect",
|
|
578
|
+
" forge-bridge status --check-updates",
|
|
579
|
+
" forge-bridge add-repo Forge D:\\Programming\\repos\\Forge",
|
|
580
|
+
" forge-bridge cleanup --stale",
|
|
581
|
+
"",
|
|
582
|
+
"Troubleshooting:",
|
|
583
|
+
" If Forge says the bridge is old: npm install -g @loucompanion/forge-bridge@next",
|
|
584
|
+
" If connect shows 502/500: leave it open; it reconnects with backoff while dev restarts.",
|
|
585
|
+
" If a provider is missing: run forge-bridge status --check-updates and verify CLI login.",
|
|
586
|
+
"",
|
|
587
|
+
"Docs:",
|
|
588
|
+
" https://github.com/coleman399/Forge/tree/develop/tools/forge-bridge#readme",
|
|
589
|
+
"",
|
|
590
|
+
].join("\n");
|
|
591
|
+
}
|
|
592
|
+
function commandHelpText(command) {
|
|
593
|
+
switch (command) {
|
|
594
|
+
case "pair":
|
|
595
|
+
return [
|
|
596
|
+
"Forge Bridge — pair",
|
|
597
|
+
"Pair this machine with Forge and save runner credentials.",
|
|
598
|
+
"",
|
|
599
|
+
"Usage:",
|
|
600
|
+
" forge-bridge pair [code] [options]",
|
|
601
|
+
"",
|
|
602
|
+
"Options:",
|
|
603
|
+
" --server <url> Forge server URL.",
|
|
604
|
+
" --repo-root <path> Folder where bridge-managed repos/worktrees live.",
|
|
605
|
+
" --name <name> Display name for this runner.",
|
|
606
|
+
" --fingerprint <value> Stable device fingerprint override.",
|
|
607
|
+
" --cli-provider <list> Comma-separated providers, e.g. codex,cursor.",
|
|
608
|
+
" --cleanup-worktrees Enable managed worktree cleanup.",
|
|
609
|
+
" --no-cleanup-worktrees Disable managed worktree cleanup.",
|
|
610
|
+
"",
|
|
611
|
+
"Example:",
|
|
612
|
+
" forge-bridge pair 1234-5678 --server https://forge.example.com --repo-root \"%USERPROFILE%\\ForgeRepos\"",
|
|
613
|
+
"",
|
|
614
|
+
].join("\n");
|
|
615
|
+
case "connect":
|
|
616
|
+
return [
|
|
617
|
+
"Forge Bridge — connect",
|
|
618
|
+
"Start the runner bridge and keep it connected for Forge dispatches.",
|
|
619
|
+
"",
|
|
620
|
+
"Usage:",
|
|
621
|
+
" forge-bridge connect [options]",
|
|
622
|
+
"",
|
|
623
|
+
"Options:",
|
|
624
|
+
" --server <url> Forge server URL; saved config is used when omitted.",
|
|
625
|
+
" --runner-id <id> Runner id; saved config is used when omitted.",
|
|
626
|
+
" --device-token <token> Device token; saved config is used when omitted.",
|
|
627
|
+
" --repo-root <path> Override repo/worktree folder for this run.",
|
|
628
|
+
" --cli-provider <list> Comma-separated providers, e.g. codex,cursor.",
|
|
629
|
+
" --heartbeat-interval-ms <n> Heartbeat interval in milliseconds.",
|
|
630
|
+
" --cleanup-worktrees Enable managed worktree cleanup for this run.",
|
|
631
|
+
" --no-cleanup-worktrees Disable managed worktree cleanup for this run.",
|
|
632
|
+
"",
|
|
633
|
+
"Example:",
|
|
634
|
+
" forge-bridge connect",
|
|
635
|
+
"",
|
|
636
|
+
].join("\n");
|
|
637
|
+
case "status":
|
|
638
|
+
return [
|
|
639
|
+
"Forge Bridge — status",
|
|
640
|
+
"Show saved configuration, paired runner identity, repo mappings, detected provider CLIs, and optional update info.",
|
|
641
|
+
"",
|
|
642
|
+
"Usage:",
|
|
643
|
+
" forge-bridge status [--check-updates]",
|
|
644
|
+
"",
|
|
645
|
+
"Example:",
|
|
646
|
+
" forge-bridge status --check-updates",
|
|
647
|
+
"",
|
|
648
|
+
].join("\n");
|
|
649
|
+
case "add-repo":
|
|
650
|
+
return [
|
|
651
|
+
"Forge Bridge — add-repo",
|
|
652
|
+
"Map a Forge repo label/name to an existing local checkout.",
|
|
653
|
+
"",
|
|
654
|
+
"Usage:",
|
|
655
|
+
" forge-bridge add-repo <label> <path>",
|
|
656
|
+
"",
|
|
657
|
+
"Example:",
|
|
658
|
+
" forge-bridge add-repo Forge D:\\Programming\\repos\\Forge",
|
|
659
|
+
"",
|
|
660
|
+
].join("\n");
|
|
661
|
+
case "remove-repo":
|
|
662
|
+
return [
|
|
663
|
+
"Forge Bridge — remove-repo",
|
|
664
|
+
"Remove a saved local repo mapping.",
|
|
665
|
+
"",
|
|
666
|
+
"Usage:",
|
|
667
|
+
" forge-bridge remove-repo <label>",
|
|
668
|
+
"",
|
|
669
|
+
].join("\n");
|
|
670
|
+
case "cleanup":
|
|
671
|
+
return [
|
|
672
|
+
"Forge Bridge — cleanup",
|
|
673
|
+
"Prune managed bridge worktrees under the configured repo folder.",
|
|
674
|
+
"",
|
|
675
|
+
"Usage:",
|
|
676
|
+
" forge-bridge cleanup [--stale|--merged|--all] [--force]",
|
|
677
|
+
"",
|
|
678
|
+
"Examples:",
|
|
679
|
+
" forge-bridge cleanup --stale",
|
|
680
|
+
" forge-bridge cleanup --all --force",
|
|
681
|
+
"",
|
|
682
|
+
].join("\n");
|
|
683
|
+
case "run-once":
|
|
684
|
+
return [
|
|
685
|
+
"Forge Bridge — run-once",
|
|
686
|
+
"Internal command used by Forge to run a single dispatch packet.",
|
|
687
|
+
"",
|
|
688
|
+
"Usage:",
|
|
689
|
+
" forge-bridge run-once --dispatch <dispatch-json-path> --result <result-json-path>",
|
|
690
|
+
"",
|
|
691
|
+
].join("\n");
|
|
692
|
+
case "mcp":
|
|
693
|
+
return [
|
|
694
|
+
"Forge Bridge — mcp",
|
|
695
|
+
"Internal MCP server used by provider CLIs to report Forge phase artifacts.",
|
|
696
|
+
"",
|
|
697
|
+
"Usage:",
|
|
698
|
+
" forge-bridge mcp --worktree <path> --work-item-id <id> --phase <phase>",
|
|
699
|
+
"",
|
|
700
|
+
].join("\n");
|
|
701
|
+
case "version":
|
|
702
|
+
return [
|
|
703
|
+
"Forge Bridge — version",
|
|
704
|
+
"Print the installed bridge package version.",
|
|
705
|
+
"",
|
|
706
|
+
"Usage:",
|
|
707
|
+
" forge-bridge version",
|
|
708
|
+
"",
|
|
709
|
+
].join("\n");
|
|
710
|
+
default:
|
|
711
|
+
return [
|
|
712
|
+
`Unknown forge-bridge command: ${command}`,
|
|
713
|
+
"",
|
|
714
|
+
"Run `forge-bridge help` to see available commands.",
|
|
715
|
+
"",
|
|
716
|
+
].join("\n");
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
function connectStartupText(config) {
|
|
720
|
+
const runner = config.runnerId
|
|
721
|
+
? `${config.runnerId}${config.deviceName ? ` (${config.deviceName})` : ""}`
|
|
722
|
+
: "not configured";
|
|
723
|
+
return [
|
|
724
|
+
`Forge Bridge ${config.version}`,
|
|
725
|
+
"Starting runner bridge...",
|
|
726
|
+
"",
|
|
727
|
+
"Startup:",
|
|
728
|
+
` server: ${config.serverUrl || "not configured"}`,
|
|
729
|
+
` runner: ${runner}`,
|
|
730
|
+
` config: ${resolveLocalConfigPath()}`,
|
|
731
|
+
` repo folder: ${config.defaultRepoRoot || "not configured"}`,
|
|
732
|
+
` providers: ${formatList(config.cliProviders)}`,
|
|
733
|
+
` cleanup worktrees: ${config.cleanupWorktrees ? "enabled" : "disabled"}`,
|
|
734
|
+
"",
|
|
735
|
+
"Press Ctrl+C to stop.",
|
|
736
|
+
"",
|
|
737
|
+
].join("\n");
|
|
738
|
+
}
|
|
739
|
+
function formatList(values) {
|
|
740
|
+
return values.length > 0 ? values.join(", ") : "none";
|
|
512
741
|
}
|
|
513
742
|
async function writeJsonAtomic(outputPath, value) {
|
|
514
743
|
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
@@ -620,6 +849,12 @@ function cliProbeCommandsFor(provider) {
|
|
|
620
849
|
];
|
|
621
850
|
}
|
|
622
851
|
async function execVersionCommand(probe, timeout = 2000) {
|
|
852
|
+
const directLaunch = process.platform === "win32"
|
|
853
|
+
? await resolveCursorAgentDirectLaunch(probe.command, probe.args)
|
|
854
|
+
: null;
|
|
855
|
+
if (directLaunch) {
|
|
856
|
+
return await execFileAsync(directLaunch.command, [...directLaunch.args], { timeout });
|
|
857
|
+
}
|
|
623
858
|
try {
|
|
624
859
|
return await execFileAsync(probe.command, probe.args, { timeout });
|
|
625
860
|
}
|
|
@@ -16,6 +16,7 @@ export interface BridgeLocalConfig {
|
|
|
16
16
|
readonly supportsManagedWorktrees?: boolean;
|
|
17
17
|
readonly supportsManagedForgeArtifacts?: boolean;
|
|
18
18
|
readonly supportsRunnerAuthoredPhaseArtifacts?: boolean;
|
|
19
|
+
readonly supportsDraftRunOnce?: boolean;
|
|
19
20
|
}
|
|
20
21
|
export interface LocalRepoMapping extends BridgeRepoMapping {
|
|
21
22
|
readonly label: string;
|
package/dist/cli/local-config.js
CHANGED
|
@@ -49,11 +49,12 @@ export function applyLocalConfigDefaults(config, local) {
|
|
|
49
49
|
providerStatuses: normalizeProviderStatuses(local.providerStatuses) ?? providerStatusesFor(cliProviders),
|
|
50
50
|
projectLabels: local.projectLabels ?? config.projectLabels,
|
|
51
51
|
repoMappings: local.repoMappings ?? config.repoMappings,
|
|
52
|
-
sandboxProfiles: local.sandboxProfiles ?? config.sandboxProfiles,
|
|
52
|
+
sandboxProfiles: normalizeSandboxProfiles(local.sandboxProfiles ?? config.sandboxProfiles, cliProviders) ?? config.sandboxProfiles,
|
|
53
53
|
supportsApiSecretGrant: local.supportsApiSecretGrant ?? config.supportsApiSecretGrant,
|
|
54
54
|
supportsManagedWorktrees: local.supportsManagedWorktrees ?? config.supportsManagedWorktrees,
|
|
55
55
|
supportsManagedForgeArtifacts: local.supportsManagedForgeArtifacts ?? config.supportsManagedForgeArtifacts,
|
|
56
|
-
supportsRunnerAuthoredPhaseArtifacts: local.supportsRunnerAuthoredPhaseArtifacts ?? config.supportsRunnerAuthoredPhaseArtifacts
|
|
56
|
+
supportsRunnerAuthoredPhaseArtifacts: local.supportsRunnerAuthoredPhaseArtifacts ?? config.supportsRunnerAuthoredPhaseArtifacts,
|
|
57
|
+
supportsDraftRunOnce: local.supportsDraftRunOnce ?? config.supportsDraftRunOnce
|
|
57
58
|
};
|
|
58
59
|
}
|
|
59
60
|
export async function addRepoMapping(label, repoPath) {
|
|
@@ -111,7 +112,8 @@ function normalizeLocalConfig(config) {
|
|
|
111
112
|
supportsApiSecretGrant: config.supportsApiSecretGrant,
|
|
112
113
|
supportsManagedWorktrees: config.supportsManagedWorktrees,
|
|
113
114
|
supportsManagedForgeArtifacts: config.supportsManagedForgeArtifacts,
|
|
114
|
-
supportsRunnerAuthoredPhaseArtifacts: config.supportsRunnerAuthoredPhaseArtifacts
|
|
115
|
+
supportsRunnerAuthoredPhaseArtifacts: config.supportsRunnerAuthoredPhaseArtifacts,
|
|
116
|
+
supportsDraftRunOnce: config.supportsDraftRunOnce
|
|
115
117
|
};
|
|
116
118
|
}
|
|
117
119
|
function emptyConfig() {
|
|
@@ -149,6 +151,19 @@ function splitCsv(value) {
|
|
|
149
151
|
.map((entry) => entry.trim())
|
|
150
152
|
.filter((entry) => entry.length > 0);
|
|
151
153
|
}
|
|
154
|
+
function normalizeSandboxProfiles(profiles, cliProviders) {
|
|
155
|
+
const normalized = cleanList(profiles);
|
|
156
|
+
if (process.platform === "win32"
|
|
157
|
+
&& cliProviders.some((provider) => provider.toLowerCase() === "codex")
|
|
158
|
+
&& normalized?.some((profile) => profile.toLowerCase() === "provider-workspace-write")
|
|
159
|
+
&& !normalized.some((profile) => profile.toLowerCase() === "danger-full-access")) {
|
|
160
|
+
return [
|
|
161
|
+
...normalized.filter((profile) => profile.toLowerCase() !== "provider-workspace-write"),
|
|
162
|
+
"danger-full-access"
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
return normalized;
|
|
166
|
+
}
|
|
152
167
|
function normalizeRepoMappings(values) {
|
|
153
168
|
return (values ?? [])
|
|
154
169
|
.filter((mapping) => !!mapping.label?.trim() && !!mapping.localPath?.trim())
|
|
@@ -159,7 +174,8 @@ function normalizeRepoMappings(values) {
|
|
|
159
174
|
repoId: cleanString(mapping.repoId),
|
|
160
175
|
name: cleanString(mapping.name) ?? label,
|
|
161
176
|
remoteUrl: cleanString(mapping.remoteUrl),
|
|
162
|
-
localPath: path.resolve(mapping.localPath)
|
|
177
|
+
localPath: path.resolve(mapping.localPath),
|
|
178
|
+
qaAppLaunchTarget: normalizeQaAppLaunchTarget(mapping.qaAppLaunchTarget)
|
|
163
179
|
};
|
|
164
180
|
})
|
|
165
181
|
.sort((left, right) => left.label.localeCompare(right.label));
|
|
@@ -171,6 +187,22 @@ function normalizeLabel(value) {
|
|
|
171
187
|
}
|
|
172
188
|
return label;
|
|
173
189
|
}
|
|
190
|
+
function normalizeQaAppLaunchTarget(value) {
|
|
191
|
+
if (!value)
|
|
192
|
+
return undefined;
|
|
193
|
+
const routes = cleanList(value.routes);
|
|
194
|
+
const normalized = {
|
|
195
|
+
url: cleanString(value.url),
|
|
196
|
+
command: cleanString(value.command),
|
|
197
|
+
working_directory: cleanString(value.working_directory),
|
|
198
|
+
ready_url: cleanString(value.ready_url),
|
|
199
|
+
routes,
|
|
200
|
+
notes: cleanString(value.notes)
|
|
201
|
+
};
|
|
202
|
+
return normalized.url || normalized.command || normalized.ready_url || routes?.length
|
|
203
|
+
? normalized
|
|
204
|
+
: undefined;
|
|
205
|
+
}
|
|
174
206
|
function cleanString(value) {
|
|
175
207
|
const clean = value?.trim();
|
|
176
208
|
return clean ? clean : undefined;
|
package/dist/config.js
CHANGED
|
@@ -2,7 +2,7 @@ import os from "node:os";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
export function defaultForgeBridgeConfig() {
|
|
4
4
|
return {
|
|
5
|
-
defaultRepoRoot: path.join(os.tmpdir(), "forge-
|
|
5
|
+
defaultRepoRoot: path.join(os.tmpdir(), "forge-repos"),
|
|
6
6
|
cleanupWorktrees: true
|
|
7
7
|
};
|
|
8
8
|
}
|
package/dist/session/index.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { BridgeSessionService } from "./session.service.js";
|
|
2
|
+
export { buildCompletionToolServerArgs, codexMcpConfigArgs, completionPhaseFromDispatch, expectedCompletionToolName, prepareCompletionToolRuntime, readCompletionArtifact, writeCompletionArtifact } from "./phase-completion.js";
|
|
3
|
+
export type { CompletionArtifactReference, CompletionPhase, CompletionToolRuntime } from "./phase-completion.js";
|
|
2
4
|
export type { SessionService } from "./session.base.js";
|
package/dist/session/index.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
1
|
export { BridgeSessionService } from "./session.service.js";
|
|
2
|
+
export { buildCompletionToolServerArgs, codexMcpConfigArgs, completionPhaseFromDispatch, expectedCompletionToolName, prepareCompletionToolRuntime, readCompletionArtifact, writeCompletionArtifact } from "./phase-completion.js";
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { parseRunOnceDispatchPacket, failureResult } from "./session.types.js";
|
|
2
|
+
export { buildCompletionToolServerArgs, codexMcpConfigArgs, completionPhaseFromDispatch, expectedCompletionToolName, prepareCompletionToolRuntime, readCompletionArtifact, writeCompletionArtifact } from "./phase-completion.js";
|
|
3
|
+
export type { CompletionArtifactReference, CompletionPhase, CompletionToolRuntime } from "./phase-completion.js";
|
|
2
4
|
export type { SessionEventSink, SessionService } from "./session.base.js";
|
|
3
|
-
export type { ApiKeySecretGrant, DispatchCycleRun, DispatchExecution, DispatchLease, DispatchSession, DispatchRepo, DispatchServerLocalConfig, DispatchWorkItem, RunOnceDispatchPacket, RunOnceResultPacket, RunnerDispatchArtifact, RunnerDispatchEvent, RunnerDispatchSpec, RunnerEventLine } from "./session.types.js";
|
|
5
|
+
export type { ApiKeySecretGrant, DispatchCycleRun, DispatchExecution, DispatchLease, DispatchPullRequestContext, DispatchSession, DispatchRepo, DispatchServerLocalConfig, DispatchWorkItem, RunOnceDispatchPacket, RunOnceResultPacket, RunnerDispatchArtifact, RunnerDispatchEvent, RunnerDispatchSpec, RunnerEventLine } from "./session.types.js";
|
|
4
6
|
export { DeterministicProviderCliService, ProcessBackedProviderCliService, RoutingProviderCliService, buildProviderCliCommand } from "./provider-cli/provider-cli.service.js";
|
|
5
7
|
export type { ProviderCliService } from "./provider-cli/provider-cli.base.js";
|
|
6
8
|
export type { ProviderCliCommand, ProviderProcessRunner, ProviderProcessRunRequest, ProviderProcessRunResult } from "./provider-cli/provider-cli.service.js";
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { parseRunOnceDispatchPacket, failureResult } from "./session.types.js";
|
|
2
|
+
export { buildCompletionToolServerArgs, codexMcpConfigArgs, completionPhaseFromDispatch, expectedCompletionToolName, prepareCompletionToolRuntime, readCompletionArtifact, writeCompletionArtifact } from "./phase-completion.js";
|
|
2
3
|
export { DeterministicProviderCliService, ProcessBackedProviderCliService, RoutingProviderCliService, buildProviderCliCommand } from "./provider-cli/provider-cli.service.js";
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { RunOnceDispatchPacket, RunnerDispatchArtifact } from "./session.types.js";
|
|
2
|
+
import type { WorktreeHandle } from "../worktree/worktree.types.js";
|
|
3
|
+
export type DevelopCompletionPhase = "implementation" | "self_review" | "validation";
|
|
4
|
+
export type DebugCompletionPhase = "debug_repro" | "debug_root_cause" | "debug_fix" | "debug_regression";
|
|
5
|
+
export type ArchitectCompletionPhase = "architect";
|
|
6
|
+
export type DraftCompletionPhase = "draft";
|
|
7
|
+
export type CompletionPhase = DevelopCompletionPhase | "qa" | DebugCompletionPhase | ArchitectCompletionPhase | DraftCompletionPhase;
|
|
8
|
+
export type QaCompletionGate = "qa_plan" | "qa_regression" | "qa_api" | "qa_manual_web" | "qa_bug_repro" | "qa_findings" | "qa_verdict";
|
|
9
|
+
export type DraftCompletionGate = "draft_intent" | "draft_repo_context" | "draft_graph_context" | "draft_product_scope" | "draft_work_order_plan" | "draft_critic" | "draft_proposal";
|
|
10
|
+
export interface CompletionToolRuntime {
|
|
11
|
+
readonly serverName: string;
|
|
12
|
+
readonly command: string;
|
|
13
|
+
readonly args: readonly string[];
|
|
14
|
+
readonly env: Readonly<Record<string, string>>;
|
|
15
|
+
readonly cursorMcpConfigPath?: string | null;
|
|
16
|
+
cleanup(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export interface CompletionToolRuntimeOptions {
|
|
19
|
+
readonly dispatch: RunOnceDispatchPacket;
|
|
20
|
+
readonly worktree: WorktreeHandle;
|
|
21
|
+
readonly phase?: CompletionPhase;
|
|
22
|
+
}
|
|
23
|
+
export interface CompletionToolServerConfig {
|
|
24
|
+
readonly worktreePath: string;
|
|
25
|
+
readonly workItemId: string;
|
|
26
|
+
readonly ownerType?: string;
|
|
27
|
+
readonly ownerId?: string;
|
|
28
|
+
readonly phase: CompletionPhase;
|
|
29
|
+
readonly producedBy?: string;
|
|
30
|
+
readonly redactionSecrets?: readonly string[];
|
|
31
|
+
readonly developCycle?: boolean;
|
|
32
|
+
readonly qaCycle?: boolean;
|
|
33
|
+
readonly debugCycle?: boolean;
|
|
34
|
+
readonly architectCycle?: boolean;
|
|
35
|
+
readonly draftCycle?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface CompletionToolCallResult {
|
|
38
|
+
readonly content: readonly {
|
|
39
|
+
readonly type: "text";
|
|
40
|
+
readonly text: string;
|
|
41
|
+
}[];
|
|
42
|
+
readonly structuredContent: CompletionArtifactReference & {
|
|
43
|
+
readonly gate: {
|
|
44
|
+
readonly phase: CompletionPhase;
|
|
45
|
+
readonly qaGate?: QaCompletionGate;
|
|
46
|
+
readonly draftGate?: DraftCompletionGate;
|
|
47
|
+
readonly status: string;
|
|
48
|
+
readonly allowed: boolean;
|
|
49
|
+
readonly reason: string;
|
|
50
|
+
};
|
|
51
|
+
readonly nextPhase: CompletionPhase | "dev_test_ready" | "qa_ready" | "sign_off_ready";
|
|
52
|
+
readonly nextInstruction: string;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export interface CompletionArtifactReference {
|
|
56
|
+
readonly phase: CompletionPhase;
|
|
57
|
+
readonly workItemId: string;
|
|
58
|
+
readonly ownerType?: string;
|
|
59
|
+
readonly ownerId?: string;
|
|
60
|
+
readonly repoPath: string;
|
|
61
|
+
readonly path: string;
|
|
62
|
+
readonly reportPath: string;
|
|
63
|
+
readonly contentHash: string;
|
|
64
|
+
readonly status: string;
|
|
65
|
+
readonly producedBy: string;
|
|
66
|
+
}
|
|
67
|
+
export declare const debugCompletionPhaseOrder: readonly DebugCompletionPhase[];
|
|
68
|
+
export declare const qaCompletionGateOrder: readonly QaCompletionGate[];
|
|
69
|
+
export declare const draftCompletionGateOrder: readonly DraftCompletionGate[];
|
|
70
|
+
export declare function completionPhaseFromDispatch(dispatch: RunOnceDispatchPacket): CompletionPhase;
|
|
71
|
+
export declare function normalizeCompletionPhase(value: string | null | undefined): CompletionPhase;
|
|
72
|
+
export declare function usesDevelopCycleCompletion(dispatch: RunOnceDispatchPacket): boolean;
|
|
73
|
+
export declare function usesQaCycleCompletion(dispatch: RunOnceDispatchPacket): boolean;
|
|
74
|
+
export declare function usesDebugCycleCompletion(dispatch: RunOnceDispatchPacket): boolean;
|
|
75
|
+
export declare function usesArchitectCycleCompletion(dispatch: RunOnceDispatchPacket): boolean;
|
|
76
|
+
export declare function completionToolNamesForPhase(phase: CompletionPhase): readonly string[];
|
|
77
|
+
export declare function completionToolNamesForQaGate(gate: QaCompletionGate): readonly string[];
|
|
78
|
+
export declare function completionToolNamesForDebugGate(gate: DebugCompletionPhase): readonly string[];
|
|
79
|
+
export declare function completionToolNamesForDraftGate(gate: DraftCompletionGate): readonly string[];
|
|
80
|
+
export declare function expectedCompletionToolName(phase: CompletionPhase): string;
|
|
81
|
+
export declare function expectedQaCompletionToolName(gate: QaCompletionGate): string;
|
|
82
|
+
export declare function expectedDebugCompletionToolName(gate: DebugCompletionPhase): string;
|
|
83
|
+
export declare function expectedDraftCompletionToolName(gate: DraftCompletionGate): string;
|
|
84
|
+
export declare function artifactRootFor(worktreePath: string, workItemId: string): string;
|
|
85
|
+
export declare function artifactRootForOwner(worktreePath: string, ownerType: string, ownerId: string): string;
|
|
86
|
+
export declare function completionArtifactJsonPath(worktreePath: string, workItemId: string, phase: CompletionPhase, ownerType?: string | null, ownerId?: string | null): string;
|
|
87
|
+
export declare function completionArtifactReportPath(worktreePath: string, workItemId: string, phase: CompletionPhase, ownerType?: string | null, ownerId?: string | null): string;
|
|
88
|
+
export declare function prepareCompletionToolRuntime(options: CompletionToolRuntimeOptions): Promise<CompletionToolRuntime>;
|
|
89
|
+
export declare function buildCompletionToolServerArgs(config: CompletionToolServerConfig): readonly string[];
|
|
90
|
+
export declare function codexMcpConfigArgs(runtime: CompletionToolRuntime): readonly string[];
|
|
91
|
+
export declare function cursorMcpServerConfig(runtime: CompletionToolRuntime): unknown;
|
|
92
|
+
export declare function completionPromptForPhase(phase: CompletionPhase): string;
|
|
93
|
+
export declare function completionPromptForDevelopCycle(phase: CompletionPhase): string;
|
|
94
|
+
export declare function completionPromptForQaCycle(currentGate?: QaCompletionGate): string;
|
|
95
|
+
export declare function completionPromptForDraftCycle(currentGate?: DraftCompletionGate): string;
|
|
96
|
+
export declare function completionPromptForDebugCycle(currentGate?: DebugCompletionPhase): string;
|
|
97
|
+
export declare function completionToolNamesForServer(config: CompletionToolServerConfig): readonly string[];
|
|
98
|
+
export declare class CompletionToolServerSession {
|
|
99
|
+
private readonly config;
|
|
100
|
+
private currentPhase;
|
|
101
|
+
private currentQaGate;
|
|
102
|
+
private currentDraftGate;
|
|
103
|
+
private completed;
|
|
104
|
+
constructor(config: CompletionToolServerConfig);
|
|
105
|
+
get instructions(): string;
|
|
106
|
+
toolDefinitions(): readonly unknown[];
|
|
107
|
+
callTool(toolName: string, input: unknown): Promise<CompletionToolCallResult>;
|
|
108
|
+
private callDraftGateTool;
|
|
109
|
+
private callDebugGateTool;
|
|
110
|
+
private callQaGateTool;
|
|
111
|
+
private advanceAfterPass;
|
|
112
|
+
private advanceAfterDraftGate;
|
|
113
|
+
private advanceAfterQaGate;
|
|
114
|
+
private advanceAfterDebugGate;
|
|
115
|
+
}
|
|
116
|
+
export declare function writeCompletionArtifact(config: CompletionToolServerConfig, toolName: string, input: unknown): Promise<CompletionArtifactReference>;
|
|
117
|
+
export declare function readCompletionArtifact(worktreePath: string, workItemId: string, phase: CompletionPhase, ownerType?: string | null, ownerId?: string | null): Promise<RunnerDispatchArtifact | null>;
|
|
118
|
+
export declare function runCompletionMcpServer(config: CompletionToolServerConfig): Promise<void>;
|
|
119
|
+
export declare function parseCompletionMcpArgs(argv: readonly string[]): {
|
|
120
|
+
readonly ok: true;
|
|
121
|
+
readonly config: CompletionToolServerConfig;
|
|
122
|
+
} | {
|
|
123
|
+
readonly ok: false;
|
|
124
|
+
readonly error: string;
|
|
125
|
+
};
|
|
126
|
+
export declare function completionRedactionEnv(secrets: readonly string[]): Readonly<Record<string, string>>;
|