openlimiter 2.0.0 → 2.0.3
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/api-spend.d.ts +29 -0
- package/dist/api-spend.d.ts.map +1 -0
- package/dist/api-spend.js +113 -0
- package/dist/api-spend.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +85 -16
- package/dist/cli.js.map +1 -1
- package/dist/codex-device-login.d.ts.map +1 -1
- package/dist/codex-device-login.js +4 -1
- package/dist/codex-device-login.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/hub-auth.d.ts +2 -0
- package/dist/hub-auth.d.ts.map +1 -1
- package/dist/hub-auth.js +18 -1
- package/dist/hub-auth.js.map +1 -1
- package/dist/hub-sync.d.ts +1 -1
- package/dist/hub-sync.js +1 -1
- package/dist/ingest.d.ts +24 -0
- package/dist/ingest.d.ts.map +1 -1
- package/dist/ingest.js +34 -2
- package/dist/ingest.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +1 -4
- package/dist/session.js.map +1 -1
- package/dist/statusline-wrapper.d.ts.map +1 -1
- package/dist/statusline-wrapper.js +2 -1
- package/dist/statusline-wrapper.js.map +1 -1
- package/dist/statusline.d.ts +2 -0
- package/dist/statusline.d.ts.map +1 -1
- package/dist/statusline.js +34 -15
- package/dist/statusline.js.map +1 -1
- package/dist/terminal-backup.d.ts +85 -1
- package/dist/terminal-backup.d.ts.map +1 -1
- package/dist/terminal-backup.js +148 -22
- package/dist/terminal-backup.js.map +1 -1
- package/dist/terminal-fallback.d.ts.map +1 -1
- package/dist/terminal-fallback.js +13 -3
- package/dist/terminal-fallback.js.map +1 -1
- package/dist/terminal-toml.d.ts +8 -0
- package/dist/terminal-toml.d.ts.map +1 -1
- package/dist/terminal-toml.js +59 -2
- package/dist/terminal-toml.js.map +1 -1
- package/dist/terminal.d.ts.map +1 -1
- package/dist/terminal.js +145 -36
- package/dist/terminal.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API money for the terminal status line.
|
|
3
|
+
*
|
|
4
|
+
* The desktop app polls provider spend and balances and persists them to
|
|
5
|
+
* `api-spend-v1.json` in the state directory, the same folder the CLI already
|
|
6
|
+
* resolves for the quota cache on every OS. This module only reads that file,
|
|
7
|
+
* once, with no network. Anything unexpected draws nothing: a status row runs
|
|
8
|
+
* inside somebody else's tool and must never fail over a money cell.
|
|
9
|
+
*/
|
|
10
|
+
export declare const API_SPEND_FILE_NAME = "api-spend-v1.json";
|
|
11
|
+
/** Money providers the desktop app saves, with the short tag the row uses. */
|
|
12
|
+
export declare const MONEY_TAGS: Readonly<Record<string, string>>;
|
|
13
|
+
export declare function readApiSpend(stateDirectory: string): Promise<unknown>;
|
|
14
|
+
export interface MoneyCell {
|
|
15
|
+
readonly plain: string;
|
|
16
|
+
/** Band value for the shared colour rule, or null when the cell stays uncoloured. */
|
|
17
|
+
readonly band: number | null;
|
|
18
|
+
readonly prefix: string;
|
|
19
|
+
readonly amount: string;
|
|
20
|
+
}
|
|
21
|
+
export interface MoneyOptions {
|
|
22
|
+
readonly now: string;
|
|
23
|
+
/** The quota cache already drew an `or` cell, so the file adds none. */
|
|
24
|
+
readonly hasOpenRouter: boolean;
|
|
25
|
+
/** Whether a provider name (`openai`, `openrouter`...) may be drawn. */
|
|
26
|
+
readonly allowed: (provider: string) => boolean;
|
|
27
|
+
}
|
|
28
|
+
export declare function moneyCells(document: unknown, options: MoneyOptions): readonly MoneyCell[];
|
|
29
|
+
//# sourceMappingURL=api-spend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-spend.d.ts","sourceRoot":"","sources":["../src/api-spend.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,eAAO,MAAM,mBAAmB,sBAAsB,CAAC;AAEvD,8EAA8E;AAC9E,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMvD,CAAC;AAIF,wBAAsB,YAAY,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAG3E;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qFAAqF;IACrF,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;CACjD;AAuBD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,GAAG,SAAS,SAAS,EAAE,CA2DzF"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { freshnessPolicy, readJsonFileSafely } from "@openlimiter/core";
|
|
3
|
+
/**
|
|
4
|
+
* API money for the terminal status line.
|
|
5
|
+
*
|
|
6
|
+
* The desktop app polls provider spend and balances and persists them to
|
|
7
|
+
* `api-spend-v1.json` in the state directory, the same folder the CLI already
|
|
8
|
+
* resolves for the quota cache on every OS. This module only reads that file,
|
|
9
|
+
* once, with no network. Anything unexpected draws nothing: a status row runs
|
|
10
|
+
* inside somebody else's tool and must never fail over a money cell.
|
|
11
|
+
*/
|
|
12
|
+
export const API_SPEND_FILE_NAME = "api-spend-v1.json";
|
|
13
|
+
/** Money providers the desktop app saves, with the short tag the row uses. */
|
|
14
|
+
export const MONEY_TAGS = {
|
|
15
|
+
openai: "oa",
|
|
16
|
+
anthropic: "an",
|
|
17
|
+
xai: "xa",
|
|
18
|
+
moonshot: "ms",
|
|
19
|
+
deepseek: "ds"
|
|
20
|
+
};
|
|
21
|
+
const MAX_SOURCES = 64;
|
|
22
|
+
export async function readApiSpend(stateDirectory) {
|
|
23
|
+
const result = await readJsonFileSafely(path.join(stateDirectory, API_SPEND_FILE_NAME)).catch(() => null);
|
|
24
|
+
return result?.ok === true ? result.value : undefined;
|
|
25
|
+
}
|
|
26
|
+
function isRecord(value) {
|
|
27
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28
|
+
}
|
|
29
|
+
function decimal(value) {
|
|
30
|
+
if (typeof value !== "string" || !/^\d+(\.\d+)?$/u.test(value))
|
|
31
|
+
return null;
|
|
32
|
+
const parsed = Number(value);
|
|
33
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
34
|
+
}
|
|
35
|
+
const money = (value) => "$" + value.toFixed(2);
|
|
36
|
+
/** Lower is worse: yellow under $5, red under $1, as the `or` balance cell. */
|
|
37
|
+
const balanceBand = (value) => value < 1 ? 95 : value < 5 ? 65 : 0;
|
|
38
|
+
/** The band rule's percent of budget; an uncoloured cell has no budget to measure. */
|
|
39
|
+
function budgetBand(spend, budget) {
|
|
40
|
+
const limit = decimal(budget);
|
|
41
|
+
return limit !== null && limit > 0 ? spend / limit * 100 : null;
|
|
42
|
+
}
|
|
43
|
+
export function moneyCells(document, options) {
|
|
44
|
+
if (!isRecord(document) || !Array.isArray(document["sources"]) || !Array.isArray(document["samples"]))
|
|
45
|
+
return [];
|
|
46
|
+
const month = options.now.slice(0, 7) + "-01";
|
|
47
|
+
const newest = new Map();
|
|
48
|
+
for (const sample of document["samples"]) {
|
|
49
|
+
if (!isRecord(sample) || typeof sample["sourceId"] !== "string")
|
|
50
|
+
continue;
|
|
51
|
+
const observed = Date.parse(String(sample["observedAt"]));
|
|
52
|
+
if (!Number.isFinite(observed))
|
|
53
|
+
continue;
|
|
54
|
+
const held = newest.get(sample["sourceId"]);
|
|
55
|
+
const heldAt = held === undefined ? -Infinity : Date.parse(String(held["observedAt"]));
|
|
56
|
+
if (observed > heldAt || observed === heldAt && Number(sample["sequence"]) > Number(held?.["sequence"])) {
|
|
57
|
+
newest.set(sample["sourceId"], sample);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const cells = [];
|
|
61
|
+
let openRouter;
|
|
62
|
+
for (const source of document["sources"].slice(0, MAX_SOURCES)) {
|
|
63
|
+
if (!isRecord(source) || source["enabled"] !== true || typeof source["id"] !== "string")
|
|
64
|
+
continue;
|
|
65
|
+
const provider = source["provider"];
|
|
66
|
+
if (typeof provider !== "string" || !options.allowed(provider))
|
|
67
|
+
continue;
|
|
68
|
+
const sample = newest.get(source["id"]);
|
|
69
|
+
if (sample === undefined)
|
|
70
|
+
continue;
|
|
71
|
+
const tag = provider === "openrouter" ? "or" : MONEY_TAGS[provider];
|
|
72
|
+
if (tag === undefined || provider === "openrouter" && options.hasOpenRouter)
|
|
73
|
+
continue;
|
|
74
|
+
const stale = freshnessPolicy({
|
|
75
|
+
sourceClass: "internal_payload", observedAt: String(sample["observedAt"]), now: options.now, writer: "desktop"
|
|
76
|
+
}).availability === "stale";
|
|
77
|
+
const prefix = tag + " ";
|
|
78
|
+
const mark = stale ? "~" : "";
|
|
79
|
+
const usd = sample["currencySource"] === "provider_usd";
|
|
80
|
+
if (provider === "moonshot" || provider === "deepseek") {
|
|
81
|
+
const balance = decimal(sample["balanceUsd"]);
|
|
82
|
+
const tooLow = source["status"] === "too_low_for_api_calls";
|
|
83
|
+
if (usd && balance !== null) {
|
|
84
|
+
const amount = mark + money(balance);
|
|
85
|
+
cells.push({ plain: prefix + amount, prefix, amount, band: tooLow ? 95 : balanceBand(balance) });
|
|
86
|
+
}
|
|
87
|
+
else if (provider === "deepseek" && sample["currencySource"] === "provider_cny" &&
|
|
88
|
+
sample["balanceUsd"] == null && sample["spendUsd"] == null) {
|
|
89
|
+
// Yuan is never converted, so the cell says the currency and no amount.
|
|
90
|
+
const amount = mark + "CNY";
|
|
91
|
+
cells.push({ plain: prefix + amount, prefix, amount, band: null });
|
|
92
|
+
}
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const spend = decimal(sample["spendUsd"]);
|
|
96
|
+
// A spend from an earlier month is not this month's spend, so it is not shown.
|
|
97
|
+
if (!usd || spend === null || sample["month"] !== month)
|
|
98
|
+
continue;
|
|
99
|
+
const amount = mark + money(spend);
|
|
100
|
+
const suffix = provider === "openrouter" ? " spent" : "";
|
|
101
|
+
const cell = { plain: prefix + amount + suffix, prefix, amount: amount + suffix, band: budgetBand(spend, source["budgetUsd"]) };
|
|
102
|
+
if (provider === "openrouter") {
|
|
103
|
+
// One `or` cell at most: the most recently observed source wins, the first on a tie.
|
|
104
|
+
const observedAt = Date.parse(String(sample["observedAt"]));
|
|
105
|
+
if (openRouter !== undefined && observedAt <= openRouter.observedAt)
|
|
106
|
+
continue;
|
|
107
|
+
openRouter = { cell, observedAt };
|
|
108
|
+
}
|
|
109
|
+
cells.push(cell);
|
|
110
|
+
}
|
|
111
|
+
return cells.filter(cell => cell.prefix !== "or " || cell === openRouter?.cell);
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=api-spend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-spend.js","sourceRoot":"","sources":["../src/api-spend.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -142,6 +142,13 @@ export declare const EXIT_OK = 0;
|
|
|
142
142
|
export declare const EXIT_FAILURE = 1;
|
|
143
143
|
export declare const EXIT_USAGE = 2;
|
|
144
144
|
export declare const EXIT_NO_DATA = 3;
|
|
145
|
+
export interface BrowserOpenInvocation {
|
|
146
|
+
readonly executable: string;
|
|
147
|
+
readonly arguments: readonly string[];
|
|
148
|
+
readonly cwd: string;
|
|
149
|
+
}
|
|
150
|
+
/** Build a direct browser opener invocation without a command shell. */
|
|
151
|
+
export declare function browserOpenInvocation(url: string, platform: NodeJS.Platform, environment?: Readonly<Record<string, string | undefined>>): BrowserOpenInvocation | null;
|
|
145
152
|
export declare function runtimeDependencies(): Pick<CliDependencies, "acquisitionTransport" | "spawnDetached" | "windowsCredentialRunner" | "probeAntigravity" | "resolveExecutablePath" | "hubTransport" | "openBrowser" | "emit" | "windowsAclRunner" | "codexDeviceLoginRunnerFactory">;
|
|
146
153
|
export interface RefreshResult {
|
|
147
154
|
snapshots: Snapshot[];
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ACQUISITION_OUTCOME_SENTENCE, ACQUISITION_PROVIDERS, registerManagedCodexAccount, CREDENTIAL_FAILURE_SENTENCE, PROVIDER_CODES, acquireRefreshLock, clearRefreshSpawnFailure, mergeAcquiredSnapshots, createFetchTransport, readRefreshSpawnFailure, antigravitySpec, buildAdvice, canonicalJson, claudeSpec, codexSpec, cursorSpec, dedupeFailures, desktopHoldsCache, failureFromConnectorReason, freshness, geminiCliSpec, grokSpec, isProviderDue, kimiSpec, mergeSnapshots, normalizeMeters, normalizeMetersReport, openrouterSpec, readAcquisitionCredential, readAcquisitionSchedule, readSnapshotCache, readWindowsCredentialWith, resolveStateDirectory, runAcquisition, spawnDetachedRefresh, writeAcquisitionSchedule, probeAntigravity, resolveAgyExecutablePath } from "@openlimiter/core";
|
|
1
|
+
import { ACQUISITION_OUTCOME_SENTENCE, captureStatuslineAccount, pruneSnapshotCache, ACQUISITION_PROVIDERS, registerManagedCodexAccount, CREDENTIAL_FAILURE_SENTENCE, PROVIDER_CODES, acquireRefreshLock, clearRefreshSpawnFailure, mergeAcquiredSnapshots, createFetchTransport, readRefreshSpawnFailure, antigravitySpec, buildAdvice, canonicalJson, claudeSpec, codexSpec, cursorSpec, dedupeFailures, desktopHoldsCache, failureFromConnectorReason, freshness, geminiCliSpec, grokSpec, isProviderDue, kimiSpec, mergeSnapshotCache, mergeSnapshots, normalizeMeters, normalizeMetersReport, openrouterSpec, readAcquisitionCredential, readAcquisitionSchedule, readSnapshotCache, readWindowsCredentialWith, resolveStateDirectory, runAcquisition, spawnDetachedRefresh, trustedHelperWorkingDirectory, writeAcquisitionSchedule, windowsSystemTool, probeAntigravity, resolveAgyExecutablePath } from "@openlimiter/core";
|
|
2
2
|
import { antigravityFixture, claudeFixture, codexFixture, grokFixture, kimiFixture, connectors, manualFixture, opencodeFixture, openrouterFixture, parseAntigravityCodeAssistPayload, parseAntigravityPayload, parseClaudePayload, parseCodexPayload, parseCursorPayload, parseGeminiCliPayload, parseGrokPayload, parseKimiPayload, parseManualPayload, parseOpencodePayload, parseOpenrouterPayload } from "@openlimiter/connectors";
|
|
3
3
|
import { AGENT_COMPATIBILITY, agentContextFromCache, agentContextSpillFromCache, agentVersionCompatibility, changeAgentHook, detectAgentInstallation, readAgentHookStatus, runAgentHook, validateAgentExecutableStamp, writeAgentContextSnapshot, loadHostedContextTrust, renderClaudeStatusline } from "@openlimiter/adapters";
|
|
4
4
|
import { execFile, spawn } from "node:child_process";
|
|
@@ -7,11 +7,12 @@ import packageJson from "../package.json" with { type: "json" };
|
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { PROVIDER_KEYS, STATUSLINE_KEYS, defaultConfig, initialize, isProviderKey, isStatuslineKey, providerValueText, readConfig, readProvidersConfig, readStatuslineConfig, setProviderValue, setStatuslineValue, statuslineValueText, writeConfig } from "./config.js";
|
|
10
|
-
import { ACQUISITION_PROVENANCE, ANTIGRAVITY_STATUSLINE_PROVENANCE, GROK_STATUSLINE_PROVENANCE, INGEST_PROVENANCE, MANUAL_PROVENANCE, STATUSLINE_PROVENANCE, environmentWithLocalMarkers, STDIN_BYTE_LIMIT, parseAntigravityStatuslinePayload, parseGrokStatuslinePayload, parseJsonText, persistSnapshots, readManualDocument, withProvenance } from "./ingest.js";
|
|
10
|
+
import { ACQUISITION_PROVENANCE, ANTIGRAVITY_STATUSLINE_PROVENANCE, GROK_STATUSLINE_PROVENANCE, INGEST_PROVENANCE, MANUAL_PROVENANCE, STATUSLINE_PROVENANCE, environmentWithLocalMarkers, STDIN_BYTE_LIMIT, parseAntigravityStatuslinePayload, parseGrokStatuslinePayload, parseJsonText, persistSnapshots, clearStatuslineFailure, readStatuslineFailure, recordStatuslineFailure, readManualDocument, withProvenance } from "./ingest.js";
|
|
11
11
|
import { UnavailableCredentialStore } from "./credentials.js";
|
|
12
12
|
import { failureLine, failureLines, renderTable, supportsColor } from "./render.js";
|
|
13
13
|
import { isStatuslineHost, renderStatuslineLayout, statuslineColor, statuslineUnicode } from "./statusline.js";
|
|
14
14
|
import { parseStatuslineSession } from "./statusline-ingest.js";
|
|
15
|
+
import { readApiSpend } from "./api-spend.js";
|
|
15
16
|
import { TERMINAL_HOST_NAMES, installHost, terminalHide, terminalShow, terminalStatusTable, uninstallHost } from "./terminal.js";
|
|
16
17
|
import { createFetchHubTransport, hubConfigured } from "./hub.js";
|
|
17
18
|
import { DELIVERY_UNCONFIRMED_SENTENCE, REVOKED_SENTENCE, ensureFreshSession, isAborted, runDeviceLogin } from "./hub-auth.js";
|
|
@@ -88,7 +89,12 @@ const NEWLINE = "\n";
|
|
|
88
89
|
const execFileRunner = async (executable, helperArguments, timeoutMilliseconds) => await new Promise((resolve) => {
|
|
89
90
|
execFile(executable, [...helperArguments], (() => {
|
|
90
91
|
if (!executable.toLowerCase().endsWith("powershell.exe")) {
|
|
91
|
-
return {
|
|
92
|
+
return {
|
|
93
|
+
cwd: trustedHelperWorkingDirectory(process.platform, process.env),
|
|
94
|
+
timeout: timeoutMilliseconds,
|
|
95
|
+
maxBuffer: 262_144,
|
|
96
|
+
windowsHide: true
|
|
97
|
+
};
|
|
92
98
|
}
|
|
93
99
|
const environment = { ...process.env };
|
|
94
100
|
/* Windows environment names ignore case, but a copied process.env keeps
|
|
@@ -100,6 +106,7 @@ const execFileRunner = async (executable, helperArguments, timeoutMilliseconds)
|
|
|
100
106
|
}
|
|
101
107
|
environment["PSModulePath"] = path.win32.join(process.env["SystemRoot"] ?? "C:\\Windows", "System32", "WindowsPowerShell", "v1.0", "Modules");
|
|
102
108
|
return {
|
|
109
|
+
cwd: trustedHelperWorkingDirectory(process.platform, environment),
|
|
103
110
|
timeout: timeoutMilliseconds,
|
|
104
111
|
maxBuffer: 262_144,
|
|
105
112
|
windowsHide: true,
|
|
@@ -109,17 +116,48 @@ const execFileRunner = async (executable, helperArguments, timeoutMilliseconds)
|
|
|
109
116
|
resolve(error === null ? { ok: true, stdout } : { ok: false });
|
|
110
117
|
});
|
|
111
118
|
});
|
|
119
|
+
/** Build a direct browser opener invocation without a command shell. */
|
|
120
|
+
export function browserOpenInvocation(url, platform, environment = process.env) {
|
|
121
|
+
if (url.length > 2_048)
|
|
122
|
+
return null;
|
|
123
|
+
try {
|
|
124
|
+
if (new URL(url).protocol !== "https:")
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
if (platform === "win32") {
|
|
131
|
+
return {
|
|
132
|
+
executable: windowsSystemTool("rundll32.exe", environment),
|
|
133
|
+
arguments: ["url.dll,FileProtocolHandler", url],
|
|
134
|
+
cwd: trustedHelperWorkingDirectory(platform, environment)
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return platform === "darwin"
|
|
138
|
+
? {
|
|
139
|
+
executable: "open",
|
|
140
|
+
arguments: [url],
|
|
141
|
+
cwd: trustedHelperWorkingDirectory(platform, environment)
|
|
142
|
+
}
|
|
143
|
+
: {
|
|
144
|
+
executable: "xdg-open",
|
|
145
|
+
arguments: [url],
|
|
146
|
+
cwd: trustedHelperWorkingDirectory(platform, environment)
|
|
147
|
+
};
|
|
148
|
+
}
|
|
112
149
|
/** Open a URL in the person's browser, best effort and never awaited. */
|
|
113
150
|
function openBrowserPlatform(url, platform) {
|
|
114
|
-
|
|
151
|
+
const invocation = browserOpenInvocation(url, platform);
|
|
152
|
+
if (invocation === null)
|
|
115
153
|
return;
|
|
116
|
-
const [command, commandArguments] = platform === "win32"
|
|
117
|
-
? ["cmd", ["/c", "start", "", url]]
|
|
118
|
-
: platform === "darwin"
|
|
119
|
-
? ["open", [url]]
|
|
120
|
-
: ["xdg-open", [url]];
|
|
121
154
|
try {
|
|
122
|
-
const child = spawn(
|
|
155
|
+
const child = spawn(invocation.executable, [...invocation.arguments], {
|
|
156
|
+
cwd: invocation.cwd,
|
|
157
|
+
stdio: "ignore",
|
|
158
|
+
detached: true,
|
|
159
|
+
shell: false
|
|
160
|
+
});
|
|
123
161
|
child.once("error", () => undefined);
|
|
124
162
|
child.unref();
|
|
125
163
|
}
|
|
@@ -138,6 +176,7 @@ export function runtimeDependencies() {
|
|
|
138
176
|
resolveExecutablePath: resolveAgyExecutablePath,
|
|
139
177
|
spawnDetached: (executable, argumentsList, options) => {
|
|
140
178
|
const child = spawn(executable, [...argumentsList], {
|
|
179
|
+
cwd: options.cwd,
|
|
141
180
|
detached: true,
|
|
142
181
|
stdio: "ignore",
|
|
143
182
|
windowsHide: true
|
|
@@ -423,6 +462,12 @@ async function acquisitionStatusRow(spec, schedule, readCredential, snapshots, n
|
|
|
423
462
|
nextAttemptAt
|
|
424
463
|
};
|
|
425
464
|
}
|
|
465
|
+
/** What each status line write failure means, in words with no dashes. */
|
|
466
|
+
const STATUSLINE_FAILURE_SENTENCE = {
|
|
467
|
+
ingest: "the last status line payload could not be read",
|
|
468
|
+
cache_write: "the last status line reading could not be saved to the cache",
|
|
469
|
+
agent_context: "the last status line reading was saved, the agent context file could not be written"
|
|
470
|
+
};
|
|
426
471
|
/** The acquisition block doctor prints under the connector block. */
|
|
427
472
|
async function acquisitionDoctorRows(dependencies, snapshots, now) {
|
|
428
473
|
const providers = await readProvidersConfig(dependencies.stateDirectory);
|
|
@@ -437,6 +482,11 @@ async function acquisitionDoctorRows(dependencies, snapshots, now) {
|
|
|
437
482
|
rows.push("REFRESH SPAWN FAILED " + failedAt +
|
|
438
483
|
" the background refresh could not be started on this machine");
|
|
439
484
|
}
|
|
485
|
+
const statusline = await readStatuslineFailure(dependencies.stateDirectory);
|
|
486
|
+
if (statusline !== null) {
|
|
487
|
+
rows.push(["STATUSLINE WRITE FAILED", statusline.at, statusline.stage, statusline.errorClass,
|
|
488
|
+
STATUSLINE_FAILURE_SENTENCE[statusline.stage]].join(" "));
|
|
489
|
+
}
|
|
440
490
|
return [ACQUISITION_HEADER, ...rows].join(NEWLINE);
|
|
441
491
|
}
|
|
442
492
|
/**
|
|
@@ -461,7 +511,7 @@ function acquisitionLine(row, snapshots = []) {
|
|
|
461
511
|
.filter((part) => part !== "")
|
|
462
512
|
.join(", ");
|
|
463
513
|
return [
|
|
464
|
-
row.provider.toLowerCase()
|
|
514
|
+
row.provider.toLowerCase(),
|
|
465
515
|
row.detected ? "yes" : "no",
|
|
466
516
|
row.status,
|
|
467
517
|
row.nextAttemptAt ?? "NONE",
|
|
@@ -702,6 +752,9 @@ async function ingestStandardInput(dependencies, now, host = "claude") {
|
|
|
702
752
|
if (host === "codex" || host === "shell")
|
|
703
753
|
return null;
|
|
704
754
|
try {
|
|
755
|
+
const accountId = await captureStatuslineAccount(host === "antigravity" ? "ANTIGRAVITY" : host === "grok" ? "GROK" : "CLAUDE", {
|
|
756
|
+
environment: dependencies.environment, homeDirectory: dependencies.homeDirectory, platform: dependencies.platform, ...(dependencies.stateDirectory ? { stateDirectory: dependencies.stateDirectory } : {}), now
|
|
757
|
+
});
|
|
705
758
|
const document = parseJsonText(await dependencies.readStandardInput());
|
|
706
759
|
if (!document.ok)
|
|
707
760
|
return null;
|
|
@@ -719,19 +772,31 @@ async function ingestStandardInput(dependencies, now, host = "claude") {
|
|
|
719
772
|
: STATUSLINE_PROVENANCE;
|
|
720
773
|
/* The host wrote this to our standard input in this session. It is a live
|
|
721
774
|
reading, and it says so. */
|
|
722
|
-
const incoming = normalizeMeters(withProvenance(meters, provenance));
|
|
775
|
+
const incoming = normalizeMeters(withProvenance(meters, provenance).map(meter => ({ ...meter, ...(accountId ? { accountId } : {}) })));
|
|
723
776
|
if (incoming.length === 0)
|
|
724
777
|
return { snapshots: null, payload: document.value };
|
|
778
|
+
/* The render still draws when a write fails, so each failure is kept for
|
|
779
|
+
doctor by its stage, and the next full success clears it. */
|
|
780
|
+
let merged;
|
|
725
781
|
try {
|
|
726
|
-
|
|
727
|
-
return { snapshots: merged, payload: document.value };
|
|
782
|
+
merged = (await mergeSnapshotCache(incoming, dependencies.stateDirectory ?? resolveStateDirectory(), Date.parse(now))).merged;
|
|
728
783
|
}
|
|
729
|
-
catch {
|
|
784
|
+
catch (error) {
|
|
785
|
+
await recordStatuslineFailure(dependencies.stateDirectory, now, "cache_write", error);
|
|
730
786
|
const existing = await cachedSnapshots(dependencies.stateDirectory);
|
|
731
787
|
return { snapshots: mergeSnapshots(existing, incoming), payload: document.value };
|
|
732
788
|
}
|
|
789
|
+
try {
|
|
790
|
+
await writeAgentContextSnapshot(merged, dependencies.stateDirectory, now);
|
|
791
|
+
await clearStatuslineFailure(dependencies.stateDirectory);
|
|
792
|
+
}
|
|
793
|
+
catch (error) {
|
|
794
|
+
await recordStatuslineFailure(dependencies.stateDirectory, now, "agent_context", error);
|
|
795
|
+
}
|
|
796
|
+
return { snapshots: merged, payload: document.value };
|
|
733
797
|
}
|
|
734
|
-
catch {
|
|
798
|
+
catch (error) {
|
|
799
|
+
await recordStatuslineFailure(dependencies.stateDirectory, now, "ingest", error);
|
|
735
800
|
return null;
|
|
736
801
|
}
|
|
737
802
|
}
|
|
@@ -956,6 +1021,7 @@ async function statuslineCommand(dependencies, argumentsList, now) {
|
|
|
956
1021
|
now,
|
|
957
1022
|
config,
|
|
958
1023
|
session: parseStatuslineSession(ingested?.payload),
|
|
1024
|
+
apiSpend: await readApiSpend(dependencies.stateDirectory ?? resolveStateDirectory()),
|
|
959
1025
|
color: statuslineColor(config.color, dependencies.environment, dependencies.colorOutput, host),
|
|
960
1026
|
unicode: statuslineUnicode(dependencies.environment),
|
|
961
1027
|
host
|
|
@@ -1766,6 +1832,9 @@ export async function runCli(argumentsList, overrides = {}) {
|
|
|
1766
1832
|
const command = argumentsList[0] ?? "setup";
|
|
1767
1833
|
const now = dependencies.now();
|
|
1768
1834
|
try {
|
|
1835
|
+
if (["snapshot", "statusline", "refresh", "status"].includes(command)) {
|
|
1836
|
+
await pruneSnapshotCache(dependencies.stateDirectory, Date.parse(now)).catch(() => 0);
|
|
1837
|
+
}
|
|
1769
1838
|
if (command === "--version" || command === "-v" || command === "version") {
|
|
1770
1839
|
return succeed(packageJson.version);
|
|
1771
1840
|
}
|