@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.
@@ -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 { ok: true, prep: { workspace, credential: { token, expiresAt, canPush }, sandbox, repoDir: workspace.worktreeDir } };
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
- const pushed = await pushChangeset(prep.workspace, { token: prep.credential.token, expiresAt: prep.credential.expiresAt });
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;