@capagents/forgeqe 2.2.4 → 2.2.6

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/HOW_TO_USE.md CHANGED
@@ -170,7 +170,7 @@ forgeqe start # the shell
170
170
  With GitHub Copilot licences instead of a model key, sign in rather than exporting one, and set `llm.active: copilot` in `forgeqe.yaml` (or `"default": "copilot"` in `llms.json`). The browser engine then runs on your Copilot seat; ForgeQE's agent and Scribe need another profile for now:
171
171
 
172
172
  ```bash
173
- forgeqe auth login copilot # a fine-grained token with Copilot Requests, or the GitHub CLI's sign-in
173
+ forgeqe auth login copilot # opens GitHub in your browser to sign in
174
174
  forgeqe models # the copilot profile is active
175
175
  ```
176
176
 
@@ -204,7 +204,7 @@ pip install forgeqe-cli
204
204
  forgeqe --help
205
205
  ```
206
206
 
207
- That is the only install step on Windows, macOS and Linux. The first run downloads Bun into `~/.forgeqe/tools` (unless a Bun 1.3+ is already installed), and `forgeqe setup` downloads the agent runtime and uv, which fetches Python. Nothing is added to PATH or shell profiles; deleting `~/.forgeqe` removes it all.
207
+ That is the only install step on Windows, macOS and Linux. npm installs Bun with ForgeQE (through your npm registry, so a company mirror works); with pip, or when npm skipped it, the first run downloads Bun into `~/.forgeqe/tools` (unless a Bun 1.3+ is already installed), and `forgeqe setup` downloads the agent runtime and uv, which fetches Python. Nothing is added to PATH or shell profiles; deleting `~/.forgeqe` removes it all.
208
208
 
209
209
  Set up the ForgeQE engine once (the first live run does it automatically if you skip this):
210
210
 
@@ -233,6 +233,38 @@ bun install
233
233
  bun src/index.ts --help
234
234
  ```
235
235
 
236
+ ### Company networks
237
+
238
+ On a company laptop, ForgeQE sets itself up for the network without anyone changing settings:
239
+
240
+ | What the network does | What ForgeQE does |
241
+ |---|---|
242
+ | Inspects HTTPS (Zscaler, Netskope, a firewall), with the company's own certificate | Uses the certificates this computer trusts: Windows' Trusted Root store, or the macOS System keychain. ForgeQE, the agent runtime, Playwright and Copilot's runtime get them through `NODE_EXTRA_CA_CERTS` (a copy in `~/.forgeqe/cache/system-ca.pem`, refreshed weekly); the Python engines check with the system through truststore; uv with `UV_SYSTEM_CERTS`. Certificates are still verified. |
243
+ | Sends traffic through a proxy set in Windows | Reads Windows' proxy setting (or the one proxy its automatic configuration script names) and passes it on as `HTTPS_PROXY`. ForgeQE's own servers on `127.0.0.1` always bypass it. |
244
+ | Blocks downloads from GitHub or the Playwright CDN | npm brings Bun through your registry; Microsoft Edge is used when Google Chrome is not installed, so no Chromium is downloaded; mirrors can stand in for the rest (below). |
245
+
246
+ To see where the network stops ForgeQE, run:
247
+
248
+ ```bash
249
+ forgeqe doctor
250
+ ```
251
+
252
+ It shows the proxy and certificates in use, checks each address ForgeQE needs (GitHub downloads, Python packages, the npm registry, your models, Azure DevOps or Jira), and for anything blocked prints what to ask IT for, ready to forward.
253
+
254
+ **For IT: mirrors and settings.** Set these as environment variables (for example by Group Policy):
255
+
256
+ | Variable | What it does |
257
+ |---|---|
258
+ | `FORGEQE_GITHUB_MIRROR` | A mirror of GitHub release downloads (`https://artifacts.example.com/github`, laid out as `<owner>/<repo>/releases/download/<tag>/<file>`): Bun, uv, the agent runtime, Python builds and Copilot's runtime come from it. Checksums are still checked. |
259
+ | `UV_DEFAULT_INDEX` (or `PIP_INDEX_URL`) | A mirror of PyPI for the engine's Python packages. |
260
+ | `PLAYWRIGHT_DOWNLOAD_HOST` | A mirror for Chromium, needed only without Chrome or Edge. |
261
+ | `HTTPS_PROXY`, `NO_PROXY` | A proxy other than Windows' setting. A proxy that needs a sign-in takes `http://user:password@host:port`. |
262
+ | `NODE_EXTRA_CA_CERTS` | Your own certificate file instead of the system's roots. |
263
+ | `FORGEQE_SYSTEM_CERTS=off` | Leaves certificates to each tool's built-in list. |
264
+ | `FORGEQE_HOME` | Where ForgeQE keeps its tools (default `~/.forgeqe`), for machines where programs may run only from certain folders. |
265
+
266
+ What still needs IT: a proxy that signs in with your Windows account only (NTLM or Kerberos), and policies that block programs in the user folder (set `FORGEQE_HOME` to an allowed folder). On macOS, the company's certificate must be in the System keychain, where device management puts it.
267
+
236
268
  ## 2. Scaffold config
237
269
 
238
270
  ```bash
@@ -288,22 +320,24 @@ With GitHub Copilot licences (Pro, Pro+, Business or Enterprise), a `github_copi
288
320
  |---|---|
289
321
  | `model` | The Copilot model, as the Copilot SDK names it (`gpt-4.1`, `claude-sonnet-4.6`). It must take screenshots. The sign-in lists your seat's models and which of them take images. |
290
322
  | `engine_model` | Optional. Another Copilot model for the browser engine and the desktop agent. |
291
- | `api_key_env` | Optional. The variable holding the GitHub token; without it ForgeQE reads `COPILOT_GITHUB_TOKEN`, then the token `forgeqe auth login copilot` saved, then the GitHub CLI's sign-in. |
323
+ | `api_key_env` | Optional. A variable that must hold the GitHub token for this profile. Without it ForgeQE uses `COPILOT_GITHUB_TOKEN`, then a token `forgeqe auth login copilot` saved, then the browser sign-in. |
292
324
 
293
- Sign in once per machine:
325
+ Sign in once per machine (Windows, macOS or Linux):
294
326
 
295
327
  ```bash
296
328
  forgeqe auth login copilot
297
329
  ```
298
330
 
299
- It offers two ways:
331
+ It offers two ways, the ones GitHub's Copilot SDK recommends:
332
+
333
+ - **Sign in with GitHub in the browser** (recommended on your own computer). ForgeQE runs GitHub's own Copilot sign-in (`copilot login` from GitHub's Copilot CLI, downloaded once into `~/.forgeqe/tools` and checked against GitHub's checksums). Your browser opens GitHub; approve **GitHub Copilot CLI** there and you are signed in. GitHub's CLI keeps the sign-in in your system's credential store (Windows Credential Manager, the macOS Keychain); ForgeQE never sees a token. On a remote machine it shows a code to enter at github.com/login/device instead.
334
+ - **A fine-grained token**, for machines without a browser. ForgeQE opens GitHub's token page: set **Resource owner** to your personal account and add the **Copilot Requests** permission, then paste the token (`github_pat_...`). Each character shows as `*`, and ForgeQE confirms what it got (`github_pat_11AB…Wz9Q (93 characters)`). The token goes to your system's credential store like ForgeQE's other secrets. Classic tokens (`ghp_`) do not work with Copilot.
300
335
 
301
- - **A fine-grained token** (recommended). ForgeQE opens GitHub's token page: set **Resource owner** to your personal account and add the **Copilot Requests** permission, then paste the token (`github_pat_...`). The token goes to your system's credential store like ForgeQE's other secrets. Classic tokens (`ghp_`) do not work with Copilot.
302
- - **The GitHub CLI's sign-in**, when `gh` is signed in.
336
+ Either way ForgeQE checks the sign-in by asking Copilot for your seat's models and lists them; the first time, the Copilot SDK downloads Copilot's runtime from GitHub (checked against GitHub's checksums). `forgeqe auth` shows the sign-in on its `Copilot` line; `forgeqe auth logout copilot` makes ForgeQE stop using it (GitHub keeps a browser sign-in until you revoke **GitHub Copilot CLI** at github.com/settings/applications).
303
337
 
304
- Either way ForgeQE checks the token by asking Copilot for your seat's models and lists them; the first time, the Copilot SDK downloads Copilot's runtime from GitHub (checked against GitHub's checksums). `forgeqe auth` shows the sign-in on its `Copilot` line; `forgeqe auth logout copilot` forgets a saved token.
338
+ If your organization uses SAML single sign-on, GitHub asks you to authorize the organization during the browser sign-in. A Copilot Business or Enterprise seat needs your organization to allow **Copilot CLI** in its Copilot policies.
305
339
 
306
- The token reaches only the gateway's process. The engine gets the gateway's address and a one-time key that works nowhere else; the gateway refuses requests without that key and any request a web page sends. Model-run shell commands never see the token or the key.
340
+ A token reaches only the gateway's process. The engine gets the gateway's address and a one-time key that works nowhere else; the gateway refuses requests without that key and any request a web page sends. Model-run shell commands never see the token or the key.
307
341
 
308
342
  Which part uses what:
309
343
 
@@ -323,13 +357,15 @@ GitHub Models, which ForgeQE used for the engine before, was [retired by GitHub]
323
357
 
324
358
  | ForgeQE says | What to do |
325
359
  |---|---|
326
- | `GitHub Copilot needs a GitHub token, and there is none` | Run `forgeqe auth login copilot` (or `/auth copilot` in the shell), or set `COPILOT_GITHUB_TOKEN`. |
327
- | `GitHub Copilot does not take classic tokens (ghp_)` | Make a fine-grained token with the Copilot Requests permission, owned by your personal account. |
360
+ | `GitHub Copilot is not signed in` | Run `forgeqe auth login copilot` (or `/auth copilot` in the shell) and sign in in the browser, or set `COPILOT_GITHUB_TOKEN`. |
361
+ | `The GitHub sign-in did not finish` | The browser sign-in was cancelled or denied. Run `forgeqe auth login copilot` again; with SAML single sign-on, authorize your organization when GitHub asks. |
362
+ | `GitHub Copilot does not take classic tokens (ghp_)` | Sign in in the browser instead, or make a fine-grained token with the Copilot Requests permission, owned by your personal account. |
328
363
  | `GitHub Copilot did not accept the token` | The token has no Copilot Requests permission, has expired, or the account has no Copilot seat. |
329
364
  | `… must be a Copilot model id`, `engine_model must be a Copilot model id` | Fix that field in `llms.json`: the sign-in lists the seat's models. A GitHub Models id such as `openai/gpt-4.1` is not a Copilot id; use `gpt-4.1`. |
330
365
  | `ForgeQE's agent and Scribe cannot run on a github_copilot profile yet` | Pick another profile for them (`--profile`, or Tab in the shell), or set `code.model`. |
331
366
  | The model is refused, or not found | The seat's plan or your organization's Copilot policies do not allow that model: pick one the sign-in listed, or ask a Copilot admin to enable it. |
332
367
  | `GitHub Copilot runs through the ForgeQE engine, which is not set up` | Run `forgeqe setup`. |
368
+ | `CERTIFICATE_VERIFY_FAILED` while downloading Copilot's runtime | Your network inspects HTTPS. Update ForgeQE and run `forgeqe setup`, then sign in again: it uses the certificates your computer trusts (see [Company networks](#company-networks)). If it still fails, run `forgeqe doctor` and send its output to IT. |
333
369
 
334
370
  ### Your prompts, skills, and product knowledge
335
371
 
@@ -407,7 +443,7 @@ In the shell, type `/auth jira` (or `/auth` alone for the status). Tokens are ty
407
443
  |--------|-----------------|
408
444
  | Azure DevOps | **Sign in with Microsoft in the browser** through the Azure CLI (`az login`); ForgeQE saves nothing, and the Azure CLI renews the sign-in. Or paste a personal access token: the token page opens with the scopes to tick. Azure DevOps Server (on-premises) uses a personal access token. |
409
445
  | GitHub | **Sign in with GitHub in the browser** through the GitHub CLI (`gh auth login`): ForgeQE shows the one-time code and opens the page. Or paste a token. Needs `gh` installed. `forgeqe auth login copilot` can use this sign-in for a `github_copilot` profile. |
410
- | GitHub Copilot | `forgeqe auth login copilot`: **Paste a fine-grained token** with the Copilot Requests permission (ForgeQE opens GitHub's token page), or use the GitHub CLI's sign-in. ForgeQE checks it with Copilot and lists the models your seat has. No other app is authorized on your GitHub account. For a `github_copilot` profile; see [GitHub Copilot seats as the model](#github-copilot-seats-as-the-model). |
446
+ | GitHub Copilot | `forgeqe auth login copilot`: **Sign in with GitHub in the browser** (GitHub's own Copilot sign-in), or paste a fine-grained token with the Copilot Requests permission. ForgeQE checks it with Copilot and lists the models your seat has. For a `github_copilot` profile; see [GitHub Copilot seats as the model](#github-copilot-seats-as-the-model). |
411
447
  | Jira, Confluence | Your Atlassian API tokens page opens (Cloud) or your personal access tokens page (Server/Data Center); create one and paste it. Confluence on the same Cloud site as Jira reuses the Jira sign-in. |
412
448
  | TestRail, Zephyr Scale, qTest, Xray Cloud | The page with your API key opens, or ForgeQE says where to find it; paste it. |
413
449
  | Anything else | `forgeqe auth login OPENAI_API_KEY` (any variable name) saves that one secret: a model key, a Teams webhook URL, an SMTP or saved-login password. |
@@ -2767,7 +2803,7 @@ the list again. For a client that ignores it, `forgeqe mcp --all-tools` lists ev
2767
2803
  | `WP_LLMS_JSON` | Path to `llms.json` |
2768
2804
  | `WP_LLM_PROFILE` | Active profile, unless `--profile` is set |
2769
2805
  | `WP_AZURE_API_KEY` / `OPENAI_API_KEY` / `WP_API_KEY` | Keys referenced by profiles |
2770
- | `COPILOT_GITHUB_TOKEN` | A `github_copilot` profile's GitHub token (fine-grained, with the Copilot Requests permission), unless the profile's `api_key_env` names another; else the saved token, then the GitHub CLI's sign-in |
2806
+ | `COPILOT_GITHUB_TOKEN` | A `github_copilot` profile's GitHub token (fine-grained, with the Copilot Requests permission), unless the profile's `api_key_env` names another; else the saved token, then the browser sign-in (`forgeqe auth login copilot`) |
2771
2807
  | `FORGEQE_COPILOT_TOKEN` | The token `forgeqe auth login copilot` saves; set by ForgeQE from the credential store, not by hand |
2772
2808
  | `FORGEQE_CONFIG_DIR` | Global config folder (default `~/.config/forgeqe`): `forgeqe.yaml`, `llms.json` and `.env` used when a project has none |
2773
2809
  | `FORGEQE_PYTHON` | A Python that already has the engine's libraries, used instead of the managed venv (skipped, with a note, when it cannot run the engine) |
@@ -57,11 +57,11 @@ ForgeQE downloads these onto the machine it runs on, from their own sources, whe
57
57
 
58
58
  | Software | What for | Licence | Source |
59
59
  |----------|----------|---------|--------|
60
- | Bun | the JavaScript runtime, when the machine has none (bin/forgeqe.js) | MIT | https://bun.sh |
60
+ | Bun | the JavaScript runtime: npm installs it with ForgeQE (@oven/bun-* optional dependencies), else it is downloaded when the machine has none (bin/forgeqe.js) | MIT | https://bun.sh |
61
61
  | OpenCode | the agent runtime for ForgeQE's agent and Scribe (src/code/runtime.ts) | MIT | https://opencode.ai |
62
62
  | uv and Python | the browser engine's interpreter (src/engine/setup.ts) | MIT or Apache-2.0; PSF | https://github.com/astral-sh/uv |
63
- | browser-use, Playwright, PyYAML, azure-identity, github-copilot-sdk | the browser engine's libraries and Chromium (src/engine/setup.ts) | MIT; Apache-2.0; MIT; MIT; MIT | https://pypi.org |
64
- | GitHub Copilot CLI runtime | downloaded by GitHub's Copilot SDK for github_copilot profiles (src/copilot.ts) | GitHub Copilot CLI license (GitHub) | https://github.com/github/copilot-cli |
63
+ | browser-use, Playwright, PyYAML, azure-identity, github-copilot-sdk, truststore | the browser engine's libraries and Chromium (src/engine/setup.ts) | MIT; Apache-2.0; MIT; MIT; MIT; MIT | https://pypi.org |
64
+ | GitHub Copilot CLI and its runtime | the runtime downloaded by GitHub's Copilot SDK for github_copilot profiles (src/copilot.ts), and the CLI for the browser sign-in (src/auth/copilotLogin.ts) | GitHub Copilot CLI license (GitHub) | https://github.com/github/copilot-cli |
65
65
 
66
66
  ## Licence texts
67
67
 
package/bin/forgeqe.d.ts CHANGED
@@ -3,3 +3,9 @@ export const BUN_VERSION: string
3
3
  export function cliEntry(root: string): string
4
4
  export function bunAsset(platform?: string, arch?: string, musl?: boolean): string | null
5
5
  export function unzipEntry(zip: Buffer, name: string): Buffer
6
+ /** The npm package with this machine's Bun build (@oven/bun-windows-x64-baseline). */
7
+ export function bunPackage(platform?: string, arch?: string, musl?: boolean): string | null
8
+ /** The Bun npm installed beside ForgeQE, or null. */
9
+ export function npmBun(resolve?: (id: string) => string, pkg?: string | null): string | null
10
+ /** `env` with NODE_EXTRA_CA_CERTS set to the system's roots ForgeQE saved, when there are some and the user set none. */
11
+ export function bunEnv(env?: Record<string, string | undefined>, home?: string): Record<string, string | undefined>
package/bin/forgeqe.js CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * npm bin for the ForgeQE CLI.
4
- * The CLI runs on Bun. Use a system Bun 1.3+ if there is one, otherwise download the pinned Bun
5
- * into ~/.forgeqe/tools (checksum verified, no PATH or shell profile changes) and run the TypeScript entry with it.
4
+ * The CLI runs on Bun: the pinned Bun npm installed with ForgeQE (an @oven/bun-* optional dependency, so it comes through
5
+ * the company's npm mirror and proxy), else a system Bun 1.3+, else the pinned Bun downloaded into ~/.forgeqe/tools
6
+ * (checksum verified, no PATH or shell profile changes).
6
7
  */
7
8
 
8
9
  import { spawnSync } from "node:child_process";
@@ -10,6 +11,7 @@ import { createHash } from "node:crypto";
10
11
  import { chmodSync, existsSync, mkdirSync, readdirSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
11
12
  import os from "node:os";
12
13
  import path from "node:path";
14
+ import { createRequire } from "node:module";
13
15
  import { fileURLToPath } from "node:url";
14
16
  import { inflateRawSync } from "node:zlib";
15
17
 
@@ -60,9 +62,28 @@ function newEnough(v) {
60
62
  return true;
61
63
  }
62
64
 
63
- /** FORGEQE_BUN, then bun on PATH, then the official installer's location, then a Bun ForgeQE downloaded earlier. */
65
+ /** The npm package with this machine's Bun build, named like its release asset (@oven/bun-windows-x64-baseline). */
66
+ export function bunPackage(platform = process.platform, arch = process.arch, musl = isMusl()) {
67
+ const asset = bunAsset(platform, arch, musl);
68
+ return asset ? `@oven/${asset.replace(/\.zip$/, "")}` : null;
69
+ }
70
+
71
+ /** The Bun npm installed beside ForgeQE, or null (another machine's install, or --no-optional). */
72
+ export function npmBun(resolve = createRequire(import.meta.url).resolve, pkg = bunPackage()) {
73
+ if (!pkg) return null;
74
+ try {
75
+ const bin = path.join(path.dirname(resolve(`${pkg}/package.json`)), "bin", exe);
76
+ return existsSync(bin) ? bin : null;
77
+ } catch {
78
+ return null;
79
+ }
80
+ }
81
+
82
+ /** FORGEQE_BUN, then the Bun npm installed, then bun on PATH, then the official installer's location, then a Bun ForgeQE downloaded earlier. */
64
83
  function findBun() {
65
84
  if (process.env.FORGEQE_BUN) return process.env.FORGEQE_BUN;
85
+ const fromNpm = npmBun();
86
+ if (fromNpm) return fromNpm;
66
87
  const home = process.env.HOME || process.env.USERPROFILE || "";
67
88
  const candidates = ["bun", ...(home ? [path.join(home, ".bun", "bin", exe)] : []), managedBun()];
68
89
  for (const c of candidates) {
@@ -132,7 +153,8 @@ async function download(url) {
132
153
  async function installBun() {
133
154
  const asset = bunAsset();
134
155
  if (!asset) throw new Error(`no Bun build for ${process.platform}/${process.arch}`);
135
- const base = `https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}`;
156
+ const mirror = (process.env.FORGEQE_GITHUB_MIRROR || "").trim().replace(/\/+$/, "");
157
+ const base = `${/^https:\/\//i.test(mirror) ? mirror : "https://github.com"}/oven-sh/bun/releases/download/bun-v${BUN_VERSION}`;
136
158
  process.stderr.write(`forgeqe: downloading Bun ${BUN_VERSION} (${asset}) into ${toolsDir()} (one time)...\n`);
137
159
  const [zip, sums] = await Promise.all([download(`${base}/${asset}`), download(`${base}/SHASUMS256.txt`)]);
138
160
  const expected = String(sums)
@@ -156,9 +178,19 @@ async function installBun() {
156
178
  return target;
157
179
  }
158
180
 
181
+ /**
182
+ * Bun reads NODE_EXTRA_CA_CERTS only at start: give it the system's trusted roots that ForgeQE saved
183
+ * (src/network.ts), for networks that inspect HTTPS. Without the file yet, ForgeQE writes it and starts itself again.
184
+ */
185
+ export function bunEnv(env = process.env, home = forgeqeHome()) {
186
+ const ca = path.join(home, "cache", "system-ca.pem");
187
+ if (env.NODE_EXTRA_CA_CERTS || /^(0|off|false|no)$/i.test(env.FORGEQE_SYSTEM_CERTS || "") || !existsSync(ca)) return env;
188
+ return { ...env, NODE_EXTRA_CA_CERTS: ca };
189
+ }
190
+
159
191
  function manualHint() {
160
192
  const install = windows ? `powershell -c "irm bun.sh/install.ps1 | iex"` : "curl -fsSL https://bun.sh/install | bash";
161
- return `Install Bun ${MIN_BUN.join(".")}+ yourself (${install}) and re-run forgeqe,\nor set FORGEQE_BUN to the full path of ${exe}.`;
193
+ return `Reinstall ForgeQE with npm (npm install -g @capagents/forgeqe), which brings Bun through your npm registry,\nor install Bun ${MIN_BUN.join(".")}+ yourself (${install}) and re-run forgeqe,\nor set FORGEQE_BUN to the full path of ${exe}.\nBehind a company mirror of GitHub downloads, set FORGEQE_GITHUB_MIRROR to it.`;
162
194
  }
163
195
 
164
196
  async function main() {
@@ -175,7 +207,7 @@ async function main() {
175
207
  process.exit(1);
176
208
  }
177
209
  }
178
- const result = spawnSync(bun, [entry, ...process.argv.slice(2)], { stdio: "inherit", env: process.env, shell: false });
210
+ const result = spawnSync(bun, [entry, ...process.argv.slice(2)], { stdio: "inherit", env: bunEnv(), shell: false });
179
211
  if (result.error) {
180
212
  console.error(`forgeqe: could not start ${bun}: ${result.error.message}\n${manualHint()}`);
181
213
  process.exit(1);