@growthagent/ci 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +39 -16
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { execFileSync, spawnSync } from "node:child_process";
5
- import { cpSync, existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
- import path4 from "node:path";
5
+ import { cpSync, existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync as writeFileSync2 } from "node:fs";
6
+ import path5 from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
 
9
9
  // ../github/src/app.ts
@@ -235,6 +235,28 @@ function validatePatch(patch, limits = DEFAULT_PATCH_LIMITS) {
235
235
  return { ok: true, report: { files, bytes, addedLines } };
236
236
  }
237
237
 
238
+ // src/provider-config.ts
239
+ import { mkdtempSync as mkdtempSync2, writeFileSync } from "node:fs";
240
+ import { tmpdir as tmpdir3 } from "node:os";
241
+ import path4 from "node:path";
242
+ var RUN_TOKEN_ENV = "GX_RUN_TOKEN";
243
+ function writeGoogleProviderConfig(model) {
244
+ const gateway = (process.env.GX_GATEWAY_URL ?? "").replace(/\/$/, "");
245
+ const dir = mkdtempSync2(path4.join(tmpdir3(), "growth-agent-pi-"));
246
+ const config = {
247
+ providers: {
248
+ "growthagent-google": {
249
+ baseUrl: `${gateway}/v1beta`,
250
+ api: "google-generative-ai",
251
+ apiKey: `$${RUN_TOKEN_ENV}`,
252
+ models: [{ id: model, name: model, input: ["text"], contextWindow: 1e6 }]
253
+ }
254
+ }
255
+ };
256
+ writeFileSync(path4.join(dir, "models.json"), JSON.stringify(config, null, 2));
257
+ return dir;
258
+ }
259
+
238
260
  // src/trust.ts
239
261
  var TRUSTED_APP_LOGIN = "growth-agent[bot]";
240
262
  function authorIsTrusted(issue, extraLogins = []) {
@@ -340,11 +362,11 @@ function buildPrompt(fields, briefing) {
340
362
  }
341
363
 
342
364
  // src/cli.ts
343
- var HERE = path4.dirname(fileURLToPath(import.meta.url));
365
+ var HERE = path5.dirname(fileURLToPath(import.meta.url));
344
366
  var PATCH_FILE = "growth-agent.patch";
345
367
  function assetsDir() {
346
- const packaged = path4.join(HERE, "assets", "skills");
347
- return existsSync2(packaged) ? packaged : path4.join(HERE, "..", "assets", "skills");
368
+ const packaged = path5.join(HERE, "assets", "skills");
369
+ return existsSync2(packaged) ? packaged : path5.join(HERE, "..", "assets", "skills");
348
370
  }
349
371
  function sh(cmd, args, opts = {}) {
350
372
  const r = spawnSync(cmd, args, { encoding: "utf8", stdio: ["inherit", "pipe", "inherit"] });
@@ -471,28 +493,25 @@ async function implement() {
471
493
  console.log(
472
494
  meta.kind === "instrumentation" ? `\u25B8 instrumentation request #${issue.number}` : `\u25B8 change request #${issue.number} \u2014 flag ${meta.flagKey} on ${meta.surface}`
473
495
  );
474
- const skillsDir = path4.resolve(process.cwd(), ".pi", "skills");
496
+ const skillsDir = path5.resolve(process.cwd(), ".pi", "skills");
475
497
  mkdirSync(skillsDir, { recursive: true });
476
498
  cpSync(assetsDir(), skillsDir, { recursive: true });
477
499
  const prompt = buildPrompt(meta, extractBriefing(issue.body));
478
500
  const provider = process.env.GX_CI_PROVIDER ?? "anthropic";
479
501
  const model = process.env.GX_CI_MODEL ?? (provider === "google" ? "gemini-3.7-flash" : "claude-opus-5");
480
- if (provider === "google") {
481
- console.log(
482
- "\u25B8 NOTE: GX_CI_PROVIDER=google routes model traffic directly to Google on your own GEMINI_API_KEY. It is billed by Google to you and is not metered by Growth Agent."
483
- );
484
- }
485
502
  const session = await openSession();
486
503
  if (!session.ok) {
487
504
  console.error(`Refusing to run the model: ${session.reason}`);
488
505
  return 1;
489
506
  }
490
- console.log(`\u25B8 running pi (${provider}/${model})`);
507
+ const piProvider = provider === "google" ? "growthagent-google" : provider;
508
+ const piConfigDir = provider === "google" ? writeGoogleProviderConfig(model) : null;
509
+ console.log(`\u25B8 running pi (${piProvider}/${model})`);
491
510
  let piStatus;
492
511
  try {
493
512
  const pi = spawnSync(
494
513
  "pi",
495
- ["-p", prompt, "--provider", provider, "--model", model, "--thinking", "high"],
514
+ ["-p", prompt, "--provider", piProvider, "--model", model, "--thinking", "high"],
496
515
  {
497
516
  stdio: ["ignore", "inherit", "inherit"],
498
517
  env: {
@@ -501,8 +520,12 @@ async function implement() {
501
520
  ANTHROPIC_BASE_URL: process.env.GX_GATEWAY_URL ?? process.env.ANTHROPIC_BASE_URL,
502
521
  ANTHROPIC_API_KEY: session.token,
503
522
  // The long-lived credential must not survive into the model's
504
- // environment, whatever else it might read there.
505
- GROWTH_AGENT_TOKEN: ""
523
+ // environment, whatever else it might read there. Nor may a raw
524
+ // provider key: everything is spent through the run token.
525
+ GROWTH_AGENT_TOKEN: "",
526
+ GEMINI_API_KEY: "",
527
+ // Only set for the Google path; pi keeps its own default otherwise.
528
+ ...piConfigDir ? { PI_CODING_AGENT_DIR: piConfigDir, [RUN_TOKEN_ENV]: session.token } : {}
506
529
  }
507
530
  }
508
531
  );
@@ -522,7 +545,7 @@ async function implement() {
522
545
  console.error("pi made no changes \u2014 nothing to open a PR for");
523
546
  return 2;
524
547
  }
525
- writeFileSync(PATCH_FILE, patch + "\n", "utf8");
548
+ writeFileSync2(PATCH_FILE, patch + "\n", "utf8");
526
549
  const verdict = validatePatch(patch);
527
550
  if (!verdict.ok) {
528
551
  console.error(`The produced patch was refused: ${verdict.reason}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growthagent/ci",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {