@cmmd-center/forge 0.10.14 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +1114 -227
- package/dist/bin.mjs +1134 -247
- package/dist/client/assets/{DiffPanel-Bnkifxef.js → DiffPanel-B1yCvMgl.js} +3 -3
- package/dist/client/assets/{DiffPanel.logic-Bk4d9mRT.js → DiffPanel.logic-DS-ZTg-9.js} +2 -2
- package/dist/client/assets/DiffPanelShell-BFklbpRe.js +2531 -0
- package/dist/client/assets/{DiffWorkerPoolProvider-BO-u17g1.js → DiffWorkerPoolProvider-Q7qqna90.js} +8 -8
- package/dist/client/assets/{PullRequestCodePanel-Bbuxcu5g.js → PullRequestCodePanel-CO19fx8r.js} +3 -3
- package/dist/client/assets/{Virtualizer-BxZHsCOD.js → Virtualizer-nnC3ZYoF.js} +2 -2
- package/dist/client/assets/{index-BeMh8T3m.js → index-ChfmpOIo.js} +59 -59
- package/dist/client/index.html +4 -4
- package/dist/forge-build.json +1 -1
- package/dist/{open-FIgTDosf.mjs → open-D8mGyWmt.mjs} +3 -3
- package/package.json +1 -1
- package/dist/client/assets/DiffPanelShell-D_nSDbjF.js +0 -2531
package/dist/bin.mjs
CHANGED
|
@@ -22,7 +22,7 @@ import { createCipheriv, createDecipheriv, createHash, createHmac, createPublicK
|
|
|
22
22
|
import * as FS$1 from "node:fs";
|
|
23
23
|
import fs, { accessSync, chmodSync, constants, createReadStream, existsSync, mkdirSync, mkdtempSync, promises, readFile, readFileSync, readdir, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
24
24
|
import * as NodeOS from "node:os";
|
|
25
|
-
import
|
|
25
|
+
import $U, { homedir, networkInterfaces, tmpdir } from "node:os";
|
|
26
26
|
import * as Path$1 from "node:path";
|
|
27
27
|
import path, { basename, delimiter, dirname, extname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
28
28
|
import { pathToFileURL } from "node:url";
|
|
@@ -16520,7 +16520,7 @@ function makeNetServiceShape(createServer = Net.createServer) {
|
|
|
16520
16520
|
}
|
|
16521
16521
|
//#endregion
|
|
16522
16522
|
//#region ../../packages/shared/src/releaseInfo.generated.ts
|
|
16523
|
-
const FORGE_RELEASE_VERSION = "0.
|
|
16523
|
+
const FORGE_RELEASE_VERSION = "0.11.0";
|
|
16524
16524
|
//#endregion
|
|
16525
16525
|
//#region ../../packages/shared/src/hostProcess.ts
|
|
16526
16526
|
/**
|
|
@@ -50107,7 +50107,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
|
50107
50107
|
const min = forceColor || 0;
|
|
50108
50108
|
if (env.TERM === "dumb") return min;
|
|
50109
50109
|
if (process$1.platform === "win32") {
|
|
50110
|
-
const osRelease =
|
|
50110
|
+
const osRelease = $U.release().split(".");
|
|
50111
50111
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
50112
50112
|
return 1;
|
|
50113
50113
|
}
|
|
@@ -52550,7 +52550,7 @@ var require_ProcessDetector = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
52550
52550
|
exports.processDetector = void 0;
|
|
52551
52551
|
const api_1 = (init_esm$1(), __toCommonJS(esm_exports$1));
|
|
52552
52552
|
const semconv_1 = require_semconv$1();
|
|
52553
|
-
const os$
|
|
52553
|
+
const os$3 = __require("os");
|
|
52554
52554
|
/**
|
|
52555
52555
|
* ProcessDetector will be used to detect the resources related current process running
|
|
52556
52556
|
* and being instrumented from the NodeJS Process module.
|
|
@@ -52572,7 +52572,7 @@ var require_ProcessDetector = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
52572
52572
|
};
|
|
52573
52573
|
if (process.argv.length > 1) attributes[semconv_1.ATTR_PROCESS_COMMAND] = process.argv[1];
|
|
52574
52574
|
try {
|
|
52575
|
-
const userInfo = os$
|
|
52575
|
+
const userInfo = os$3.userInfo();
|
|
52576
52576
|
attributes[semconv_1.ATTR_PROCESS_OWNER] = userInfo.username;
|
|
52577
52577
|
} catch (e) {
|
|
52578
52578
|
api_1.diag.debug(`error obtaining process owner: ${e}`);
|
|
@@ -65495,7 +65495,7 @@ function normalizeNumberish(value) {
|
|
|
65495
65495
|
}
|
|
65496
65496
|
//#endregion
|
|
65497
65497
|
//#region package.json
|
|
65498
|
-
var version$1 = "0.
|
|
65498
|
+
var version$1 = "0.11.0";
|
|
65499
65499
|
//#endregion
|
|
65500
65500
|
//#region src/sentry.ts
|
|
65501
65501
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -65968,7 +65968,7 @@ function readCmmdAvatarUrl(raw) {
|
|
|
65968
65968
|
function readTrimmedString$2(value) {
|
|
65969
65969
|
return typeof value === "string" ? value.trim() || null : null;
|
|
65970
65970
|
}
|
|
65971
|
-
function asRecord$
|
|
65971
|
+
function asRecord$14(value) {
|
|
65972
65972
|
return value && typeof value === "object" ? value : null;
|
|
65973
65973
|
}
|
|
65974
65974
|
function readDirectPlanLabelFromRecord(obj) {
|
|
@@ -65980,26 +65980,26 @@ function readNestedPlanLabelFromRecord(obj) {
|
|
|
65980
65980
|
function readCmmdPlanLabel(obj) {
|
|
65981
65981
|
const direct = readDirectPlanLabelFromRecord(obj);
|
|
65982
65982
|
if (direct) return direct;
|
|
65983
|
-
const plan = asRecord$
|
|
65983
|
+
const plan = asRecord$14(obj["plan"]);
|
|
65984
65984
|
if (plan) {
|
|
65985
65985
|
const label = readNestedPlanLabelFromRecord(plan);
|
|
65986
65986
|
if (label) return label;
|
|
65987
65987
|
}
|
|
65988
|
-
const subscription = asRecord$
|
|
65988
|
+
const subscription = asRecord$14(obj["subscription"]);
|
|
65989
65989
|
if (subscription) {
|
|
65990
65990
|
const label = readNestedPlanLabelFromRecord(subscription);
|
|
65991
65991
|
if (label) return label;
|
|
65992
|
-
const subscriptionPlan = asRecord$
|
|
65992
|
+
const subscriptionPlan = asRecord$14(subscription["plan"]);
|
|
65993
65993
|
if (subscriptionPlan) {
|
|
65994
65994
|
const nestedLabel = readNestedPlanLabelFromRecord(subscriptionPlan);
|
|
65995
65995
|
if (nestedLabel) return nestedLabel;
|
|
65996
65996
|
}
|
|
65997
65997
|
}
|
|
65998
|
-
const billing = asRecord$
|
|
65998
|
+
const billing = asRecord$14(obj["billing"]);
|
|
65999
65999
|
if (billing) {
|
|
66000
66000
|
const label = readNestedPlanLabelFromRecord(billing);
|
|
66001
66001
|
if (label) return label;
|
|
66002
|
-
const billingPlan = asRecord$
|
|
66002
|
+
const billingPlan = asRecord$14(billing["plan"]);
|
|
66003
66003
|
if (billingPlan) return readNestedPlanLabelFromRecord(billingPlan);
|
|
66004
66004
|
}
|
|
66005
66005
|
return null;
|
|
@@ -69794,22 +69794,22 @@ const EMPTY_PARSED_ERROR = {
|
|
|
69794
69794
|
message: null,
|
|
69795
69795
|
raw: null
|
|
69796
69796
|
};
|
|
69797
|
-
function asRecord$
|
|
69797
|
+
function asRecord$13(value) {
|
|
69798
69798
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
69799
69799
|
}
|
|
69800
69800
|
function readTrimmedString$1(value) {
|
|
69801
69801
|
return (typeof value === "string" ? value.trim() : "") || null;
|
|
69802
69802
|
}
|
|
69803
69803
|
function readErrorEnvelope(record) {
|
|
69804
|
-
return asRecord$
|
|
69804
|
+
return asRecord$13(record["error"]);
|
|
69805
69805
|
}
|
|
69806
69806
|
function readUpstreamErrorCode(body) {
|
|
69807
|
-
const record = asRecord$
|
|
69807
|
+
const record = asRecord$13(body);
|
|
69808
69808
|
if (record === null) return null;
|
|
69809
69809
|
return readTrimmedString$1(record["code"]) ?? readTrimmedString$1(readErrorEnvelope(record)?.["code"]);
|
|
69810
69810
|
}
|
|
69811
69811
|
function readErrorMessage$2(raw) {
|
|
69812
|
-
const record = asRecord$
|
|
69812
|
+
const record = asRecord$13(raw);
|
|
69813
69813
|
if (record === null) return null;
|
|
69814
69814
|
const error = record["error"];
|
|
69815
69815
|
const nestedMessage = readErrorEnvelope(record)?.["message"];
|
|
@@ -71891,7 +71891,7 @@ function readStringJS(length) {
|
|
|
71891
71891
|
if (units.length > 0) result += fromCharCode.apply(String, units);
|
|
71892
71892
|
return result;
|
|
71893
71893
|
}
|
|
71894
|
-
function readString$
|
|
71894
|
+
function readString$5(source, start, length) {
|
|
71895
71895
|
let existingSrc = src;
|
|
71896
71896
|
src = source;
|
|
71897
71897
|
position$1 = start;
|
|
@@ -73756,7 +73756,7 @@ function readStruct(src, position, srcEnd, unpackr) {
|
|
|
73756
73756
|
}
|
|
73757
73757
|
if (end == null) end = source.bytesEnd - refStart;
|
|
73758
73758
|
if (source.srcString) return source.srcString.slice(ref, end);
|
|
73759
|
-
return readString$
|
|
73759
|
+
return readString$5(src, ref + refStart, end - ref);
|
|
73760
73760
|
};
|
|
73761
73761
|
break;
|
|
73762
73762
|
case UTF8:
|
|
@@ -74232,7 +74232,7 @@ var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) =>
|
|
|
74232
74232
|
var fs$6 = __require("fs");
|
|
74233
74233
|
var path$5 = __require("path");
|
|
74234
74234
|
var url$1 = __require("url");
|
|
74235
|
-
var os$
|
|
74235
|
+
var os$2 = __require("os");
|
|
74236
74236
|
var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
74237
74237
|
var vars = process.config && process.config.variables || {};
|
|
74238
74238
|
var prebuildsOnly = !!process.env.PREBUILDS_ONLY;
|
|
@@ -74240,8 +74240,8 @@ var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) =>
|
|
|
74240
74240
|
var abi = versions.modules;
|
|
74241
74241
|
if (versions.deno || process.isBun) abi = "unsupported";
|
|
74242
74242
|
var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
|
|
74243
|
-
var arch = process.env.npm_config_arch || os$
|
|
74244
|
-
var platform = process.env.npm_config_platform || os$
|
|
74243
|
+
var arch = process.env.npm_config_arch || os$2.arch();
|
|
74244
|
+
var platform = process.env.npm_config_platform || os$2.platform();
|
|
74245
74245
|
var libc = process.env.LIBC || (isMusl(platform) ? "musl" : "glibc");
|
|
74246
74246
|
var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars.arm_version) || "";
|
|
74247
74247
|
var uv = (versions.uv || "").split(".")[0];
|
|
@@ -77105,7 +77105,7 @@ succeed$2(TraceDiagnostics, TraceDiagnostics.of({ read: (options) => succeed(mak
|
|
|
77105
77105
|
* through the server is what lets it ask at all.
|
|
77106
77106
|
*/
|
|
77107
77107
|
const RUNTIMES_PATH = "/api/forge/context/runtimes";
|
|
77108
|
-
function asRecord$
|
|
77108
|
+
function asRecord$12(value) {
|
|
77109
77109
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
77110
77110
|
}
|
|
77111
77111
|
/** A trimmed string, or null. Empty and whitespace are the same as absent. */
|
|
@@ -77120,7 +77120,7 @@ function text$1(value) {
|
|
|
77120
77120
|
* something that cannot work.
|
|
77121
77121
|
*/
|
|
77122
77122
|
function toSummary(value) {
|
|
77123
|
-
const row = asRecord$
|
|
77123
|
+
const row = asRecord$12(value);
|
|
77124
77124
|
if (!row) return null;
|
|
77125
77125
|
const runtimeId = text$1(row["runtimeId"]);
|
|
77126
77126
|
const organizationId = row["organizationId"];
|
|
@@ -77144,7 +77144,7 @@ function listCloudRuntimes(input) {
|
|
|
77144
77144
|
return gen(function* () {
|
|
77145
77145
|
const jwt = input.jwt;
|
|
77146
77146
|
if (jwt === null || jwt.trim().length === 0) return yield* fail(new CloudRuntimeListError({ failure: "not_signed_in" }));
|
|
77147
|
-
const runtimes = asRecord$
|
|
77147
|
+
const runtimes = asRecord$12(asRecord$12(yield* tryPromise({
|
|
77148
77148
|
try: () => input.request({
|
|
77149
77149
|
method: "GET",
|
|
77150
77150
|
jwt,
|
|
@@ -91971,7 +91971,7 @@ const launchDetached = (launch) => gen(function* () {
|
|
|
91971
91971
|
});
|
|
91972
91972
|
const OpenLive = effect(Open, gen(function* () {
|
|
91973
91973
|
const open = yield* tryPromise({
|
|
91974
|
-
try: () => import("./open-
|
|
91974
|
+
try: () => import("./open-D8mGyWmt.mjs"),
|
|
91975
91975
|
catch: (cause) => new OpenError$1({
|
|
91976
91976
|
message: "failed to load browser opener",
|
|
91977
91977
|
cause
|
|
@@ -92650,7 +92650,7 @@ var AnalyticsService = class AnalyticsService extends Service$1()("forge/telemet
|
|
|
92650
92650
|
var RuntimeReplication = class extends Service$1()("forge/runtimeReplication/Services/RuntimeReplication") {};
|
|
92651
92651
|
//#endregion
|
|
92652
92652
|
//#region src/runtimeDrain/forgeGitCommit.ts
|
|
92653
|
-
const execFileAsync$
|
|
92653
|
+
const execFileAsync$7 = promisify$1(execFile);
|
|
92654
92654
|
const FORGE_GIT_COMMIT_PATTERN = /^[0-9a-f]{7,40}$/i;
|
|
92655
92655
|
/**
|
|
92656
92656
|
* Resolve the commit this forge checkout actually booted from, so the
|
|
@@ -92674,7 +92674,7 @@ const FORGE_GIT_COMMIT_PATTERN = /^[0-9a-f]{7,40}$/i;
|
|
|
92674
92674
|
* it yet.
|
|
92675
92675
|
*/
|
|
92676
92676
|
let cached$1 = null;
|
|
92677
|
-
async function resolveForgeGitCommit(execImpl = (command, args) => execFileAsync$
|
|
92677
|
+
async function resolveForgeGitCommit(execImpl = (command, args) => execFileAsync$7(command, [...args], { timeout: 5e3 })) {
|
|
92678
92678
|
if (cached$1 !== null) return cached$1;
|
|
92679
92679
|
try {
|
|
92680
92680
|
const { stdout } = await execImpl("git", ["rev-parse", "HEAD"]);
|
|
@@ -94606,7 +94606,7 @@ function mightCarryUsage(line, provider) {
|
|
|
94606
94606
|
* a line at a time, so the usual cause is a tail the scan opened mid-write,
|
|
94607
94607
|
* and a count that keeps growing is a file the scan can no longer read.
|
|
94608
94608
|
*/
|
|
94609
|
-
function parseJsonObject$
|
|
94609
|
+
function parseJsonObject$2(line, diagnostics) {
|
|
94610
94610
|
let parsed;
|
|
94611
94611
|
try {
|
|
94612
94612
|
parsed = JSON.parse(line);
|
|
@@ -94636,7 +94636,7 @@ function claudeDedupeKey(messageId, requestId) {
|
|
|
94636
94636
|
return `${messageId ?? ""}:${requestId ?? ""}`;
|
|
94637
94637
|
}
|
|
94638
94638
|
function parseClaudeLine(line, diagnostics) {
|
|
94639
|
-
const record = parseJsonObject$
|
|
94639
|
+
const record = parseJsonObject$2(line, diagnostics);
|
|
94640
94640
|
if (record === null) return null;
|
|
94641
94641
|
if (record["type"] !== "assistant") return null;
|
|
94642
94642
|
const messageRecord = objectAt(record, "message");
|
|
@@ -94783,7 +94783,7 @@ function parseCodexTokenCount(record, payloadRecord, state) {
|
|
|
94783
94783
|
};
|
|
94784
94784
|
}
|
|
94785
94785
|
function parseCodexLine(line, state, diagnostics) {
|
|
94786
|
-
const record = parseJsonObject$
|
|
94786
|
+
const record = parseJsonObject$2(line, diagnostics);
|
|
94787
94787
|
if (record === null) return null;
|
|
94788
94788
|
const payloadRecord = objectAt(record, "payload");
|
|
94789
94789
|
if (payloadRecord === null) return null;
|
|
@@ -99160,7 +99160,7 @@ function buildDiscoveryResult(probes) {
|
|
|
99160
99160
|
}
|
|
99161
99161
|
//#endregion
|
|
99162
99162
|
//#region src/sourceControl/probe.ts
|
|
99163
|
-
const execFileAsync$
|
|
99163
|
+
const execFileAsync$6 = promisify$1(execFile);
|
|
99164
99164
|
/**
|
|
99165
99165
|
* How long a machine's toolchain answer stays good for.
|
|
99166
99166
|
*
|
|
@@ -99171,7 +99171,7 @@ const execFileAsync$5 = promisify$1(execFile);
|
|
|
99171
99171
|
*/
|
|
99172
99172
|
const DISCOVERY_TTL = "5 minutes";
|
|
99173
99173
|
/** Probes time out because `gh auth status` reaches the network on a cold keyring. */
|
|
99174
|
-
const PROBE_TIMEOUT_MS = 5e3;
|
|
99174
|
+
const PROBE_TIMEOUT_MS$1 = 5e3;
|
|
99175
99175
|
/**
|
|
99176
99176
|
* Runs one probe. A tool that is absent, fails, or hangs is reported the same
|
|
99177
99177
|
* way — `null`, meaning "no answer" — because the discovery result only
|
|
@@ -99183,7 +99183,7 @@ const PROBE_TIMEOUT_MS = 5e3;
|
|
|
99183
99183
|
*/
|
|
99184
99184
|
const probe = (command, args) => promise(async () => {
|
|
99185
99185
|
try {
|
|
99186
|
-
const { stdout, stderr } = await execFileAsync$
|
|
99186
|
+
const { stdout, stderr } = await execFileAsync$6(command, [...args], { timeout: PROBE_TIMEOUT_MS$1 });
|
|
99187
99187
|
const output = `${stdout}\n${stderr}`.trim();
|
|
99188
99188
|
return { output: output.length === 0 ? null : output };
|
|
99189
99189
|
} catch (cause) {
|
|
@@ -100501,7 +100501,7 @@ const EMPTY_PLAN_VALUES = new Set([
|
|
|
100501
100501
|
"none",
|
|
100502
100502
|
"null"
|
|
100503
100503
|
]);
|
|
100504
|
-
function asRecord$
|
|
100504
|
+
function asRecord$11(value) {
|
|
100505
100505
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
100506
100506
|
return value;
|
|
100507
100507
|
}
|
|
@@ -100518,7 +100518,7 @@ function decodeJwtClaims(token) {
|
|
|
100518
100518
|
try {
|
|
100519
100519
|
const decoded = Buffer.from(payload, "base64url");
|
|
100520
100520
|
if (decoded.byteLength > MAX_CLAIMS_BYTES) return void 0;
|
|
100521
|
-
return asRecord$
|
|
100521
|
+
return asRecord$11(JSON.parse(decoded.toString("utf8")));
|
|
100522
100522
|
} catch {
|
|
100523
100523
|
return;
|
|
100524
100524
|
}
|
|
@@ -100540,7 +100540,7 @@ function findPlanType(claims) {
|
|
|
100540
100540
|
const direct = fromRecord(claims);
|
|
100541
100541
|
if (direct) return direct;
|
|
100542
100542
|
for (const value of Object.values(claims)) {
|
|
100543
|
-
const nested = asRecord$
|
|
100543
|
+
const nested = asRecord$11(value);
|
|
100544
100544
|
if (!nested) continue;
|
|
100545
100545
|
const found = fromRecord(nested);
|
|
100546
100546
|
if (found) return found;
|
|
@@ -100557,7 +100557,7 @@ function readCodexAuthIdentity(authJson) {
|
|
|
100557
100557
|
} catch {
|
|
100558
100558
|
return {};
|
|
100559
100559
|
}
|
|
100560
|
-
const claims = decodeJwtClaims(asRecord$
|
|
100560
|
+
const claims = decodeJwtClaims(asRecord$11(asRecord$11(parsed)?.tokens)?.id_token);
|
|
100561
100561
|
if (!claims) return {};
|
|
100562
100562
|
const email = findEmail(claims);
|
|
100563
100563
|
const planType = findPlanType(claims);
|
|
@@ -100720,6 +100720,7 @@ const CLAUDE_IDENTITY_KEYS = new Set([
|
|
|
100720
100720
|
"provider",
|
|
100721
100721
|
"credentialGenerationId"
|
|
100722
100722
|
]);
|
|
100723
|
+
const CLAUDE_IDENTITY_OPTIONAL_KEYS = new Set(["providerAccountIdentity"]);
|
|
100723
100724
|
function isRecord$13(value) {
|
|
100724
100725
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
100725
100726
|
}
|
|
@@ -100744,6 +100745,10 @@ function daysInMonth$1(year, month) {
|
|
|
100744
100745
|
function hasExactKeys$2(value, keys) {
|
|
100745
100746
|
return Object.keys(value).every((key) => keys.has(key)) && Object.keys(value).length === keys.size;
|
|
100746
100747
|
}
|
|
100748
|
+
function hasAllowedKeys(value, required, optional) {
|
|
100749
|
+
const keys = Object.keys(value);
|
|
100750
|
+
return keys.every((key) => required.has(key) || optional.has(key)) && [...required].every((key) => keys.includes(key));
|
|
100751
|
+
}
|
|
100747
100752
|
function parseDeliveryIdentity(value, provider) {
|
|
100748
100753
|
if (!isRecord$13(value)) return null;
|
|
100749
100754
|
if (provider === "openai") return parseCodexDeliveryIdentity(value);
|
|
@@ -100758,12 +100763,25 @@ function parseCodexDeliveryIdentity(value) {
|
|
|
100758
100763
|
providerAccountIdentity: value.providerAccountIdentity
|
|
100759
100764
|
};
|
|
100760
100765
|
}
|
|
100766
|
+
/**
|
|
100767
|
+
* Absent means the writer does not name the Provider Account yet, and the
|
|
100768
|
+
* identity keeps the shape it has always had. Present but malformed means the
|
|
100769
|
+
* identity is not the one CMMD signed, so it is refused the same way a
|
|
100770
|
+
* malformed Codex account identity is.
|
|
100771
|
+
*/
|
|
100772
|
+
function claudeAccountFields(value) {
|
|
100773
|
+
if (value === void 0) return {};
|
|
100774
|
+
if (typeof value !== "string" || !PROVIDER_ACCOUNT_IDENTITY$2.test(value)) return null;
|
|
100775
|
+
return { providerAccountIdentity: value };
|
|
100776
|
+
}
|
|
100761
100777
|
function parseClaudeDeliveryIdentity(value) {
|
|
100762
|
-
|
|
100778
|
+
const account = claudeAccountFields(value.providerAccountIdentity);
|
|
100779
|
+
if (!account || !hasAllowedKeys(value, CLAUDE_IDENTITY_KEYS, CLAUDE_IDENTITY_OPTIONAL_KEYS) || value.kind !== "credential_generation" || value.provider !== "claude" || typeof value.credentialGenerationId !== "string" || !UUID.test(value.credentialGenerationId)) return null;
|
|
100763
100780
|
return {
|
|
100764
100781
|
kind: "credential_generation",
|
|
100765
100782
|
provider: "claude",
|
|
100766
|
-
credentialGenerationId: value.credentialGenerationId
|
|
100783
|
+
credentialGenerationId: value.credentialGenerationId,
|
|
100784
|
+
...account
|
|
100767
100785
|
};
|
|
100768
100786
|
}
|
|
100769
100787
|
function parseProviderRuntimeDeliveryReceipt(value, expected) {
|
|
@@ -100802,10 +100820,49 @@ function readProviderDeliveryBinding(value) {
|
|
|
100802
100820
|
identity
|
|
100803
100821
|
};
|
|
100804
100822
|
}
|
|
100823
|
+
/** The Provider Account a delivery identity names, or null when it names none. */
|
|
100824
|
+
function providerDeliveryAccount(identity) {
|
|
100825
|
+
return identity.kind === "provider_account" ? identity.providerAccountIdentity : identity.providerAccountIdentity ?? null;
|
|
100826
|
+
}
|
|
100827
|
+
function sameDeliveredCredential(left, right) {
|
|
100828
|
+
if (left.kind === "provider_account" && right.kind === "provider_account") return left.providerAccountIdentity === right.providerAccountIdentity;
|
|
100829
|
+
return left.kind === "credential_generation" && right.kind === "credential_generation" && left.credentialGenerationId === right.credentialGenerationId;
|
|
100830
|
+
}
|
|
100831
|
+
/**
|
|
100832
|
+
* Names how one thread's delivery binding moved, which is the question the
|
|
100833
|
+
* delivery layer could not answer while Claude's identity carried no account.
|
|
100834
|
+
*
|
|
100835
|
+
* `account_switch` needs both sides to name an account, the accounts to differ,
|
|
100836
|
+
* the delivered credential to change with them, and the runtime, machine and
|
|
100837
|
+
* epoch to hold. A switch changes the account and the generation together, so
|
|
100838
|
+
* two accounts on one generation would be two accounts sharing a credential,
|
|
100839
|
+
* and a switch that also moved the epoch changed more than the identity. Both
|
|
100840
|
+
* are `unknown`.
|
|
100841
|
+
*
|
|
100842
|
+
* The same delivered credential is `unchanged` whether or not either side names
|
|
100843
|
+
* an account, so a binding recorded before CMMD named the account still resumes
|
|
100844
|
+
* against the credential it ran on. A changed credential where either side
|
|
100845
|
+
* names no account is `unknown` rather than guessed at, which keeps an
|
|
100846
|
+
* account-less binding compared on exactly the fields it was compared on before.
|
|
100847
|
+
*
|
|
100848
|
+
* `providerDeliveryBindingsEqual` asks only whether the answer is `unchanged`.
|
|
100849
|
+
* `permitsProviderAccountTransition` in `provider/providerAccountTransition.ts`
|
|
100850
|
+
* asks whether it is `account_switch`, and is the Codex resume gate's rule
|
|
100851
|
+
* today. #3055 wires the Claude resume gate onto it.
|
|
100852
|
+
*/
|
|
100853
|
+
function classifyProviderDeliveryBindingChange(prior, current) {
|
|
100854
|
+
if (prior.runtimeId !== current.runtimeId || prior.machineId !== current.machineId || prior.identity.kind !== current.identity.kind || prior.identity.provider !== current.identity.provider) return "unknown";
|
|
100855
|
+
const sameEpoch = prior.credentialEpoch === current.credentialEpoch;
|
|
100856
|
+
const sameCredential = sameDeliveredCredential(prior.identity, current.identity);
|
|
100857
|
+
const priorAccount = providerDeliveryAccount(prior.identity);
|
|
100858
|
+
const currentAccount = providerDeliveryAccount(current.identity);
|
|
100859
|
+
if (priorAccount !== null && currentAccount !== null && priorAccount !== currentAccount) return sameEpoch && !sameCredential ? "account_switch" : "unknown";
|
|
100860
|
+
if (sameEpoch && sameCredential) return "unchanged";
|
|
100861
|
+
if (priorAccount === null || currentAccount === null) return "unknown";
|
|
100862
|
+
return "credential_refresh";
|
|
100863
|
+
}
|
|
100805
100864
|
function providerDeliveryBindingsEqual(left, right) {
|
|
100806
|
-
|
|
100807
|
-
if (left.identity.kind === "provider_account" && right.identity.kind === "provider_account") return left.identity.providerAccountIdentity === right.identity.providerAccountIdentity;
|
|
100808
|
-
return left.identity.kind === "credential_generation" && right.identity.kind === "credential_generation" && left.identity.credentialGenerationId === right.identity.credentialGenerationId;
|
|
100865
|
+
return classifyProviderDeliveryBindingChange(left, right) === "unchanged";
|
|
100809
100866
|
}
|
|
100810
100867
|
//#endregion
|
|
100811
100868
|
//#region src/provider/claudeReadinessReason.ts
|
|
@@ -101084,8 +101141,128 @@ const runClaudeLogout = (runtime) => new Promise((resolve, reject) => {
|
|
|
101084
101141
|
});
|
|
101085
101142
|
});
|
|
101086
101143
|
//#endregion
|
|
101144
|
+
//#region src/usage/providerRateLimits.ts
|
|
101145
|
+
/**
|
|
101146
|
+
* `new Date(ms).toISOString()` throws past +-8.64e15 ms, so a `resetsAt` past
|
|
101147
|
+
* the same bound in seconds is refused rather than allowed to throw inside a
|
|
101148
|
+
* decoder whose whole job is to fail closed.
|
|
101149
|
+
*/
|
|
101150
|
+
const MAX_RESETS_AT_SECONDS = 864e10;
|
|
101151
|
+
/**
|
|
101152
|
+
* One reset time in unix seconds as an ISO string, or null.
|
|
101153
|
+
*
|
|
101154
|
+
* Both mappers read reset times through this, so a malformed `resetsAt`
|
|
101155
|
+
* yields the null that means "this observation said nothing" instead of a
|
|
101156
|
+
* RangeError thrown out of a decoder the ingestion layer runs on every Claude
|
|
101157
|
+
* rate-limit event.
|
|
101158
|
+
*/
|
|
101159
|
+
function resetsAtIso(resetsAt) {
|
|
101160
|
+
if (!Number.isFinite(resetsAt) || resetsAt <= 0 || resetsAt > MAX_RESETS_AT_SECONDS) return null;
|
|
101161
|
+
return (/* @__PURE__ */ new Date(resetsAt * 1e3)).toISOString();
|
|
101162
|
+
}
|
|
101163
|
+
/**
|
|
101164
|
+
* The SDK only ever reports ONE window per event. Only `five_hour` and
|
|
101165
|
+
* `seven_day` map to the two windows the Usage page shows; the
|
|
101166
|
+
* per-model/overage variants (seven_day_opus, overage, ...) are ignored here
|
|
101167
|
+
* on purpose rather than silently overwriting the plain 7-day figure.
|
|
101168
|
+
*/
|
|
101169
|
+
function mapClaudeRateLimitInfo(info) {
|
|
101170
|
+
if (typeof info.utilization !== "number" || typeof info.resetsAt !== "number" || !info.status) return null;
|
|
101171
|
+
const kind = info.rateLimitType === "five_hour" ? "fiveHour" : info.rateLimitType === "seven_day" ? "sevenDay" : null;
|
|
101172
|
+
if (!kind) return null;
|
|
101173
|
+
const resetsAt = resetsAtIso(info.resetsAt);
|
|
101174
|
+
if (resetsAt === null) return null;
|
|
101175
|
+
return {
|
|
101176
|
+
kind,
|
|
101177
|
+
window: {
|
|
101178
|
+
usedPercent: Math.round(info.utilization * 100),
|
|
101179
|
+
resetsAt,
|
|
101180
|
+
status: info.status
|
|
101181
|
+
}
|
|
101182
|
+
};
|
|
101183
|
+
}
|
|
101184
|
+
/**
|
|
101185
|
+
* The one place a used-percent becomes a status. `unifiedWindows` carries no
|
|
101186
|
+
* per-window status of its own, and the `/usage` parser derives one the same
|
|
101187
|
+
* way, so both Claude usage sources agree on where "warning" starts.
|
|
101188
|
+
*/
|
|
101189
|
+
function usageStatus(usedPercent) {
|
|
101190
|
+
if (usedPercent >= 100) return "rejected";
|
|
101191
|
+
return usedPercent >= 80 ? "allowed_warning" : "allowed";
|
|
101192
|
+
}
|
|
101193
|
+
/**
|
|
101194
|
+
* One untyped `utilization` as a whole percent, or nothing. The [0, 100] bound
|
|
101195
|
+
* matches `claudeUsageCli.ts`'s own bound on the `/usage` text, which in turn
|
|
101196
|
+
* matches CMMD's parser.
|
|
101197
|
+
*/
|
|
101198
|
+
function readUsedPercent(utilization) {
|
|
101199
|
+
if (typeof utilization !== "number" || !Number.isFinite(utilization)) return null;
|
|
101200
|
+
const usedPercent = Math.round(utilization * 100);
|
|
101201
|
+
return usedPercent < 0 || usedPercent > 100 ? null : usedPercent;
|
|
101202
|
+
}
|
|
101203
|
+
/**
|
|
101204
|
+
* Reads one `unifiedWindows` entry, or nothing. Every field is checked because
|
|
101205
|
+
* none of them are typed: a non-object entry, a utilization that is not a
|
|
101206
|
+
* finite number in [0, 1], or a reset time that is not a positive finite
|
|
101207
|
+
* number of seconds all mean "this observation said nothing about this
|
|
101208
|
+
* window".
|
|
101209
|
+
*/
|
|
101210
|
+
function readUnifiedWindow(raw) {
|
|
101211
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
101212
|
+
const { utilization, resetsAt } = raw;
|
|
101213
|
+
const usedPercent = readUsedPercent(utilization);
|
|
101214
|
+
if (usedPercent === null) return null;
|
|
101215
|
+
if (typeof resetsAt !== "number") return null;
|
|
101216
|
+
const resetsAtIsoValue = resetsAtIso(resetsAt);
|
|
101217
|
+
if (resetsAtIsoValue === null) return null;
|
|
101218
|
+
return {
|
|
101219
|
+
usedPercent,
|
|
101220
|
+
resetsAt: resetsAtIsoValue,
|
|
101221
|
+
status: usageStatus(usedPercent)
|
|
101222
|
+
};
|
|
101223
|
+
}
|
|
101224
|
+
/**
|
|
101225
|
+
* Reads the `unifiedWindows` sub-object of `rate_limit_info` into the two
|
|
101226
|
+
* rolling windows. The SDK rebuilds that sub-object on every turn from the
|
|
101227
|
+
* `anthropic-ratelimit-unified-{5h,7d}-utilization` and `-reset` response
|
|
101228
|
+
* headers.
|
|
101229
|
+
*
|
|
101230
|
+
* The top-level `rateLimitType` names one window per event and carries a
|
|
101231
|
+
* utilization only for `five_hour` or `seven_day`. This sub-object reports
|
|
101232
|
+
* every window it knows on every observation, which makes it the only
|
|
101233
|
+
* SDK-side source that can feed the Usage card without waking a Sprite and
|
|
101234
|
+
* delivering a credential.
|
|
101235
|
+
*
|
|
101236
|
+
* Its `seven_day_overage_included` window is skipped. `/usage` names each model
|
|
101237
|
+
* row after the display name Anthropic's usage API gives that model, one row
|
|
101238
|
+
* per allowlisted model, and the `7d_oi` header behind this window is one
|
|
101239
|
+
* aggregate that carries no name. Any name given to it here would be a guess
|
|
101240
|
+
* that could disagree with the rows the probe writes, so model rows come from
|
|
101241
|
+
* the probe alone.
|
|
101242
|
+
*
|
|
101243
|
+
* It is also undocumented internal surface stripped from the SDK's exported
|
|
101244
|
+
* types, so this decoder fails closed everywhere. It returns null when the
|
|
101245
|
+
* sub-object is absent or is not a plain object, and null when neither window
|
|
101246
|
+
* in it survives validation. A window that fails validation is reported as
|
|
101247
|
+
* null, and an unrecognized key is skipped, never fatal. A caller that gets
|
|
101248
|
+
* null has learned nothing and must not publish, because a partial write could
|
|
101249
|
+
* overwrite a good `/usage` reading with a guess.
|
|
101250
|
+
*/
|
|
101251
|
+
function mapClaudeUnifiedWindows(info) {
|
|
101252
|
+
const raw = info.unifiedWindows;
|
|
101253
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
101254
|
+
const windows = raw;
|
|
101255
|
+
const fiveHour = readUnifiedWindow(windows.five_hour);
|
|
101256
|
+
const sevenDay = readUnifiedWindow(windows.seven_day);
|
|
101257
|
+
if (!fiveHour && !sevenDay) return null;
|
|
101258
|
+
return {
|
|
101259
|
+
fiveHour,
|
|
101260
|
+
sevenDay
|
|
101261
|
+
};
|
|
101262
|
+
}
|
|
101263
|
+
//#endregion
|
|
101087
101264
|
//#region src/usage/claudeUsageCli.ts
|
|
101088
|
-
const execFileAsync$
|
|
101265
|
+
const execFileAsync$5 = promisify$1(execFile);
|
|
101089
101266
|
const USAGE_ARGS = [
|
|
101090
101267
|
"--safe-mode",
|
|
101091
101268
|
"--no-session-persistence",
|
|
@@ -101109,7 +101286,7 @@ const MONTH_BY_NAME = {
|
|
|
101109
101286
|
Dec: 11
|
|
101110
101287
|
};
|
|
101111
101288
|
async function runClaudeUsageCommand(binaryPath, args, claudeConfigDir) {
|
|
101112
|
-
const { stdout } = await execFileAsync$
|
|
101289
|
+
const { stdout } = await execFileAsync$5(binaryPath, [...args], {
|
|
101113
101290
|
env: {
|
|
101114
101291
|
...process.env,
|
|
101115
101292
|
TZ: "UTC",
|
|
@@ -101184,10 +101361,6 @@ function parseResetAt(value, now) {
|
|
|
101184
101361
|
});
|
|
101185
101362
|
return resetAt === null || Number.isNaN(resetAt.getTime()) ? null : resetAt.toISOString();
|
|
101186
101363
|
}
|
|
101187
|
-
function usageStatus(usedPercent) {
|
|
101188
|
-
if (usedPercent >= 100) return "rejected";
|
|
101189
|
-
return usedPercent >= 80 ? "allowed_warning" : "allowed";
|
|
101190
|
-
}
|
|
101191
101364
|
function parseUsageLine(output, label, now) {
|
|
101192
101365
|
const escapedLabel = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
101193
101366
|
const match = new RegExp(`^${escapedLabel}: (\\d+(?:\\.\\d+)?)% used(?: · resets (.+))?$`, "m").exec(output);
|
|
@@ -101256,6 +101429,29 @@ async function fetchClaudeUsageViaOfficialClient(input) {
|
|
|
101256
101429
|
};
|
|
101257
101430
|
}
|
|
101258
101431
|
//#endregion
|
|
101432
|
+
//#region src/usage/claudeUsageTelemetryRequest.ts
|
|
101433
|
+
/** The owner-session route for a Claude usage reading. */
|
|
101434
|
+
const CLAUDE_USAGE_TELEMETRY_PATH = "/api/forge/context/providers/usage/claude/telemetry";
|
|
101435
|
+
/**
|
|
101436
|
+
* Builds the body for either lane.
|
|
101437
|
+
*
|
|
101438
|
+
* `modelWindows` is sent only when the reading carries it. CMMD keeps the
|
|
101439
|
+
* stored model rows when the key is absent, so a reading that cannot name
|
|
101440
|
+
* model rows the way `/usage` does leaves the key out.
|
|
101441
|
+
*/
|
|
101442
|
+
function claudeUsageTelemetryBody(input) {
|
|
101443
|
+
return {
|
|
101444
|
+
providerAccountId: input.attribution.providerAccountId,
|
|
101445
|
+
credentialGenerationId: input.attribution.credentialGenerationId,
|
|
101446
|
+
...input.account,
|
|
101447
|
+
source: input.source,
|
|
101448
|
+
fiveHour: input.windows.fiveHour,
|
|
101449
|
+
sevenDay: input.windows.sevenDay,
|
|
101450
|
+
...input.windows.modelWindows !== void 0 ? { modelWindows: input.windows.modelWindows } : {},
|
|
101451
|
+
observedAt: input.observedAt
|
|
101452
|
+
};
|
|
101453
|
+
}
|
|
101454
|
+
//#endregion
|
|
101259
101455
|
//#region src/cmmd/syncClaudeCredential.ts
|
|
101260
101456
|
/**
|
|
101261
101457
|
* Card 1359 (harvest path A, Claude) — publish a personal Claude subscription
|
|
@@ -101271,7 +101467,6 @@ async function fetchClaudeUsageViaOfficialClient(input) {
|
|
|
101271
101467
|
const USER_CLAUDE_CREDENTIAL_PATH = "/api/forge/context/providers/claude/credential";
|
|
101272
101468
|
const USER_CLAUDE_LINKED_ACCOUNT_PATH = "/api/forge/context/providers/claude/accounts";
|
|
101273
101469
|
const AUTH_RUNTIME_CLAUDE_CREDENTIAL_PATH = "/api/forge/context/auth-runtime-claude-credential";
|
|
101274
|
-
const USER_CLAUDE_USAGE_PATH = "/api/forge/context/providers/usage/claude/telemetry";
|
|
101275
101470
|
const AUTH_RUNTIME_CLAUDE_USAGE_PATH = "/api/forge/context/auth-runtime-claude-credential/usage";
|
|
101276
101471
|
/** Path to the local Claude credentials.json, mirroring Claude Code's resolution. */
|
|
101277
101472
|
function resolveLocalClaudeCredentialPath(homePath) {
|
|
@@ -101300,7 +101495,7 @@ function resolveLocalClaudeCredentialPath(homePath) {
|
|
|
101300
101495
|
* it reaches — this is the first gate and the only one near the user, who is
|
|
101301
101496
|
* standing in front of the sign-in and can simply try again.
|
|
101302
101497
|
*
|
|
101303
|
-
* The floor is ONE token, not both,
|
|
101498
|
+
* The floor is ONE token, not both, because an
|
|
101304
101499
|
* expired or blank access token beside a live refresh token is the normal
|
|
101305
101500
|
* resting state of a rotating login, which Claude Code refreshes silently on
|
|
101306
101501
|
* use. Demanding both would reject a perfectly recoverable credential and break
|
|
@@ -101378,10 +101573,10 @@ function extractClaudeAccountMetadata(credentialJson) {
|
|
|
101378
101573
|
return {};
|
|
101379
101574
|
}
|
|
101380
101575
|
}
|
|
101381
|
-
const execFileAsync$
|
|
101576
|
+
const execFileAsync$4 = promisify$1(execFile);
|
|
101382
101577
|
async function loadClaudeAccountMetadataFromCli(homePath) {
|
|
101383
101578
|
try {
|
|
101384
|
-
const { stdout } = await execFileAsync$
|
|
101579
|
+
const { stdout } = await execFileAsync$4(resolveClaudeBinaryPath("claude"), ["auth", "status"], {
|
|
101385
101580
|
timeout: 2e4,
|
|
101386
101581
|
maxBuffer: 1024 * 1024,
|
|
101387
101582
|
env: {
|
|
@@ -101477,18 +101672,20 @@ async function publishClaudeUsageAfterSync(input) {
|
|
|
101477
101672
|
binaryPath: resolveClaudeBinaryPath("claude"),
|
|
101478
101673
|
...input.homePath ? { claudeConfigDir: input.homePath } : {}
|
|
101479
101674
|
});
|
|
101480
|
-
await publishUsageBody(input.auth.jwt, input.auth.path === AUTH_RUNTIME_CLAUDE_CREDENTIAL_PATH ? AUTH_RUNTIME_CLAUDE_USAGE_PATH :
|
|
101481
|
-
|
|
101482
|
-
|
|
101483
|
-
|
|
101484
|
-
|
|
101485
|
-
|
|
101675
|
+
await publishUsageBody(input.auth.jwt, input.auth.path === AUTH_RUNTIME_CLAUDE_CREDENTIAL_PATH ? AUTH_RUNTIME_CLAUDE_USAGE_PATH : CLAUDE_USAGE_TELEMETRY_PATH, claudeUsageTelemetryBody({
|
|
101676
|
+
attribution: {
|
|
101677
|
+
providerAccountId: input.providerAccountId,
|
|
101678
|
+
credentialGenerationId: input.credentialGenerationId
|
|
101679
|
+
},
|
|
101680
|
+
account: {
|
|
101681
|
+
accountEmail: input.accountEmail,
|
|
101682
|
+
accountDisplayName: input.accountDisplayName,
|
|
101683
|
+
planType: input.planType
|
|
101684
|
+
},
|
|
101486
101685
|
source: "active_poll",
|
|
101487
|
-
|
|
101488
|
-
sevenDay: usage.sevenDay,
|
|
101489
|
-
modelWindows: usage.modelWindows,
|
|
101686
|
+
windows: usage,
|
|
101490
101687
|
observedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
101491
|
-
});
|
|
101688
|
+
}));
|
|
101492
101689
|
return null;
|
|
101493
101690
|
} catch (cause) {
|
|
101494
101691
|
return { cause };
|
|
@@ -149004,11 +149201,11 @@ function readObject(value) {
|
|
|
149004
149201
|
function readArray(value) {
|
|
149005
149202
|
return Array.isArray(value) ? value : void 0;
|
|
149006
149203
|
}
|
|
149007
|
-
function readString$
|
|
149204
|
+
function readString$4(value) {
|
|
149008
149205
|
return typeof value === "string" ? value : void 0;
|
|
149009
149206
|
}
|
|
149010
149207
|
function nonEmptyTrimmed$1(value) {
|
|
149011
|
-
const candidate = readString$
|
|
149208
|
+
const candidate = readString$4(value)?.trim();
|
|
149012
149209
|
return candidate ? candidate : void 0;
|
|
149013
149210
|
}
|
|
149014
149211
|
function readBoolean$2(value) {
|
|
@@ -149790,7 +149987,7 @@ function issueCmmdLocalUserProviderEgressCredential(input) {
|
|
|
149790
149987
|
});
|
|
149791
149988
|
}
|
|
149792
149989
|
//#endregion
|
|
149793
|
-
//#region src/provider/
|
|
149990
|
+
//#region src/provider/providerAccountTransition.ts
|
|
149794
149991
|
function explicitProviderAccountTransition(fromAccountId, toAccountId) {
|
|
149795
149992
|
if (!fromAccountId || !toAccountId || fromAccountId === toAccountId) return void 0;
|
|
149796
149993
|
return {
|
|
@@ -149798,13 +149995,20 @@ function explicitProviderAccountTransition(fromAccountId, toAccountId) {
|
|
|
149798
149995
|
toAccountId
|
|
149799
149996
|
};
|
|
149800
149997
|
}
|
|
149801
|
-
|
|
149998
|
+
/**
|
|
149999
|
+
* Whether a thread may move from `prior` to `current` because the person asked
|
|
150000
|
+
* for another Provider Account. The credential broker authorizes the selected
|
|
150001
|
+
* account, so only the identity may change, which is what `account_switch`
|
|
150002
|
+
* means.
|
|
150003
|
+
*
|
|
150004
|
+
* Nothing in the rule is specific to a provider, but the Codex resume gate in
|
|
150005
|
+
* `codexAppServerManager.ts` is its only caller. The Claude half is parked
|
|
150006
|
+
* until #3055 wires the Claude resume gate onto it.
|
|
150007
|
+
*/
|
|
150008
|
+
function permitsProviderAccountTransition(input) {
|
|
149802
150009
|
const { transition, requestedAccountId, prior, current } = input;
|
|
149803
|
-
if (!transition || transition.fromAccountId === transition.toAccountId || transition.toAccountId !== requestedAccountId
|
|
149804
|
-
return
|
|
149805
|
-
...prior,
|
|
149806
|
-
identity: current.identity
|
|
149807
|
-
}, current);
|
|
150010
|
+
if (!transition || transition.fromAccountId === transition.toAccountId || transition.toAccountId !== requestedAccountId) return false;
|
|
150011
|
+
return classifyProviderDeliveryBindingChange(prior, current) === "account_switch";
|
|
149808
150012
|
}
|
|
149809
150013
|
//#endregion
|
|
149810
150014
|
//#region src/providerAuth/codexLocalAccountIdentity.ts
|
|
@@ -150304,7 +150508,7 @@ var CodexAppServerManager = class extends EventEmitter$1 {
|
|
|
150304
150508
|
const rawPriorDeliveryBinding = input.resumeCursor.providerDeliveryBinding;
|
|
150305
150509
|
const priorDeliveryBinding = readProviderDeliveryBinding(rawPriorDeliveryBinding);
|
|
150306
150510
|
if (rawPriorDeliveryBinding !== void 0 && rawPriorDeliveryBinding !== null && !priorDeliveryBinding) throw new Error("Existing Codex history cannot resume because its persisted delivered provider account binding is unreadable.");
|
|
150307
|
-
if (priorDeliveryBinding && !providerDeliveryBindingsEqual(priorDeliveryBinding, currentDeliveryBinding) && !
|
|
150511
|
+
if (priorDeliveryBinding && !providerDeliveryBindingsEqual(priorDeliveryBinding, currentDeliveryBinding) && !permitsProviderAccountTransition({
|
|
150308
150512
|
transition: input.accountTransition,
|
|
150309
150513
|
requestedAccountId: input.providerAccountId,
|
|
150310
150514
|
prior: priorDeliveryBinding,
|
|
@@ -189006,7 +189210,7 @@ var Vce = {
|
|
|
189006
189210
|
function Ve() {
|
|
189007
189211
|
return Vce;
|
|
189008
189212
|
}
|
|
189009
|
-
var Us =
|
|
189213
|
+
var Us = $U.homedir(), lk = $U.tmpdir(), { env: Vc } = process$1, Yce = (e) => {
|
|
189010
189214
|
let t = path.join(Us, "Library");
|
|
189011
189215
|
return {
|
|
189012
189216
|
data: path.join(t, "Application Support", e),
|
|
@@ -199206,12 +199410,12 @@ function qZ(e, t) {
|
|
|
199206
199410
|
}
|
|
199207
199411
|
return n;
|
|
199208
199412
|
}
|
|
199209
|
-
var os$
|
|
199413
|
+
var os$1 = "https://code.claude.com/docs/en", kKe = [
|
|
199210
199414
|
{
|
|
199211
199415
|
matches: (e) => e.path === "permissions.defaultMode" && e.code === "invalid_value",
|
|
199212
199416
|
tip: {
|
|
199213
199417
|
suggestion: "Valid modes: \"acceptEdits\" (ask before file changes), \"plan\" (analysis only), \"bypassPermissions\" (auto-accept all), or \"default\" (standard behavior)",
|
|
199214
|
-
docLink: `${os$
|
|
199418
|
+
docLink: `${os$1}/iam#permission-modes`
|
|
199215
199419
|
}
|
|
199216
199420
|
},
|
|
199217
199421
|
{
|
|
@@ -199226,7 +199430,7 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
|
|
|
199226
199430
|
matches: (e) => e.path.startsWith("env.") && e.code === "invalid_type",
|
|
199227
199431
|
tip: {
|
|
199228
199432
|
suggestion: "Environment variables must be strings. Wrap numbers and booleans in quotes. Example: \"DEBUG\": \"true\", \"PORT\": \"3000\"",
|
|
199229
|
-
docLink: `${os$
|
|
199433
|
+
docLink: `${os$1}/settings#environment-variables`
|
|
199230
199434
|
}
|
|
199231
199435
|
},
|
|
199232
199436
|
{
|
|
@@ -199237,14 +199441,14 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
|
|
|
199237
199441
|
matches: (e) => e.path.startsWith("hooks.") && e.code === "invalid_key",
|
|
199238
199442
|
tip: {
|
|
199239
199443
|
suggestion: "Not a recognized hook event. Common events: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, SessionEnd, Stop. Check spelling and capitalization.",
|
|
199240
|
-
docLink: `${os$
|
|
199444
|
+
docLink: `${os$1}/hooks`
|
|
199241
199445
|
}
|
|
199242
199446
|
},
|
|
199243
199447
|
{
|
|
199244
199448
|
matches: (e) => /\.hooks\.\d+\.command$/.test(e.path) && e.code === "invalid_type" && e.received === "undefined",
|
|
199245
199449
|
tip: {
|
|
199246
199450
|
suggestion: "Command hooks require `command`. For exec form (no shell), set `command` to the executable and `args` to its arguments: {\"type\": \"command\", \"command\": \"echo\", \"args\": [\"hi\"]}. For shell form, set `command` to the full shell string: {\"type\": \"command\", \"command\": \"echo hi\"}.",
|
|
199247
|
-
docLink: `${os$
|
|
199451
|
+
docLink: `${os$1}/hooks#exec-form-and-shell-form`
|
|
199248
199452
|
}
|
|
199249
199453
|
},
|
|
199250
199454
|
{
|
|
@@ -199259,7 +199463,7 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
|
|
|
199259
199463
|
matches: (e) => e.code === "unrecognized_keys",
|
|
199260
199464
|
tip: {
|
|
199261
199465
|
suggestion: "Check for typos or refer to the documentation for valid fields",
|
|
199262
|
-
docLink: `${os$
|
|
199466
|
+
docLink: `${os$1}/settings`
|
|
199263
199467
|
}
|
|
199264
199468
|
},
|
|
199265
199469
|
{
|
|
@@ -199274,13 +199478,13 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
|
|
|
199274
199478
|
matches: (e) => e.path === "permissions.additionalDirectories" && e.code === "invalid_type",
|
|
199275
199479
|
tip: {
|
|
199276
199480
|
suggestion: "Must be an array of directory paths. Example: [\"~/projects\", \"/tmp/workspace\"]. You can also use --add-dir flag or /add-dir command",
|
|
199277
|
-
docLink: `${os$
|
|
199481
|
+
docLink: `${os$1}/iam#working-directories`
|
|
199278
199482
|
}
|
|
199279
199483
|
}
|
|
199280
199484
|
], AKe = {
|
|
199281
|
-
permissions: `${os$
|
|
199282
|
-
env: `${os$
|
|
199283
|
-
hooks: `${os$
|
|
199485
|
+
permissions: `${os$1}/iam#configuring-permissions`,
|
|
199486
|
+
env: `${os$1}/settings#environment-variables`,
|
|
199487
|
+
hooks: `${os$1}/hooks`
|
|
199284
199488
|
};
|
|
199285
199489
|
function GZ(e) {
|
|
199286
199490
|
let t = kKe.find((r) => r.matches(e));
|
|
@@ -206523,7 +206727,7 @@ function createCodexBrowserDynamicTools() {
|
|
|
206523
206727
|
function readArgs$3(value) {
|
|
206524
206728
|
return value && typeof value === "object" ? value : {};
|
|
206525
206729
|
}
|
|
206526
|
-
function readString$
|
|
206730
|
+
function readString$3(args, key) {
|
|
206527
206731
|
const value = args[key];
|
|
206528
206732
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
206529
206733
|
}
|
|
@@ -206552,27 +206756,27 @@ function createCodexBrowserDynamicToolHandler(browserManager, options = {}) {
|
|
|
206552
206756
|
const toolScope = options.environmentId === void 0 ? {} : { environmentId: options.environmentId };
|
|
206553
206757
|
const handlers = makeBrowserHandlers(browserManager, ThreadId.make(input.threadId), toolScope);
|
|
206554
206758
|
const args = readArgs$3(input.arguments);
|
|
206555
|
-
const tabId = readString$
|
|
206759
|
+
const tabId = readString$3(args, "tabId");
|
|
206556
206760
|
switch (input.tool) {
|
|
206557
206761
|
case "forge_browser_list_sessions": return toCodexDynamicToolResult$2(await handlers.listSessions());
|
|
206558
206762
|
case "forge_browser_name_session": {
|
|
206559
|
-
const name = readString$
|
|
206763
|
+
const name = readString$3(args, "name");
|
|
206560
206764
|
if (!name) throw new Error("forge_browser_name_session requires a name.");
|
|
206561
206765
|
return toCodexDynamicToolResult$2(await handlers.nameSession(name));
|
|
206562
206766
|
}
|
|
206563
206767
|
case "forge_browser_set_visibility": {
|
|
206564
|
-
const visibility = readString$
|
|
206768
|
+
const visibility = readString$3(args, "visibility");
|
|
206565
206769
|
if (visibility !== "hidden" && visibility !== "visible") throw new Error("forge_browser_set_visibility requires visibility hidden or visible.");
|
|
206566
206770
|
return toCodexDynamicToolResult$2(await handlers.setVisibility(visibility));
|
|
206567
206771
|
}
|
|
206568
206772
|
case "forge_browser_close_session":
|
|
206569
206773
|
case "forge_browser_reset_session": return toCodexDynamicToolResult$2(await handlers.closeSession());
|
|
206570
206774
|
case "forge_browser_navigate": {
|
|
206571
|
-
const url = readString$
|
|
206775
|
+
const url = readString$3(args, "url");
|
|
206572
206776
|
if (!url) throw new Error("forge_browser_navigate requires a URL.");
|
|
206573
206777
|
return toCodexDynamicToolResult$2(await handlers.navigate(url, tabId));
|
|
206574
206778
|
}
|
|
206575
|
-
case "forge_browser_new_tab": return toCodexDynamicToolResult$2(await handlers.newTab(readString$
|
|
206779
|
+
case "forge_browser_new_tab": return toCodexDynamicToolResult$2(await handlers.newTab(readString$3(args, "url")));
|
|
206576
206780
|
case "forge_browser_close_tab":
|
|
206577
206781
|
if (!tabId) throw new Error("forge_browser_close_tab requires a tabId.");
|
|
206578
206782
|
return toCodexDynamicToolResult$2(await handlers.closeTab(tabId));
|
|
@@ -206583,33 +206787,33 @@ function createCodexBrowserDynamicToolHandler(browserManager, options = {}) {
|
|
|
206583
206787
|
case "forge_browser_go_forward": return toCodexDynamicToolResult$2(await handlers.goForward(tabId));
|
|
206584
206788
|
case "forge_browser_reload": return toCodexDynamicToolResult$2(await handlers.reload(tabId));
|
|
206585
206789
|
case "forge_browser_click": {
|
|
206586
|
-
const selector = readString$
|
|
206790
|
+
const selector = readString$3(args, "selector");
|
|
206587
206791
|
if (!selector) throw new Error("forge_browser_click requires a selector.");
|
|
206588
206792
|
return toCodexDynamicToolResult$2(await handlers.click(selector, tabId));
|
|
206589
206793
|
}
|
|
206590
206794
|
case "forge_browser_click_point": {
|
|
206591
206795
|
const x = readNumber$2(args, "x");
|
|
206592
206796
|
const y = readNumber$2(args, "y");
|
|
206593
|
-
const button = readString$
|
|
206797
|
+
const button = readString$3(args, "button");
|
|
206594
206798
|
const clickCount = readNumber$2(args, "clickCount");
|
|
206595
206799
|
if (x === void 0 || y === void 0) throw new Error("forge_browser_click_point requires x and y.");
|
|
206596
206800
|
if (button !== void 0 && button !== "left" && button !== "middle" && button !== "right") throw new Error("forge_browser_click_point requires button left, middle, or right.");
|
|
206597
206801
|
return toCodexDynamicToolResult$2(await handlers.clickPoint(x, y, button, clickCount, tabId));
|
|
206598
206802
|
}
|
|
206599
206803
|
case "forge_browser_type": {
|
|
206600
|
-
const selector = readString$
|
|
206804
|
+
const selector = readString$3(args, "selector");
|
|
206601
206805
|
if (!selector) throw new Error("forge_browser_type requires a selector.");
|
|
206602
|
-
const text = readString$
|
|
206806
|
+
const text = readString$3(args, "text");
|
|
206603
206807
|
if (text === void 0) throw new Error("forge_browser_type requires text.");
|
|
206604
206808
|
return toCodexDynamicToolResult$2(await handlers.type(selector, text, readBoolean$1(args, "append"), tabId));
|
|
206605
206809
|
}
|
|
206606
206810
|
case "forge_browser_type_text": {
|
|
206607
|
-
const text = readString$
|
|
206811
|
+
const text = readString$3(args, "text");
|
|
206608
206812
|
if (text === void 0) throw new Error("forge_browser_type_text requires text.");
|
|
206609
206813
|
return toCodexDynamicToolResult$2(await handlers.typeText(text, readBoolean$1(args, "pressEnter"), tabId));
|
|
206610
206814
|
}
|
|
206611
206815
|
case "forge_browser_press_key": {
|
|
206612
|
-
const key = readString$
|
|
206816
|
+
const key = readString$3(args, "key");
|
|
206613
206817
|
if (!key) throw new Error("forge_browser_press_key requires a key.");
|
|
206614
206818
|
return toCodexDynamicToolResult$2(await handlers.pressKey(key, tabId));
|
|
206615
206819
|
}
|
|
@@ -206923,7 +207127,7 @@ function createCodexComputerDynamicToolHandler(computerManager) {
|
|
|
206923
207127
|
function readArgs$1(value) {
|
|
206924
207128
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
206925
207129
|
}
|
|
206926
|
-
function readString$
|
|
207130
|
+
function readString$2(args, key) {
|
|
206927
207131
|
const value = args[key];
|
|
206928
207132
|
return typeof value === "string" ? value : void 0;
|
|
206929
207133
|
}
|
|
@@ -206943,7 +207147,7 @@ function readStringArray(args, key) {
|
|
|
206943
207147
|
return value.filter((entry) => typeof entry === "string");
|
|
206944
207148
|
}
|
|
206945
207149
|
function readRequiredString(args, key, toolName) {
|
|
206946
|
-
const value = readString$
|
|
207150
|
+
const value = readString$2(args, key)?.trim();
|
|
206947
207151
|
if (!value) throw new Error(`${toolName} requires ${key}.`);
|
|
206948
207152
|
return value;
|
|
206949
207153
|
}
|
|
@@ -206978,7 +207182,7 @@ async function runGitHubLifecycleTool(toolName, context) {
|
|
|
206978
207182
|
action,
|
|
206979
207183
|
args: lifecycleArgs,
|
|
206980
207184
|
mode
|
|
206981
|
-
}), mode === "write" ? readString$
|
|
207185
|
+
}), mode === "write" ? readString$2(args, "confirmationId") : void 0);
|
|
206982
207186
|
}
|
|
206983
207187
|
//#endregion
|
|
206984
207188
|
//#region src/github/GitHubCodexPullRequestTools.ts
|
|
@@ -206988,8 +207192,8 @@ async function runGitHubPullRequestTool(toolName, context) {
|
|
|
206988
207192
|
case "forge_github_create_pull_request": {
|
|
206989
207193
|
const cwd = readRequiredString(args, "cwd", toolName);
|
|
206990
207194
|
const title = readRequiredString(args, "title", toolName);
|
|
206991
|
-
const body = readString$
|
|
206992
|
-
const headBranch = readString$
|
|
207195
|
+
const body = readString$2(args, "body");
|
|
207196
|
+
const headBranch = readString$2(args, "headBranch")?.trim();
|
|
206993
207197
|
if (body === void 0) throw new Error("forge_github_create_pull_request requires body.");
|
|
206994
207198
|
const request = {
|
|
206995
207199
|
cwd,
|
|
@@ -206998,7 +207202,7 @@ async function runGitHubPullRequestTool(toolName, context) {
|
|
|
206998
207202
|
baseBranch: readRequiredString(args, "baseBranch", toolName),
|
|
206999
207203
|
...headBranch ? { headBranch } : {}
|
|
207000
207204
|
};
|
|
207001
|
-
return runTool(request, () => broker.createPullRequest(auth, request), readString$
|
|
207205
|
+
return runTool(request, () => broker.createPullRequest(auth, request), readString$2(args, "confirmationId"));
|
|
207002
207206
|
}
|
|
207003
207207
|
case "forge_github_get_pull_request": {
|
|
207004
207208
|
const cwd = readRequiredString(args, "cwd", toolName);
|
|
@@ -207020,29 +207224,29 @@ function runStatus$1({ auth, broker, runTool }) {
|
|
|
207020
207224
|
return runTool({}, () => broker.status(auth));
|
|
207021
207225
|
}
|
|
207022
207226
|
function runListRepositories({ args, auth, broker, runTool }) {
|
|
207023
|
-
const search = readString$
|
|
207227
|
+
const search = readString$2(args, "search");
|
|
207024
207228
|
return runTool({ search: search ?? null }, () => broker.listRepositories(auth, { search }));
|
|
207025
207229
|
}
|
|
207026
207230
|
function runCloneRepository({ args, auth, broker, runTool }) {
|
|
207027
207231
|
const toolName = "forge_github_clone_repository";
|
|
207028
207232
|
const repoFullName = readRequiredString(args, "repoFullName", toolName);
|
|
207029
207233
|
const destination = readRequiredString(args, "destination", toolName);
|
|
207030
|
-
const branch = readString$
|
|
207234
|
+
const branch = readString$2(args, "branch");
|
|
207031
207235
|
const request = {
|
|
207032
207236
|
repoFullName,
|
|
207033
207237
|
destination,
|
|
207034
207238
|
...branch ? { branch } : {}
|
|
207035
207239
|
};
|
|
207036
|
-
return runTool(request, () => broker.cloneRepository(auth, request), readString$
|
|
207240
|
+
return runTool(request, () => broker.cloneRepository(auth, request), readString$2(args, "confirmationId"));
|
|
207037
207241
|
}
|
|
207038
207242
|
function runCreateRepository({ args, auth, broker, runTool }) {
|
|
207039
207243
|
const toolName = "forge_github_create_repository";
|
|
207040
207244
|
const owner = readRequiredString(args, "owner", toolName);
|
|
207041
207245
|
const name = readRequiredString(args, "name", toolName);
|
|
207042
|
-
const visibility = readString$
|
|
207246
|
+
const visibility = readString$2(args, "visibility");
|
|
207043
207247
|
if (visibility !== void 0 && visibility !== "private" && visibility !== "public") throw new Error("forge_github_create_repository visibility must be private or public.");
|
|
207044
207248
|
const repositoryVisibility = visibility;
|
|
207045
|
-
const description = readString$
|
|
207249
|
+
const description = readString$2(args, "description");
|
|
207046
207250
|
const request = {
|
|
207047
207251
|
owner,
|
|
207048
207252
|
name,
|
|
@@ -207050,32 +207254,32 @@ function runCreateRepository({ args, auth, broker, runTool }) {
|
|
|
207050
207254
|
...repositoryVisibility ? { visibility: repositoryVisibility } : {},
|
|
207051
207255
|
...description !== void 0 ? { description } : {}
|
|
207052
207256
|
};
|
|
207053
|
-
return runTool(request, () => broker.createRepository(auth, request), readString$
|
|
207257
|
+
return runTool(request, () => broker.createRepository(auth, request), readString$2(args, "confirmationId"));
|
|
207054
207258
|
}
|
|
207055
207259
|
function runLinkProjectRepository({ args, auth, broker, runTool }) {
|
|
207056
207260
|
const toolName = "forge_github_link_project_repository";
|
|
207057
207261
|
const cwd = readRequiredString(args, "cwd", toolName);
|
|
207058
207262
|
const repoFullName = readRequiredString(args, "repoFullName", toolName);
|
|
207059
|
-
const branch = readString$
|
|
207263
|
+
const branch = readString$2(args, "branch");
|
|
207060
207264
|
const request = {
|
|
207061
207265
|
cwd,
|
|
207062
207266
|
repoFullName,
|
|
207063
207267
|
...branch ? { branch } : {}
|
|
207064
207268
|
};
|
|
207065
|
-
return runTool(request, () => broker.linkProjectRepository(auth, request), readString$
|
|
207269
|
+
return runTool(request, () => broker.linkProjectRepository(auth, request), readString$2(args, "confirmationId"));
|
|
207066
207270
|
}
|
|
207067
207271
|
function runPushBranch({ args, auth, broker, runTool }) {
|
|
207068
207272
|
const cwd = readRequiredString(args, "cwd", "forge_github_push_branch");
|
|
207069
|
-
const branch = readString$
|
|
207273
|
+
const branch = readString$2(args, "branch");
|
|
207070
207274
|
const request = {
|
|
207071
207275
|
cwd,
|
|
207072
207276
|
...branch ? { branch } : {}
|
|
207073
207277
|
};
|
|
207074
|
-
return runTool(request, () => broker.pushProjectBranch(auth, request), readString$
|
|
207278
|
+
return runTool(request, () => broker.pushProjectBranch(auth, request), readString$2(args, "confirmationId"));
|
|
207075
207279
|
}
|
|
207076
207280
|
function runRefreshCredentials({ args, auth, broker, runTool }) {
|
|
207077
207281
|
const request = { cwd: readRequiredString(args, "cwd", "forge_github_refresh_credentials") };
|
|
207078
|
-
return runTool(request, () => broker.refreshProjectCredentials(auth, request), readString$
|
|
207282
|
+
return runTool(request, () => broker.refreshProjectCredentials(auth, request), readString$2(args, "confirmationId"));
|
|
207079
207283
|
}
|
|
207080
207284
|
const repositoryToolHandlers = {
|
|
207081
207285
|
forge_github_status: runStatus$1,
|
|
@@ -207113,7 +207317,7 @@ async function runGitHubWorkflowTool(toolName, context) {
|
|
|
207113
207317
|
}
|
|
207114
207318
|
function listWorkflowRuns({ args, auth, broker, runTool }) {
|
|
207115
207319
|
const cwd = readRequiredString(args, "cwd", "forge_github_list_workflow_runs");
|
|
207116
|
-
const branch = readString$
|
|
207320
|
+
const branch = readString$2(args, "branch")?.trim();
|
|
207117
207321
|
const limit = readNumber$1(args, "limit");
|
|
207118
207322
|
const request = {
|
|
207119
207323
|
cwd,
|
|
@@ -207132,7 +207336,7 @@ function watchWorkflowRun({ args, auth, broker, runTool }) {
|
|
|
207132
207336
|
}
|
|
207133
207337
|
function getWorkflowRunLogs({ args, auth, broker, runTool }) {
|
|
207134
207338
|
const { cwd, runId } = readWorkflowRunRequest(args, "forge_github_get_workflow_run_logs");
|
|
207135
|
-
const jobId = readString$
|
|
207339
|
+
const jobId = readString$2(args, "jobId")?.trim();
|
|
207136
207340
|
const request = {
|
|
207137
207341
|
cwd,
|
|
207138
207342
|
runId,
|
|
@@ -207142,16 +207346,16 @@ function getWorkflowRunLogs({ args, auth, broker, runTool }) {
|
|
|
207142
207346
|
}
|
|
207143
207347
|
function downloadWorkflowArtifacts({ args, auth, broker, runTool }) {
|
|
207144
207348
|
const cwd = readRequiredString(args, "cwd", "forge_github_download_workflow_artifacts");
|
|
207145
|
-
const runId = readString$
|
|
207146
|
-
const artifactName = readString$
|
|
207147
|
-
const destinationDir = readString$
|
|
207349
|
+
const runId = readString$2(args, "runId")?.trim();
|
|
207350
|
+
const artifactName = readString$2(args, "artifactName")?.trim();
|
|
207351
|
+
const destinationDir = readString$2(args, "destinationDir")?.trim();
|
|
207148
207352
|
const request = {
|
|
207149
207353
|
cwd,
|
|
207150
207354
|
...runId ? { runId } : {},
|
|
207151
207355
|
...artifactName ? { artifactName } : {},
|
|
207152
207356
|
...destinationDir ? { destinationDir } : {}
|
|
207153
207357
|
};
|
|
207154
|
-
return runTool(request, () => broker.downloadWorkflowArtifacts(auth, request), readString$
|
|
207358
|
+
return runTool(request, () => broker.downloadWorkflowArtifacts(auth, request), readString$2(args, "confirmationId"));
|
|
207155
207359
|
}
|
|
207156
207360
|
function listWorkflows({ args, auth, broker, runTool }) {
|
|
207157
207361
|
const cwd = readRequiredString(args, "cwd", "forge_github_list_workflows");
|
|
@@ -207170,7 +207374,7 @@ function getWorkflow({ args, auth, broker, runTool }) {
|
|
|
207170
207374
|
}
|
|
207171
207375
|
function runWorkflow({ args, auth, broker, runTool }) {
|
|
207172
207376
|
const { cwd, workflow } = readWorkflowRequest(args, "forge_github_run_workflow");
|
|
207173
|
-
const ref = readString$
|
|
207377
|
+
const ref = readString$2(args, "ref")?.trim();
|
|
207174
207378
|
const fields = readStringRecord(args, "fields");
|
|
207175
207379
|
const request = {
|
|
207176
207380
|
cwd,
|
|
@@ -207178,7 +207382,7 @@ function runWorkflow({ args, auth, broker, runTool }) {
|
|
|
207178
207382
|
...ref ? { ref } : {},
|
|
207179
207383
|
...fields && Object.keys(fields).length > 0 ? { fields } : {}
|
|
207180
207384
|
};
|
|
207181
|
-
return runTool(request, () => broker.runWorkflow(auth, request), readString$
|
|
207385
|
+
return runTool(request, () => broker.runWorkflow(auth, request), readString$2(args, "confirmationId"));
|
|
207182
207386
|
}
|
|
207183
207387
|
function enableWorkflow(context) {
|
|
207184
207388
|
return runWorkflowToggle("forge_github_enable_workflow", context, "enableWorkflow");
|
|
@@ -207201,12 +207405,12 @@ function readWorkflowRequest(args, toolName) {
|
|
|
207201
207405
|
async function runWorkflowRunMutation(toolName, context, brokerMethod) {
|
|
207202
207406
|
const { args, auth, broker, runTool } = context;
|
|
207203
207407
|
const request = readWorkflowRunRequest(args, toolName);
|
|
207204
|
-
return runTool(request, () => broker[brokerMethod](auth, request), readString$
|
|
207408
|
+
return runTool(request, () => broker[brokerMethod](auth, request), readString$2(args, "confirmationId"));
|
|
207205
207409
|
}
|
|
207206
207410
|
function runWorkflowToggle(toolName, context, brokerMethod) {
|
|
207207
207411
|
const { args, auth, broker, runTool } = context;
|
|
207208
207412
|
const request = readWorkflowRequest(args, toolName);
|
|
207209
|
-
return runTool(request, () => broker[brokerMethod](auth, request), readString$
|
|
207413
|
+
return runTool(request, () => broker[brokerMethod](auth, request), readString$2(args, "confirmationId"));
|
|
207210
207414
|
}
|
|
207211
207415
|
//#endregion
|
|
207212
207416
|
//#region src/github/GitHubCodexWorkspaceGitTools.ts
|
|
@@ -207223,7 +207427,7 @@ async function runWorkspaceGitTool(toolName, context) {
|
|
|
207223
207427
|
case "forge_git_commit": return runCommit("forge_git_commit", context);
|
|
207224
207428
|
case "forge_git_reset_hard": return runResetHard("forge_git_reset_hard", context);
|
|
207225
207429
|
case "forge_git": {
|
|
207226
|
-
const operation = readString$
|
|
207430
|
+
const operation = readString$2(args, "operation")?.trim();
|
|
207227
207431
|
switch (operation) {
|
|
207228
207432
|
case "status": return runStatus(toolName, context, "status");
|
|
207229
207433
|
case "log": return runLog(toolName, context);
|
|
@@ -207252,7 +207456,7 @@ function runLog(toolName, { args, broker, runTool }) {
|
|
|
207252
207456
|
}
|
|
207253
207457
|
function runStash(toolName, { args, broker, runTool }, operation) {
|
|
207254
207458
|
const cwd = readRequiredString(args, "cwd", toolName);
|
|
207255
|
-
const message = readString$
|
|
207459
|
+
const message = readString$2(args, "message")?.trim();
|
|
207256
207460
|
return runTool({
|
|
207257
207461
|
cwd,
|
|
207258
207462
|
...operation ? { operation } : {},
|
|
@@ -207260,12 +207464,12 @@ function runStash(toolName, { args, broker, runTool }, operation) {
|
|
|
207260
207464
|
}, () => broker.stashWorkspace({
|
|
207261
207465
|
cwd,
|
|
207262
207466
|
...message ? { message } : {}
|
|
207263
|
-
}), readString$
|
|
207467
|
+
}), readString$2(args, "confirmationId"));
|
|
207264
207468
|
}
|
|
207265
207469
|
function runCommit(toolName, { args, broker, runTool }, operation) {
|
|
207266
207470
|
const cwd = readRequiredString(args, "cwd", toolName);
|
|
207267
207471
|
const message = readRequiredString(args, "message", operation ? `${toolName} operation=commit` : toolName);
|
|
207268
|
-
const branch = readString$
|
|
207472
|
+
const branch = readString$2(args, "branch")?.trim();
|
|
207269
207473
|
return runTool({
|
|
207270
207474
|
cwd,
|
|
207271
207475
|
...operation ? { operation } : {},
|
|
@@ -207275,11 +207479,11 @@ function runCommit(toolName, { args, broker, runTool }, operation) {
|
|
|
207275
207479
|
cwd,
|
|
207276
207480
|
message,
|
|
207277
207481
|
...branch ? { branch } : {}
|
|
207278
|
-
}), readString$
|
|
207482
|
+
}), readString$2(args, "confirmationId"));
|
|
207279
207483
|
}
|
|
207280
207484
|
function runResetHard(toolName, { args, broker, runTool }, operation) {
|
|
207281
207485
|
const cwd = readRequiredString(args, "cwd", toolName);
|
|
207282
|
-
const ref = readString$
|
|
207486
|
+
const ref = readString$2(args, "ref")?.trim();
|
|
207283
207487
|
return runTool({
|
|
207284
207488
|
cwd,
|
|
207285
207489
|
...operation ? { operation } : {},
|
|
@@ -207287,7 +207491,7 @@ function runResetHard(toolName, { args, broker, runTool }, operation) {
|
|
|
207287
207491
|
}, () => broker.resetWorkspaceHard({
|
|
207288
207492
|
cwd,
|
|
207289
207493
|
...ref ? { ref } : {}
|
|
207290
|
-
}), readString$
|
|
207494
|
+
}), readString$2(args, "confirmationId"));
|
|
207291
207495
|
}
|
|
207292
207496
|
//#endregion
|
|
207293
207497
|
//#region src/github/GitHubCodexToolRunner.ts
|
|
@@ -207921,7 +208125,7 @@ function createCodexGitHubDynamicToolHandler(options = {}) {
|
|
|
207921
208125
|
error: "Forge could not resolve the authenticated user session for this thread. Send a new turn from the Forge UI and retry."
|
|
207922
208126
|
}, false);
|
|
207923
208127
|
const args = readArgs$1(input.arguments);
|
|
207924
|
-
const dispatchOperation = input.tool === "forge_git" ? readString$
|
|
208128
|
+
const dispatchOperation = input.tool === "forge_git" ? readString$2(args, "operation")?.trim() : void 0;
|
|
207925
208129
|
const operation = dispatchOperation ?? operationForTool(input.tool);
|
|
207926
208130
|
const requiresConfirmation = !bypassWriteConfirmation && (isGitHubWriteTool(input.tool) || dispatchOperation !== void 0 && FORGE_GIT_WRITE_OPERATIONS.has(dispatchOperation));
|
|
207927
208131
|
const runTool = async (request, execute, confirmationId) => {
|
|
@@ -208443,7 +208647,7 @@ function createCodexDevServerDynamicTools() {
|
|
|
208443
208647
|
function readArgs(value) {
|
|
208444
208648
|
return value && typeof value === "object" ? value : {};
|
|
208445
208649
|
}
|
|
208446
|
-
function readString(args, key) {
|
|
208650
|
+
function readString$1(args, key) {
|
|
208447
208651
|
const value = args[key];
|
|
208448
208652
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
208449
208653
|
}
|
|
@@ -208456,8 +208660,8 @@ function readBoolean(args, key) {
|
|
|
208456
208660
|
return typeof value === "boolean" ? value : void 0;
|
|
208457
208661
|
}
|
|
208458
208662
|
function runDevServerStart(handlers, args) {
|
|
208459
|
-
const command = readString(args, "command");
|
|
208460
|
-
const cwd = readString(args, "cwd");
|
|
208663
|
+
const command = readString$1(args, "command");
|
|
208664
|
+
const cwd = readString$1(args, "cwd");
|
|
208461
208665
|
const port = readNumber(args, "port");
|
|
208462
208666
|
const readyTimeoutMs = readNumber(args, "readyTimeoutMs");
|
|
208463
208667
|
const openPreview = readBoolean(args, "openPreview");
|
|
@@ -208613,7 +208817,7 @@ function readConnectorParams(tool, args) {
|
|
|
208613
208817
|
switch (tool.tool) {
|
|
208614
208818
|
case "forge_gmail_search": return {
|
|
208615
208819
|
...params,
|
|
208616
|
-
query: readString$
|
|
208820
|
+
query: readString$2(args, "query") ?? ""
|
|
208617
208821
|
};
|
|
208618
208822
|
case "forge_gmail_list_recent": return {
|
|
208619
208823
|
...params,
|
|
@@ -208625,7 +208829,7 @@ function readConnectorParams(tool, args) {
|
|
|
208625
208829
|
}
|
|
208626
208830
|
}
|
|
208627
208831
|
function appendString(target, args, key) {
|
|
208628
|
-
const value = readString$
|
|
208832
|
+
const value = readString$2(args, key);
|
|
208629
208833
|
if (value !== void 0) target[key] = value;
|
|
208630
208834
|
}
|
|
208631
208835
|
function appendNumber(target, args, key) {
|
|
@@ -209214,7 +209418,7 @@ const collectStreamAsString = (stream) => stream.pipe(decodeText$1(), runFold(()
|
|
|
209214
209418
|
//#region src/orchestration/WorkerPlanCodexTools.ts
|
|
209215
209419
|
const FORGE_PROPOSE_WORKER_TASKS_TOOL = "forge_propose_worker_tasks";
|
|
209216
209420
|
const MAX_WORKER_PLAN_ITEMS = 5;
|
|
209217
|
-
function asRecord$
|
|
209421
|
+
function asRecord$10(value) {
|
|
209218
209422
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
209219
209423
|
}
|
|
209220
209424
|
function readTrimmedString(value) {
|
|
@@ -209230,7 +209434,7 @@ function isValidWorkerSummary(value) {
|
|
|
209230
209434
|
return value !== void 0 && value.length > 0 && value.length <= 4e3;
|
|
209231
209435
|
}
|
|
209232
209436
|
function decodeWorkerPlanItem(value) {
|
|
209233
|
-
const item = asRecord$
|
|
209437
|
+
const item = asRecord$10(value);
|
|
209234
209438
|
const slug = readTrimmedString(item?.slug);
|
|
209235
209439
|
const title = readTrimmedString(item?.title);
|
|
209236
209440
|
const summary = readTrimmedString(item?.summary);
|
|
@@ -209294,7 +209498,7 @@ function createWorkerPlanDynamicTools() {
|
|
|
209294
209498
|
function createWorkerPlanDynamicToolHandler() {
|
|
209295
209499
|
return async (input) => {
|
|
209296
209500
|
if (input.tool !== "forge_propose_worker_tasks") throw new Error(`Unsupported worker-plan tool: ${input.tool}`);
|
|
209297
|
-
if (!decodeWorkerPlanItems(asRecord$
|
|
209501
|
+
if (!decodeWorkerPlanItems(asRecord$10(input.arguments)?.items)) throw new Error("Worker plans require one to five unique, fully described items.");
|
|
209298
209502
|
return {
|
|
209299
209503
|
success: true,
|
|
209300
209504
|
contentItems: [{
|
|
@@ -210785,6 +210989,374 @@ function withoutInheritedAnthropicCredentials(env, options) {
|
|
|
210785
210989
|
return contained;
|
|
210786
210990
|
}
|
|
210787
210991
|
//#endregion
|
|
210992
|
+
//#region src/provider/personalClaudeInVm.ts
|
|
210993
|
+
/**
|
|
210994
|
+
* CMMD writes this marker when it activates a personal Claude subscription
|
|
210995
|
+
* inside a running sprite. Activation always happens after boot, so a
|
|
210996
|
+
* boot-time env var alone cannot reflect it (FOR-36249).
|
|
210997
|
+
*
|
|
210998
|
+
* CMMD names the file in the Service env (FORGE_PERSONAL_CLAUDE_MARKER_PATH).
|
|
210999
|
+
* Without that, the marker sits directly under FORGE_HOME, which is the same
|
|
211000
|
+
* guest home CMMD derives every sprite path from. A server with neither has no
|
|
211001
|
+
* sprite home to look in, and a desktop never has a marker, so it reads none.
|
|
211002
|
+
*/
|
|
211003
|
+
const PERSONAL_CLAUDE_IN_VM_MARKER_NAME = ".forge-personal-claude-in-vm";
|
|
211004
|
+
const PERSONAL_CLAUDE_GENERATION_FILE_NAME = ".forge-personal-claude-generation";
|
|
211005
|
+
function markerPath(env) {
|
|
211006
|
+
if (env.FORGE_PERSONAL_CLAUDE_MARKER_PATH) return env.FORGE_PERSONAL_CLAUDE_MARKER_PATH;
|
|
211007
|
+
if (env.FORGE_HOME) return join(env.FORGE_HOME, PERSONAL_CLAUDE_IN_VM_MARKER_NAME);
|
|
211008
|
+
return null;
|
|
211009
|
+
}
|
|
211010
|
+
/**
|
|
211011
|
+
* True when this runtime should offer the personal Claude subscription lane.
|
|
211012
|
+
*
|
|
211013
|
+
* Accepts either signal: the boot-time env var (backward compatible) or the
|
|
211014
|
+
* activation marker on disk. Reading the marker at call time lets the provider
|
|
211015
|
+
* catalog and the per-turn readiness guard self-heal once CMMD delivers,
|
|
211016
|
+
* without a sprite reboot. Callers still verify the credential is non-empty
|
|
211017
|
+
* before treating it as usable; this only decides whether the lane is active.
|
|
211018
|
+
*/
|
|
211019
|
+
function personalClaudeInVmEnabled(env = process.env) {
|
|
211020
|
+
if (env.FORGE_PERSONAL_CLAUDE_IN_VM === "true") return true;
|
|
211021
|
+
const marker = markerPath(env);
|
|
211022
|
+
if (!marker) return false;
|
|
211023
|
+
try {
|
|
211024
|
+
return existsSync(marker);
|
|
211025
|
+
} catch {
|
|
211026
|
+
return false;
|
|
211027
|
+
}
|
|
211028
|
+
}
|
|
211029
|
+
/** Exported at boot. This is the restart proof CMMD waits on, so it must never read the file. */
|
|
211030
|
+
function bootExportedPersonalClaudeGenerationHash(env = process.env) {
|
|
211031
|
+
return env.FORGE_PERSONAL_CLAUDE_GENERATION_HASH?.trim() ?? "";
|
|
211032
|
+
}
|
|
211033
|
+
/**
|
|
211034
|
+
* The sha256 of the Claude credential generation this runtime holds, or "" when
|
|
211035
|
+
* it holds none. The readiness guard compares it with the generation CMMD
|
|
211036
|
+
* applied. The restart proof must not use it; it uses the boot-exported hash.
|
|
211037
|
+
*
|
|
211038
|
+
* The boot preload exports FORGE_PERSONAL_CLAUDE_GENERATION_HASH, but only when
|
|
211039
|
+
* the marker already exists at boot. Activation in place writes the generation
|
|
211040
|
+
* file and touches the marker without setting any env, so an empty variable
|
|
211041
|
+
* falls back to that file, hashed the way the preload hashes it. CMMD puts the
|
|
211042
|
+
* file beside the marker in the guest home and names no path for it. A marker
|
|
211043
|
+
* with no generation file is a legacy runtime, and gets no hash here either.
|
|
211044
|
+
*/
|
|
211045
|
+
function personalClaudeGenerationHash(env = process.env) {
|
|
211046
|
+
const exported = bootExportedPersonalClaudeGenerationHash(env);
|
|
211047
|
+
if (exported) return exported;
|
|
211048
|
+
const marker = markerPath(env);
|
|
211049
|
+
if (!marker) return "";
|
|
211050
|
+
try {
|
|
211051
|
+
if (!existsSync(marker)) return "";
|
|
211052
|
+
const generation = readFileSync(join(dirname(marker), PERSONAL_CLAUDE_GENERATION_FILE_NAME), "utf8").trim();
|
|
211053
|
+
return generation ? createHash("sha256").update(generation).digest("hex") : "";
|
|
211054
|
+
} catch {
|
|
211055
|
+
return "";
|
|
211056
|
+
}
|
|
211057
|
+
}
|
|
211058
|
+
//#endregion
|
|
211059
|
+
//#region src/provider/claudeCredentialDirectory.ts
|
|
211060
|
+
/**
|
|
211061
|
+
* Per-account credential directories for Claude turns.
|
|
211062
|
+
*
|
|
211063
|
+
* Claude Code keeps conversation transcripts under
|
|
211064
|
+
* `${CLAUDE_CONFIG_DIR}/projects/<cwd-slug>/<sessionId>.jsonl` and its login
|
|
211065
|
+
* under `.credentials.json`. `CLAUDE_SECURESTORAGE_CONFIG_DIR` moves only the
|
|
211066
|
+
* credential file, so each Provider Account can hold its own login while every
|
|
211067
|
+
* session file stays in one place and `--resume` keeps finding its target. See
|
|
211068
|
+
* `docs/adr/claude-accounts-split-credentials-not-sessions.md`.
|
|
211069
|
+
*
|
|
211070
|
+
* That variable is not documented. Under
|
|
211071
|
+
* `docs/adr/unpublished-provider-surface-needs-a-fallback.md` the dependency is
|
|
211072
|
+
* only allowed because absence degrades to a named behaviour that already
|
|
211073
|
+
* works: one account per Environment, exactly what runs today. This module is
|
|
211074
|
+
* the gate that keeps the degrade honest. It sets the variable only when a
|
|
211075
|
+
* readable `.credentials.json` is already sitting in the resolved directory,
|
|
211076
|
+
* because a missing or empty directory makes the CLI report logged out and a
|
|
211077
|
+
* live turn would fail. Nothing writes these directories yet, so every runtime
|
|
211078
|
+
* today takes the skip path.
|
|
211079
|
+
*
|
|
211080
|
+
* Pure and dependency-free on purpose, for the same reason as
|
|
211081
|
+
* `claudeCredentialContainment.ts`: ClaudeAdapter drags in Effect platform
|
|
211082
|
+
* services, the browser manager and the server config, so a unit test that
|
|
211083
|
+
* imports it cannot run in a worktree. This rule decides which account pays for
|
|
211084
|
+
* a turn, so it has to stay directly testable.
|
|
211085
|
+
*/
|
|
211086
|
+
/** Repoints `.credentials.json` without moving the transcripts. */
|
|
211087
|
+
const CLAUDE_SECURESTORAGE_CONFIG_DIR_ENV = "CLAUDE_SECURESTORAGE_CONFIG_DIR";
|
|
211088
|
+
/** The one file Claude Code writes into the credential directory. */
|
|
211089
|
+
const CLAUDE_CREDENTIAL_FILE_NAME = ".credentials.json";
|
|
211090
|
+
/**
|
|
211091
|
+
* Runtime env var naming the Provider Account this runtime's Claude turns
|
|
211092
|
+
* belong to.
|
|
211093
|
+
*
|
|
211094
|
+
* NOTHING SETS THIS. It has no writer anywhere in Forge or CMMD, so as shipped
|
|
211095
|
+
* the per-account lane can never turn on and every runtime keeps today's single
|
|
211096
|
+
* account. The writer waits on issue 3073, which decides which identity keys
|
|
211097
|
+
* the directory. That has to be settled first: the credential generation
|
|
211098
|
+
* rotates on every re-delivery, so keying on it would give a new directory
|
|
211099
|
+
* each time and defeat the point.
|
|
211100
|
+
*
|
|
211101
|
+
* It is also not the seam. The seam is the `credentialDirectory` parameter on
|
|
211102
|
+
* `ClaudeAdapter.buildClaudeQueryEnv`, which is what tests and callers drive.
|
|
211103
|
+
* This variable is one bridge into that parameter, and the delivery step is
|
|
211104
|
+
* free to replace it with something better rather than adopt it.
|
|
211105
|
+
*/
|
|
211106
|
+
const CLAUDE_PROVIDER_ACCOUNT_IDENTITY_ENV = "FORGE_CLAUDE_PROVIDER_ACCOUNT_IDENTITY";
|
|
211107
|
+
/** Operator escape hatch for the directory root, mostly for local debugging. */
|
|
211108
|
+
const CLAUDE_CREDENTIAL_ROOT_ENV = "FORGE_CLAUDE_CREDENTIAL_ROOT";
|
|
211109
|
+
const CREDENTIAL_ROOT_DIRECTORY_NAME = "claude-credentials";
|
|
211110
|
+
const FALLBACK_ROOT_DIRECTORY_NAME = ".forge-claude-credentials";
|
|
211111
|
+
const DEFAULT_CLAUDE_CONFIG_DIRECTORY_NAME = ".claude";
|
|
211112
|
+
const MAX_ACCOUNT_IDENTITY_LENGTH = 256;
|
|
211113
|
+
const DIRECTORY_NAME_HASH_LENGTH = 32;
|
|
211114
|
+
/**
|
|
211115
|
+
* Directory name for an account. A hash, so no email or display name reaches a
|
|
211116
|
+
* filesystem path and no path-illegal character can appear in one either.
|
|
211117
|
+
*/
|
|
211118
|
+
function claudeCredentialDirectoryName(accountIdentity) {
|
|
211119
|
+
return `account-${createHash("sha256").update(accountIdentity, "utf8").digest("hex").slice(0, DIRECTORY_NAME_HASH_LENGTH)}`;
|
|
211120
|
+
}
|
|
211121
|
+
/** The account this runtime's Claude turns belong to, or null on every runtime today. */
|
|
211122
|
+
function resolveClaudeProviderAccountIdentity(env) {
|
|
211123
|
+
const identity = env[CLAUDE_PROVIDER_ACCOUNT_IDENTITY_ENV]?.trim();
|
|
211124
|
+
if (!identity || identity.length > MAX_ACCOUNT_IDENTITY_LENGTH) return null;
|
|
211125
|
+
return identity;
|
|
211126
|
+
}
|
|
211127
|
+
/**
|
|
211128
|
+
* Where per-account credential directories live. Always outside
|
|
211129
|
+
* `CLAUDE_CONFIG_DIR`, so one of them can never be mistaken for the main
|
|
211130
|
+
* credential store, and so a stray read of the config directory cannot walk
|
|
211131
|
+
* into another account's login.
|
|
211132
|
+
*/
|
|
211133
|
+
function claudeCredentialDirectoryRoot(env) {
|
|
211134
|
+
const root = credentialRootCandidate(env);
|
|
211135
|
+
if (!root || !isAbsolute(root)) return null;
|
|
211136
|
+
return isInside(root, claudeConfigDirectory(env)) ? null : resolve(root);
|
|
211137
|
+
}
|
|
211138
|
+
/** Absolute credential directory for one account, or null when either half is missing. */
|
|
211139
|
+
function resolveClaudeCredentialDirectory(input) {
|
|
211140
|
+
if (!input.accountIdentity) return null;
|
|
211141
|
+
const root = claudeCredentialDirectoryRoot(input.env);
|
|
211142
|
+
if (!root) return null;
|
|
211143
|
+
return join(root, claudeCredentialDirectoryName(input.accountIdentity));
|
|
211144
|
+
}
|
|
211145
|
+
/**
|
|
211146
|
+
* Decide whether this turn may point at a per-account credential directory.
|
|
211147
|
+
*
|
|
211148
|
+
* The cheap checks run first and the capability probe runs last, so a runtime
|
|
211149
|
+
* with no delivered credential never spawns the CLI. Any failure is a skip, so
|
|
211150
|
+
* the worst outcome is today's behaviour.
|
|
211151
|
+
*/
|
|
211152
|
+
async function decideClaudeCredentialDirectory(input) {
|
|
211153
|
+
if (!input.accountIdentity) return skip("no-account");
|
|
211154
|
+
if (input.env["CLAUDE_SECURESTORAGE_CONFIG_DIR"]?.trim()) return skip("operator-override");
|
|
211155
|
+
const directory = resolveClaudeCredentialDirectory(input);
|
|
211156
|
+
if (!directory) return skip("no-root");
|
|
211157
|
+
if (!(input.credentialFilePresent ?? hasReadableCredentialFile)(directory)) return skip("no-credential-file");
|
|
211158
|
+
return await probeSupportSafely(input.probeSupport) === "supported" ? {
|
|
211159
|
+
outcome: "set",
|
|
211160
|
+
directory
|
|
211161
|
+
} : skip("capability-not-supported");
|
|
211162
|
+
}
|
|
211163
|
+
/** True when `.credentials.json` is in the directory and readable right now. */
|
|
211164
|
+
function hasReadableCredentialFile(directory) {
|
|
211165
|
+
const file = join(directory, CLAUDE_CREDENTIAL_FILE_NAME);
|
|
211166
|
+
try {
|
|
211167
|
+
if (!statSync(file).isFile()) return false;
|
|
211168
|
+
accessSync(file, constants.R_OK);
|
|
211169
|
+
return true;
|
|
211170
|
+
} catch {
|
|
211171
|
+
return false;
|
|
211172
|
+
}
|
|
211173
|
+
}
|
|
211174
|
+
function skip(reason) {
|
|
211175
|
+
return {
|
|
211176
|
+
outcome: "skipped",
|
|
211177
|
+
reason
|
|
211178
|
+
};
|
|
211179
|
+
}
|
|
211180
|
+
/** `inconclusive` is treated as `unsupported`, and so is a probe that throws. */
|
|
211181
|
+
async function probeSupportSafely(probeSupport) {
|
|
211182
|
+
try {
|
|
211183
|
+
return await probeSupport();
|
|
211184
|
+
} catch {
|
|
211185
|
+
return "inconclusive";
|
|
211186
|
+
}
|
|
211187
|
+
}
|
|
211188
|
+
function credentialRootCandidate(env) {
|
|
211189
|
+
const explicit = env[CLAUDE_CREDENTIAL_ROOT_ENV]?.trim();
|
|
211190
|
+
if (explicit) return explicit;
|
|
211191
|
+
const forgeHome = env.FORGE_HOME?.trim();
|
|
211192
|
+
if (forgeHome) return join(forgeHome, CREDENTIAL_ROOT_DIRECTORY_NAME);
|
|
211193
|
+
try {
|
|
211194
|
+
return join(homedir(), FALLBACK_ROOT_DIRECTORY_NAME);
|
|
211195
|
+
} catch {
|
|
211196
|
+
return null;
|
|
211197
|
+
}
|
|
211198
|
+
}
|
|
211199
|
+
function claudeConfigDirectory(env) {
|
|
211200
|
+
const configured = env.CLAUDE_CONFIG_DIR?.trim();
|
|
211201
|
+
if (configured) return configured;
|
|
211202
|
+
try {
|
|
211203
|
+
return join(homedir(), DEFAULT_CLAUDE_CONFIG_DIRECTORY_NAME);
|
|
211204
|
+
} catch {
|
|
211205
|
+
return null;
|
|
211206
|
+
}
|
|
211207
|
+
}
|
|
211208
|
+
function isInside(candidate, parent) {
|
|
211209
|
+
if (!parent) return false;
|
|
211210
|
+
const step = relative(resolve(parent), resolve(candidate));
|
|
211211
|
+
return step === "" || !step.startsWith("..") && !isAbsolute(step);
|
|
211212
|
+
}
|
|
211213
|
+
//#endregion
|
|
211214
|
+
//#region src/provider/claudeCredentialDirectoryProbe.ts
|
|
211215
|
+
/**
|
|
211216
|
+
* Capability probe for `CLAUDE_SECURESTORAGE_CONFIG_DIR`.
|
|
211217
|
+
*
|
|
211218
|
+
* The variable is unpublished, so a runtime is asked rather than assumed to
|
|
211219
|
+
* honour it (`docs/adr/unpublished-provider-surface-needs-a-fallback.md`). The
|
|
211220
|
+
* question the probe can actually answer: point the variable at an empty
|
|
211221
|
+
* directory and a runtime that honours it flips from logged in to logged out.
|
|
211222
|
+
* A runtime with no credential at all is logged out either way and cannot
|
|
211223
|
+
* discriminate, which is `inconclusive`, and callers read that as unsupported.
|
|
211224
|
+
*
|
|
211225
|
+
* `claude auth status` is a pure read. It sends no prompt and leaves
|
|
211226
|
+
* `.credentials.json` byte-identical, so probing spends no subscription usage.
|
|
211227
|
+
* It prints JSON on stdout and exits 1 when logged out, so the exit code is not
|
|
211228
|
+
* a failure signal and the `loggedIn` field is what gets read.
|
|
211229
|
+
*/
|
|
211230
|
+
const execFileAsync$3 = promisify$1(execFile);
|
|
211231
|
+
const AUTH_STATUS_ARGS = ["auth", "status"];
|
|
211232
|
+
const VERSION_ARGS = ["--version"];
|
|
211233
|
+
/**
|
|
211234
|
+
* The probe sits in front of a turn, so a provider process that never answers
|
|
211235
|
+
* must not hold that turn open. `execFile` kills the child at this bound, and
|
|
211236
|
+
* `runBounded` gives up on it at the same bound whatever the deps do, so a
|
|
211237
|
+
* hung or unkillable process costs a few seconds and an `inconclusive`.
|
|
211238
|
+
*/
|
|
211239
|
+
const PROBE_TIMEOUT_MS = 5e3;
|
|
211240
|
+
const PROBE_DIRECTORY_PREFIX = "forge-claude-capability-";
|
|
211241
|
+
const VERSION_PATTERN = /\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/;
|
|
211242
|
+
function makeClaudeCredentialDirectoryProbe(deps) {
|
|
211243
|
+
const answers = /* @__PURE__ */ new Map();
|
|
211244
|
+
return { support: async () => {
|
|
211245
|
+
const version = await readVersionSafely(deps);
|
|
211246
|
+
if (version === null) return runProbe(deps);
|
|
211247
|
+
const cached = answers.get(version);
|
|
211248
|
+
if (cached) return cached;
|
|
211249
|
+
const pending = runProbe(deps);
|
|
211250
|
+
answers.set(version, pending);
|
|
211251
|
+
return pending;
|
|
211252
|
+
} };
|
|
211253
|
+
}
|
|
211254
|
+
/**
|
|
211255
|
+
* One probe per binary path for the life of the process. The probe spawns the
|
|
211256
|
+
* CLI twice, and every Claude session start would otherwise pay for it.
|
|
211257
|
+
*/
|
|
211258
|
+
const probesByBinaryPath = /* @__PURE__ */ new Map();
|
|
211259
|
+
function claudeCredentialDirectoryProbeFor(binaryPath) {
|
|
211260
|
+
const binary = binaryPath?.trim() || "claude";
|
|
211261
|
+
const existing = probesByBinaryPath.get(binary);
|
|
211262
|
+
if (existing) return existing;
|
|
211263
|
+
const probe = makeClaudeCredentialDirectoryProbe(claudeCredentialDirectoryProbeDeps(binary));
|
|
211264
|
+
probesByBinaryPath.set(binary, probe);
|
|
211265
|
+
return probe;
|
|
211266
|
+
}
|
|
211267
|
+
function claudeCredentialDirectoryProbeDeps(binaryPath) {
|
|
211268
|
+
return {
|
|
211269
|
+
runAuthStatus: ({ credentialDirectory }) => runClaudeCommand$1(binaryPath, AUTH_STATUS_ARGS, credentialDirectory),
|
|
211270
|
+
readBinaryVersion: async () => {
|
|
211271
|
+
const { stdout } = await runClaudeCommand$1(binaryPath, VERSION_ARGS, null);
|
|
211272
|
+
return VERSION_PATTERN.exec(stdout)?.[0] ?? null;
|
|
211273
|
+
},
|
|
211274
|
+
makeProbeDirectory: () => mkdtemp(join(tmpdir(), PROBE_DIRECTORY_PREFIX)),
|
|
211275
|
+
removeProbeDirectory: (directory) => rm(directory, {
|
|
211276
|
+
recursive: true,
|
|
211277
|
+
force: true
|
|
211278
|
+
})
|
|
211279
|
+
};
|
|
211280
|
+
}
|
|
211281
|
+
async function runProbe(deps) {
|
|
211282
|
+
let probeDirectory = null;
|
|
211283
|
+
try {
|
|
211284
|
+
if (parseLoggedIn(await runBounded(deps, () => deps.runAuthStatus({ credentialDirectory: null }))) !== true) return "inconclusive";
|
|
211285
|
+
probeDirectory = await deps.makeProbeDirectory();
|
|
211286
|
+
const directory = probeDirectory;
|
|
211287
|
+
const redirected = parseLoggedIn(await runBounded(deps, () => deps.runAuthStatus({ credentialDirectory: directory })));
|
|
211288
|
+
if (redirected === false) return "supported";
|
|
211289
|
+
return redirected === true ? "unsupported" : "inconclusive";
|
|
211290
|
+
} catch {
|
|
211291
|
+
return "inconclusive";
|
|
211292
|
+
} finally {
|
|
211293
|
+
if (probeDirectory !== null) await deps.removeProbeDirectory(probeDirectory).catch(() => void 0);
|
|
211294
|
+
}
|
|
211295
|
+
}
|
|
211296
|
+
async function readVersionSafely(deps) {
|
|
211297
|
+
try {
|
|
211298
|
+
return await runBounded(deps, () => deps.readBinaryVersion());
|
|
211299
|
+
} catch {
|
|
211300
|
+
return null;
|
|
211301
|
+
}
|
|
211302
|
+
}
|
|
211303
|
+
/**
|
|
211304
|
+
* Give up on a call that outlives the bound. The underlying promise is left to
|
|
211305
|
+
* settle on its own; the real deps hand `execFile` the same bound, so its child
|
|
211306
|
+
* is already being killed by the time this rejects.
|
|
211307
|
+
*/
|
|
211308
|
+
async function runBounded(deps, operation) {
|
|
211309
|
+
const timeoutMs = deps.timeoutMs ?? PROBE_TIMEOUT_MS;
|
|
211310
|
+
let timer;
|
|
211311
|
+
try {
|
|
211312
|
+
return await Promise.race([operation(), new Promise((_resolve, reject) => {
|
|
211313
|
+
timer = setTimeout(() => reject(/* @__PURE__ */ new Error("claude auth status timed out")), timeoutMs);
|
|
211314
|
+
})]);
|
|
211315
|
+
} finally {
|
|
211316
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
211317
|
+
}
|
|
211318
|
+
}
|
|
211319
|
+
/** The `loggedIn` field, or null when the output does not carry one. */
|
|
211320
|
+
function parseLoggedIn(run) {
|
|
211321
|
+
const parsed = parseJsonObject$1(run.stdout);
|
|
211322
|
+
return typeof parsed?.loggedIn === "boolean" ? parsed.loggedIn : null;
|
|
211323
|
+
}
|
|
211324
|
+
function parseJsonObject$1(output) {
|
|
211325
|
+
const trimmed = output.trim();
|
|
211326
|
+
const start = trimmed.indexOf("{");
|
|
211327
|
+
const end = trimmed.lastIndexOf("}");
|
|
211328
|
+
if (start === -1 || end <= start) return null;
|
|
211329
|
+
try {
|
|
211330
|
+
const parsed = JSON.parse(trimmed.slice(start, end + 1));
|
|
211331
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : null;
|
|
211332
|
+
} catch {
|
|
211333
|
+
return null;
|
|
211334
|
+
}
|
|
211335
|
+
}
|
|
211336
|
+
async function runClaudeCommand$1(binaryPath, args, credentialDirectory) {
|
|
211337
|
+
try {
|
|
211338
|
+
const { stdout } = await execFileAsync$3(binaryPath, [...args], {
|
|
211339
|
+
env: {
|
|
211340
|
+
...process.env,
|
|
211341
|
+
...credentialDirectory ? { [CLAUDE_SECURESTORAGE_CONFIG_DIR_ENV]: credentialDirectory } : {}
|
|
211342
|
+
},
|
|
211343
|
+
maxBuffer: 1024 * 1024,
|
|
211344
|
+
timeout: PROBE_TIMEOUT_MS
|
|
211345
|
+
});
|
|
211346
|
+
return {
|
|
211347
|
+
stdout,
|
|
211348
|
+
exitCode: 0
|
|
211349
|
+
};
|
|
211350
|
+
} catch (cause) {
|
|
211351
|
+
const failure = cause;
|
|
211352
|
+
if (typeof failure.stdout !== "string") throw cause;
|
|
211353
|
+
return {
|
|
211354
|
+
stdout: failure.stdout,
|
|
211355
|
+
exitCode: typeof failure.code === "number" ? failure.code : null
|
|
211356
|
+
};
|
|
211357
|
+
}
|
|
211358
|
+
}
|
|
211359
|
+
//#endregion
|
|
210788
211360
|
//#region src/provider/makeManagedServerProvider.ts
|
|
210789
211361
|
const makeManagedServerProvider = fn$1("makeManagedServerProvider")(function* (input) {
|
|
210790
211362
|
const refreshSemaphore = yield* make$43(1);
|
|
@@ -210873,43 +211445,6 @@ const makeProviderAuthProvenance = (input) => {
|
|
|
210873
211445
|
};
|
|
210874
211446
|
};
|
|
210875
211447
|
//#endregion
|
|
210876
|
-
//#region src/provider/personalClaudeInVm.ts
|
|
210877
|
-
/**
|
|
210878
|
-
* CMMD writes this marker when it activates a personal Claude subscription
|
|
210879
|
-
* inside a running sprite. Activation always happens after boot, so a
|
|
210880
|
-
* boot-time env var alone cannot reflect it (FOR-36249).
|
|
210881
|
-
*
|
|
210882
|
-
* CMMD names the file in the Service env (FORGE_PERSONAL_CLAUDE_MARKER_PATH).
|
|
210883
|
-
* Without that, the marker sits directly under FORGE_HOME, which is the same
|
|
210884
|
-
* guest home CMMD derives every sprite path from. A server with neither has no
|
|
210885
|
-
* sprite home to look in, and a desktop never has a marker, so it reads none.
|
|
210886
|
-
*/
|
|
210887
|
-
const PERSONAL_CLAUDE_IN_VM_MARKER_NAME = ".forge-personal-claude-in-vm";
|
|
210888
|
-
function markerPath(env) {
|
|
210889
|
-
if (env.FORGE_PERSONAL_CLAUDE_MARKER_PATH) return env.FORGE_PERSONAL_CLAUDE_MARKER_PATH;
|
|
210890
|
-
if (env.FORGE_HOME) return join(env.FORGE_HOME, PERSONAL_CLAUDE_IN_VM_MARKER_NAME);
|
|
210891
|
-
return null;
|
|
210892
|
-
}
|
|
210893
|
-
/**
|
|
210894
|
-
* True when this runtime should offer the personal Claude subscription lane.
|
|
210895
|
-
*
|
|
210896
|
-
* Accepts either signal: the boot-time env var (backward compatible) or the
|
|
210897
|
-
* activation marker on disk. Reading the marker at call time lets the provider
|
|
210898
|
-
* catalog and the per-turn readiness guard self-heal once CMMD delivers,
|
|
210899
|
-
* without a sprite reboot. Callers still verify the credential is non-empty
|
|
210900
|
-
* before treating it as usable; this only decides whether the lane is active.
|
|
210901
|
-
*/
|
|
210902
|
-
function personalClaudeInVmEnabled(env = process.env) {
|
|
210903
|
-
if (env.FORGE_PERSONAL_CLAUDE_IN_VM === "true") return true;
|
|
210904
|
-
const marker = markerPath(env);
|
|
210905
|
-
if (!marker) return false;
|
|
210906
|
-
try {
|
|
210907
|
-
return existsSync(marker);
|
|
210908
|
-
} catch {
|
|
210909
|
-
return false;
|
|
210910
|
-
}
|
|
210911
|
-
}
|
|
210912
|
-
//#endregion
|
|
210913
211448
|
//#region src/provider/Layers/ClaudeProvider.ts
|
|
210914
211449
|
function endpointProfileDisplayNames(settings) {
|
|
210915
211450
|
return Object.fromEntries(settings.endpointProfiles.filter((p) => p.displayName).map((p) => [p.slug, p.displayName]));
|
|
@@ -211110,25 +211645,25 @@ const AUTH_METHOD_CONTAINER_KEYS = [
|
|
|
211110
211645
|
/** Lift an unknown value into `Option<string>` if it is a non-empty string. */
|
|
211111
211646
|
const asNonEmptyString = (v) => typeof v === "string" && v.length > 0 ? some(v) : none$4();
|
|
211112
211647
|
/** Lift an unknown value into `Option<Record>` if it is a plain object. */
|
|
211113
|
-
const asRecord$
|
|
211648
|
+
const asRecord$9 = (v) => typeof v === "object" && v !== null && !globalThis.Array.isArray(v) ? some(v) : none$4();
|
|
211114
211649
|
/**
|
|
211115
211650
|
* Walk an unknown parsed JSON value looking for a subscription/plan
|
|
211116
211651
|
* identifier, returning the first match as an `Option`.
|
|
211117
211652
|
*/
|
|
211118
211653
|
function findSubscriptionType(value) {
|
|
211119
211654
|
if (globalThis.Array.isArray(value)) return firstSomeOf(value.map(findSubscriptionType));
|
|
211120
|
-
return asRecord$
|
|
211655
|
+
return asRecord$9(value).pipe(flatMap$2((record) => {
|
|
211121
211656
|
const direct = firstSomeOf(SUBSCRIPTION_TYPE_KEYS.map((key) => asNonEmptyString(record[key])));
|
|
211122
211657
|
if (isSome(direct)) return direct;
|
|
211123
|
-
return firstSomeOf(SUBSCRIPTION_CONTAINER_KEYS.map((key) => asRecord$
|
|
211658
|
+
return firstSomeOf(SUBSCRIPTION_CONTAINER_KEYS.map((key) => asRecord$9(record[key]).pipe(flatMap$2(findSubscriptionType))));
|
|
211124
211659
|
}));
|
|
211125
211660
|
}
|
|
211126
211661
|
function findAuthMethod(value) {
|
|
211127
211662
|
if (globalThis.Array.isArray(value)) return firstSomeOf(value.map(findAuthMethod));
|
|
211128
|
-
return asRecord$
|
|
211663
|
+
return asRecord$9(value).pipe(flatMap$2((record) => {
|
|
211129
211664
|
const direct = firstSomeOf(AUTH_METHOD_KEYS.map((key) => asNonEmptyString(record[key])));
|
|
211130
211665
|
if (isSome(direct)) return direct;
|
|
211131
|
-
return firstSomeOf(AUTH_METHOD_CONTAINER_KEYS.map((key) => asRecord$
|
|
211666
|
+
return firstSomeOf(AUTH_METHOD_CONTAINER_KEYS.map((key) => asRecord$9(record[key]).pipe(flatMap$2(findAuthMethod))));
|
|
211132
211667
|
}));
|
|
211133
211668
|
}
|
|
211134
211669
|
/**
|
|
@@ -212328,10 +212863,27 @@ function claudeForgeAttributionPrompt() {
|
|
|
212328
212863
|
const attribution = resolveAgentAttribution();
|
|
212329
212864
|
return `When creating git commits, do NOT add any "Claude" or "Anthropic" co-author trailers. Use exactly:\n ${attribution.coauthorTrailer}\n\nWhen creating GitHub pull request descriptions, do NOT add any "🤖 Generated with [Claude Code]" footer. Use exactly:\n ${attribution.prFooter}`;
|
|
212330
212865
|
}
|
|
212331
|
-
|
|
212866
|
+
/**
|
|
212867
|
+
* `credentialDirectory` points this turn at one Provider Account's own
|
|
212868
|
+
* `.credentials.json` without moving the transcripts, which is what lets an
|
|
212869
|
+
* account switch keep `--resume` working
|
|
212870
|
+
* (`docs/adr/claude-accounts-split-credentials-not-sessions.md`). It is null on
|
|
212871
|
+
* every runtime today: `decideClaudeCredentialDirectory` only hands back a
|
|
212872
|
+
* directory that already holds a readable credential file, and nothing writes
|
|
212873
|
+
* one yet, so the environment stays byte-for-byte what it was.
|
|
212874
|
+
*
|
|
212875
|
+
* It applies to the personal-subscription branch below and to nothing else. On
|
|
212876
|
+
* the profile / proxy branch the credential file is not what authenticates the
|
|
212877
|
+
* turn, an API key is, so pointing the variable there would add a second source
|
|
212878
|
+
* of truth that a later reader has to rule out.
|
|
212879
|
+
*/
|
|
212880
|
+
function buildClaudeQueryEnv(baseProcessEnv, endpointProfile, claudeSettings, providerProxyCredential, credentialDirectory) {
|
|
212332
212881
|
const baseEnv = scrubForgeProviderChildEnvSecrets(baseProcessEnv);
|
|
212333
212882
|
const effectiveBaseUrl = resolveClaudeQueryBaseUrl(endpointProfile, claudeSettings, providerProxyCredential);
|
|
212334
|
-
if (!effectiveBaseUrl) return
|
|
212883
|
+
if (!effectiveBaseUrl) return {
|
|
212884
|
+
...withoutInheritedAnthropicCredentials(baseEnv, { stripEndpointOverride: personalClaudeInVmEnabled(baseProcessEnv) }),
|
|
212885
|
+
...credentialDirectory ? { [CLAUDE_SECURESTORAGE_CONFIG_DIR_ENV]: credentialDirectory } : {}
|
|
212886
|
+
};
|
|
212335
212887
|
const effectiveApiKey = resolveClaudeQueryApiKey(endpointProfile, claudeSettings, providerProxyCredential?.token ?? null);
|
|
212336
212888
|
return {
|
|
212337
212889
|
...baseEnv,
|
|
@@ -212374,7 +212926,7 @@ function userInputQuestionsFromToolInput(toolInput) {
|
|
|
212374
212926
|
return (Array.isArray(toolInput.questions) ? toolInput.questions : []).map((question, idx) => userInputQuestionFromUnknown(question, idx));
|
|
212375
212927
|
}
|
|
212376
212928
|
function userInputQuestionFromUnknown(question, idx) {
|
|
212377
|
-
const value = asRecord$
|
|
212929
|
+
const value = asRecord$8(question) ?? {};
|
|
212378
212930
|
return {
|
|
212379
212931
|
id: typeof value.header === "string" ? value.header : `q-${idx}`,
|
|
212380
212932
|
header: typeof value.header === "string" ? value.header : `Question ${idx + 1}`,
|
|
@@ -212384,13 +212936,13 @@ function userInputQuestionFromUnknown(question, idx) {
|
|
|
212384
212936
|
};
|
|
212385
212937
|
}
|
|
212386
212938
|
function userInputOptionFromUnknown(option) {
|
|
212387
|
-
const value = asRecord$
|
|
212939
|
+
const value = asRecord$8(option) ?? {};
|
|
212388
212940
|
return {
|
|
212389
212941
|
label: typeof value.label === "string" ? value.label : "",
|
|
212390
212942
|
description: typeof value.description === "string" ? value.description : ""
|
|
212391
212943
|
};
|
|
212392
212944
|
}
|
|
212393
|
-
function asRecord$
|
|
212945
|
+
function asRecord$8(value) {
|
|
212394
212946
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
212395
212947
|
}
|
|
212396
212948
|
function permissionResultFromDecision(decision, toolInput, suggestions) {
|
|
@@ -213294,7 +213846,7 @@ const makeClaudeAdapter = fn$1("makeClaudeAdapter")(function* (options) {
|
|
|
213294
213846
|
const updateResumeCursor = fn$1("updateResumeCursor")(function* (context) {
|
|
213295
213847
|
const threadId = context.session.threadId;
|
|
213296
213848
|
if (!threadId) return;
|
|
213297
|
-
const providerDeliveryBinding = readProviderDeliveryBinding(asRecord$
|
|
213849
|
+
const providerDeliveryBinding = readProviderDeliveryBinding(asRecord$8(context.session.resumeCursor)?.providerDeliveryBinding);
|
|
213298
213850
|
const resumeCursor = {
|
|
213299
213851
|
threadId,
|
|
213300
213852
|
...context.resumeSessionId ? { resume: context.resumeSessionId } : {},
|
|
@@ -214599,6 +215151,11 @@ const makeClaudeAdapter = fn$1("makeClaudeAdapter")(function* (options) {
|
|
|
214599
215151
|
})));
|
|
214600
215152
|
const claudeSettings = serverSettings.providers.claudeAgent;
|
|
214601
215153
|
const claudeBinaryPath = claudeSettings.binaryPath;
|
|
215154
|
+
const claudeCredentialDirectory = yield* tryPromise(() => decideClaudeCredentialDirectory({
|
|
215155
|
+
env: process.env,
|
|
215156
|
+
accountIdentity: resolveClaudeProviderAccountIdentity(process.env),
|
|
215157
|
+
probeSupport: () => claudeCredentialDirectoryProbeFor(claudeBinaryPath).support()
|
|
215158
|
+
})).pipe(map$3((decision) => decision.outcome === "set" ? decision.directory : null), orElseSucceed(() => null));
|
|
214602
215159
|
const modelConfig = resolveClaudeSessionModelConfig(input.modelSelection, claudeSettings.endpointProfiles);
|
|
214603
215160
|
const providerProxyCredential = yield* issueClaudeProviderProxyCredential(modelConfig.endpointProfile, modelConfig.modelSlug ?? modelConfig.effectiveApiModelId ?? "unknown", threadId);
|
|
214604
215161
|
const permissionMode = permissionModeForRuntimeMode(input.runtimeMode);
|
|
@@ -214623,7 +215180,7 @@ const makeClaudeAdapter = fn$1("makeClaudeAdapter")(function* (options) {
|
|
|
214623
215180
|
existingResumeSessionId,
|
|
214624
215181
|
newSessionId,
|
|
214625
215182
|
canUseTool,
|
|
214626
|
-
env: buildClaudeQueryEnv(process.env, modelConfig.endpointProfile, claudeSettings, providerProxyCredential),
|
|
215183
|
+
env: buildClaudeQueryEnv(process.env, modelConfig.endpointProfile, claudeSettings, providerProxyCredential, claudeCredentialDirectory),
|
|
214627
215184
|
browserManager,
|
|
214628
215185
|
threadId,
|
|
214629
215186
|
browserToolEnvironmentId,
|
|
@@ -218369,7 +218926,7 @@ function makeAcpContentDeltaEvent(input) {
|
|
|
218369
218926
|
}
|
|
218370
218927
|
//#endregion
|
|
218371
218928
|
//#region ../../packages/shared/src/toolActivity.ts
|
|
218372
|
-
function asRecord$
|
|
218929
|
+
function asRecord$7(value) {
|
|
218373
218930
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
218374
218931
|
}
|
|
218375
218932
|
function asTrimmedString(value) {
|
|
@@ -218395,10 +218952,10 @@ function extractCommandFromTitle$1(title) {
|
|
|
218395
218952
|
return /`([^`]+)`/u.exec(title)?.[1]?.trim() || void 0;
|
|
218396
218953
|
}
|
|
218397
218954
|
function extractToolCommand(data, title) {
|
|
218398
|
-
const item = asRecord$
|
|
218399
|
-
const itemInput = asRecord$
|
|
218400
|
-
const itemResult = asRecord$
|
|
218401
|
-
const rawInput = asRecord$
|
|
218955
|
+
const item = asRecord$7(data?.item);
|
|
218956
|
+
const itemInput = asRecord$7(item?.input);
|
|
218957
|
+
const itemResult = asRecord$7(item?.result);
|
|
218958
|
+
const rawInput = asRecord$7(data?.rawInput);
|
|
218402
218959
|
const direct = [
|
|
218403
218960
|
normalizeCommandValue$1(item?.command),
|
|
218404
218961
|
normalizeCommandValue$1(itemInput?.command),
|
|
@@ -218426,7 +218983,7 @@ function collectPaths(value, paths, seen, depth) {
|
|
|
218426
218983
|
}
|
|
218427
218984
|
return;
|
|
218428
218985
|
}
|
|
218429
|
-
const record = asRecord$
|
|
218986
|
+
const record = asRecord$7(value);
|
|
218430
218987
|
if (!record) return;
|
|
218431
218988
|
for (const key of [
|
|
218432
218989
|
"path",
|
|
@@ -218485,7 +219042,7 @@ function deriveToolActivityPresentation(input) {
|
|
|
218485
219042
|
const title = asTrimmedString(input.title);
|
|
218486
219043
|
const detail = stripTrailingExitCode(asTrimmedString(input.detail));
|
|
218487
219044
|
const fallbackSummary = asTrimmedString(input.fallbackSummary) ?? "Tool";
|
|
218488
|
-
const data = asRecord$
|
|
219045
|
+
const data = asRecord$7(input.data);
|
|
218489
219046
|
const command = extractToolCommand(data, title);
|
|
218490
219047
|
const primaryPath = extractPrimaryPath(data);
|
|
218491
219048
|
const action = classifyToolAction({
|
|
@@ -218509,7 +219066,7 @@ function deriveToolActivityPresentation(input) {
|
|
|
218509
219066
|
...primaryPath ? { detail: primaryPath } : {}
|
|
218510
219067
|
};
|
|
218511
219068
|
if (action === "search") {
|
|
218512
|
-
const query = asTrimmedString(asRecord$
|
|
219069
|
+
const query = asTrimmedString(asRecord$7(data?.rawInput)?.query) ?? asTrimmedString(asRecord$7(data?.rawInput)?.pattern) ?? asTrimmedString(asRecord$7(data?.rawInput)?.searchTerm);
|
|
218513
219070
|
return {
|
|
218514
219071
|
summary: "Searched files",
|
|
218515
219072
|
...query ? { detail: query } : {}
|
|
@@ -229697,7 +230254,7 @@ function claudeReadinessGuardEffect(config, dependencies, resolved, input) {
|
|
|
229697
230254
|
});
|
|
229698
230255
|
}
|
|
229699
230256
|
function requiresNativeClaudeReadiness(config, provider) {
|
|
229700
|
-
return provider === "claudeAgent" && config.forgeCmmdRuntimeMode === true &&
|
|
230257
|
+
return provider === "claudeAgent" && config.forgeCmmdRuntimeMode === true && personalClaudeInVmEnabled();
|
|
229701
230258
|
}
|
|
229702
230259
|
function resolveGuardAuthority(config, readSessionId, input) {
|
|
229703
230260
|
const runtimeId = config.forgeCmmdRuntimeId?.trim();
|
|
@@ -229709,7 +230266,7 @@ function resolveGuardAuthority(config, readSessionId, input) {
|
|
|
229709
230266
|
runtimeId,
|
|
229710
230267
|
machineId,
|
|
229711
230268
|
credentialEpoch,
|
|
229712
|
-
localGenerationHash:
|
|
230269
|
+
localGenerationHash: personalClaudeGenerationHash(),
|
|
229713
230270
|
sessionId
|
|
229714
230271
|
};
|
|
229715
230272
|
}
|
|
@@ -265212,7 +265769,7 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
|
|
|
265212
265769
|
var dgram = __require("dgram");
|
|
265213
265770
|
var thunky = require_thunky();
|
|
265214
265771
|
var events = __require("events");
|
|
265215
|
-
var os
|
|
265772
|
+
var os = __require("os");
|
|
265216
265773
|
var noop = function() {};
|
|
265217
265774
|
module.exports = function(opts) {
|
|
265218
265775
|
if (!opts) opts = {};
|
|
@@ -265343,14 +265900,14 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
|
|
|
265343
265900
|
return that;
|
|
265344
265901
|
};
|
|
265345
265902
|
function defaultInterface() {
|
|
265346
|
-
var networks = os
|
|
265903
|
+
var networks = os.networkInterfaces();
|
|
265347
265904
|
var names = Object.keys(networks);
|
|
265348
265905
|
for (var i = 0; i < names.length; i++) {
|
|
265349
265906
|
var net = networks[names[i]];
|
|
265350
265907
|
for (var j = 0; j < net.length; j++) {
|
|
265351
265908
|
var iface = net[j];
|
|
265352
265909
|
if (isIPv4(iface.family) && !iface.internal) {
|
|
265353
|
-
if (os
|
|
265910
|
+
if (os.platform() === "darwin" && names[i] === "en0") return iface.address;
|
|
265354
265911
|
return "0.0.0.0";
|
|
265355
265912
|
}
|
|
265356
265913
|
}
|
|
@@ -265358,7 +265915,7 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
|
|
|
265358
265915
|
return "127.0.0.1";
|
|
265359
265916
|
}
|
|
265360
265917
|
function allInterfaces() {
|
|
265361
|
-
var networks = os
|
|
265918
|
+
var networks = os.networkInterfaces();
|
|
265362
265919
|
var names = Object.keys(networks);
|
|
265363
265920
|
var res = [];
|
|
265364
265921
|
for (var i = 0; i < names.length; i++) {
|
|
@@ -285311,7 +285868,7 @@ function makeApprovedWorkerPlanEventState() {
|
|
|
285311
285868
|
function promptKey(masterThreadId, promptId) {
|
|
285312
285869
|
return `${masterThreadId}\n${promptId}`;
|
|
285313
285870
|
}
|
|
285314
|
-
function asRecord$
|
|
285871
|
+
function asRecord$6(value) {
|
|
285315
285872
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
285316
285873
|
}
|
|
285317
285874
|
function exactMaterializedTaskIds(value, expectedCount) {
|
|
@@ -285321,7 +285878,7 @@ function exactMaterializedTaskIds(value, expectedCount) {
|
|
|
285321
285878
|
}
|
|
285322
285879
|
function readyBatchFromActivity(state, event) {
|
|
285323
285880
|
if (event.payload.activity.kind !== "plan.task-creation.batch-succeeded") return null;
|
|
285324
|
-
const payload = asRecord$
|
|
285881
|
+
const payload = asRecord$6(event.payload.activity.payload);
|
|
285325
285882
|
const promptId = payload?.["promptId"];
|
|
285326
285883
|
if (typeof promptId !== "string") return null;
|
|
285327
285884
|
const key = promptKey(event.payload.threadId, promptId);
|
|
@@ -286497,7 +287054,7 @@ function runtimeGrantNeedsRefresh(token, identity, nowSeconds = Math.floor(Date.
|
|
|
286497
287054
|
return claims.expiresAtSeconds - nowSeconds <= RUNTIME_GRANT_REFRESH_SKEW_SECONDS;
|
|
286498
287055
|
}
|
|
286499
287056
|
/** Narrow an unknown to a plain object, or null. */
|
|
286500
|
-
function asRecord$
|
|
287057
|
+
function asRecord$5(value) {
|
|
286501
287058
|
return value !== null && typeof value === "object" ? value : null;
|
|
286502
287059
|
}
|
|
286503
287060
|
/** The raw (untrimmed) string when it is present and not blank, else null. */
|
|
@@ -286509,7 +287066,7 @@ function asTimestamp(value) {
|
|
|
286509
287066
|
return typeof value === "string" && Number.isFinite(Date.parse(value)) ? value : null;
|
|
286510
287067
|
}
|
|
286511
287068
|
function parseRuntimeGrantRefreshResponse(runtimeId, value) {
|
|
286512
|
-
const data = asRecord$
|
|
287069
|
+
const data = asRecord$5(asRecord$5(value)?.data);
|
|
286513
287070
|
if (!data || data.runtimeId !== runtimeId) return null;
|
|
286514
287071
|
const grantToken = asNonBlankString(data.grantToken);
|
|
286515
287072
|
const expiresAt = asTimestamp(data.expiresAt);
|
|
@@ -286570,7 +287127,6 @@ async function refreshRuntimeProvisioningGrant(input) {
|
|
|
286570
287127
|
async function maybeRefreshRuntimeProvisioningGrant(input) {
|
|
286571
287128
|
const current = runtimeGrantToken();
|
|
286572
287129
|
if (!current) return "skipped";
|
|
286573
|
-
if (!runtimeGrantNeedsRefresh(current, input)) return "skipped";
|
|
286574
287130
|
const exactIdentity = exactRuntimeControlIdentity(input);
|
|
286575
287131
|
const currentClaims = currentGrantClaimsForRefresh({
|
|
286576
287132
|
currentGrant: current,
|
|
@@ -286579,6 +287135,11 @@ async function maybeRefreshRuntimeProvisioningGrant(input) {
|
|
|
286579
287135
|
});
|
|
286580
287136
|
if (!currentClaims) throw invalidRefreshResponse("Current runtime provisioning grant is unverifiable.");
|
|
286581
287137
|
const currentAuthorityClass = currentGrantAuthorityClass(currentClaims, exactIdentity);
|
|
287138
|
+
const controlAuthorityMissing = currentAuthorityClass === "exact" && exactIdentity !== null && readRuntimeControlAuthority({
|
|
287139
|
+
runtimeId: input.runtimeId,
|
|
287140
|
+
...exactIdentity
|
|
287141
|
+
}) === null;
|
|
287142
|
+
if (!runtimeGrantNeedsRefresh(current, input) && !controlAuthorityMissing) return "skipped";
|
|
286582
287143
|
const refreshed = await refreshRuntimeProvisioningGrant({
|
|
286583
287144
|
runtimeId: input.runtimeId,
|
|
286584
287145
|
currentGrant: current,
|
|
@@ -286865,25 +287426,124 @@ const makeRuntimeDrainReporter = gen(function* () {
|
|
|
286865
287426
|
var RuntimeDrainReporter = class extends Service$1()("forge/runtimeDrain/RuntimeDrainReporter") {};
|
|
286866
287427
|
const RuntimeDrainReporterLive = effect(RuntimeDrainReporter, makeRuntimeDrainReporter);
|
|
286867
287428
|
//#endregion
|
|
286868
|
-
//#region src/usage/
|
|
286869
|
-
/**
|
|
286870
|
-
*
|
|
286871
|
-
*
|
|
286872
|
-
*
|
|
286873
|
-
*
|
|
286874
|
-
|
|
286875
|
-
|
|
286876
|
-
|
|
286877
|
-
|
|
286878
|
-
|
|
286879
|
-
|
|
286880
|
-
|
|
286881
|
-
|
|
286882
|
-
|
|
286883
|
-
|
|
286884
|
-
|
|
286885
|
-
|
|
286886
|
-
|
|
287429
|
+
//#region src/usage/Services/ClaudeUsageTelemetry.ts
|
|
287430
|
+
/**
|
|
287431
|
+
* Claude usage the SDK reports on every turn, published as passive telemetry.
|
|
287432
|
+
*
|
|
287433
|
+
* Every Claude turn carries a `rate_limit_event` whose `unifiedWindows`
|
|
287434
|
+
* sub-object holds the two rolling windows the Usage card renders. Reading it
|
|
287435
|
+
* costs nothing: no `claude` process, no credential delivery, no woken sprite.
|
|
287436
|
+
*
|
|
287437
|
+
* This module is the whole publish lane behind one call. `observe` takes what
|
|
287438
|
+
* an observation knows and returns void. Whether the floor admits it, which
|
|
287439
|
+
* credential generation it ran on, whether the value is new, and whether CMMD
|
|
287440
|
+
* accepted it are all decided here, so the ingestion layer stays a decoder and
|
|
287441
|
+
* a hand-off. Nothing decided here can fail a turn, and no admitted
|
|
287442
|
+
* observation outlives `CLAUDE_USAGE_TELEMETRY_PUBLISH_TIMEOUT_MS`.
|
|
287443
|
+
*
|
|
287444
|
+
* The `/usage` probe in `syncClaudeCredential.ts` is a separate lane that
|
|
287445
|
+
* publishes `source: "active_poll"`. This one publishes
|
|
287446
|
+
* `source: "passive_sdk_telemetry"`, and both lanes build their body with
|
|
287447
|
+
* `claudeUsageTelemetryBody`.
|
|
287448
|
+
*
|
|
287449
|
+
* @module ClaudeUsageTelemetry
|
|
287450
|
+
*/
|
|
287451
|
+
var ClaudeUsageTelemetry = class extends Service$1()("forge/usage/Services/ClaudeUsageTelemetry") {};
|
|
287452
|
+
/**
|
|
287453
|
+
* The longest one admitted observation may take, from the credential
|
|
287454
|
+
* generation lookup through the CMMD credential and the POST, before it is
|
|
287455
|
+
* abandoned and counted as failed. Ingestion does not wait on it, so this
|
|
287456
|
+
* bounds how long a slow lookup or a slow CMMD keeps a fiber alive, not how
|
|
287457
|
+
* long a turn waits. Together with the floor it caps how many observations for
|
|
287458
|
+
* one account can be in flight at once.
|
|
287459
|
+
*/
|
|
287460
|
+
const CLAUDE_USAGE_TELEMETRY_PUBLISH_TIMEOUT_MS = 1e4;
|
|
287461
|
+
/** How many accounts, and how many pairs, the observer remembers before evicting the oldest. */
|
|
287462
|
+
const MAX_TRACKED_KEYS = 32;
|
|
287463
|
+
/**
|
|
287464
|
+
* Why one publish did not land. Every reason is the same to `observe`, which
|
|
287465
|
+
* logs it and moves on, so the type exists to name the lane rather than to be
|
|
287466
|
+
* matched on.
|
|
287467
|
+
*/
|
|
287468
|
+
var ClaudeUsageTelemetryPublishError = class extends TaggedError("ClaudeUsageTelemetryPublishError") {};
|
|
287469
|
+
function pairKey(attribution) {
|
|
287470
|
+
return `${attribution.providerAccountId}\u0000${attribution.credentialGenerationId}`;
|
|
287471
|
+
}
|
|
287472
|
+
function windowFingerprint(value) {
|
|
287473
|
+
return value === null ? "-" : `${value.usedPercent}|${value.resetsAt}|${value.status}`;
|
|
287474
|
+
}
|
|
287475
|
+
/**
|
|
287476
|
+
* A value fingerprint built field by field rather than by `JSON.stringify` on
|
|
287477
|
+
* the whole object, so the comparison does not depend on key insertion order
|
|
287478
|
+
* and cannot silently start ignoring a field a future mapper adds without a
|
|
287479
|
+
* matching change here.
|
|
287480
|
+
*/
|
|
287481
|
+
function windowsFingerprint(windows) {
|
|
287482
|
+
return `${windowFingerprint(windows.fiveHour)}/${windowFingerprint(windows.sevenDay)}`;
|
|
287483
|
+
}
|
|
287484
|
+
/**
|
|
287485
|
+
* Stores `value` as the newest entry for `key` and evicts the oldest entry past
|
|
287486
|
+
* the cap. Map iteration is insertion-ordered, so the first key is the one
|
|
287487
|
+
* touched longest ago. An idle account or a rotated credential generation
|
|
287488
|
+
* rarely comes back, so evicting it costs at most one early lookup or one
|
|
287489
|
+
* redundant publish.
|
|
287490
|
+
*/
|
|
287491
|
+
function remember(map, key, value) {
|
|
287492
|
+
map.delete(key);
|
|
287493
|
+
map.set(key, value);
|
|
287494
|
+
if (map.size > MAX_TRACKED_KEYS) {
|
|
287495
|
+
const oldest = map.keys().next();
|
|
287496
|
+
if (!oldest.done) map.delete(oldest.value);
|
|
287497
|
+
}
|
|
287498
|
+
}
|
|
287499
|
+
/**
|
|
287500
|
+
* Builds the observer over injected dependencies. The HTTP call and the clock
|
|
287501
|
+
* both arrive from outside, so a test drives the whole policy without a network
|
|
287502
|
+
* or a timer.
|
|
287503
|
+
*/
|
|
287504
|
+
function makeClaudeUsageTelemetry(dependencies) {
|
|
287505
|
+
const now = dependencies.now ?? (() => Date.now());
|
|
287506
|
+
/** When each account last had an observation admitted, whatever came of it. */
|
|
287507
|
+
const admittedAt = /* @__PURE__ */ new Map();
|
|
287508
|
+
/** The fingerprint of the last value CMMD accepted, per pair. */
|
|
287509
|
+
const publishedFingerprints = /* @__PURE__ */ new Map();
|
|
287510
|
+
const publishIfNew = (windows, providerAccountId, lookupCredentialGenerationId, at) => gen(function* () {
|
|
287511
|
+
const credentialGenerationId = yield* lookupCredentialGenerationId;
|
|
287512
|
+
if (!credentialGenerationId) return;
|
|
287513
|
+
const attribution = {
|
|
287514
|
+
providerAccountId,
|
|
287515
|
+
credentialGenerationId
|
|
287516
|
+
};
|
|
287517
|
+
const key = pairKey(attribution);
|
|
287518
|
+
const fingerprint = windowsFingerprint(windows);
|
|
287519
|
+
if (publishedFingerprints.get(key) === fingerprint) return;
|
|
287520
|
+
yield* dependencies.publish(claudeUsageTelemetryBody({
|
|
287521
|
+
attribution,
|
|
287522
|
+
source: "passive_sdk_telemetry",
|
|
287523
|
+
windows,
|
|
287524
|
+
observedAt: new Date(at).toISOString()
|
|
287525
|
+
}));
|
|
287526
|
+
remember(publishedFingerprints, key, fingerprint);
|
|
287527
|
+
});
|
|
287528
|
+
/**
|
|
287529
|
+
* The floor is read and claimed inside the returned effect, never while
|
|
287530
|
+
* building it, and before anything suspends. Two turns on one account can be
|
|
287531
|
+
* in flight at once, and the floor only holds them apart if the second one
|
|
287532
|
+
* reads what the first one wrote.
|
|
287533
|
+
*/
|
|
287534
|
+
const observe = (observation) => suspend$1(() => {
|
|
287535
|
+
const { windows, providerAccountId } = observation;
|
|
287536
|
+
if (!windows || !providerAccountId) return void_$1;
|
|
287537
|
+
const at = now();
|
|
287538
|
+
const lastAdmittedAt = admittedAt.get(providerAccountId);
|
|
287539
|
+
if (lastAdmittedAt !== void 0 && at - lastAdmittedAt < 5e3) return void_$1;
|
|
287540
|
+
remember(admittedAt, providerAccountId, at);
|
|
287541
|
+
return publishIfNew(windows, providerAccountId, observation.credentialGenerationId, at).pipe(timeout$1(CLAUDE_USAGE_TELEMETRY_PUBLISH_TIMEOUT_MS), catchCause((cause) => logWarning$1("claude usage passive telemetry publish failed", {
|
|
287542
|
+
providerAccountId,
|
|
287543
|
+
cause
|
|
287544
|
+
})), asVoid);
|
|
287545
|
+
});
|
|
287546
|
+
return { observe };
|
|
286887
287547
|
}
|
|
286888
287548
|
//#endregion
|
|
286889
287549
|
//#region src/orchestration/Layers/ProviderRuntimeIngestion.ts
|
|
@@ -286898,20 +287558,37 @@ const BUFFERED_PROPOSED_PLAN_BY_ID_TTL = minutes(120);
|
|
|
286898
287558
|
const MAX_BUFFERED_ASSISTANT_CHARS = 24e3;
|
|
286899
287559
|
const PROVIDER_INGESTION_CONCURRENCY = 8;
|
|
286900
287560
|
const STRICT_PROVIDER_LIFECYCLE_GUARD = process.env.FORGE_STRICT_PROVIDER_LIFECYCLE_GUARD !== "0";
|
|
286901
|
-
|
|
287561
|
+
/**
|
|
287562
|
+
* The SDK's own `rate_limit_info` off one ClaudeAdapter rate-limit event, or
|
|
287563
|
+
* nothing. Both readers below start here: the banner takes the single window
|
|
287564
|
+
* `rateLimitType` names, and passive usage telemetry takes the `unifiedWindows`
|
|
287565
|
+
* sub-object riding along on the same object.
|
|
287566
|
+
*/
|
|
287567
|
+
function readClaudeRateLimitInfo(raw) {
|
|
286902
287568
|
const rateLimitInfo = raw.rate_limit_info;
|
|
286903
|
-
if (rateLimitInfo === null || typeof rateLimitInfo !== "object") return
|
|
287569
|
+
if (rateLimitInfo === null || typeof rateLimitInfo !== "object") return null;
|
|
287570
|
+
return rateLimitInfo;
|
|
287571
|
+
}
|
|
287572
|
+
function decodeClaudeRateLimitWindows(raw) {
|
|
287573
|
+
const rateLimitInfo = readClaudeRateLimitInfo(raw);
|
|
287574
|
+
if (!rateLimitInfo) return [];
|
|
286904
287575
|
const mapped = mapClaudeRateLimitInfo(rateLimitInfo);
|
|
286905
287576
|
return mapped ? [{
|
|
286906
287577
|
window: mapped.kind === "fiveHour" ? "five_hour" : "seven_day",
|
|
286907
287578
|
...mapped.window
|
|
286908
287579
|
}] : [];
|
|
286909
287580
|
}
|
|
287581
|
+
/** The raw telemetry object ClaudeAdapter/CodexAdapter wrap as `payload.rateLimits`. */
|
|
287582
|
+
function readRateLimitPayload(event) {
|
|
287583
|
+
if (event.type !== "account.rate-limits.updated") return null;
|
|
287584
|
+
const raw = event.payload.rateLimits;
|
|
287585
|
+
return raw !== null && typeof raw === "object" ? raw : null;
|
|
287586
|
+
}
|
|
286910
287587
|
/**
|
|
286911
287588
|
* Codex's app-server protocol shape isn't type-published anywhere this
|
|
286912
|
-
* codebase has access to, so it's read defensively
|
|
286913
|
-
* is skipped rather than thrown, since this is a best-effort in-chat notice
|
|
286914
|
-
*
|
|
287589
|
+
* codebase has access to, so it's read defensively. An unrecognized shape
|
|
287590
|
+
* is skipped rather than thrown, since this is a best-effort in-chat notice
|
|
287591
|
+
* that the turn itself never depends on.
|
|
286915
287592
|
*/
|
|
286916
287593
|
/**
|
|
286917
287594
|
* Decodes ONE window of the codex app-server `account/rateLimits/updated`
|
|
@@ -287224,6 +287901,9 @@ function shouldApplyProviderTurnCompleted(activeTurnId, eventTurnId) {
|
|
|
287224
287901
|
}
|
|
287225
287902
|
const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, fn$1("make")(function* () {
|
|
287226
287903
|
const orchestrationEngine = yield* OrchestrationEngineService;
|
|
287904
|
+
const claudeUsageTelemetry = yield* ClaudeUsageTelemetry;
|
|
287905
|
+
const claudeUsageScope = yield* make$37("parallel");
|
|
287906
|
+
yield* addFinalizer$2(() => close(claudeUsageScope, void_$3));
|
|
287227
287907
|
const providerService = yield* ProviderService;
|
|
287228
287908
|
const projectionTurnRepository = yield* ProjectionTurnRepository;
|
|
287229
287909
|
const serverSettingsService = yield* ServerSettingsService;
|
|
@@ -287631,18 +288311,53 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, fn$
|
|
|
287631
288311
|
* SDKRateLimitInfo, reused from apps/server/src/usage/providerRateLimits.ts
|
|
287632
288312
|
* (same decoder the Usage page cache uses). Codex's app-server protocol
|
|
287633
288313
|
* shape isn't type-published anywhere this codebase has access to, so it's
|
|
287634
|
-
* read defensively
|
|
287635
|
-
* thrown error, since this is a best-effort in-chat notice
|
|
287636
|
-
*
|
|
288314
|
+
* read defensively. An unrecognized shape means no window update, not a
|
|
288315
|
+
* thrown error, since this is a best-effort in-chat notice that the turn
|
|
288316
|
+
* itself never depends on.
|
|
287637
288317
|
*/
|
|
287638
288318
|
function decodeRateLimitWindows(event) {
|
|
287639
|
-
|
|
287640
|
-
|
|
287641
|
-
if (raw === null || typeof raw !== "object") return [];
|
|
288319
|
+
const raw = readRateLimitPayload(event);
|
|
288320
|
+
if (!raw) return [];
|
|
287642
288321
|
if (event.provider === "claudeAgent") return decodeClaudeRateLimitWindows(raw);
|
|
287643
288322
|
if (event.provider === "codex") return decodeCodexRateLimitWindows(raw);
|
|
287644
288323
|
return [];
|
|
287645
288324
|
}
|
|
288325
|
+
/**
|
|
288326
|
+
* The credential generation a Claude thread's live session runs on, or null.
|
|
288327
|
+
*
|
|
288328
|
+
* It is the delivery identity the readiness guard verified before the
|
|
288329
|
+
* session started, which ProviderService writes into the provider resume
|
|
288330
|
+
* cursor and `listSessions` hands back on the live session. Claude's
|
|
288331
|
+
* delivery identity is `{kind: "credential_generation"}` and carries NO
|
|
288332
|
+
* account field, so it cannot stand in for the account the thread's model
|
|
288333
|
+
* selection names, and neither id is derivable from the other. The usage
|
|
288334
|
+
* observer drops an observation missing either one, because publishing under
|
|
288335
|
+
* a guessed id would write one account's usage onto another account's card.
|
|
288336
|
+
*/
|
|
288337
|
+
const claudeCredentialGenerationId = fn$1("claudeCredentialGenerationId")(function* (threadId) {
|
|
288338
|
+
const cursor = (yield* providerService.listSessions()).find((entry) => entry.threadId === threadId && entry.provider === "claudeAgent")?.resumeCursor;
|
|
288339
|
+
const binding = cursor !== null && typeof cursor === "object" ? readProviderDeliveryBinding(cursor.providerDeliveryBinding) : null;
|
|
288340
|
+
return binding?.identity.kind === "credential_generation" ? binding.identity.credentialGenerationId : null;
|
|
288341
|
+
});
|
|
288342
|
+
/**
|
|
288343
|
+
* Publishes the unified windows the same event already carried for the
|
|
288344
|
+
* banner. Passive: no probe, no credential delivery, no woken sprite. The SDK
|
|
288345
|
+
* reports these figures on every turn whether anything reads them or not.
|
|
288346
|
+
* The observer owns the floor, the credential generation lookup, dedupe, the
|
|
288347
|
+
* timeout and the HTTP call, and never fails.
|
|
288348
|
+
*/
|
|
288349
|
+
const observeClaudeUsage = fn$1("observeClaudeUsage")(function* (event, thread) {
|
|
288350
|
+
if (event.provider !== "claudeAgent") return;
|
|
288351
|
+
const raw = readRateLimitPayload(event);
|
|
288352
|
+
const rateLimitInfo = raw ? readClaudeRateLimitInfo(raw) : null;
|
|
288353
|
+
const windows = rateLimitInfo ? mapClaudeUnifiedWindows(rateLimitInfo) : null;
|
|
288354
|
+
if (!windows) return;
|
|
288355
|
+
yield* claudeUsageTelemetry.observe({
|
|
288356
|
+
windows,
|
|
288357
|
+
providerAccountId: readProviderAccountId(thread.modelSelection) ?? null,
|
|
288358
|
+
credentialGenerationId: claudeCredentialGenerationId(thread.id)
|
|
288359
|
+
});
|
|
288360
|
+
});
|
|
287646
288361
|
const handleRateLimitEvent = fn$1("handleRateLimitEvent")(function* (event, thread) {
|
|
287647
288362
|
const provider = event.provider === "claudeAgent" ? "claude" : event.provider === "codex" ? "codex" : null;
|
|
287648
288363
|
if (!provider) return;
|
|
@@ -287658,6 +288373,10 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, fn$
|
|
|
287658
288373
|
resetsAt: win.resetsAt,
|
|
287659
288374
|
createdAt: event.createdAt
|
|
287660
288375
|
}), { discard: true });
|
|
288376
|
+
yield* observeClaudeUsage(event, thread).pipe(catchCause((cause) => hasInterruptsOnly(cause) ? void_$1 : logWarning$1("claude usage observation failed", {
|
|
288377
|
+
eventId: event.eventId,
|
|
288378
|
+
cause
|
|
288379
|
+
})), forkIn(claudeUsageScope));
|
|
287661
288380
|
});
|
|
287662
288381
|
const processRuntimeEvent = fn$1("processRuntimeEvent")(function* (event) {
|
|
287663
288382
|
const thread = (yield* orchestrationEngine.getReadModel()).threads.find((entry) => entry.id === event.threadId);
|
|
@@ -287772,6 +288491,44 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, fn$
|
|
|
287772
288491
|
};
|
|
287773
288492
|
})()).pipe(provide$1(ProjectionTurnRepositoryLive));
|
|
287774
288493
|
//#endregion
|
|
288494
|
+
//#region src/usage/Layers/ClaudeUsageTelemetry.ts
|
|
288495
|
+
/**
|
|
288496
|
+
* The real publish behind `ClaudeUsageTelemetry`: the owner's CMMD credential
|
|
288497
|
+
* and one POST to `CLAUDE_USAGE_TELEMETRY_PATH`.
|
|
288498
|
+
*
|
|
288499
|
+
* The call always goes out on the owner session credential, never on a
|
|
288500
|
+
* runtime grant.
|
|
288501
|
+
*
|
|
288502
|
+
* A machine with nobody signed in, or one whose CMMD credential cannot be
|
|
288503
|
+
* minted, fails the publish rather than reporting success. The observer only
|
|
288504
|
+
* remembers a value it managed to send, so a quiet success here would suppress
|
|
288505
|
+
* the publish for as long as that reading stayed the same, which on a stable
|
|
288506
|
+
* account is hours.
|
|
288507
|
+
*
|
|
288508
|
+
* @module ClaudeUsageTelemetryLive
|
|
288509
|
+
*/
|
|
288510
|
+
const LOG_PREFIX = "claude-usage-passive-telemetry";
|
|
288511
|
+
const ClaudeUsageTelemetryLive = effect(ClaudeUsageTelemetry, gen(function* () {
|
|
288512
|
+
const sql = yield* SqlClient;
|
|
288513
|
+
const publish = (body) => gen(function* () {
|
|
288514
|
+
const credential = yield* resolveCmmdOwnerSessionCredential(sql, { logPrefix: LOG_PREFIX });
|
|
288515
|
+
if (credential.kind !== "credential") return yield* new ClaudeUsageTelemetryPublishError({ reason: credential.kind });
|
|
288516
|
+
yield* tryPromise({
|
|
288517
|
+
try: () => cmmdGateway.request({
|
|
288518
|
+
method: "POST",
|
|
288519
|
+
jwt: credential.jwt,
|
|
288520
|
+
path: CLAUDE_USAGE_TELEMETRY_PATH,
|
|
288521
|
+
body
|
|
288522
|
+
}),
|
|
288523
|
+
catch: (cause) => new ClaudeUsageTelemetryPublishError({
|
|
288524
|
+
reason: "request-failed",
|
|
288525
|
+
cause
|
|
288526
|
+
})
|
|
288527
|
+
});
|
|
288528
|
+
});
|
|
288529
|
+
return makeClaudeUsageTelemetry({ publish });
|
|
288530
|
+
}));
|
|
288531
|
+
//#endregion
|
|
287775
288532
|
//#region ../../packages/shared/src/DrainableWorker.ts
|
|
287776
288533
|
/**
|
|
287777
288534
|
* Create a drainable worker that processes items from an unbounded queue.
|
|
@@ -293520,7 +294277,7 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
293520
294277
|
* environment descriptor down instead of reporting an honest "unknown".
|
|
293521
294278
|
*/
|
|
293522
294279
|
function readBakedBuildCommit() {
|
|
293523
|
-
return "
|
|
294280
|
+
return "8cafbb87b6204b98e93c1c69253c1c590af5f8a1";
|
|
293524
294281
|
}
|
|
293525
294282
|
async function resolveServerBuildCommit(input) {
|
|
293526
294283
|
if (isFullCommitSha(input.baked)) return input.baked;
|
|
@@ -302551,7 +303308,7 @@ const LocalAutomationSchedulerLive = effectDiscard(gen(function* () {
|
|
|
302551
303308
|
}));
|
|
302552
303309
|
//#endregion
|
|
302553
303310
|
//#region src/command/providerUsageEnvironment.ts
|
|
302554
|
-
function asRecord$
|
|
303311
|
+
function asRecord$4(value) {
|
|
302555
303312
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
302556
303313
|
}
|
|
302557
303314
|
function positiveInteger(value) {
|
|
@@ -302571,7 +303328,7 @@ function readRuntimeId(row) {
|
|
|
302571
303328
|
return typeof runtimeId === "string" && runtimeId.trim() !== "" ? runtimeId : null;
|
|
302572
303329
|
}
|
|
302573
303330
|
function readCandidate(runtime, ownerUserId) {
|
|
302574
|
-
const row = asRecord$
|
|
303331
|
+
const row = asRecord$4(runtime);
|
|
302575
303332
|
if (row === null || !isOwnedRuntime(row, ownerUserId) || row["desiredState"] !== "running") return null;
|
|
302576
303333
|
const organizationId = positiveInteger(row["organizationId"]);
|
|
302577
303334
|
const environmentId = readEnvironmentId(row["environmentId"]);
|
|
@@ -302606,15 +303363,17 @@ function deduplicateCandidates(candidates) {
|
|
|
302606
303363
|
* organization from browser input.
|
|
302607
303364
|
*/
|
|
302608
303365
|
function selectProviderUsageEnvironment(rawCatalog, ownerUserId) {
|
|
302609
|
-
const runtimes = asRecord$
|
|
303366
|
+
const runtimes = asRecord$4(asRecord$4(rawCatalog)?.["data"])?.["runtimes"];
|
|
302610
303367
|
if (!Array.isArray(runtimes)) return { kind: "unavailable" };
|
|
302611
303368
|
const candidates = deduplicateCandidates(runtimes.map((runtime) => readCandidate(runtime, ownerUserId)).filter((candidate) => candidate !== null));
|
|
302612
303369
|
const bestRank = Math.min(...candidates.map((candidate) => statusRank(candidate.status)));
|
|
302613
303370
|
const bestCandidates = candidates.filter((candidate) => statusRank(candidate.status) === bestRank);
|
|
302614
303371
|
if (bestCandidates.length !== 1) return { kind: "unavailable" };
|
|
303372
|
+
const selected = bestCandidates[0];
|
|
302615
303373
|
return {
|
|
302616
303374
|
kind: "ready",
|
|
302617
|
-
organizationId:
|
|
303375
|
+
organizationId: selected.organizationId,
|
|
303376
|
+
runtimeId: selected.runtimeId
|
|
302618
303377
|
};
|
|
302619
303378
|
}
|
|
302620
303379
|
//#endregion
|
|
@@ -302635,13 +303394,22 @@ function selectProviderUsageEnvironment(rawCatalog, ownerUserId) {
|
|
|
302635
303394
|
const FAILURE_TEXT = {
|
|
302636
303395
|
CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE: "Refresh needs your Environment running.",
|
|
302637
303396
|
CLAUDE_USAGE_CREDENTIAL_NOT_READY: "The Claude credential is not applied to your Environment yet.",
|
|
303397
|
+
CLAUDE_USAGE_CREDENTIAL_APPLY_FAILED: "Your Environment rejected the delivered Claude credential.",
|
|
303398
|
+
CLAUDE_USAGE_CREDENTIAL_REFRESHING: "The delivered Claude credential expired. CMMD is sending a replacement.",
|
|
302638
303399
|
CLAUDE_USAGE_REFRESH_IN_PROGRESS: "A refresh for this account is already running.",
|
|
302639
303400
|
CLAUDE_USAGE_PROBE_FAILED: "Your Environment could not read Claude usage.",
|
|
302640
303401
|
CLAUDE_USAGE_RECONNECT_REQUIRED: "Reconnect this Claude account to refresh usage.",
|
|
302641
303402
|
CLAUDE_USAGE_ACCOUNT_NOT_FOUND: "This Claude account is no longer connected.",
|
|
302642
303403
|
CLAUDE_USAGE_REFRESH_UNAVAILABLE: "Claude usage refresh is unavailable."
|
|
302643
303404
|
};
|
|
302644
|
-
/**
|
|
303405
|
+
/**
|
|
303406
|
+
* Verbatim from CMMD `personal-claude-usage.ts` and `provider-usage-service.ts`.
|
|
303407
|
+
*
|
|
303408
|
+
* Three reasons land on `CLAUDE_USAGE_CREDENTIAL_NOT_READY` because the refresh
|
|
303409
|
+
* response cannot tell them apart. `claudeUsageCredentialReadiness.ts` splits
|
|
303410
|
+
* them afterwards by reading the runtime's readiness, which is the only place
|
|
303411
|
+
* that knows whether a delivery is on its way or has already failed.
|
|
303412
|
+
*/
|
|
302645
303413
|
const CODE_BY_UPSTREAM_REASON = {
|
|
302646
303414
|
runtime_not_active: "CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE",
|
|
302647
303415
|
claude_native_delivery_disabled: "CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE",
|
|
@@ -302654,7 +303422,12 @@ const CODE_BY_UPSTREAM_REASON = {
|
|
|
302654
303422
|
usage_receipt_invalid: "CLAUDE_USAGE_PROBE_FAILED",
|
|
302655
303423
|
usage_output_invalid: "CLAUDE_USAGE_PROBE_FAILED"
|
|
302656
303424
|
};
|
|
302657
|
-
|
|
303425
|
+
/**
|
|
303426
|
+
* The one place a failure body is built, so every sentence a browser reads is
|
|
303427
|
+
* written in `FAILURE_TEXT` above. `claudeUsageCredentialReadiness.ts` calls it
|
|
303428
|
+
* to reach the two codes no upstream reason maps to.
|
|
303429
|
+
*/
|
|
303430
|
+
function describeClaudeUsageRefreshFailureCode(code, status) {
|
|
302658
303431
|
return {
|
|
302659
303432
|
status,
|
|
302660
303433
|
body: {
|
|
@@ -302663,6 +303436,9 @@ function failure(code, status) {
|
|
|
302663
303436
|
}
|
|
302664
303437
|
};
|
|
302665
303438
|
}
|
|
303439
|
+
function failure(code, status) {
|
|
303440
|
+
return describeClaudeUsageRefreshFailureCode(code, status);
|
|
303441
|
+
}
|
|
302666
303442
|
/**
|
|
302667
303443
|
* CMMD sends the reason as `{ "error": "<reason>" }`. Anything else, including
|
|
302668
303444
|
* a longer sentence that happens to start with an allowlisted word, is treated
|
|
@@ -302683,6 +303459,77 @@ function describeClaudeUsageRefreshFailure(error) {
|
|
|
302683
303459
|
return failure("CLAUDE_USAGE_REFRESH_UNAVAILABLE", error.status === 503 ? 503 : 502);
|
|
302684
303460
|
}
|
|
302685
303461
|
//#endregion
|
|
303462
|
+
//#region src/command/claudeUsageCredentialReadiness.ts
|
|
303463
|
+
/**
|
|
303464
|
+
* Which "credential not ready" a Claude usage refresh actually hit.
|
|
303465
|
+
*
|
|
303466
|
+
* CMMD answers a blocked refresh with one reason for three different worlds:
|
|
303467
|
+
* a delivery still travelling, a delivery CMMD cannot produce, and a delivery
|
|
303468
|
+
* the Environment already ran and rejected. `claudeUsageRefreshFailure.ts` has
|
|
303469
|
+
* to collapse all three into `CLAUDE_USAGE_CREDENTIAL_NOT_READY`, because the
|
|
303470
|
+
* refresh response carries nothing that separates them. The card then says
|
|
303471
|
+
* "not applied yet", which asks the user to wait even when waiting can never
|
|
303472
|
+
* work: on 2026-09-08 CMMD's reconciler failed native credential validation on
|
|
303473
|
+
* runtime 523cd771 forty-three times in seventy-two minutes, and the card said
|
|
303474
|
+
* "yet" through all of it.
|
|
303475
|
+
*
|
|
303476
|
+
* The runtime readiness read is the one source that separates the worlds, so
|
|
303477
|
+
* the handler makes that read only for the collapsed code and hands the answer
|
|
303478
|
+
* here. Every sentence is written in `claudeUsageRefreshFailure.ts`; nothing
|
|
303479
|
+
* from CMMD's `lastErrorCode` or `lastErrorMessage` is repeated to a browser.
|
|
303480
|
+
*/
|
|
303481
|
+
const CMMD_READINESS_PATH_PREFIX = "/api/forge/context/providers/claude/runtime-readiness/";
|
|
303482
|
+
/** Typed as the gateway's absolute-path shape so the call site needs no cast. */
|
|
303483
|
+
function claudeUsageReadinessPath(runtimeId) {
|
|
303484
|
+
return `${CMMD_READINESS_PATH_PREFIX}${encodeURIComponent(runtimeId)}`;
|
|
303485
|
+
}
|
|
303486
|
+
/**
|
|
303487
|
+
* Whether a readiness read would tell the caller anything it does not know.
|
|
303488
|
+
*
|
|
303489
|
+
* Only the collapsed code is ambiguous. Every other failure already names its
|
|
303490
|
+
* own cause, so asking CMMD again would cost a round trip on the error path
|
|
303491
|
+
* and could only produce a less specific answer.
|
|
303492
|
+
*/
|
|
303493
|
+
function claudeUsageFailureWantsReadiness(failure) {
|
|
303494
|
+
return failure.body.code === "CLAUDE_USAGE_CREDENTIAL_NOT_READY";
|
|
303495
|
+
}
|
|
303496
|
+
function asRecord$3(value) {
|
|
303497
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
303498
|
+
}
|
|
303499
|
+
function readString(value) {
|
|
303500
|
+
return typeof value === "string" && value !== "" ? value : null;
|
|
303501
|
+
}
|
|
303502
|
+
/**
|
|
303503
|
+
* Refines the collapsed credential failure using the runtime's readiness.
|
|
303504
|
+
*
|
|
303505
|
+
* The ranking is by what the user can act on, not by which field looks most
|
|
303506
|
+
* severe:
|
|
303507
|
+
*
|
|
303508
|
+
* 1. A reason of `credential_reconnect_required` or `credential_not_connected`
|
|
303509
|
+
* means CMMD holds no usable grant. That is upstream of delivery, so it wins
|
|
303510
|
+
* even when the delivery it caused also failed. No re-delivery helps; only a
|
|
303511
|
+
* reconnect does.
|
|
303512
|
+
* 2. `deliveryStatus: "failed"` means CMMD had a credential, sent it, and the
|
|
303513
|
+
* Environment rejected it. Waiting cannot fix that, which is exactly what
|
|
303514
|
+
* the collapsed sentence tells the user to do.
|
|
303515
|
+
* 3. `credential_expired_refreshing` means a replacement is already in flight,
|
|
303516
|
+
* so the honest instruction is to retry shortly.
|
|
303517
|
+
*
|
|
303518
|
+
* Anything else, including a readiness read that failed or answered a shape
|
|
303519
|
+
* this module does not recognise, keeps the collapsed failure. A refinement
|
|
303520
|
+
* that guessed would be worse than the vague sentence it replaced.
|
|
303521
|
+
*/
|
|
303522
|
+
function refineClaudeUsageRefreshFailure(failure, rawReadiness) {
|
|
303523
|
+
if (!claudeUsageFailureWantsReadiness(failure)) return failure;
|
|
303524
|
+
const readiness = asRecord$3(asRecord$3(rawReadiness)?.["data"]);
|
|
303525
|
+
if (readiness === null) return failure;
|
|
303526
|
+
const reasonCode = claudeReadinessReasonCode(readString(readiness["reason"]));
|
|
303527
|
+
if (reasonCode === "CREDENTIAL_RECONNECT_REQUIRED") return describeClaudeUsageRefreshFailureCode("CLAUDE_USAGE_RECONNECT_REQUIRED", 409);
|
|
303528
|
+
if (readiness["deliveryStatus"] === "failed") return describeClaudeUsageRefreshFailureCode("CLAUDE_USAGE_CREDENTIAL_APPLY_FAILED", 503);
|
|
303529
|
+
if (reasonCode === "CREDENTIAL_EXPIRED_REFRESHING") return describeClaudeUsageRefreshFailureCode("CLAUDE_USAGE_CREDENTIAL_REFRESHING", 503);
|
|
303530
|
+
return failure;
|
|
303531
|
+
}
|
|
303532
|
+
//#endregion
|
|
302686
303533
|
//#region src/command/wsToken.ts
|
|
302687
303534
|
/**
|
|
302688
303535
|
* Build the WS token response given an optional PAT and the configured CMMD
|
|
@@ -304547,6 +305394,25 @@ function buildClaudeUsageRefreshErrorResponse(error) {
|
|
|
304547
305394
|
const failure = describeClaudeUsageRefreshFailure(error);
|
|
304548
305395
|
return jsonUnsafe(failure.body, { status: failure.status });
|
|
304549
305396
|
}
|
|
305397
|
+
/**
|
|
305398
|
+
* Answers a refresh CMMD refused, asking the selected runtime what state its
|
|
305399
|
+
* Claude credential is in when the refusal alone cannot say. Only the collapsed
|
|
305400
|
+
* credential code costs the extra read, and a readiness read that itself fails
|
|
305401
|
+
* leaves the original answer untouched.
|
|
305402
|
+
*/
|
|
305403
|
+
function answerBlockedClaudeUsageRefresh(input) {
|
|
305404
|
+
return gen(function* () {
|
|
305405
|
+
const failure = describeClaudeUsageRefreshFailure(input.error);
|
|
305406
|
+
if (!claudeUsageFailureWantsReadiness(failure)) return jsonUnsafe(failure.body, { status: failure.status });
|
|
305407
|
+
const readinessOutcome = yield* runCmmdRestProxy(input.session, (jwt) => cmmdGateway.request({
|
|
305408
|
+
method: "GET",
|
|
305409
|
+
jwt,
|
|
305410
|
+
path: claudeUsageReadinessPath(input.runtimeId)
|
|
305411
|
+
}), { jwt: input.requestJwt });
|
|
305412
|
+
const refined = refineClaudeUsageRefreshFailure(failure, readinessOutcome.ok ? readinessOutcome.value : null);
|
|
305413
|
+
return jsonUnsafe(refined.body, { status: refined.status });
|
|
305414
|
+
});
|
|
305415
|
+
}
|
|
304550
305416
|
function makeRefreshProviderUsageHandler(dependencies = {}) {
|
|
304551
305417
|
return gen(function* () {
|
|
304552
305418
|
const request = yield* HttpServerRequest;
|
|
@@ -304583,7 +305449,12 @@ function makeRefreshProviderUsageHandler(dependencies = {}) {
|
|
|
304583
305449
|
body: { providerAccountId: input.providerAccountId },
|
|
304584
305450
|
orgId: String(target.organizationId)
|
|
304585
305451
|
}), { jwt: requestJwt });
|
|
304586
|
-
if (!refreshOutcome.ok) return
|
|
305452
|
+
if (!refreshOutcome.ok) return yield* answerBlockedClaudeUsageRefresh({
|
|
305453
|
+
session,
|
|
305454
|
+
requestJwt,
|
|
305455
|
+
runtimeId: target.runtimeId,
|
|
305456
|
+
error: refreshOutcome.error
|
|
305457
|
+
});
|
|
304587
305458
|
const snapshotOutcome = yield* runCmmdRestProxy(session, (jwt) => cmmdGateway.request({
|
|
304588
305459
|
method: "GET",
|
|
304589
305460
|
jwt,
|
|
@@ -304682,6 +305553,22 @@ registerProxyRoute({
|
|
|
304682
305553
|
responseShape: "unwrapData",
|
|
304683
305554
|
successStatus: 200
|
|
304684
305555
|
});
|
|
305556
|
+
registerProxyRoute({
|
|
305557
|
+
forgePath: "/api/cmmd/forge-runtimes/:runtimeId/execution-intents",
|
|
305558
|
+
method: "POST",
|
|
305559
|
+
cmmdPath: "/api/forge/context/runtimes/:runtimeId/execution-intents",
|
|
305560
|
+
orgIdSource: "none",
|
|
305561
|
+
responseShape: "unwrapData",
|
|
305562
|
+
successStatus: 200
|
|
305563
|
+
});
|
|
305564
|
+
registerProxyRoute({
|
|
305565
|
+
forgePath: "/api/cmmd/forge-runtimes/:runtimeId/execution-intents/:intentId/dispatched",
|
|
305566
|
+
method: "POST",
|
|
305567
|
+
cmmdPath: "/api/forge/context/runtimes/:runtimeId/execution-intents/:intentId/dispatched",
|
|
305568
|
+
orgIdSource: "none",
|
|
305569
|
+
responseShape: "unwrapData",
|
|
305570
|
+
successStatus: 200
|
|
305571
|
+
});
|
|
304685
305572
|
registerProxyRoute({
|
|
304686
305573
|
forgePath: "/api/cmmd/devices/:deviceId/revoke",
|
|
304687
305574
|
method: "POST",
|
|
@@ -315874,7 +316761,7 @@ function claimClaudeReadinessJti(input) {
|
|
|
315874
316761
|
return "claimed";
|
|
315875
316762
|
}
|
|
315876
316763
|
const readUsableClaudeGenerationHash = (config) => {
|
|
315877
|
-
const configuredGenerationHash =
|
|
316764
|
+
const configuredGenerationHash = bootExportedPersonalClaudeGenerationHash();
|
|
315878
316765
|
if (!/^[a-f0-9]{64}$/.test(configuredGenerationHash)) return succeed(null);
|
|
315879
316766
|
return tryPromise(() => readFile$1(resolveLocalClaudeCredentialPath(config.forgeClaudeConfigPath), "utf8")).pipe(map$3((credentialJson) => isClaudeSubscriptionCredential(credentialJson) ? configuredGenerationHash : null), orElseSucceed(() => null));
|
|
315880
316767
|
};
|
|
@@ -316389,7 +317276,7 @@ const PlatformServicesLive = unwrap(gen(function* () {
|
|
|
316389
317276
|
}));
|
|
316390
317277
|
const RuntimeReplicationLayerLive = RuntimeReplicationLive.pipe(provideMerge(RuntimeReplicationClientLive), provideMerge(RuntimeReplicationOutboxLive), provideMerge(ActiveHourMeterLive.pipe(provide$1(RuntimeReplicationOutboxLive))));
|
|
316391
317278
|
const RuntimeDrainLayerLive = RuntimeDrainReporterLive.pipe(provideMerge(RuntimeDrainStateLive));
|
|
316392
|
-
const RuntimeAwareProviderReactorsLive = mergeAll(ProviderRuntimeIngestionLive, ProviderCommandReactorLive).pipe(provide$1(RuntimeDrainLayerLive));
|
|
317279
|
+
const RuntimeAwareProviderReactorsLive = mergeAll(ProviderRuntimeIngestionLive, ProviderCommandReactorLive).pipe(provide$1(RuntimeDrainLayerLive), provide$1(ClaudeUsageTelemetryLive));
|
|
316393
317280
|
const PipelineWorkerCoordinatorLayerLive = PipelineWorkerCoordinatorLive.pipe(provideMerge(PipelineRunClientLive));
|
|
316394
317281
|
const AgentProtocolReactorLayerLive = AgentProtocolReactorLive.pipe(provideMerge(PipelineRunStoreLive));
|
|
316395
317282
|
const PipelineRunCoordinatorLayerLive = PipelineRunCoordinatorLive.pipe(provideMerge(PipelineProviderAuthorityInstallerLive), provideMerge(PipelineChangeInspectorLive), provideMerge(PipelineContextArtifactStoreLive), provideMerge(PipelineModelPolicyLive), provideMerge(PipelineRunClientLive), provideMerge(PipelineRunStoreLive), provideMerge(PipelineRunWorkspaceAllocatorLive), provideMerge(PipelineStagePolicyLive), provideMerge(PipelineWorkerCoordinatorLayerLive));
|
|
@@ -317378,7 +318265,7 @@ const connectLoginCommand = make$34("login", {
|
|
|
317378
318265
|
})).json();
|
|
317379
318266
|
},
|
|
317380
318267
|
openBrowser: async (url) => {
|
|
317381
|
-
const open = (await import("./open-
|
|
318268
|
+
const open = (await import("./open-D8mGyWmt.mjs")).default;
|
|
317382
318269
|
await open(url);
|
|
317383
318270
|
return true;
|
|
317384
318271
|
},
|