firegoat 0.21.0 → 0.22.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.
- package/README.md +12 -2
- package/dist/cli.js +118 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ npx firegoat@latest init # Creates firegoat.yaml and .firegoat/, installs th
|
|
|
12
12
|
npx firegoat@latest eval # Runs your claude on each test, in .firegoat/runs/<runId>/<test>/<n>/work
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
That's it: two commands. Use `npx firegoat@latest`: plain `npx firegoat` can keep running an old cached version. `eval` prints its version first (`firegoat X.Y.Z`), and `summary.json` records it as `firegoatVersion`. `claude` must be installed and logged in (run `claude` once).
|
|
15
|
+
That's it: two commands. `npx firegoat@latest eval --dry-run` checks `firegoat.yaml` and lists the runs it would start (`<test>#<n> [model]`, with step counts) without building, running claude or uploading; config errors exit 3 as in a real eval. Use `npx firegoat@latest`: plain `npx firegoat` can keep running an old cached version. `eval` prints its version first (`firegoat X.Y.Z`), and `summary.json` records it as `firegoatVersion`. `claude` must be installed and logged in (run `claude` once).
|
|
16
16
|
|
|
17
17
|
`init` installs the firegoat-onboarding skill globally, into `~/.claude/skills/` (in a terminal it asks: global, this repo, or none). `--local-skills` puts it in this repo's `.claude/skills/` instead (commit it so teammates and CI get it); `--no-skills` skips it.
|
|
18
18
|
|
|
@@ -224,7 +224,17 @@ Limits:
|
|
|
224
224
|
- The agent gets your environment plus `env:`, never `vars:`. Vars that npm/npx set for firegoat itself (`npm_config_*`, `npm_lifecycle_*`, `npm_package_*`, `npm_execpath`, `npm_node_execpath`, `npm_command`, `INIT_CWD`) are removed from every env firegoat starts (agent, build, install, checks): `npx -y firegoat` sets `npm_config_yes=true`, which would make the agent's `npm init` / `npx create-*` skip their prompts. Set one under `env:` to keep it. Every `FIREGOAT_*` var firegoat was started with (`FIREGOAT_API_KEY` above all) is removed too (claude then gets the run's own `FIREGOAT_OUT`, `FIREGOAT_WORKDIR` and `FIREGOAT_HOME`, for MCP configs; the agent's commands see them too), and so are the vars a parent Claude Code session sets (when firegoat runs from claude, or its agent runs firegoat), so the claude under test doesn't act as a nested session: `CLAUDECODE`, `CLAUDE_CODE_*`, `CLAUDE_PID`, `CLAUDE_ENV_FILE`, `CLAUDE_SESSION_ID`, `CLAUDE_PROJECT_DIR`, `CLAUDE_EFFORT`, `CLAUDE_AGENT_SDK_*`, `CLAUDE_PLUGIN_ROOT`, `CLAUDE_PLUGIN_DATA`, `CLAUDE_SKILL_DIR`. Login and provider vars stay: `ANTHROPIC_*`, `CLAUDE_CODE_OAUTH_TOKEN`, `CLAUDE_CODE_OAUTH_REFRESH_TOKEN`, `CLAUDE_CODE_USE_*` (Bedrock, Vertex, ...), `CLAUDE_CODE_SKIP_*_AUTH`, `CLAUDE_CODE_CLIENT_CERT`, `CLAUDE_CODE_CLIENT_KEY`, `CLAUDE_CODE_CLIENT_KEY_PASSPHRASE`.
|
|
225
225
|
- Runs (tests × models × `runs`) go 4 at a time, each in its own folder with its own session and HOME; `--parallel N` changes that, `--parallel 1` runs them one after another. `summary.json` lists them in `firegoat.yaml` order.
|
|
226
226
|
- Script checks run in the agent's folder afterwards and set the exit code. `end_goal` is not graded by firegoat: it's for you, or your coding agent, to judge from `run.json`, the session and the folder.
|
|
227
|
-
- Output per run, in `.firegoat/runs/<runId>/<test>/<n>/`: `transcript.txt` (each tool call with its command and short output, the final answer, files made; checks get it as `$FIREGOAT_TRANSCRIPT`;
|
|
227
|
+
- Output per run, in `.firegoat/runs/<runId>/<test>/<n>/`: `transcript.txt` (each tool call with its command and short output, the final answer, files made; checks get it as `$FIREGOAT_TRANSCRIPT`; format below), `run.json` (status, prompt, end_goal, check results, paths), `work/`, `home/` and `home.env` (the run's HOME), logs. Read the transcript to judge `end_goal`; claude's full session file is ~50x larger. `summary.json` covers the whole eval; `--json` prints it.
|
|
228
|
+
|
|
229
|
+
### The transcript
|
|
230
|
+
|
|
231
|
+
`transcript.txt` is greppable line by line; `n` counts the agent's tool calls:
|
|
232
|
+
|
|
233
|
+
- `[n] $ <command>`: a shell call, on every line of the command (heredocs and multi-line scripts too), never cut. So `grep -E '^\[[0-9]+\] \$ .*npm install'` matches only commands.
|
|
234
|
+
- `[n] <Tool> <target>`: a tool (MCP ones too) whose input has a `file_path`, `path`, `url`, `pattern` or `query`: just that value, e.g. `[2] Write src/app.ts`. The written body is not in the transcript: check the file in the workdir.
|
|
235
|
+
- `[n] <Tool> <json>`: any other tool, MCP tools included, with its whole input as one line of JSON, cut at 4000 chars: `[3] mcp__acme__push_html {"canvas":"demo","html":"<p>Hi</p>"}`. So `grep -Eq '^\[[0-9]+\] mcp__acme__push_html .*<h1>' "$FIREGOAT_TRANSCRIPT"` checks what the agent pushed.
|
|
236
|
+
- ` → ok` or ` → error`, then the tool's output indented 4 spaces, cut at 15 lines or 1500 chars (`… (N more chars)`).
|
|
237
|
+
- `agent: ` on every line of the agent's text (cut at 30 lines or 1500 chars) and of its final answer (uncut), then the files in the workdir (without `node_modules`, `.git`, `.venv`, `__pycache__`).
|
|
228
238
|
|
|
229
239
|
`firegoat.yaml` keys: `runs`, `model`, `build`, `install`, `cleanup`, `vars`, `env`, `claude_args`, and `tests` (`name`, `prompt`, `end_goal`, `checks`, `timeout`, `env`, `install`, `cleanup`, `claude_args`). Unknown keys are errors. Full reference: `npx firegoat init --help`.
|
|
230
240
|
|
package/dist/cli.js
CHANGED
|
@@ -6947,7 +6947,7 @@ var require_public_api = __commonJS(function(exports) {
|
|
|
6947
6947
|
// package.json
|
|
6948
6948
|
var package_default = {
|
|
6949
6949
|
name: "firegoat",
|
|
6950
|
-
version: "0.
|
|
6950
|
+
version: "0.22.0",
|
|
6951
6951
|
repository: {
|
|
6952
6952
|
type: "git",
|
|
6953
6953
|
url: "git+https://github.com/firegoat-ai/firegoat-cli.git"
|
|
@@ -8010,11 +8010,9 @@ firegoat.yaml reference:
|
|
|
8010
8010
|
vars, plus $FIREGOAT_OUT and $FIREGOAT_WORKDIR; install, checks and cleanup also
|
|
8011
8011
|
get $FIREGOAT_HOME, the run's own home, as HOME (build keeps your real HOME).
|
|
8012
8012
|
Checks also get $FIREGOAT_TRANSCRIPT, a short plain-text
|
|
8013
|
-
transcript, greppable line by line:
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
final answer. So '^\\[[0-9]+\\] \\$ .*foo' matches only commands. Checks also
|
|
8017
|
-
get $FIREGOAT_SESSION (claude's full session file, JSON lines).
|
|
8013
|
+
transcript, greppable line by line (format: eval --help, Transcript). So
|
|
8014
|
+
'^\\[[0-9]+\\] \\$ .*foo' matches only commands. Checks also get
|
|
8015
|
+
$FIREGOAT_SESSION (claude's full session file, JSON lines).
|
|
8018
8016
|
|
|
8019
8017
|
The agent never gets vars, only env:. The vars npm/npx set for firegoat itself
|
|
8020
8018
|
(npm_config_*, npm_lifecycle_*, npm_package_*, npm_execpath, npm_node_execpath,
|
|
@@ -8098,7 +8096,8 @@ page.
|
|
|
8098
8096
|
Exit codes: 0 logged out (or was not logged in), 3 the file or ~/.firegoat is a
|
|
8099
8097
|
symlink, not a regular file/directory, or someone else's (left alone).`;
|
|
8100
8098
|
var EVAL_HELP = `Usage: firegoat eval [test..] [--runs N] [--model NAME].. [--parallel N]
|
|
8101
|
-
[--var KEY=value].. [--json] [--no-upload] [--
|
|
8099
|
+
[--var KEY=value].. [--json] [--no-upload] [--dry-run]
|
|
8100
|
+
[--claude-bin PATH]
|
|
8102
8101
|
|
|
8103
8102
|
Runs each test in firegoat.yaml (in the current folder): your claude gets the prompt
|
|
8104
8103
|
in a fresh, empty folder, .firegoat/runs/<runId>/<test>/<n>/work, with build:'s
|
|
@@ -8152,6 +8151,14 @@ fixtures and build: stay in the current folder.
|
|
|
8152
8151
|
--json The summary as JSON on stdout (or {error, exitCode} if eval
|
|
8153
8152
|
stops early); progress goes to stderr
|
|
8154
8153
|
--no-upload Keep runs local even when logged in to firegoat.ai
|
|
8154
|
+
--dry-run Check firegoat.yaml as eval would (test names, --var,
|
|
8155
|
+
--model, missing vars), print each planned run
|
|
8156
|
+
("<test>#<n> [model]" with its install, checks and cleanup
|
|
8157
|
+
counts and rendered claude_args, paths under
|
|
8158
|
+
runs/<runId>/) and exit 0. Builds nothing, starts no
|
|
8159
|
+
claude, uploads nothing, writes no files. Config errors
|
|
8160
|
+
exit 3. With --json: {dryRun, build, runs: [{test, n,
|
|
8161
|
+
model, claudeArgs, install, checks, cleanup}]}
|
|
8155
8162
|
--claude-bin PATH Path to claude (default: claude on PATH)
|
|
8156
8163
|
|
|
8157
8164
|
Upload: after firegoat login (or with FIREGOAT_API_KEY), each run's claude session,
|
|
@@ -8181,14 +8188,25 @@ Output: .firegoat/runs/<runId>/
|
|
|
8181
8188
|
checksLog, cleanupLog; startedAt; upload ({status: "uploaded",
|
|
8182
8189
|
url} | {status: "failed", error} | {status:
|
|
8183
8190
|
"skipped", reason}, null when not uploading)
|
|
8184
|
-
<test>/<n>/transcript.txt What the agent did
|
|
8185
|
-
("[n] $ " on every line of a shell call), short output
|
|
8186
|
-
(indented), its text and final answer ("agent: " on
|
|
8187
|
-
every line), files it made
|
|
8191
|
+
<test>/<n>/transcript.txt What the agent did, below
|
|
8188
8192
|
<test>/<n>/home/, home.env The run's HOME, and the file claude sources
|
|
8189
8193
|
before the agent's Bash commands
|
|
8190
8194
|
build.txt, <test>/<n>/install.txt, checks.txt, cleanup.txt Logs
|
|
8191
8195
|
|
|
8196
|
+
Transcript ($FIREGOAT_TRANSCRIPT), greppable line by line; n counts tool calls:
|
|
8197
|
+
[n] $ <command> A shell call, on every line of the command, uncut
|
|
8198
|
+
[n] <Tool> <target> A tool (MCP too) with a file_path, path, url, pattern
|
|
8199
|
+
or query input: that value, e.g. "[2] Write src/a.ts"
|
|
8200
|
+
[n] <Tool> <json> Any other tool, MCP ones included: its input as one
|
|
8201
|
+
line of JSON, e.g. [3] mcp__<server>__<tool> {"q":"x"};
|
|
8202
|
+
cut at 4000 chars
|
|
8203
|
+
→ ok | → error Then the tool's output, indented 4 spaces, cut at 15
|
|
8204
|
+
lines or 1500 chars ("… (N more chars)")
|
|
8205
|
+
agent: <text> Every line of the agent's text (cut at 30 lines or
|
|
8206
|
+
1500 chars) and of its final answer (uncut)
|
|
8207
|
+
Then the files in the workdir (not node_modules, .git, .venv, __pycache__).
|
|
8208
|
+
A written file's body is not in the transcript: check the file in the workdir.
|
|
8209
|
+
|
|
8192
8210
|
Grading: firegoat runs only the script checks; they set the exit code. end_goal is
|
|
8193
8211
|
yours to judge: read transcript.txt first (a few KB); the session file has everything
|
|
8194
8212
|
(often 50x larger).
|
|
@@ -8199,6 +8217,7 @@ Examples:
|
|
|
8199
8217
|
npx firegoat eval --model claude-haiku-5-5 --model claude-sonnet-5-5
|
|
8200
8218
|
Each test on both models
|
|
8201
8219
|
npx firegoat eval --var SHA=$(git rev-parse HEAD)
|
|
8220
|
+
npx firegoat eval --dry-run Validate firegoat.yaml, list the runs
|
|
8202
8221
|
|
|
8203
8222
|
A run can take minutes (default timeout 600s per test).
|
|
8204
8223
|
|
|
@@ -8295,6 +8314,7 @@ import { join as join4, relative } from "node:path";
|
|
|
8295
8314
|
var OUTPUT_LINES = 15;
|
|
8296
8315
|
var OUTPUT_CHARS = 1500;
|
|
8297
8316
|
var TEXT_CHARS = 1500;
|
|
8317
|
+
var INPUT_CHARS = 4000;
|
|
8298
8318
|
var SKIP_DIRS = new Set(["node_modules", ".git", ".venv", "__pycache__"]);
|
|
8299
8319
|
function truncate(text, maxLines, maxChars) {
|
|
8300
8320
|
let out = text.length > maxChars ? text.slice(0, maxChars) : text;
|
|
@@ -8319,7 +8339,7 @@ function describeCall(step, name, input) {
|
|
|
8319
8339
|
for (const key of ["file_path", "path", "url", "pattern", "query"])
|
|
8320
8340
|
if (typeof input[key] === "string")
|
|
8321
8341
|
return `[${step}] ${name} ${input[key]}`;
|
|
8322
|
-
return `[${step}] ${name} ${truncate(JSON.stringify(input), 1,
|
|
8342
|
+
return `[${step}] ${name} ${truncate(JSON.stringify(input), 1, INPUT_CHARS)}`;
|
|
8323
8343
|
}
|
|
8324
8344
|
function describeToolUse(step, b) {
|
|
8325
8345
|
if (typeof b.name !== "string" || b.name === "")
|
|
@@ -8879,6 +8899,7 @@ function parseArgs(args) {
|
|
|
8879
8899
|
parallel: 4,
|
|
8880
8900
|
json: false,
|
|
8881
8901
|
noUpload: false,
|
|
8902
|
+
dryRun: false,
|
|
8882
8903
|
vars: {},
|
|
8883
8904
|
claudeBin: "claude"
|
|
8884
8905
|
};
|
|
@@ -8899,6 +8920,8 @@ function parseArgs(args) {
|
|
|
8899
8920
|
opts.json = true;
|
|
8900
8921
|
else if (flag === "--no-upload")
|
|
8901
8922
|
opts.noUpload = true;
|
|
8923
|
+
else if (flag === "--dry-run")
|
|
8924
|
+
opts.dryRun = true;
|
|
8902
8925
|
else if (flag === "--var") {
|
|
8903
8926
|
const kv = value();
|
|
8904
8927
|
const at = kv.indexOf("=");
|
|
@@ -8989,6 +9012,51 @@ async function uploadPreflight(opts, ui, project, runGroup, git) {
|
|
|
8989
9012
|
};
|
|
8990
9013
|
}
|
|
8991
9014
|
}
|
|
9015
|
+
function runFill(runId, n, testName, out, dir) {
|
|
9016
|
+
return {
|
|
9017
|
+
"run.id": runId,
|
|
9018
|
+
"run.n": String(n),
|
|
9019
|
+
"test.name": testName,
|
|
9020
|
+
FIREGOAT_OUT: out,
|
|
9021
|
+
FIREGOAT_WORKDIR: join6(dir, "work"),
|
|
9022
|
+
FIREGOAT_HOME: join6(dir, "home")
|
|
9023
|
+
};
|
|
9024
|
+
}
|
|
9025
|
+
function renderClaudeArgs(test, vars, fill) {
|
|
9026
|
+
return test.claudeArgs.map((a, i) => renderValue(a, vars, fill, `claude_args[${i}]`));
|
|
9027
|
+
}
|
|
9028
|
+
function planRuns(tests, models, runs) {
|
|
9029
|
+
const matrix = models.length > 0 ? models : [null];
|
|
9030
|
+
return tests.flatMap((t) => matrix.flatMap((model, m) => Array.from({ length: runs }, (_, i) => ({
|
|
9031
|
+
test: t.name,
|
|
9032
|
+
n: m * runs + i + 1,
|
|
9033
|
+
model
|
|
9034
|
+
}))));
|
|
9035
|
+
}
|
|
9036
|
+
var DRY_RUN_ID = "<runId>";
|
|
9037
|
+
function dryRun(opts) {
|
|
9038
|
+
const out = join6(opts.runsRoot, DRY_RUN_ID, "out");
|
|
9039
|
+
const runs = opts.plan.map((p) => {
|
|
9040
|
+
const test = opts.tests.find((t) => t.name === p.test);
|
|
9041
|
+
const fill = runFill(DRY_RUN_ID, p.n, test.name, out, join6(opts.runsRoot, DRY_RUN_ID, test.name, String(p.n)));
|
|
9042
|
+
renderEnv(test.env, opts.vars, fill);
|
|
9043
|
+
return {
|
|
9044
|
+
...p,
|
|
9045
|
+
claudeArgs: renderClaudeArgs(test, opts.vars, fill),
|
|
9046
|
+
install: [...opts.config.install, ...test.install],
|
|
9047
|
+
checks: test.checks.map((c) => render(c.run, opts.vars)),
|
|
9048
|
+
cleanup: [...test.cleanup, ...opts.config.cleanup]
|
|
9049
|
+
};
|
|
9050
|
+
});
|
|
9051
|
+
if (opts.json) {
|
|
9052
|
+
console.log(JSON.stringify({ dryRun: true, build: opts.config.build, runs }, null, 2));
|
|
9053
|
+
return;
|
|
9054
|
+
}
|
|
9055
|
+
const count = (n, what) => `${n} ${what}${n === 1 ? "" : "s"}`;
|
|
9056
|
+
opts.say(`Dry run: ${count(runs.length, "run")}, build ${count(opts.config.build.length, "step")}. Nothing was started.`);
|
|
9057
|
+
for (const r of runs)
|
|
9058
|
+
opts.say(`${r.test}#${r.n}${r.model === null ? "" : ` [${r.model}]`} install ${r.install.length}, checks ${r.checks.length}, cleanup ${r.cleanup.length}${r.claudeArgs.length > 0 ? `, claude_args ${JSON.stringify(r.claudeArgs)}` : ""}`);
|
|
9059
|
+
}
|
|
8992
9060
|
async function runOne(opts) {
|
|
8993
9061
|
const { test, n } = opts;
|
|
8994
9062
|
const started = Date.now();
|
|
@@ -8996,20 +9064,13 @@ async function runOne(opts) {
|
|
|
8996
9064
|
const workdir = join6(dir, "work");
|
|
8997
9065
|
mkdirSync4(workdir, { recursive: true });
|
|
8998
9066
|
const { home, envFile } = makeRunHome(dir);
|
|
8999
|
-
const fill =
|
|
9000
|
-
"run.id": opts.runId,
|
|
9001
|
-
"run.n": String(n),
|
|
9002
|
-
"test.name": test.name,
|
|
9003
|
-
FIREGOAT_OUT: opts.out,
|
|
9004
|
-
FIREGOAT_WORKDIR: workdir,
|
|
9005
|
-
FIREGOAT_HOME: home
|
|
9006
|
-
};
|
|
9067
|
+
const fill = runFill(opts.runId, n, test.name, opts.out, dir);
|
|
9007
9068
|
const r = {
|
|
9008
9069
|
test: test.name,
|
|
9009
9070
|
n,
|
|
9010
9071
|
model: opts.model,
|
|
9011
9072
|
claudeModel: null,
|
|
9012
|
-
claudeArgs: test
|
|
9073
|
+
claudeArgs: renderClaudeArgs(test, opts.vars, fill),
|
|
9013
9074
|
status: "error",
|
|
9014
9075
|
reason: "",
|
|
9015
9076
|
prompt: render(test.prompt, opts.vars),
|
|
@@ -9206,6 +9267,30 @@ async function evalCommand(args, root) {
|
|
|
9206
9267
|
];
|
|
9207
9268
|
if (missing.length > 0)
|
|
9208
9269
|
return fatal(EXIT.usage, `No value for ${missing.map((k) => `{{vars.${k}}}`).join(", ")}: pass --var ${missing[0]}=... or set it under vars: in firegoat.yaml`);
|
|
9270
|
+
if (opts.dryRun) {
|
|
9271
|
+
let repo;
|
|
9272
|
+
try {
|
|
9273
|
+
repo = gitRepo(root);
|
|
9274
|
+
} catch (e) {
|
|
9275
|
+
return fatal(EXIT.usage, e.message);
|
|
9276
|
+
}
|
|
9277
|
+
try {
|
|
9278
|
+
dryRun({
|
|
9279
|
+
config,
|
|
9280
|
+
tests,
|
|
9281
|
+
plan: planRuns(tests, models, runs),
|
|
9282
|
+
vars,
|
|
9283
|
+
runsRoot: join6(repo?.mainCheckout ?? root, ".firegoat", "runs"),
|
|
9284
|
+
json: opts.json,
|
|
9285
|
+
say
|
|
9286
|
+
});
|
|
9287
|
+
} catch (e) {
|
|
9288
|
+
if (e instanceof ConfigError)
|
|
9289
|
+
return fatal(EXIT.usage, e.message);
|
|
9290
|
+
throw e;
|
|
9291
|
+
}
|
|
9292
|
+
return EXIT.pass;
|
|
9293
|
+
}
|
|
9209
9294
|
const claudePath = resolveBin(opts.claudeBin);
|
|
9210
9295
|
if (!claudePath)
|
|
9211
9296
|
return fatal(EXIT.notGraded, `Not graded: claude binary ${JSON.stringify(opts.claudeBin)} not found. Install Claude Code (https://claude.com/claude-code) or pass --claude-bin PATH.`);
|
|
@@ -9249,14 +9334,11 @@ async function evalCommand(args, root) {
|
|
|
9249
9334
|
FIREGOAT_OUT: out,
|
|
9250
9335
|
PATH: [out, process.env.PATH ?? ""].join(delimiter2)
|
|
9251
9336
|
};
|
|
9252
|
-
const
|
|
9253
|
-
|
|
9254
|
-
test: t.name,
|
|
9255
|
-
n: m * runs + i + 1,
|
|
9256
|
-
model,
|
|
9337
|
+
const rows = planRuns(tests, models, runs).map((p) => ({
|
|
9338
|
+
...p,
|
|
9257
9339
|
state: "queued",
|
|
9258
9340
|
turns: 0
|
|
9259
|
-
}))
|
|
9341
|
+
}));
|
|
9260
9342
|
if (config.build.length > 0) {
|
|
9261
9343
|
const buildStarted = Date.now();
|
|
9262
9344
|
ui.build("running");
|
|
@@ -9664,7 +9746,8 @@ var TIMEOUT_MS2 = 30000;
|
|
|
9664
9746
|
var MAX_EXPIRES_S = 900;
|
|
9665
9747
|
var MAX_INTERVAL_S = 60;
|
|
9666
9748
|
var FORM = { "Content-Type": "application/x-www-form-urlencoded" };
|
|
9667
|
-
var
|
|
9749
|
+
var UPLOADS_ON = "Eval uploads each session to firegoat.ai as a private trace; --no-upload keeps runs local.";
|
|
9750
|
+
var UPLOAD_NOTICE = `${UPLOADS_ON} A session holds prompts, tool outputs and file contents: possibly secrets from your env or repo. The agent runs as you and can read ~/.firegoat (and a FIREGOAT_API_KEY in firegoat's own process env).`;
|
|
9668
9751
|
var TERMS = "By logging in, you agree to the firegoat.ai Terms of Service (https://firegoat.ai/terms) and Privacy Policy (https://firegoat.ai/privacy).";
|
|
9669
9752
|
function str2(o, k, step) {
|
|
9670
9753
|
const v = o?.[k];
|
|
@@ -9967,13 +10050,16 @@ async function askScope(question) {
|
|
|
9967
10050
|
var LOGIN_HINT = "Runs stay local: npx firegoat login uploads them to firegoat.ai";
|
|
9968
10051
|
async function loginStep(interactive, done, paint) {
|
|
9969
10052
|
const label = "firegoat.ai login";
|
|
10053
|
+
const uploadsOn = () => console.log(` ${paint("90", UPLOADS_ON)}`);
|
|
9970
10054
|
const key = resolveKey(baseUrl());
|
|
9971
10055
|
if (key) {
|
|
9972
10056
|
const who = key.source === "env" ? "FIREGOAT_API_KEY is set" : `Logged in as ${key.email}`;
|
|
9973
|
-
if (interactive)
|
|
10057
|
+
if (interactive) {
|
|
9974
10058
|
done(label, who);
|
|
9975
|
-
|
|
9976
|
-
|
|
10059
|
+
uploadsOn();
|
|
10060
|
+
} else
|
|
10061
|
+
console.log(`${who}
|
|
10062
|
+
${UPLOADS_ON}`);
|
|
9977
10063
|
return EXIT.pass;
|
|
9978
10064
|
}
|
|
9979
10065
|
if (!interactive) {
|
|
@@ -9989,6 +10075,7 @@ async function loginStep(interactive, done, paint) {
|
|
|
9989
10075
|
try {
|
|
9990
10076
|
const email = await loginFlow((l) => console.log(` ${l}`), true);
|
|
9991
10077
|
done(label, `Logged in as ${email}`);
|
|
10078
|
+
uploadsOn();
|
|
9992
10079
|
return EXIT.pass;
|
|
9993
10080
|
} catch (e) {
|
|
9994
10081
|
if (!(e instanceof ApiError))
|