github-router 0.3.313 → 0.3.315
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/aic-ledger-BS47yb_C.js +298 -0
- package/dist/aic-ledger-BS47yb_C.js.map +1 -0
- package/dist/{attribution-settings-Dnt04Iyn.js → attribution-settings-DXmmyuEE.js} +8 -8
- package/dist/{attribution-settings-Dnt04Iyn.js.map → attribution-settings-DXmmyuEE.js.map} +1 -1
- package/dist/{auth-O_UG1ABO.js → auth-D8t41c5D.js} +3 -3
- package/dist/{auth-O_UG1ABO.js.map → auth-D8t41c5D.js.map} +1 -1
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/{check-usage-dicJOWML.js → check-usage-BpBKOjGZ.js} +4 -4
- package/dist/{check-usage-dicJOWML.js.map → check-usage-BpBKOjGZ.js.map} +1 -1
- package/dist/{claude-D_Me80Yi.js → claude-Cmmu3-cL.js} +140 -20
- package/dist/claude-Cmmu3-cL.js.map +1 -0
- package/dist/{codex-CE7zZm-M.js → codex-DqTKKoEx.js} +5 -5
- package/dist/{codex-CE7zZm-M.js.map → codex-DqTKKoEx.js.map} +1 -1
- package/dist/copilot-discount-IbXR5A28.js +220 -0
- package/dist/copilot-discount-IbXR5A28.js.map +1 -0
- package/dist/{debug-2PgImRnr.js → debug-CA7pg8V9.js} +2 -2
- package/dist/{debug-2PgImRnr.js.map → debug-CA7pg8V9.js.map} +1 -1
- package/dist/engine-b9XaKLp0.js +2 -0
- package/dist/{exec-y8C_MU8A.js → exec-COUT4QsS.js} +2 -2
- package/dist/{exec-y8C_MU8A.js.map → exec-COUT4QsS.js.map} +1 -1
- package/dist/{gate-discovery-_flEdhYo.js → gate-discovery-CPk6Z8yc.js} +6 -6
- package/dist/{gate-discovery-_flEdhYo.js.map → gate-discovery-CPk6Z8yc.js.map} +1 -1
- package/dist/{get-copilot-usage-Cjy8TiFf.js → get-copilot-usage-FSQE8szQ.js} +2 -2
- package/dist/{get-copilot-usage-Cjy8TiFf.js.map → get-copilot-usage-FSQE8szQ.js.map} +1 -1
- package/dist/hooks.mjs +615 -15
- package/dist/hooks.sha256 +1 -1
- package/dist/internal-aic-status-BN0xRLSF.js +432 -0
- package/dist/internal-aic-status-BN0xRLSF.js.map +1 -0
- package/dist/{internal-artifact-open-CxeIfsW_.js → internal-artifact-open-B3stUu74.js} +2 -2
- package/dist/{internal-artifact-open-CxeIfsW_.js.map → internal-artifact-open-B3stUu74.js.map} +1 -1
- package/dist/{internal-fast-dispatch-guard-D8YNlnuQ.js → internal-fast-dispatch-guard-2QjUlhqO.js} +1 -1
- package/dist/{internal-fast-dispatch-guard-19xQEouu.js → internal-fast-dispatch-guard-CAwYj_Yz.js} +2 -2
- package/dist/{internal-fast-dispatch-guard-19xQEouu.js.map → internal-fast-dispatch-guard-CAwYj_Yz.js.map} +1 -1
- package/dist/{internal-first-mate-guard-BuweUgjb.js → internal-first-mate-guard-B0rbLb0L.js} +3 -3
- package/dist/{internal-first-mate-guard-BuweUgjb.js.map → internal-first-mate-guard-B0rbLb0L.js.map} +1 -1
- package/dist/{internal-first-mate-guard-Nf1H0mCK.js → internal-first-mate-guard-VgnHBp0s.js} +1 -1
- package/dist/{internal-max-dispatch-guard-_Pp4LltM.js → internal-max-dispatch-guard-BzKsY9ze.js} +1 -1
- package/dist/{internal-max-dispatch-guard-DZ7KLWxP.js → internal-max-dispatch-guard-l1NGYHtF.js} +2 -2
- package/dist/{internal-max-dispatch-guard-DZ7KLWxP.js.map → internal-max-dispatch-guard-l1NGYHtF.js.map} +1 -1
- package/dist/{internal-plan-review-DLduDz41.js → internal-plan-review-QUvNZmMR.js} +3 -3
- package/dist/{internal-plan-review-DLduDz41.js.map → internal-plan-review-QUvNZmMR.js.map} +1 -1
- package/dist/{internal-prompt-submit-DG94f8E-.js → internal-prompt-submit-fj5ccft_.js} +5 -5
- package/dist/{internal-prompt-submit-DG94f8E-.js.map → internal-prompt-submit-fj5ccft_.js.map} +1 -1
- package/dist/{internal-session-bind-D6Hpl-84.js → internal-session-bind-0Hmak8PR.js} +2 -2
- package/dist/{internal-session-bind-D6Hpl-84.js.map → internal-session-bind-0Hmak8PR.js.map} +1 -1
- package/dist/{internal-stop-hook-BJ3o61-L.js → internal-stop-hook-BKiKYeda.js} +6 -6
- package/dist/{internal-stop-hook-BJ3o61-L.js.map → internal-stop-hook-BKiKYeda.js.map} +1 -1
- package/dist/{internal-stop-review-DlKzZgFm.js → internal-stop-review-8v2llYDB.js} +2 -2
- package/dist/{internal-stop-review-DlKzZgFm.js.map → internal-stop-review-8v2llYDB.js.map} +1 -1
- package/dist/{internal-worker-guard-D7sfByyo.js → internal-worker-guard-BI4Pl0Ew.js} +2 -2
- package/dist/{internal-worker-guard-D7sfByyo.js.map → internal-worker-guard-BI4Pl0Ew.js.map} +1 -1
- package/dist/{internal-workspace-header-CU5OjLCB.js → internal-workspace-header-kg9FoNXK.js} +2 -2
- package/dist/{internal-workspace-header-CU5OjLCB.js.map → internal-workspace-header-kg9FoNXK.js.map} +1 -1
- package/dist/lifecycle-BpqAyaAj.js +2 -0
- package/dist/lifecycle-CL2WiyeQ.js +2 -0
- package/dist/{lifecycle-CtRA3Y7p.js → lifecycle-Cx1wVrLb.js} +3 -3
- package/dist/{lifecycle-CtRA3Y7p.js.map → lifecycle-Cx1wVrLb.js.map} +1 -1
- package/dist/{lifecycle-BCnarXJV.js → lifecycle-IwotwedG.js} +2 -2
- package/dist/{lifecycle-BCnarXJV.js.map → lifecycle-IwotwedG.js.map} +1 -1
- package/dist/main.js +21 -20
- package/dist/main.js.map +1 -1
- package/dist/{mcp-workspace-header-B60WSR1Y.js → mcp-workspace-header-BYgWioLH.js} +2 -2
- package/dist/{mcp-workspace-header-B60WSR1Y.js.map → mcp-workspace-header-BYgWioLH.js.map} +1 -1
- package/dist/{models-COuMOfM6.js → models-DgLeb-Dv.js} +3 -3
- package/dist/{models-COuMOfM6.js.map → models-DgLeb-Dv.js.map} +1 -1
- package/dist/{orchestration-DjLA-lxB.js → orchestration-Dc3mO0G3.js} +3 -3
- package/dist/{orchestration-DjLA-lxB.js.map → orchestration-Dc3mO0G3.js.map} +1 -1
- package/dist/paths-BXXCVFvA.js +2 -0
- package/dist/{paths-De1Qh9Q9.js → paths-CMxlQEfQ.js} +3 -3
- package/dist/{paths-De1Qh9Q9.js.map → paths-CMxlQEfQ.js.map} +1 -1
- package/dist/{peer-mcp-personas-DklYru_1.js → peer-mcp-personas-CYWJSxPK.js} +40 -13
- package/dist/{peer-mcp-personas-DklYru_1.js.map → peer-mcp-personas-CYWJSxPK.js.map} +1 -1
- package/dist/{plan-review-hook-vL6BYmA8.js → plan-review-hook-BIMwWkh6.js} +4 -4
- package/dist/{plan-review-hook-vL6BYmA8.js.map → plan-review-hook-BIMwWkh6.js.map} +1 -1
- package/dist/{prompt-submit-hook-DfqufWC1.js → prompt-submit-hook-BcFpjHXg.js} +3 -3
- package/dist/{prompt-submit-hook-DfqufWC1.js.map → prompt-submit-hook-BcFpjHXg.js.map} +1 -1
- package/dist/{provision-BYdIsKcp.js → provision-AwSjt-vR.js} +9 -5
- package/dist/provision-AwSjt-vR.js.map +1 -0
- package/dist/{self-invocation-B84JCHps.js → self-invocation-cRl4RqfW.js} +2 -2
- package/dist/{self-invocation-B84JCHps.js.map → self-invocation-cRl4RqfW.js.map} +1 -1
- package/dist/{serve-DI4QgSSB.js → serve-BG2O6Slz.js} +13 -13
- package/dist/{serve-DI4QgSSB.js.map → serve-BG2O6Slz.js.map} +1 -1
- package/dist/{server-setup-s2Os8RfG.js → server-setup-D7ABbEs1.js} +109 -10
- package/dist/server-setup-D7ABbEs1.js.map +1 -0
- package/dist/{start-DV5iDhbP.js → start-Dpwpnq99.js} +3 -3
- package/dist/{start-DV5iDhbP.js.map → start-Dpwpnq99.js.map} +1 -1
- package/dist/{stop-gate-hook-BEU8qQrC.js → stop-gate-hook-BUCxa5Kc.js} +4 -4
- package/dist/{stop-gate-hook-BEU8qQrC.js.map → stop-gate-hook-BUCxa5Kc.js.map} +1 -1
- package/dist/{stop-gate-policy-qzqXfRxE.js → stop-gate-policy--bpyxxXU.js} +3 -3
- package/dist/{stop-gate-policy-qzqXfRxE.js.map → stop-gate-policy--bpyxxXU.js.map} +1 -1
- package/dist/{token-CCg-Ov5q.js → token-ClzS23Y2.js} +2 -2
- package/dist/{token-CCg-Ov5q.js.map → token-ClzS23Y2.js.map} +1 -1
- package/dist/{worker-dispatch-CH-NN5FW.js → worker-dispatch-DMIdrlsF.js} +2 -2
- package/dist/{worker-dispatch-CH-NN5FW.js.map → worker-dispatch-DMIdrlsF.js.map} +1 -1
- package/package.json +1 -1
- package/dist/claude-D_Me80Yi.js.map +0 -1
- package/dist/engine-DyzGsCUb.js +0 -2
- package/dist/lifecycle-B0USSJvd.js +0 -2
- package/dist/lifecycle-CQFjaQc0.js +0 -2
- package/dist/paths-DyX6SEnd.js +0 -2
- package/dist/provision-BYdIsKcp.js.map +0 -1
- package/dist/server-setup-s2Os8RfG.js.map +0 -1
package/dist/hooks.mjs
CHANGED
|
@@ -6,9 +6,9 @@ import g, { stdin, stdout } from "node:process";
|
|
|
6
6
|
import * as tty from "node:tty";
|
|
7
7
|
import { WriteStream } from "node:tty";
|
|
8
8
|
import f from "node:readline";
|
|
9
|
-
import { existsSync, mkdirSync, promises, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
9
|
+
import fs, { existsSync, mkdirSync, promises, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
|
-
import { spawn } from "node:child_process";
|
|
11
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
12
12
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
13
13
|
import os, { homedir, tmpdir } from "node:os";
|
|
14
14
|
import "node:fs/promises";
|
|
@@ -34621,6 +34621,120 @@ var init_mcp_workspace_header = __esmMin((() => {
|
|
|
34621
34621
|
MCP_WORKSPACE_HEADER = "X-GH-Workspace";
|
|
34622
34622
|
}));
|
|
34623
34623
|
//#endregion
|
|
34624
|
+
//#region src/lib/aic-usage.ts
|
|
34625
|
+
/** Convert nano-AIU to AI credits. */
|
|
34626
|
+
function nanoAiuToCredits(nanoAiu) {
|
|
34627
|
+
if (!Number.isFinite(nanoAiu) || nanoAiu <= 0) return 0;
|
|
34628
|
+
return nanoAiu / NANO_AIU_PER_CREDIT;
|
|
34629
|
+
}
|
|
34630
|
+
/**
|
|
34631
|
+
* Compact display: `12.42` for totals ≥ 0.01, more precision below.
|
|
34632
|
+
* Always a plain number string — callers add units/context.
|
|
34633
|
+
*/
|
|
34634
|
+
function formatAic(credits) {
|
|
34635
|
+
if (!Number.isFinite(credits) || credits <= 0) return "0";
|
|
34636
|
+
if (credits >= 100) return credits.toFixed(1);
|
|
34637
|
+
if (credits >= 1) return credits.toFixed(2);
|
|
34638
|
+
if (credits >= .01) return credits.toFixed(3);
|
|
34639
|
+
return credits.toPrecision(2);
|
|
34640
|
+
}
|
|
34641
|
+
var NANO_AIU_PER_CREDIT;
|
|
34642
|
+
var init_aic_usage = __esmMin((() => {
|
|
34643
|
+
NANO_AIU_PER_CREDIT = 1e9;
|
|
34644
|
+
}));
|
|
34645
|
+
//#endregion
|
|
34646
|
+
//#region src/lib/aic-ledger.ts
|
|
34647
|
+
/**
|
|
34648
|
+
* Per-launch AIC (AI Credits) ledger.
|
|
34649
|
+
*
|
|
34650
|
+
* Every route handler records upstream `copilot_usage` here as a side effect
|
|
34651
|
+
* (bytes pass through untouched). The totals feed two consumers:
|
|
34652
|
+
*
|
|
34653
|
+
* 1. `internal-aic-status` — runs as a SEPARATE process on every status-line
|
|
34654
|
+
* tick, so it cannot read in-process memory. Each `record()` therefore
|
|
34655
|
+
* also persists an atomic snapshot file; the ledger path is handed to the
|
|
34656
|
+
* spawned child via `GH_ROUTER_AIC_LEDGER` and baked into the statusLine
|
|
34657
|
+
* command.
|
|
34658
|
+
* 2. The `claude` exit summary — reads the in-process snapshot.
|
|
34659
|
+
*
|
|
34660
|
+
* Keyed per proxy process (one file per PID under `<APP_DIR>/aic-ledger/`),
|
|
34661
|
+
* so concurrent launches never mix. Snapshot writes are best-effort and never
|
|
34662
|
+
* throw into the request path. Stale files (dead PID, >24h) are swept at
|
|
34663
|
+
* startup of the next record and on shutdown.
|
|
34664
|
+
*/
|
|
34665
|
+
function emptyModelTokens() {
|
|
34666
|
+
return {
|
|
34667
|
+
input: 0,
|
|
34668
|
+
cache_read: 0,
|
|
34669
|
+
cache_write: 0,
|
|
34670
|
+
output: 0,
|
|
34671
|
+
other: 0
|
|
34672
|
+
};
|
|
34673
|
+
}
|
|
34674
|
+
/**
|
|
34675
|
+
* Read a persisted snapshot file (the status-hook path). Returns undefined
|
|
34676
|
+
* when missing/unreadable — the hook then prints nothing.
|
|
34677
|
+
*/
|
|
34678
|
+
function readAicSnapshotFile(file) {
|
|
34679
|
+
try {
|
|
34680
|
+
const raw = fs.readFileSync(file, "utf8");
|
|
34681
|
+
const parsed = JSON.parse(raw);
|
|
34682
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return;
|
|
34683
|
+
const record = parsed;
|
|
34684
|
+
if (typeof record.totalNanoAiu !== "number" || typeof record.requests !== "number") return;
|
|
34685
|
+
return {
|
|
34686
|
+
totalNanoAiu: Math.max(0, Math.floor(record.totalNanoAiu)),
|
|
34687
|
+
requests: Math.max(0, Math.floor(record.requests)),
|
|
34688
|
+
perModel: record.perModel && typeof record.perModel === "object" ? record.perModel : {},
|
|
34689
|
+
perTokenType: record.perTokenType && typeof record.perTokenType === "object" ? record.perTokenType : {},
|
|
34690
|
+
tokensByModel: sanitizeModelTokens(record.tokensByModel)
|
|
34691
|
+
};
|
|
34692
|
+
} catch {
|
|
34693
|
+
return;
|
|
34694
|
+
}
|
|
34695
|
+
}
|
|
34696
|
+
/** Best-effort sanitize of persisted per-model token counts; `{}` when absent. */
|
|
34697
|
+
function sanitizeModelTokens(value) {
|
|
34698
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
34699
|
+
const out = {};
|
|
34700
|
+
for (const [model, counts] of Object.entries(value)) {
|
|
34701
|
+
if (!counts || typeof counts !== "object" || Array.isArray(counts)) continue;
|
|
34702
|
+
const entry = counts;
|
|
34703
|
+
const clean = emptyModelTokens();
|
|
34704
|
+
let ok = true;
|
|
34705
|
+
for (const field of [
|
|
34706
|
+
"input",
|
|
34707
|
+
"cache_read",
|
|
34708
|
+
"cache_write",
|
|
34709
|
+
"output",
|
|
34710
|
+
"other"
|
|
34711
|
+
]) {
|
|
34712
|
+
const n = entry[field];
|
|
34713
|
+
if (n === void 0) continue;
|
|
34714
|
+
if (typeof n !== "number" || !Number.isFinite(n) || n < 0) {
|
|
34715
|
+
ok = false;
|
|
34716
|
+
break;
|
|
34717
|
+
}
|
|
34718
|
+
clean[field] = Math.floor(n);
|
|
34719
|
+
}
|
|
34720
|
+
if (ok) out[model] = clean;
|
|
34721
|
+
}
|
|
34722
|
+
return out;
|
|
34723
|
+
}
|
|
34724
|
+
/** Total credits for a snapshot. */
|
|
34725
|
+
function aicTotalCredits(snapshot) {
|
|
34726
|
+
return nanoAiuToCredits(snapshot.totalNanoAiu);
|
|
34727
|
+
}
|
|
34728
|
+
/** One-line status fragment: `[AIC 12.42]`. Empty string when nothing recorded. */
|
|
34729
|
+
function formatAicStatus(snapshot) {
|
|
34730
|
+
if (snapshot.requests === 0 || snapshot.totalNanoAiu <= 0) return "";
|
|
34731
|
+
return `[AIC ${formatAic(aicTotalCredits(snapshot))}]`;
|
|
34732
|
+
}
|
|
34733
|
+
var init_aic_ledger = __esmMin((() => {
|
|
34734
|
+
init_paths();
|
|
34735
|
+
init_aic_usage();
|
|
34736
|
+
}));
|
|
34737
|
+
//#endregion
|
|
34624
34738
|
//#region src/lib/orchestration/gate-discovery.ts
|
|
34625
34739
|
/**
|
|
34626
34740
|
* Evidence-pinned model FALLBACK for the structural Stop-gate — the last resort
|
|
@@ -34912,7 +35026,7 @@ var internal_stop_hook_exports = /* @__PURE__ */ __exportAll({
|
|
|
34912
35026
|
* no such handle. Hooks always receive piped/redirected stdin, so this never
|
|
34913
35027
|
* blocks (guarded against an interactive TTY, and any error -> "").
|
|
34914
35028
|
*/
|
|
34915
|
-
function readStdin$
|
|
35029
|
+
function readStdin$6() {
|
|
34916
35030
|
try {
|
|
34917
35031
|
if (process.stdin.isTTY) return "";
|
|
34918
35032
|
return readFileSync(0, "utf8");
|
|
@@ -35099,7 +35213,7 @@ var init_internal_stop_hook = __esmMin((() => {
|
|
|
35099
35213
|
description: "Internal: the structural-gate Stop hook. Reads the Claude Code hook payload on stdin, runs the sealed gate, exits 2 (blocks the stop) on a red gate or gate-weakening diff."
|
|
35100
35214
|
},
|
|
35101
35215
|
async run() {
|
|
35102
|
-
const stdin = readStdin$
|
|
35216
|
+
const stdin = readStdin$6();
|
|
35103
35217
|
const reviewEnabled = stopReviewEnabled() && hookMcpRuntimeFromEnv() !== void 0;
|
|
35104
35218
|
let transcriptPath = "";
|
|
35105
35219
|
let userPrompt = "";
|
|
@@ -35267,7 +35381,7 @@ var internal_prompt_submit_exports = /* @__PURE__ */ __exportAll({ internalPromp
|
|
|
35267
35381
|
* no such handle. Hooks always receive piped/redirected stdin, so this never
|
|
35268
35382
|
* blocks (guarded against an interactive TTY, and any error -> "").
|
|
35269
35383
|
*/
|
|
35270
|
-
function readStdin$
|
|
35384
|
+
function readStdin$5() {
|
|
35271
35385
|
try {
|
|
35272
35386
|
if (process.stdin.isTTY) return "";
|
|
35273
35387
|
return readFileSync(0, "utf8");
|
|
@@ -35304,7 +35418,7 @@ var init_internal_prompt_submit = __esmMin((() => {
|
|
|
35304
35418
|
},
|
|
35305
35419
|
async run() {
|
|
35306
35420
|
try {
|
|
35307
|
-
const stdin = readStdin$
|
|
35421
|
+
const stdin = readStdin$5();
|
|
35308
35422
|
const steerEnabled = parseBoolEnv(process.env.GH_ROUTER_DISABLE_PROMPT_STEER) !== true;
|
|
35309
35423
|
const runtime = hookMcpRuntimeFromEnv();
|
|
35310
35424
|
let decision;
|
|
@@ -35599,7 +35713,7 @@ var init_plan_review_hook = __esmMin((() => {
|
|
|
35599
35713
|
//#endregion
|
|
35600
35714
|
//#region src/internal-plan-review.ts
|
|
35601
35715
|
var internal_plan_review_exports = /* @__PURE__ */ __exportAll({ internalPlanReview: () => internalPlanReview });
|
|
35602
|
-
function readStdin$
|
|
35716
|
+
function readStdin$4() {
|
|
35603
35717
|
try {
|
|
35604
35718
|
if (process.stdin.isTTY) return "";
|
|
35605
35719
|
return readFileSync(0, "utf8");
|
|
@@ -35692,7 +35806,7 @@ var init_internal_plan_review = __esmMin((() => {
|
|
|
35692
35806
|
return;
|
|
35693
35807
|
}
|
|
35694
35808
|
const decision = await decidePlanReviewHook({
|
|
35695
|
-
stdin: readStdin$
|
|
35809
|
+
stdin: readStdin$4(),
|
|
35696
35810
|
runtimeAvailable: true,
|
|
35697
35811
|
debounce: filePlanReviewDebounce(stopReviewStateDir()),
|
|
35698
35812
|
fallbackCwd: process.cwd()
|
|
@@ -35715,7 +35829,7 @@ var internal_session_bind_exports = /* @__PURE__ */ __exportAll({
|
|
|
35715
35829
|
* teardown and trips a `uv_async_send` assertion; a synchronous read has no such
|
|
35716
35830
|
* handle. Hooks always receive piped stdin (guarded against a TTY; any error -> "").
|
|
35717
35831
|
*/
|
|
35718
|
-
function readStdin$
|
|
35832
|
+
function readStdin$3() {
|
|
35719
35833
|
try {
|
|
35720
35834
|
if (process.stdin.isTTY) return "";
|
|
35721
35835
|
return readFileSync(0, "utf8");
|
|
@@ -35809,7 +35923,7 @@ var init_internal_session_bind = __esmMin((() => {
|
|
|
35809
35923
|
try {
|
|
35810
35924
|
const out = typeof args.out === "string" ? args.out.trim() : "";
|
|
35811
35925
|
if (!out) return;
|
|
35812
|
-
const record = decodeSessionBind(readStdin$
|
|
35926
|
+
const record = decodeSessionBind(readStdin$3());
|
|
35813
35927
|
if (record) writeSidecar(out, record);
|
|
35814
35928
|
} catch {}
|
|
35815
35929
|
process.exitCode = 0;
|
|
@@ -37622,7 +37736,7 @@ var internal_artifact_open_exports = /* @__PURE__ */ __exportAll({
|
|
|
37622
37736
|
internalArtifactOpen: () => internalArtifactOpen,
|
|
37623
37737
|
parseExitPlanPayload: () => parseExitPlanPayload
|
|
37624
37738
|
});
|
|
37625
|
-
function readStdin$
|
|
37739
|
+
function readStdin$2() {
|
|
37626
37740
|
try {
|
|
37627
37741
|
if (process.stdin.isTTY) return "";
|
|
37628
37742
|
return readFileSync(0, "utf8");
|
|
@@ -37722,7 +37836,7 @@ var init_internal_artifact_open = __esmMin((() => {
|
|
|
37722
37836
|
},
|
|
37723
37837
|
async run() {
|
|
37724
37838
|
try {
|
|
37725
|
-
const raw = readStdin$
|
|
37839
|
+
const raw = readStdin$2();
|
|
37726
37840
|
let payload;
|
|
37727
37841
|
try {
|
|
37728
37842
|
payload = JSON.parse(raw);
|
|
@@ -37941,7 +38055,7 @@ var internal_worker_guard_exports = /* @__PURE__ */ __exportAll({ internalWorker
|
|
|
37941
38055
|
* libuv FS request that races Windows process teardown. Hooks always receive
|
|
37942
38056
|
* piped stdin (guarded against an interactive TTY; any error → "").
|
|
37943
38057
|
*/
|
|
37944
|
-
function readStdin() {
|
|
38058
|
+
function readStdin$1() {
|
|
37945
38059
|
try {
|
|
37946
38060
|
if (process.stdin.isTTY) return "";
|
|
37947
38061
|
return readFileSync(0, "utf8");
|
|
@@ -37972,7 +38086,7 @@ var init_internal_worker_guard = __esmMin((() => {
|
|
|
37972
38086
|
},
|
|
37973
38087
|
run(ctx) {
|
|
37974
38088
|
try {
|
|
37975
|
-
const stdin = readStdin();
|
|
38089
|
+
const stdin = readStdin$1();
|
|
37976
38090
|
const workersKey = String(ctx.args["workers-key"] ?? "").trim();
|
|
37977
38091
|
if (workersKey.length > 0) {
|
|
37978
38092
|
const { output } = decideWorkerGuard({
|
|
@@ -38429,6 +38543,491 @@ var init_internal_max_dispatch_guard = __esmMin((() => {
|
|
|
38429
38543
|
});
|
|
38430
38544
|
}));
|
|
38431
38545
|
//#endregion
|
|
38546
|
+
//#region src/lib/copilot-discount.ts
|
|
38547
|
+
function row(factor, source) {
|
|
38548
|
+
return {
|
|
38549
|
+
factor,
|
|
38550
|
+
source
|
|
38551
|
+
};
|
|
38552
|
+
}
|
|
38553
|
+
/**
|
|
38554
|
+
* Clamp a factor to usable range. Non-finite or negative factors (corrupt
|
|
38555
|
+
* table edit, bad injection) degrade to 1.0 — unfactored but never NaN,
|
|
38556
|
+
* never negative on screen.
|
|
38557
|
+
*/
|
|
38558
|
+
function sanitizeFactor(raw) {
|
|
38559
|
+
return typeof raw === "number" && Number.isFinite(raw) && raw >= 0 ? raw : 1;
|
|
38560
|
+
}
|
|
38561
|
+
/**
|
|
38562
|
+
* Session discounted USD from a ledger snapshot. Total function: never
|
|
38563
|
+
* throws (guards non-finite/negative ledger values defensively — a corrupt
|
|
38564
|
+
* snapshot must degrade the status line, not crash the hook).
|
|
38565
|
+
*/
|
|
38566
|
+
function discountedUsdForSnapshot(snapshot) {
|
|
38567
|
+
return discountedUsdWithFactors(snapshot, DISCOUNT_FACTORS);
|
|
38568
|
+
}
|
|
38569
|
+
/**
|
|
38570
|
+
* Factor-injectable core (exported for unit tests — e.g. proving a
|
|
38571
|
+
* fat-finger factor can never overstate spend). `factors` falls back to
|
|
38572
|
+
* 1.0 per model exactly like the table lookup.
|
|
38573
|
+
*/
|
|
38574
|
+
function discountedUsdWithFactors(snapshot, factors) {
|
|
38575
|
+
const byModel = {};
|
|
38576
|
+
let sum = 0;
|
|
38577
|
+
for (const [model, entry] of Object.entries(snapshot.perModel)) {
|
|
38578
|
+
const nano = typeof entry.nanoAiu === "number" && Number.isFinite(entry.nanoAiu) ? Math.max(0, entry.nanoAiu) : 0;
|
|
38579
|
+
const factor = sanitizeFactor(factors[model]?.factor);
|
|
38580
|
+
const usd = nano / 1e9 * USD_PER_AIC_CREDIT * factor;
|
|
38581
|
+
byModel[model] = usd;
|
|
38582
|
+
sum += usd;
|
|
38583
|
+
}
|
|
38584
|
+
const cap = aicTotalCredits(snapshot) * USD_PER_AIC_CREDIT;
|
|
38585
|
+
if (!(sum > cap * (1 + 1e-9)) || sum <= 0) return {
|
|
38586
|
+
total: sum,
|
|
38587
|
+
byModel,
|
|
38588
|
+
capped: false
|
|
38589
|
+
};
|
|
38590
|
+
const scale = cap / sum;
|
|
38591
|
+
const scaled = {};
|
|
38592
|
+
for (const [model, usd] of Object.entries(byModel)) scaled[model] = usd * scale;
|
|
38593
|
+
return {
|
|
38594
|
+
total: cap,
|
|
38595
|
+
byModel: scaled,
|
|
38596
|
+
capped: true
|
|
38597
|
+
};
|
|
38598
|
+
}
|
|
38599
|
+
var USD_PER_AIC_CREDIT, DISCOUNT_FACTORS;
|
|
38600
|
+
var init_copilot_discount = __esmMin((() => {
|
|
38601
|
+
init_aic_ledger();
|
|
38602
|
+
USD_PER_AIC_CREDIT = .01;
|
|
38603
|
+
DISCOUNT_FACTORS = Object.freeze({
|
|
38604
|
+
"gpt-5.6-sol": row(.28, "0.27-0.29 across 28d/MTD/7d/pre/9-9; write-mix refuted"),
|
|
38605
|
+
"claude-opus-5": row(.62, "0.61-0.64 across windows; 9/7 1.02 excluded as single-day artifact"),
|
|
38606
|
+
"claude-sonnet-5": row(.66, "0.62-0.71 across windows"),
|
|
38607
|
+
"gpt-5.3-codex": row(.6, "0.59-0.62 across windows"),
|
|
38608
|
+
"gpt-5.4": row(.6, "single snapshot 0.598"),
|
|
38609
|
+
"gemini-3.7-flash": row(.29, "September regime; pre-Sep 1.00x — volatile"),
|
|
38610
|
+
"gpt-5.6-luna": row(1, "long-tier portal + writes, no discount"),
|
|
38611
|
+
"grok-4.6": row(1, "portal default, six windows"),
|
|
38612
|
+
"gemini-3.8-flash": row(1, "portal exact, six windows to the dollar")
|
|
38613
|
+
});
|
|
38614
|
+
}));
|
|
38615
|
+
//#endregion
|
|
38616
|
+
//#region src/lib/default-statusline.ts
|
|
38617
|
+
/**
|
|
38618
|
+
* Rich default status line for `github-router claude` sessions.
|
|
38619
|
+
*
|
|
38620
|
+
* Replaces the need for users to maintain their own PowerShell/bash status
|
|
38621
|
+
* script: `internal-aic-status` renders these segments natively from the
|
|
38622
|
+
* Claude Code stdin JSON, then pins the `[AIC x.xx]` ledger fragment ahead
|
|
38623
|
+
* of them. Zero dependencies, cross-platform (Windows 11 is the primary
|
|
38624
|
+
* target — no `powershell`/`bash`/`jq` required), and fast (no shell
|
|
38625
|
+
* startup; one bounded `git rev-parse` at most).
|
|
38626
|
+
*
|
|
38627
|
+
* Segment order (left = highest priority; narrow terminals drop from the
|
|
38628
|
+
* right, `AIC` is never dropped):
|
|
38629
|
+
*
|
|
38630
|
+
* `[AIC] | [ctx bar] % | model | dir (branch) | ~$actual | in/out | dur | +a -r`
|
|
38631
|
+
*
|
|
38632
|
+
* The `$` segment is the DISCOUNTED actual (`~$` = factor-discounted
|
|
38633
|
+
* session total from `copilot-discount.ts`, capped at AIC × $0.01) — NOT
|
|
38634
|
+
* Claude's `cost.total_cost_usd`, which prices at Anthropic list rates and
|
|
38635
|
+
* reads ~10-100x high against Copilot billing (a 28-day sample: 41.1B tokens
|
|
38636
|
+
* for $5,142 ≈ $0.13/1M blended vs $15-75/1M list). Rendering list price
|
|
38637
|
+
* next to the real `[AIC]` billing unit would show two contradictory
|
|
38638
|
+
* numbers, so list price is deliberately not rendered at all.
|
|
38639
|
+
*
|
|
38640
|
+
* Width comes from `COLUMNS` (set by Claude Code for status scripts since
|
|
38641
|
+
* v2.1.153); fallback is 120 when unset/unparseable so a missing `COLUMNS`
|
|
38642
|
+
* never needlessly drops segments.
|
|
38643
|
+
*/
|
|
38644
|
+
function asRecord(value) {
|
|
38645
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
38646
|
+
return value;
|
|
38647
|
+
}
|
|
38648
|
+
function finiteNumber(value) {
|
|
38649
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
38650
|
+
}
|
|
38651
|
+
function nonEmptyString(value) {
|
|
38652
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
38653
|
+
}
|
|
38654
|
+
/**
|
|
38655
|
+
* Parse Claude Code's status-line stdin JSON into the fields we render.
|
|
38656
|
+
* Total function: malformed JSON or unexpected shapes yield `{}` (every
|
|
38657
|
+
* segment then renders its `--` placeholder or is omitted) — never throws.
|
|
38658
|
+
*/
|
|
38659
|
+
function parseStatusInput(raw) {
|
|
38660
|
+
if (!raw.trim()) return {};
|
|
38661
|
+
let parsed;
|
|
38662
|
+
try {
|
|
38663
|
+
parsed = JSON.parse(raw);
|
|
38664
|
+
} catch {
|
|
38665
|
+
return {};
|
|
38666
|
+
}
|
|
38667
|
+
const root = asRecord(parsed);
|
|
38668
|
+
if (!root) return {};
|
|
38669
|
+
const out = {};
|
|
38670
|
+
const ctx = asRecord(root.context_window);
|
|
38671
|
+
if (ctx) {
|
|
38672
|
+
const pct = finiteNumber(ctx.used_percentage);
|
|
38673
|
+
if (pct !== void 0) out.usedPct = pct;
|
|
38674
|
+
const tIn = finiteNumber(ctx.total_input_tokens);
|
|
38675
|
+
if (tIn !== void 0 && tIn >= 0) out.totalInputTokens = Math.floor(tIn);
|
|
38676
|
+
const tOut = finiteNumber(ctx.total_output_tokens);
|
|
38677
|
+
if (tOut !== void 0 && tOut >= 0) out.totalOutputTokens = Math.floor(tOut);
|
|
38678
|
+
}
|
|
38679
|
+
const cost = asRecord(root.cost);
|
|
38680
|
+
if (cost) {
|
|
38681
|
+
const dur = finiteNumber(cost.total_duration_ms);
|
|
38682
|
+
if (dur !== void 0 && dur >= 0) out.totalDurationMs = dur;
|
|
38683
|
+
const la = finiteNumber(cost.total_lines_added);
|
|
38684
|
+
if (la !== void 0 && la >= 0) out.linesAdded = Math.floor(la);
|
|
38685
|
+
const lr = finiteNumber(cost.total_lines_removed);
|
|
38686
|
+
if (lr !== void 0 && lr >= 0) out.linesRemoved = Math.floor(lr);
|
|
38687
|
+
}
|
|
38688
|
+
const workspace = asRecord(root.workspace);
|
|
38689
|
+
const cwd = (workspace ? nonEmptyString(workspace.current_dir) : void 0) ?? nonEmptyString(root.cwd);
|
|
38690
|
+
if (cwd) out.cwd = cwd;
|
|
38691
|
+
const model = asRecord(root.model);
|
|
38692
|
+
if (model) {
|
|
38693
|
+
const name = nonEmptyString(model.display_name) ?? nonEmptyString(model.id);
|
|
38694
|
+
if (name) out.modelName = name;
|
|
38695
|
+
}
|
|
38696
|
+
return out;
|
|
38697
|
+
}
|
|
38698
|
+
/** Compact token count: `999` → `999`, `15234` → `15.2k`, `2.1M`. */
|
|
38699
|
+
function compactTokens(n) {
|
|
38700
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
38701
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
|
|
38702
|
+
return `${n}`;
|
|
38703
|
+
}
|
|
38704
|
+
/** `45000` → `0m45s`, `198000` → `3m18s`, `3720000` → `1h02m`. */
|
|
38705
|
+
function formatDurationMs(ms) {
|
|
38706
|
+
const totalSeconds = Math.floor(ms / 1e3);
|
|
38707
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
38708
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
38709
|
+
const seconds = totalSeconds % 60;
|
|
38710
|
+
if (hours >= 1) return `${hours}h${String(minutes).padStart(2, "0")}m`;
|
|
38711
|
+
return `${minutes}m${String(seconds).padStart(2, "0")}s`;
|
|
38712
|
+
}
|
|
38713
|
+
/** Basename of a cwd, tolerant of `/` and `\` separators + trailing slash. */
|
|
38714
|
+
function dirLeaf(cwd) {
|
|
38715
|
+
const trimmed = cwd.replace(/[/\\]+$/, "");
|
|
38716
|
+
const parts = trimmed.split(/[/\\]/);
|
|
38717
|
+
return parts[parts.length - 1] ?? trimmed;
|
|
38718
|
+
}
|
|
38719
|
+
function buildCtxSegment(usedPct) {
|
|
38720
|
+
if (usedPct === void 0 || !Number.isFinite(usedPct)) return {
|
|
38721
|
+
text: `${DIM}[----------] --%${RESET}`,
|
|
38722
|
+
plain: "[----------] --%"
|
|
38723
|
+
};
|
|
38724
|
+
const u = Math.max(0, Math.min(100, Math.round(usedPct)));
|
|
38725
|
+
let filled = Math.round(u / 100 * CTX_BAR_LEN);
|
|
38726
|
+
if (filled > CTX_BAR_LEN) filled = CTX_BAR_LEN;
|
|
38727
|
+
if (filled < 0) filled = 0;
|
|
38728
|
+
const bar = `#`.repeat(filled) + `-`.repeat(CTX_BAR_LEN - filled);
|
|
38729
|
+
return {
|
|
38730
|
+
text: `${u < 50 ? GREEN : u < 75 ? YELLOW : RED}[${bar}] ${u}%${RESET}`,
|
|
38731
|
+
plain: `[${bar}] ${u}%`
|
|
38732
|
+
};
|
|
38733
|
+
}
|
|
38734
|
+
/**
|
|
38735
|
+
* Discounted-actual `$` segment. Callers pass the capped total from
|
|
38736
|
+
* `discountedUsdForSnapshot` — this function only formats. Placeholder
|
|
38737
|
+
* `~$--` until the first priced upstream response lands in the ledger.
|
|
38738
|
+
* Thresholds are tuned for ACTUALS scale (a session runs ~$0.10-2.00) —
|
|
38739
|
+
* not the old list-price scale.
|
|
38740
|
+
*/
|
|
38741
|
+
function buildUsdSegment(actualUsd) {
|
|
38742
|
+
if (actualUsd === void 0 || !Number.isFinite(actualUsd) || actualUsd < 0) return {
|
|
38743
|
+
text: `${DIM}~$--${RESET}`,
|
|
38744
|
+
plain: "~$--"
|
|
38745
|
+
};
|
|
38746
|
+
const c = actualUsd;
|
|
38747
|
+
const color = c < 1 ? GREEN : c < 5 ? YELLOW : c < 20 ? ORANGE : RED;
|
|
38748
|
+
const formatted = c < 10 ? c.toFixed(2) : c.toFixed(1);
|
|
38749
|
+
return {
|
|
38750
|
+
text: `${color}~$${formatted}${RESET}`,
|
|
38751
|
+
plain: `~$${formatted}`
|
|
38752
|
+
};
|
|
38753
|
+
}
|
|
38754
|
+
function buildToksSegment(tIn, tOut) {
|
|
38755
|
+
if (tIn === void 0 || tOut === void 0) return {
|
|
38756
|
+
text: `${DIM}--/--${RESET}`,
|
|
38757
|
+
plain: "--/--"
|
|
38758
|
+
};
|
|
38759
|
+
const cin = compactTokens(tIn);
|
|
38760
|
+
const cout = compactTokens(tOut);
|
|
38761
|
+
return {
|
|
38762
|
+
text: `${CYAN}${cin}${RESET}${DIM}/${RESET}${MAGENTA}${cout}${RESET}`,
|
|
38763
|
+
plain: `${cin}/${cout}`
|
|
38764
|
+
};
|
|
38765
|
+
}
|
|
38766
|
+
function buildDurSegment(durMs) {
|
|
38767
|
+
if (durMs === void 0 || !Number.isFinite(durMs) || durMs < 0) return {
|
|
38768
|
+
text: `${DIM}--${RESET}`,
|
|
38769
|
+
plain: "--"
|
|
38770
|
+
};
|
|
38771
|
+
const formatted = formatDurationMs(durMs);
|
|
38772
|
+
return {
|
|
38773
|
+
text: `${DIM}${formatted}${RESET}`,
|
|
38774
|
+
plain: formatted
|
|
38775
|
+
};
|
|
38776
|
+
}
|
|
38777
|
+
function buildLinesSegment(added, removed) {
|
|
38778
|
+
const a = added ?? 0;
|
|
38779
|
+
const r = removed ?? 0;
|
|
38780
|
+
return {
|
|
38781
|
+
text: `${GREEN}+${a}${RESET} ${RED}-${r}${RESET}`,
|
|
38782
|
+
plain: `+${a} -${r}`
|
|
38783
|
+
};
|
|
38784
|
+
}
|
|
38785
|
+
function buildModelSegment(modelName) {
|
|
38786
|
+
if (!modelName || modelName === "--") return void 0;
|
|
38787
|
+
return {
|
|
38788
|
+
text: `${MAGENTA}${modelName}${RESET}`,
|
|
38789
|
+
plain: modelName
|
|
38790
|
+
};
|
|
38791
|
+
}
|
|
38792
|
+
function buildDirGitSegment(cwd, branch) {
|
|
38793
|
+
const leaf = cwd ? dirLeaf(cwd) : "";
|
|
38794
|
+
const br = branch && branch !== "HEAD" ? branch : "";
|
|
38795
|
+
if (!leaf && !br) return void 0;
|
|
38796
|
+
if (leaf && br) return {
|
|
38797
|
+
text: `${BLUE}${leaf}${RESET} ${CYAN}(${br})${RESET}`,
|
|
38798
|
+
plain: `${leaf} (${br})`
|
|
38799
|
+
};
|
|
38800
|
+
if (leaf) return {
|
|
38801
|
+
text: `${BLUE}${leaf}${RESET}`,
|
|
38802
|
+
plain: leaf
|
|
38803
|
+
};
|
|
38804
|
+
return {
|
|
38805
|
+
text: `${CYAN}${br}${RESET}`,
|
|
38806
|
+
plain: br
|
|
38807
|
+
};
|
|
38808
|
+
}
|
|
38809
|
+
/**
|
|
38810
|
+
* Resolve the current git branch for `cwd`. Bounded (`GIT_TIMEOUT_MS`) and
|
|
38811
|
+
* total: any failure (no git binary, not a repo, detached `HEAD`, timeout,
|
|
38812
|
+
* `%` in a Windows path that cmd.exe cannot safely quote) yields `""`
|
|
38813
|
+
* (caller omits the branch) — never throws.
|
|
38814
|
+
*/
|
|
38815
|
+
function resolveGitBranch(cwd) {
|
|
38816
|
+
try {
|
|
38817
|
+
if (!cwd) return "";
|
|
38818
|
+
const gitPath = resolveExecutable("git");
|
|
38819
|
+
if (!gitPath) return "";
|
|
38820
|
+
const { command, args, shell } = buildExecInvocation([
|
|
38821
|
+
gitPath,
|
|
38822
|
+
"-C",
|
|
38823
|
+
cwd,
|
|
38824
|
+
"rev-parse",
|
|
38825
|
+
"--abbrev-ref",
|
|
38826
|
+
"HEAD"
|
|
38827
|
+
]);
|
|
38828
|
+
const result = spawnSync(command, args, {
|
|
38829
|
+
shell,
|
|
38830
|
+
timeout: GIT_TIMEOUT_MS,
|
|
38831
|
+
encoding: "utf8",
|
|
38832
|
+
stdio: [
|
|
38833
|
+
"ignore",
|
|
38834
|
+
"pipe",
|
|
38835
|
+
"ignore"
|
|
38836
|
+
]
|
|
38837
|
+
});
|
|
38838
|
+
if (result.error) return "";
|
|
38839
|
+
const out = typeof result.stdout === "string" ? result.stdout.trim() : "";
|
|
38840
|
+
if (!out || out === "HEAD") return "";
|
|
38841
|
+
return out.split("\n")[0]?.trim() ?? "";
|
|
38842
|
+
} catch {
|
|
38843
|
+
return "";
|
|
38844
|
+
}
|
|
38845
|
+
}
|
|
38846
|
+
/** Terminal width for the drop-right-to-left assembly. */
|
|
38847
|
+
function terminalWidth(env = process.env) {
|
|
38848
|
+
const raw = env.COLUMNS;
|
|
38849
|
+
if (raw !== void 0) {
|
|
38850
|
+
const n = Number(raw);
|
|
38851
|
+
if (Number.isInteger(n) && n > 0) return n;
|
|
38852
|
+
}
|
|
38853
|
+
return WIDTH_FALLBACK;
|
|
38854
|
+
}
|
|
38855
|
+
/**
|
|
38856
|
+
* Assemble the full status line. `aicFragment` (e.g. `[AIC 12.42]`) is
|
|
38857
|
+
* PINNED — it renders even when nothing else fits. `actualUsd` feeds the
|
|
38858
|
+
* `~$` segment (the capped factor-discounted total from
|
|
38859
|
+
* `discountedUsdForSnapshot`, same snapshot the fragment came from).
|
|
38860
|
+
* Droppable segments in priority order: ctx, model, dir/git, ~$, toks,
|
|
38861
|
+
* dur, lines.
|
|
38862
|
+
*/
|
|
38863
|
+
function assembleStatusLine(aicFragment, input, opts = {}) {
|
|
38864
|
+
const aic = aicFragment.trim();
|
|
38865
|
+
const aicText = aic ? `${AIC_COLOR}${aic}${RESET}` : "";
|
|
38866
|
+
const droppable = [buildCtxSegment(input.usedPct)];
|
|
38867
|
+
const model = buildModelSegment(input.modelName);
|
|
38868
|
+
if (model) droppable.push(model);
|
|
38869
|
+
const dirGit = buildDirGitSegment(input.cwd, opts.branch);
|
|
38870
|
+
if (dirGit) droppable.push(dirGit);
|
|
38871
|
+
droppable.push(buildUsdSegment(opts.actualUsd), buildToksSegment(input.totalInputTokens, input.totalOutputTokens), buildDurSegment(input.totalDurationMs), buildLinesSegment(input.linesAdded, input.linesRemoved));
|
|
38872
|
+
const width = opts.width ?? terminalWidth();
|
|
38873
|
+
const sepLen = 3;
|
|
38874
|
+
let count = droppable.length;
|
|
38875
|
+
while (count > 0) {
|
|
38876
|
+
let total = aic ? aic.length : 0;
|
|
38877
|
+
for (let i = 0; i < count; i++) {
|
|
38878
|
+
if (aic || i > 0) total += sepLen;
|
|
38879
|
+
total += droppable[i].plain.length;
|
|
38880
|
+
}
|
|
38881
|
+
if (total <= width) break;
|
|
38882
|
+
if (count === 1 && !aic) break;
|
|
38883
|
+
count--;
|
|
38884
|
+
}
|
|
38885
|
+
const parts = [];
|
|
38886
|
+
if (aic) parts.push(aicText);
|
|
38887
|
+
for (let i = 0; i < count; i++) parts.push(droppable[i].text);
|
|
38888
|
+
if (parts.length === 0) return "";
|
|
38889
|
+
return parts.join(SEP_TEXT);
|
|
38890
|
+
}
|
|
38891
|
+
/**
|
|
38892
|
+
* Convenience: parse stdin JSON + assemble with branch resolution.
|
|
38893
|
+
* `branchOverride` skips the `git` spawn (used by tests). `actualUsd`
|
|
38894
|
+
* comes from `discountedUsdForSnapshot` on the same ledger snapshot as
|
|
38895
|
+
* `aicFragment` (read by the caller — this module never touches the
|
|
38896
|
+
* ledger file).
|
|
38897
|
+
*/
|
|
38898
|
+
function buildRichStatusLine(stdinRaw, aicFragment, opts = {}) {
|
|
38899
|
+
const input = parseStatusInput(stdinRaw);
|
|
38900
|
+
const branch = opts.branchOverride !== void 0 ? opts.branchOverride : resolveGitBranch(input.cwd);
|
|
38901
|
+
return assembleStatusLine(aicFragment, input, {
|
|
38902
|
+
...opts.width !== void 0 ? { width: opts.width } : {},
|
|
38903
|
+
...branch ? { branch } : {},
|
|
38904
|
+
...opts.actualUsd !== void 0 ? { actualUsd: opts.actualUsd } : {}
|
|
38905
|
+
});
|
|
38906
|
+
}
|
|
38907
|
+
var ESC, RESET, DIM, GREEN, YELLOW, RED, ORANGE, CYAN, MAGENTA, BLUE, AIC_COLOR, CTX_BAR_LEN, GIT_TIMEOUT_MS, WIDTH_FALLBACK, SEP_TEXT;
|
|
38908
|
+
var init_default_statusline = __esmMin((() => {
|
|
38909
|
+
init_copilot_discount();
|
|
38910
|
+
init_exec();
|
|
38911
|
+
ESC = "\x1B";
|
|
38912
|
+
RESET = `${ESC}[0m`;
|
|
38913
|
+
DIM = `${ESC}[90m`;
|
|
38914
|
+
GREEN = `${ESC}[32m`;
|
|
38915
|
+
YELLOW = `${ESC}[33m`;
|
|
38916
|
+
RED = `${ESC}[31m`;
|
|
38917
|
+
ORANGE = `${ESC}[38;5;208m`;
|
|
38918
|
+
CYAN = `${ESC}[36m`;
|
|
38919
|
+
MAGENTA = `${ESC}[35m`;
|
|
38920
|
+
BLUE = `${ESC}[34m`;
|
|
38921
|
+
AIC_COLOR = `${ESC}[1;33m`;
|
|
38922
|
+
CTX_BAR_LEN = 10;
|
|
38923
|
+
GIT_TIMEOUT_MS = 300;
|
|
38924
|
+
WIDTH_FALLBACK = 120;
|
|
38925
|
+
SEP_TEXT = `${DIM} | ${RESET}`;
|
|
38926
|
+
}));
|
|
38927
|
+
//#endregion
|
|
38928
|
+
//#region src/internal-aic-status.ts
|
|
38929
|
+
var internal_aic_status_exports = /* @__PURE__ */ __exportAll({
|
|
38930
|
+
composeAicStatusLine: () => composeAicStatusLine,
|
|
38931
|
+
internalAicStatus: () => internalAicStatus,
|
|
38932
|
+
runUserStatusLine: () => runUserStatusLine
|
|
38933
|
+
});
|
|
38934
|
+
/**
|
|
38935
|
+
* Read stdin synchronously. Claude Code pipes session JSON to the
|
|
38936
|
+
* statusLine command; an unread pipe can SIGPIPE the writer on some
|
|
38937
|
+
* platforms. Returns "" when stdin is a TTY or unreadable — callers render
|
|
38938
|
+
* placeholders/omit segments rather than failing.
|
|
38939
|
+
*/
|
|
38940
|
+
function readStdin() {
|
|
38941
|
+
try {
|
|
38942
|
+
if (process.stdin.isTTY) return "";
|
|
38943
|
+
return readFileSync(0, "utf8");
|
|
38944
|
+
} catch {
|
|
38945
|
+
return "";
|
|
38946
|
+
}
|
|
38947
|
+
}
|
|
38948
|
+
/**
|
|
38949
|
+
* Pure core: compose the status line from an AIC fragment and a user line.
|
|
38950
|
+
* Exported for unit tests.
|
|
38951
|
+
*/
|
|
38952
|
+
function composeAicStatusLine(aicFragment, userLine) {
|
|
38953
|
+
const aic = aicFragment.trim();
|
|
38954
|
+
const user = userLine.split("\n")[0]?.trim() ?? "";
|
|
38955
|
+
if (aic && user) return `${aic} ${user}`;
|
|
38956
|
+
return aic || user;
|
|
38957
|
+
}
|
|
38958
|
+
/**
|
|
38959
|
+
* Run the user's own statusLine command (from `GH_ROUTER_AIC_USER_STATUSLINE`),
|
|
38960
|
+
* returning its first stdout line or "" on any failure/timeout. Exported for
|
|
38961
|
+
* unit tests (command + timeout injectable).
|
|
38962
|
+
*/
|
|
38963
|
+
function runUserStatusLine(command, timeoutMs = 5e3) {
|
|
38964
|
+
if (!command.trim()) return "";
|
|
38965
|
+
try {
|
|
38966
|
+
const result = spawnSync(command, {
|
|
38967
|
+
shell: true,
|
|
38968
|
+
timeout: timeoutMs,
|
|
38969
|
+
encoding: "utf8",
|
|
38970
|
+
stdio: [
|
|
38971
|
+
"ignore",
|
|
38972
|
+
"pipe",
|
|
38973
|
+
"ignore"
|
|
38974
|
+
],
|
|
38975
|
+
env: process.env
|
|
38976
|
+
});
|
|
38977
|
+
if (result.error) return "";
|
|
38978
|
+
return (typeof result.stdout === "string" ? result.stdout : "").split("\n")[0]?.trim() ?? "";
|
|
38979
|
+
} catch {
|
|
38980
|
+
return "";
|
|
38981
|
+
}
|
|
38982
|
+
}
|
|
38983
|
+
function readAicSnapshot() {
|
|
38984
|
+
try {
|
|
38985
|
+
const ledgerPath = process.env.GH_ROUTER_AIC_LEDGER;
|
|
38986
|
+
if (!ledgerPath) return { fragment: "" };
|
|
38987
|
+
const snapshot = readAicSnapshotFile(ledgerPath);
|
|
38988
|
+
if (!snapshot) return { fragment: "" };
|
|
38989
|
+
if (snapshot.requests === 0 || snapshot.totalNanoAiu <= 0) return { fragment: "" };
|
|
38990
|
+
return {
|
|
38991
|
+
fragment: formatAicStatus(snapshot),
|
|
38992
|
+
actualUsd: discountedUsdForSnapshot(snapshot).total
|
|
38993
|
+
};
|
|
38994
|
+
} catch {
|
|
38995
|
+
return { fragment: "" };
|
|
38996
|
+
}
|
|
38997
|
+
}
|
|
38998
|
+
var internalAicStatus;
|
|
38999
|
+
var init_internal_aic_status = __esmMin((() => {
|
|
39000
|
+
init_dist$1();
|
|
39001
|
+
init_aic_ledger();
|
|
39002
|
+
init_copilot_discount();
|
|
39003
|
+
init_default_statusline();
|
|
39004
|
+
internalAicStatus = defineCommand({
|
|
39005
|
+
meta: {
|
|
39006
|
+
name: "internal-aic-status",
|
|
39007
|
+
description: "Internal: Claude Code statusLine handler printing AIC total + rich session line."
|
|
39008
|
+
},
|
|
39009
|
+
async run() {
|
|
39010
|
+
const stdinRaw = readStdin();
|
|
39011
|
+
const { fragment: aic, actualUsd } = readAicSnapshot();
|
|
39012
|
+
const userCommand = process.env.GH_ROUTER_AIC_USER_STATUSLINE ?? "";
|
|
39013
|
+
if (userCommand.trim()) {
|
|
39014
|
+
const line = composeAicStatusLine(aic, runUserStatusLine(userCommand));
|
|
39015
|
+
if (line) process.stdout.write(`${line}\n`);
|
|
39016
|
+
process.exitCode = 0;
|
|
39017
|
+
return;
|
|
39018
|
+
}
|
|
39019
|
+
let line;
|
|
39020
|
+
try {
|
|
39021
|
+
line = buildRichStatusLine(stdinRaw, aic, { ...actualUsd !== void 0 ? { actualUsd } : {} });
|
|
39022
|
+
} catch {
|
|
39023
|
+
line = aic.trim();
|
|
39024
|
+
}
|
|
39025
|
+
if (line) process.stdout.write(`${line}\n`);
|
|
39026
|
+
process.exitCode = 0;
|
|
39027
|
+
}
|
|
39028
|
+
});
|
|
39029
|
+
}));
|
|
39030
|
+
//#endregion
|
|
38432
39031
|
//#region src/hooks.ts
|
|
38433
39032
|
init_dist$1();
|
|
38434
39033
|
init_dist();
|
|
@@ -38459,7 +39058,8 @@ await runMain(defineCommand({
|
|
|
38459
39058
|
"internal-first-mate-guard": () => Promise.resolve().then(() => (init_internal_first_mate_guard(), internal_first_mate_guard_exports)).then((m) => m.internalFirstMateGuard),
|
|
38460
39059
|
"internal-worker-guard": () => Promise.resolve().then(() => (init_internal_worker_guard(), internal_worker_guard_exports)).then((m) => m.internalWorkerGuard),
|
|
38461
39060
|
"internal-fast-dispatch-guard": () => Promise.resolve().then(() => (init_internal_fast_dispatch_guard(), internal_fast_dispatch_guard_exports)).then((m) => m.internalFastDispatchGuard),
|
|
38462
|
-
"internal-max-dispatch-guard": () => Promise.resolve().then(() => (init_internal_max_dispatch_guard(), internal_max_dispatch_guard_exports)).then((m) => m.internalMaxDispatchGuard)
|
|
39061
|
+
"internal-max-dispatch-guard": () => Promise.resolve().then(() => (init_internal_max_dispatch_guard(), internal_max_dispatch_guard_exports)).then((m) => m.internalMaxDispatchGuard),
|
|
39062
|
+
"internal-aic-status": () => Promise.resolve().then(() => (init_internal_aic_status(), internal_aic_status_exports)).then((m) => m.internalAicStatus)
|
|
38463
39063
|
}
|
|
38464
39064
|
}), { rawArgs: argv });
|
|
38465
39065
|
//#endregion
|