@rosthq/cli 0.7.37 → 0.7.38
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/forge-workspace.d.ts +20 -0
- package/dist/forge-workspace.d.ts.map +1 -1
- package/dist/index.js +177 -6
- package/dist/index.js.map +3 -3
- package/dist/runner-serve.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -36,5 +36,25 @@ export declare function prepareForgeWorkspace(options: PrepareForgeWorkspaceOpti
|
|
|
36
36
|
export declare function pushChangeset(ws: ForgeWorkspace, credential: ForgeCredential): Promise<{
|
|
37
37
|
commitSha: string;
|
|
38
38
|
} | null>;
|
|
39
|
+
export type OpenDraftPullRequestOptions = {
|
|
40
|
+
repo: {
|
|
41
|
+
owner: string;
|
|
42
|
+
name: string;
|
|
43
|
+
default_branch: string;
|
|
44
|
+
};
|
|
45
|
+
branch: string;
|
|
46
|
+
buildRequestId: string;
|
|
47
|
+
commitSha: string;
|
|
48
|
+
credential: ForgeCredential;
|
|
49
|
+
apiBaseUrl?: string;
|
|
50
|
+
fetchImpl?: typeof fetch;
|
|
51
|
+
};
|
|
52
|
+
export type ForgePullRequest = {
|
|
53
|
+
url: string;
|
|
54
|
+
number: number;
|
|
55
|
+
draft: boolean;
|
|
56
|
+
adopted: boolean;
|
|
57
|
+
};
|
|
58
|
+
export declare function openDraftPullRequest(options: OpenDraftPullRequestOptions): Promise<ForgePullRequest>;
|
|
39
59
|
export declare function removeWorkspace(ws: ForgeWorkspace): Promise<void>;
|
|
40
60
|
//# sourceMappingURL=forge-workspace.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forge-workspace.d.ts","sourceRoot":"","sources":["../src/forge-workspace.ts"],"names":[],"mappings":"AAmDA,eAAO,MAAM,yBAAyB,gFAQ5B,CAAC;AAEX,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAW7E;AAID,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAG3B,QAAQ,EAAE,MAAM,CAAC;IAGjB,WAAW,EAAE,MAAM,CAAC;IAGpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAKlB,OAAO,EAAE,MAAM,CAAC;IAIhB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEvD,MAAM,MAAM,4BAA4B,GAAG;IAEzC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,OAAO,EAAE,qBAAqB,CAAC;IAE/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,UAAU,EAAE,eAAe,CAAC;IAI5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA+QF,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC,cAAc,CAAC,CAmC1G;AA8CD,wBAAsB,aAAa,CACjC,EAAE,EAAE,cAAc,EAClB,UAAU,EAAE,eAAe,GAC1B,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA4BvC;AAiBD,wBAAsB,eAAe,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAOvE"}
|
|
1
|
+
{"version":3,"file":"forge-workspace.d.ts","sourceRoot":"","sources":["../src/forge-workspace.ts"],"names":[],"mappings":"AAmDA,eAAO,MAAM,yBAAyB,gFAQ5B,CAAC;AAEX,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAW7E;AAID,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAG3B,QAAQ,EAAE,MAAM,CAAC;IAGjB,WAAW,EAAE,MAAM,CAAC;IAGpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAKlB,OAAO,EAAE,MAAM,CAAC;IAIhB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEvD,MAAM,MAAM,4BAA4B,GAAG;IAEzC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,OAAO,EAAE,qBAAqB,CAAC;IAE/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,UAAU,EAAE,eAAe,CAAC;IAI5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA+QF,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC,cAAc,CAAC,CAmC1G;AA8CD,wBAAsB,aAAa,CACjC,EAAE,EAAE,cAAc,EAClB,UAAU,EAAE,eAAe,GAC1B,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA4BvC;AAcD,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IAE9D,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,eAAe,CAAC;IAG5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAE7B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IAGf,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAoGF,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAkC1G;AAiBD,wBAAsB,eAAe,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAOvE"}
|
package/dist/index.js
CHANGED
|
@@ -52046,6 +52046,125 @@ async function pushChangeset(ws, credential) {
|
|
|
52046
52046
|
});
|
|
52047
52047
|
return { commitSha: head2 };
|
|
52048
52048
|
}
|
|
52049
|
+
var GITHUB_API_BASE_URL = "https://api.github.com";
|
|
52050
|
+
var PR_OPEN_RETRY_ATTEMPTS = 3;
|
|
52051
|
+
var PR_OPEN_RETRY_BASE_DELAY_MS = 500;
|
|
52052
|
+
function githubHeaders(credential) {
|
|
52053
|
+
return {
|
|
52054
|
+
accept: "application/vnd.github+json",
|
|
52055
|
+
authorization: `Bearer ${credential.token}`,
|
|
52056
|
+
"content-type": "application/json",
|
|
52057
|
+
"x-github-api-version": "2022-11-28",
|
|
52058
|
+
// GitHub requires a User-Agent; derived from the BRAND constants (never a literal brand
|
|
52059
|
+
// string — ADR-0001) and carries no credential.
|
|
52060
|
+
"user-agent": `${BRAND.name}-${RSF_ADDON_BRAND.shortName}`.replace(/\s+/g, "-")
|
|
52061
|
+
};
|
|
52062
|
+
}
|
|
52063
|
+
function draftPrTitle(options) {
|
|
52064
|
+
return `${RSF_ADDON_BRAND.shortName} build ${shortRequestId(options.buildRequestId)}`;
|
|
52065
|
+
}
|
|
52066
|
+
function draftPrBody(options) {
|
|
52067
|
+
return [
|
|
52068
|
+
`Draft PR opened by the ${BRAND.name} ${RSF_ADDON_BRAND.shortName} runner harness.`,
|
|
52069
|
+
"",
|
|
52070
|
+
`- Build request: ${options.buildRequestId}`,
|
|
52071
|
+
`- Branch: ${options.branch}`,
|
|
52072
|
+
`- Commit: ${options.commitSha}`,
|
|
52073
|
+
"",
|
|
52074
|
+
`The approved plan and task checklist are tracked on the ${RSF_ADDON_BRAND.shortName} build request above. This PR is a draft; the runner never merges.`
|
|
52075
|
+
].join("\n");
|
|
52076
|
+
}
|
|
52077
|
+
async function withGithubRetries(op, fn) {
|
|
52078
|
+
let lastError;
|
|
52079
|
+
for (let attempt = 1; attempt <= PR_OPEN_RETRY_ATTEMPTS; attempt += 1) {
|
|
52080
|
+
try {
|
|
52081
|
+
const response = await fn();
|
|
52082
|
+
if ((response.status >= 500 || response.status === 429) && attempt < PR_OPEN_RETRY_ATTEMPTS) {
|
|
52083
|
+
lastError = new Error(`${op} transient status ${response.status}`);
|
|
52084
|
+
await sleep2(PR_OPEN_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1));
|
|
52085
|
+
continue;
|
|
52086
|
+
}
|
|
52087
|
+
return response;
|
|
52088
|
+
} catch (error51) {
|
|
52089
|
+
lastError = error51;
|
|
52090
|
+
if (attempt >= PR_OPEN_RETRY_ATTEMPTS) {
|
|
52091
|
+
break;
|
|
52092
|
+
}
|
|
52093
|
+
await sleep2(PR_OPEN_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1));
|
|
52094
|
+
}
|
|
52095
|
+
}
|
|
52096
|
+
throw new Error(`${op} exhausted retries: ${lastError instanceof Error ? redactSecrets(lastError.message) : redactSecrets(String(lastError))}`);
|
|
52097
|
+
}
|
|
52098
|
+
async function readErrorText(response) {
|
|
52099
|
+
try {
|
|
52100
|
+
return (await response.text()).slice(0, 1e3);
|
|
52101
|
+
} catch {
|
|
52102
|
+
return `status ${response.status}`;
|
|
52103
|
+
}
|
|
52104
|
+
}
|
|
52105
|
+
function toForgePullRequest(json2, adopted) {
|
|
52106
|
+
const record2 = json2 && typeof json2 === "object" && !Array.isArray(json2) ? json2 : {};
|
|
52107
|
+
const url2 = typeof record2.html_url === "string" ? record2.html_url : "";
|
|
52108
|
+
const number4 = typeof record2.number === "number" ? record2.number : Number.NaN;
|
|
52109
|
+
const draft = typeof record2.draft === "boolean" ? record2.draft : true;
|
|
52110
|
+
if (!url2 || !Number.isFinite(number4)) {
|
|
52111
|
+
throw new Error("github pr response missing html_url or number");
|
|
52112
|
+
}
|
|
52113
|
+
return { url: url2, number: number4, draft, adopted };
|
|
52114
|
+
}
|
|
52115
|
+
async function adoptExistingPullRequest(baseUrl, fetchImpl, options) {
|
|
52116
|
+
const { owner, name } = options.repo;
|
|
52117
|
+
const head2 = `${owner}:${options.branch}`;
|
|
52118
|
+
const response = await withGithubRetries(
|
|
52119
|
+
"github pr list",
|
|
52120
|
+
async () => fetchImpl(`${baseUrl}/repos/${owner}/${name}/pulls?state=open&head=${encodeURIComponent(head2)}`, {
|
|
52121
|
+
method: "GET",
|
|
52122
|
+
headers: githubHeaders(options.credential)
|
|
52123
|
+
})
|
|
52124
|
+
);
|
|
52125
|
+
if (response.status !== 200) {
|
|
52126
|
+
return null;
|
|
52127
|
+
}
|
|
52128
|
+
const list = await response.json();
|
|
52129
|
+
if (!Array.isArray(list) || list.length === 0) {
|
|
52130
|
+
return null;
|
|
52131
|
+
}
|
|
52132
|
+
return toForgePullRequest(list[0], true);
|
|
52133
|
+
}
|
|
52134
|
+
async function openDraftPullRequest(options) {
|
|
52135
|
+
const baseUrl = (options.apiBaseUrl ?? GITHUB_API_BASE_URL).replace(/\/+$/, "");
|
|
52136
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
52137
|
+
const { owner, name } = options.repo;
|
|
52138
|
+
const created = await withGithubRetries(
|
|
52139
|
+
"github pr create",
|
|
52140
|
+
async () => fetchImpl(`${baseUrl}/repos/${owner}/${name}/pulls`, {
|
|
52141
|
+
method: "POST",
|
|
52142
|
+
headers: githubHeaders(options.credential),
|
|
52143
|
+
body: JSON.stringify({
|
|
52144
|
+
title: draftPrTitle(options),
|
|
52145
|
+
head: options.branch,
|
|
52146
|
+
base: options.repo.default_branch,
|
|
52147
|
+
body: draftPrBody(options),
|
|
52148
|
+
draft: true,
|
|
52149
|
+
maintainer_can_modify: true
|
|
52150
|
+
})
|
|
52151
|
+
})
|
|
52152
|
+
);
|
|
52153
|
+
if (created.status === 201) {
|
|
52154
|
+
return toForgePullRequest(await created.json(), false);
|
|
52155
|
+
}
|
|
52156
|
+
if (created.status === 422) {
|
|
52157
|
+
const detail = await readErrorText(created);
|
|
52158
|
+
if (/already exists/i.test(detail)) {
|
|
52159
|
+
const existing = await adoptExistingPullRequest(baseUrl, fetchImpl, options);
|
|
52160
|
+
if (existing) {
|
|
52161
|
+
return existing;
|
|
52162
|
+
}
|
|
52163
|
+
}
|
|
52164
|
+
throw new Error(`github pr create failed 422: ${redactSecrets(detail).slice(0, 500)}`);
|
|
52165
|
+
}
|
|
52166
|
+
throw new Error(`github pr create failed ${created.status}: ${redactSecrets(await readErrorText(created)).slice(0, 500)}`);
|
|
52167
|
+
}
|
|
52049
52168
|
async function removeWorktree(baseRepo, worktreeDir) {
|
|
52050
52169
|
try {
|
|
52051
52170
|
await runGit(["worktree", "remove", "--force", worktreeDir], baseRepo);
|
|
@@ -52807,7 +52926,13 @@ async function executeClaimedUnit(fetchImpl, appUrl2, state, config2, io, runtim
|
|
|
52807
52926
|
status: result.ok ? "succeeded" : "failed",
|
|
52808
52927
|
summary: result.summary,
|
|
52809
52928
|
log_ref: input.logRef(id),
|
|
52810
|
-
model: (turnRuntime ?? runtime) === "codex" ? "codex-cli" : "claude-cli"
|
|
52929
|
+
model: (turnRuntime ?? runtime) === "codex" ? "codex-cli" : "claude-cli",
|
|
52930
|
+
// Artifact-bearing result fields (T1.5) — only sent when the harness produced them, so
|
|
52931
|
+
// the result route's `.strict()` schema still accepts a plain read-status report.
|
|
52932
|
+
...result.prUrl ? { pr_url: result.prUrl } : {},
|
|
52933
|
+
...result.branch ? { branch: result.branch } : {},
|
|
52934
|
+
...result.commitSha ? { commit_sha: result.commitSha } : {},
|
|
52935
|
+
...result.evidence && result.evidence.length > 0 ? { evidence: result.evidence } : {}
|
|
52811
52936
|
}, state.runner_secret);
|
|
52812
52937
|
if (reported.status !== 200) {
|
|
52813
52938
|
throw new Error(`${claimedUnitLabel(input.kind)} result failed ${reported.status}: ${redactForLog(JSON.stringify(reported.json))}`);
|
|
@@ -52981,14 +53106,23 @@ async function prepareForgeTurn(ctx, workOrder, execution) {
|
|
|
52981
53106
|
],
|
|
52982
53107
|
allowNetwork: ctx.config.sandboxAllowNetwork
|
|
52983
53108
|
});
|
|
52984
|
-
return {
|
|
53109
|
+
return {
|
|
53110
|
+
ok: true,
|
|
53111
|
+
prep: {
|
|
53112
|
+
workspace,
|
|
53113
|
+
credential: { token, expiresAt, canPush },
|
|
53114
|
+
sandbox,
|
|
53115
|
+
repoDir: workspace.worktreeDir,
|
|
53116
|
+
repo: { owner, name, default_branch: defaultBranch },
|
|
53117
|
+
buildRequestId
|
|
53118
|
+
}
|
|
53119
|
+
};
|
|
52985
53120
|
}
|
|
52986
53121
|
async function finalizeForgeBuildPush(prep, result) {
|
|
53122
|
+
const credential = { token: prep.credential.token, expiresAt: prep.credential.expiresAt };
|
|
53123
|
+
let pushed;
|
|
52987
53124
|
try {
|
|
52988
|
-
|
|
52989
|
-
const note = pushed ? `[forge] pushed ${pushed.commitSha} to ${prep.workspace.branch}` : "[forge] no changes to push";
|
|
52990
|
-
return { ...result, summary: `${result.summary}
|
|
52991
|
-
${note}`.slice(0, 12e3) };
|
|
53125
|
+
pushed = await pushChangeset(prep.workspace, credential);
|
|
52992
53126
|
} catch (error51) {
|
|
52993
53127
|
return {
|
|
52994
53128
|
...result,
|
|
@@ -52997,6 +53131,43 @@ ${note}`.slice(0, 12e3) };
|
|
|
52997
53131
|
[forge] push failed: ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}`.slice(0, 12e3)
|
|
52998
53132
|
};
|
|
52999
53133
|
}
|
|
53134
|
+
if (!pushed) {
|
|
53135
|
+
return { ...result, summary: `${result.summary}
|
|
53136
|
+
[forge] no changes to push`.slice(0, 12e3) };
|
|
53137
|
+
}
|
|
53138
|
+
try {
|
|
53139
|
+
const pr = await openDraftPullRequest({
|
|
53140
|
+
repo: prep.repo,
|
|
53141
|
+
branch: prep.workspace.branch,
|
|
53142
|
+
buildRequestId: prep.buildRequestId,
|
|
53143
|
+
commitSha: pushed.commitSha,
|
|
53144
|
+
credential
|
|
53145
|
+
});
|
|
53146
|
+
const evidence = [
|
|
53147
|
+
{ kind: "note", summary: `pushed ${pushed.commitSha} to ${prep.workspace.branch}`.slice(0, 500) },
|
|
53148
|
+
{ kind: "note", summary: `${pr.adopted ? "adopted existing" : "opened"} draft PR ${pr.url}`.slice(0, 500) }
|
|
53149
|
+
];
|
|
53150
|
+
return {
|
|
53151
|
+
...result,
|
|
53152
|
+
prUrl: pr.url,
|
|
53153
|
+
branch: prep.workspace.branch,
|
|
53154
|
+
commitSha: pushed.commitSha,
|
|
53155
|
+
evidence,
|
|
53156
|
+
summary: `${result.summary}
|
|
53157
|
+
[forge] pushed ${pushed.commitSha} to ${prep.workspace.branch}; ${pr.adopted ? "adopted" : "opened"} draft PR ${pr.url}`.slice(0, 12e3)
|
|
53158
|
+
};
|
|
53159
|
+
} catch (error51) {
|
|
53160
|
+
return {
|
|
53161
|
+
...result,
|
|
53162
|
+
ok: false,
|
|
53163
|
+
// The branch reached GitHub but the draft PR did not — fail so the phase requeues
|
|
53164
|
+
// rather than advancing a headless push. Re-entry re-pushes (no-op) and re-opens (adopt).
|
|
53165
|
+
branch: prep.workspace.branch,
|
|
53166
|
+
commitSha: pushed.commitSha,
|
|
53167
|
+
summary: `${result.summary}
|
|
53168
|
+
[forge] pushed ${pushed.commitSha} to ${prep.workspace.branch}; draft PR open failed: ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}`.slice(0, 12e3)
|
|
53169
|
+
};
|
|
53170
|
+
}
|
|
53000
53171
|
}
|
|
53001
53172
|
function buildTurnPrompt(input) {
|
|
53002
53173
|
const { kind, workOrder, execution } = input;
|