claudish 14.15.1 → 14.16.1
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/index.js +535 -201
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -5491,7 +5491,7 @@ var init_onepassword_config = __esm(() => {
|
|
|
5491
5491
|
});
|
|
5492
5492
|
|
|
5493
5493
|
// src/version.ts
|
|
5494
|
-
var VERSION = "14.
|
|
5494
|
+
var VERSION = "14.16.1";
|
|
5495
5495
|
|
|
5496
5496
|
// src/redact.ts
|
|
5497
5497
|
function isPlaceholder(value) {
|
|
@@ -49347,10 +49347,11 @@ var init_devin_request = __esm(() => {
|
|
|
49347
49347
|
function unaryMetadata(apiKey) {
|
|
49348
49348
|
return msg(bytes(1, "chisel"), bytes(2, DEVIN_CLI_VERSION), bytes(3, apiKey), bytes(4, "en"), bytes(5, process.platform), bytes(7, DEVIN_CLI_VERSION));
|
|
49349
49349
|
}
|
|
49350
|
-
async function
|
|
49350
|
+
async function postDevinUnaryRaw(path, apiKey, opts = {}) {
|
|
49351
49351
|
const url = `${readDevinServerUrl()}${path}`;
|
|
49352
|
+
let response;
|
|
49352
49353
|
try {
|
|
49353
|
-
|
|
49354
|
+
response = await fetch(url, {
|
|
49354
49355
|
method: "POST",
|
|
49355
49356
|
headers: {
|
|
49356
49357
|
authorization: `Basic ${apiKey}-${apiKey}`,
|
|
@@ -49358,17 +49359,38 @@ async function postUnary(path, apiKey) {
|
|
|
49358
49359
|
"connect-protocol-version": "1"
|
|
49359
49360
|
},
|
|
49360
49361
|
body: msg(bytes(1, unaryMetadata(apiKey))),
|
|
49361
|
-
signal:
|
|
49362
|
+
signal: opts.signal
|
|
49362
49363
|
});
|
|
49363
|
-
|
|
49364
|
-
|
|
49365
|
-
|
|
49366
|
-
|
|
49367
|
-
return
|
|
49368
|
-
}
|
|
49369
|
-
|
|
49364
|
+
} catch (error) {
|
|
49365
|
+
return { kind: "unreachable", url, error };
|
|
49366
|
+
}
|
|
49367
|
+
if (!response.ok) {
|
|
49368
|
+
return { kind: "answered", url, status: response.status, body: { kind: "unread" } };
|
|
49369
|
+
}
|
|
49370
|
+
try {
|
|
49371
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
49372
|
+
return { kind: "answered", url, status: response.status, body: { kind: "bytes", bytes } };
|
|
49373
|
+
} catch (error) {
|
|
49374
|
+
return { kind: "answered", url, status: response.status, body: { kind: "read-failed", error } };
|
|
49375
|
+
}
|
|
49376
|
+
}
|
|
49377
|
+
async function postUnary(path, apiKey) {
|
|
49378
|
+
const raw = await postDevinUnaryRaw(path, apiKey, {
|
|
49379
|
+
signal: AbortSignal.timeout(UNARY_TIMEOUT_MS)
|
|
49380
|
+
});
|
|
49381
|
+
if (raw.kind === "unreachable") {
|
|
49382
|
+
log(`[Devin] ${path} error: ${raw.error}`);
|
|
49370
49383
|
return null;
|
|
49371
49384
|
}
|
|
49385
|
+
if (raw.status < 200 || raw.status >= 300) {
|
|
49386
|
+
log(`[Devin] ${path} failed: HTTP ${raw.status}`);
|
|
49387
|
+
return null;
|
|
49388
|
+
}
|
|
49389
|
+
if (raw.body.kind === "read-failed") {
|
|
49390
|
+
log(`[Devin] ${path} error: ${raw.body.error}`);
|
|
49391
|
+
return null;
|
|
49392
|
+
}
|
|
49393
|
+
return raw.body.kind === "bytes" ? raw.body.bytes : null;
|
|
49372
49394
|
}
|
|
49373
49395
|
function decodeModelDetails(payload) {
|
|
49374
49396
|
let maxOutput = 0;
|
|
@@ -49496,13 +49518,17 @@ async function fetchDevinAllowedUids(apiKey) {
|
|
|
49496
49518
|
const body = await postUnary(TEAM_SETTINGS_PATH, apiKey);
|
|
49497
49519
|
if (!body)
|
|
49498
49520
|
return [];
|
|
49521
|
+
const uids = decodeAllowedUids(body);
|
|
49522
|
+
log(`[Devin] GetCliTeamSettings: ${uids.length} allowed uids`);
|
|
49523
|
+
return uids;
|
|
49524
|
+
}
|
|
49525
|
+
function decodeAllowedUids(body) {
|
|
49499
49526
|
const uids = [];
|
|
49500
49527
|
for (const field of topLevelDelimited(body, 7)) {
|
|
49501
49528
|
const uid = readString(field).trim();
|
|
49502
49529
|
if (uid)
|
|
49503
49530
|
uids.push(uid);
|
|
49504
49531
|
}
|
|
49505
|
-
log(`[Devin] GetCliTeamSettings: ${uids.length} allowed uids`);
|
|
49506
49532
|
return uids;
|
|
49507
49533
|
}
|
|
49508
49534
|
async function getServedDevinModels(opts) {
|
|
@@ -53744,6 +53770,7 @@ data: ${JSON.stringify(data)}
|
|
|
53744
53770
|
if (frame.flags === FRAME_FLAG_END_OF_STREAM) {
|
|
53745
53771
|
const raw = new TextDecoder().decode(frame.payload).trim();
|
|
53746
53772
|
if (!raw || raw === "{}") {
|
|
53773
|
+
opts.onEndOfStream?.();
|
|
53747
53774
|
finalize("done");
|
|
53748
53775
|
return;
|
|
53749
53776
|
}
|
|
@@ -55921,6 +55948,27 @@ function startAllowanceFlight() {
|
|
|
55921
55948
|
return () => {};
|
|
55922
55949
|
}
|
|
55923
55950
|
}
|
|
55951
|
+
function replaceUnsupportedParts(messages, types) {
|
|
55952
|
+
const replacePart = (part) => {
|
|
55953
|
+
if (part?.type === "tool_result" && Array.isArray(part.content)) {
|
|
55954
|
+
return { ...part, content: part.content.map(replacePart) };
|
|
55955
|
+
}
|
|
55956
|
+
if (!types.includes(part?.type))
|
|
55957
|
+
return part;
|
|
55958
|
+
const kind = part.type === "document" ? "document" : "image";
|
|
55959
|
+
return { type: "text", text: `[${kind} omitted: this model does not accept ${kind} input]` };
|
|
55960
|
+
};
|
|
55961
|
+
for (const msg of messages) {
|
|
55962
|
+
if (!Array.isArray(msg.content))
|
|
55963
|
+
continue;
|
|
55964
|
+
msg.content = msg.content.map(replacePart);
|
|
55965
|
+
if (msg.content.length === 1 && msg.content[0].type === "text") {
|
|
55966
|
+
msg.content = msg.content[0].text;
|
|
55967
|
+
} else if (msg.content.length === 0) {
|
|
55968
|
+
msg.content = "";
|
|
55969
|
+
}
|
|
55970
|
+
}
|
|
55971
|
+
}
|
|
55924
55972
|
function extractAuthHeaders(c) {
|
|
55925
55973
|
const headers = c.req.header();
|
|
55926
55974
|
const auth = {};
|
|
@@ -56430,20 +56478,24 @@ class ComposedHandler {
|
|
|
56430
56478
|
tools = tools.slice(0, maxToolCount);
|
|
56431
56479
|
}
|
|
56432
56480
|
if (!this.getModelSupportsVision()) {
|
|
56481
|
+
const isUnsupportedPart = (part) => part?.type === "image_url" || part?.type === "image" || part?.type === "document";
|
|
56433
56482
|
const imageBlocks = [];
|
|
56483
|
+
let nestedImageCount = 0;
|
|
56434
56484
|
for (let msgIdx = 0;msgIdx < messages.length; msgIdx++) {
|
|
56435
56485
|
const msg = messages[msgIdx];
|
|
56436
56486
|
if (Array.isArray(msg.content)) {
|
|
56437
56487
|
for (let partIdx = 0;partIdx < msg.content.length; partIdx++) {
|
|
56438
56488
|
const part = msg.content[partIdx];
|
|
56439
|
-
if (part
|
|
56489
|
+
if (isUnsupportedPart(part)) {
|
|
56440
56490
|
imageBlocks.push({ msgIdx, partIdx, block: part });
|
|
56491
|
+
} else if (part?.type === "tool_result" && Array.isArray(part.content)) {
|
|
56492
|
+
nestedImageCount += part.content.filter(isUnsupportedPart).length;
|
|
56441
56493
|
}
|
|
56442
56494
|
}
|
|
56443
56495
|
}
|
|
56444
56496
|
}
|
|
56445
|
-
if (imageBlocks.length > 0) {
|
|
56446
|
-
log(`[ComposedHandler] Non-vision model received ${imageBlocks.length} image(s), calling vision proxy`);
|
|
56497
|
+
if (imageBlocks.length + nestedImageCount > 0) {
|
|
56498
|
+
log(`[ComposedHandler] Non-vision model received ${imageBlocks.length + nestedImageCount} image(s), calling vision proxy`);
|
|
56447
56499
|
const openAIImageBlocks = imageBlocks.filter((b) => b.block.type === "image_url");
|
|
56448
56500
|
let descriptions = null;
|
|
56449
56501
|
if (openAIImageBlocks.length > 0) {
|
|
@@ -56459,28 +56511,10 @@ class ComposedHandler {
|
|
|
56459
56511
|
};
|
|
56460
56512
|
}
|
|
56461
56513
|
log(`[ComposedHandler] Vision proxy described ${descriptions.length} image(s)`);
|
|
56462
|
-
|
|
56463
|
-
if (Array.isArray(msg.content)) {
|
|
56464
|
-
msg.content = msg.content.filter((part) => part.type !== "image" && part.type !== "document");
|
|
56465
|
-
if (msg.content.length === 1 && msg.content[0].type === "text") {
|
|
56466
|
-
msg.content = msg.content[0].text;
|
|
56467
|
-
} else if (msg.content.length === 0) {
|
|
56468
|
-
msg.content = "";
|
|
56469
|
-
}
|
|
56470
|
-
}
|
|
56471
|
-
}
|
|
56514
|
+
replaceUnsupportedParts(messages, ["image", "document"]);
|
|
56472
56515
|
} else {
|
|
56473
56516
|
log("[ComposedHandler] Stripping image/document blocks (vision not supported)");
|
|
56474
|
-
|
|
56475
|
-
if (Array.isArray(msg.content)) {
|
|
56476
|
-
msg.content = msg.content.filter((part) => part.type !== "image_url" && part.type !== "image" && part.type !== "document");
|
|
56477
|
-
if (msg.content.length === 1 && msg.content[0].type === "text") {
|
|
56478
|
-
msg.content = msg.content[0].text;
|
|
56479
|
-
} else if (msg.content.length === 0) {
|
|
56480
|
-
msg.content = "";
|
|
56481
|
-
}
|
|
56482
|
-
}
|
|
56483
|
-
}
|
|
56517
|
+
replaceUnsupportedParts(messages, ["image_url", "image", "document"]);
|
|
56484
56518
|
}
|
|
56485
56519
|
}
|
|
56486
56520
|
}
|
|
@@ -94280,6 +94314,13 @@ ${h("SUBCOMMANDS")}
|
|
|
94280
94314
|
Send the file's Gemini request once through the
|
|
94281
94315
|
Antigravity login (generateContent, no retry, model
|
|
94282
94316
|
${dim("id unchanged); print the raw answer as one JSON line")}
|
|
94317
|
+
${green("claudish subscription models devin")} ${yellow("--raw --json")}
|
|
94318
|
+
Print Devin's decoded GetCliModelConfigs rows and
|
|
94319
|
+
${dim("allowed model uids as one JSON line (for models-index)")}
|
|
94320
|
+
${green("claudish subscription probe devin")} ${yellow("--model <uid> --request-file <path> --raw --json")}
|
|
94321
|
+
Send the file's Anthropic Messages request once through
|
|
94322
|
+
the Devin login (GetChatMessage, no retry, uid unchanged);
|
|
94323
|
+
${dim("print the decoded answer as one JSON line")}
|
|
94283
94324
|
${green("claudish update")} Check for updates and install the latest version
|
|
94284
94325
|
|
|
94285
94326
|
${bold("Profiles:")}
|
|
@@ -110404,6 +110445,94 @@ var init_subscription_antigravity_session = __esm(() => {
|
|
|
110404
110445
|
TIMED_OUT = Symbol("timed-out");
|
|
110405
110446
|
});
|
|
110406
110447
|
|
|
110448
|
+
// src/subscription-devin-models.ts
|
|
110449
|
+
var exports_subscription_devin_models = {};
|
|
110450
|
+
__export(exports_subscription_devin_models, {
|
|
110451
|
+
readDevinSecrets: () => readDevinSecrets,
|
|
110452
|
+
runDevinRawModelList: () => runDevinRawModelList
|
|
110453
|
+
});
|
|
110454
|
+
function readDevinSecrets(secrets) {
|
|
110455
|
+
const apiKey = readDevinApiKey();
|
|
110456
|
+
if (!apiKey)
|
|
110457
|
+
return;
|
|
110458
|
+
addSecret(secrets, apiKey);
|
|
110459
|
+
if (apiKey.startsWith(DEVIN_SESSION_TOKEN_PREFIX)) {
|
|
110460
|
+
addSecret(secrets, apiKey.slice(DEVIN_SESSION_TOKEN_PREFIX.length));
|
|
110461
|
+
}
|
|
110462
|
+
return apiKey;
|
|
110463
|
+
}
|
|
110464
|
+
async function postBounded(path, apiKey, timeoutMs) {
|
|
110465
|
+
const controller = new AbortController;
|
|
110466
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
110467
|
+
try {
|
|
110468
|
+
const raw = await postDevinUnaryRaw(path, apiKey, { signal: controller.signal });
|
|
110469
|
+
return { raw, timedOut: controller.signal.aborted };
|
|
110470
|
+
} finally {
|
|
110471
|
+
clearTimeout(timer);
|
|
110472
|
+
}
|
|
110473
|
+
}
|
|
110474
|
+
async function determineDevin(deps, secrets, facts) {
|
|
110475
|
+
const apiKey = readDevinSecrets(secrets);
|
|
110476
|
+
if (!apiKey) {
|
|
110477
|
+
return modelListFail(facts, modelListFailureOf("credential_missing", "devin_credential_absent"));
|
|
110478
|
+
}
|
|
110479
|
+
const configsCall = await postBounded(MODEL_CONFIGS_PATH, apiKey, deps.httpTimeoutMs);
|
|
110480
|
+
facts.sourceUrl = stripUserinfo(configsCall.raw.url);
|
|
110481
|
+
facts.clientVersion = DEVIN_CLI_VERSION;
|
|
110482
|
+
const configsNetwork = configsCall.timedOut ? "model_configs_timeout" : "model_configs_network_error";
|
|
110483
|
+
if (configsCall.raw.kind === "unreachable") {
|
|
110484
|
+
return modelListFail(facts, modelListFailureOf("network", configsNetwork));
|
|
110485
|
+
}
|
|
110486
|
+
facts.fetchedAt = new Date(deps.now()).toISOString();
|
|
110487
|
+
facts.httpStatus = configsCall.raw.status;
|
|
110488
|
+
if (configsCall.raw.status < 200 || configsCall.raw.status >= 300) {
|
|
110489
|
+
return { envelope: buildModelListEnvelope(facts), exitCode: EXIT.ok };
|
|
110490
|
+
}
|
|
110491
|
+
if (configsCall.raw.body.kind !== "bytes") {
|
|
110492
|
+
return modelListFail(facts, modelListFailureOf("network", configsNetwork, configsCall.raw.status));
|
|
110493
|
+
}
|
|
110494
|
+
const configs = decodeModelConfigs(configsCall.raw.body.bytes);
|
|
110495
|
+
if (configs.length === 0) {
|
|
110496
|
+
return modelListFail(facts, modelListFailureOf("list_incomplete", "configs_empty", configsCall.raw.status));
|
|
110497
|
+
}
|
|
110498
|
+
const teamCall = await postBounded(TEAM_SETTINGS_PATH, apiKey, deps.httpTimeoutMs);
|
|
110499
|
+
facts.teamSettingsUrl = stripUserinfo(teamCall.raw.url);
|
|
110500
|
+
const teamNetwork = teamCall.timedOut ? "team_settings_timeout" : "team_settings_network_error";
|
|
110501
|
+
if (teamCall.raw.kind === "unreachable") {
|
|
110502
|
+
return modelListFail(facts, modelListFailureOf("network", teamNetwork));
|
|
110503
|
+
}
|
|
110504
|
+
facts.teamSettingsHttpStatus = teamCall.raw.status;
|
|
110505
|
+
if (teamCall.raw.status < 200 || teamCall.raw.status >= 300) {
|
|
110506
|
+
return modelListFail(facts, modelListFailureOf("list_incomplete", "team_settings_http_error", teamCall.raw.status));
|
|
110507
|
+
}
|
|
110508
|
+
if (teamCall.raw.body.kind !== "bytes") {
|
|
110509
|
+
return modelListFail(facts, modelListFailureOf("network", teamNetwork, teamCall.raw.status));
|
|
110510
|
+
}
|
|
110511
|
+
const allowedModelUids = decodeAllowedUids(teamCall.raw.body.bytes);
|
|
110512
|
+
if (allowedModelUids.length === 0) {
|
|
110513
|
+
return modelListFail(facts, modelListFailureOf("list_incomplete", "allowed_uids_empty", teamCall.raw.status));
|
|
110514
|
+
}
|
|
110515
|
+
const body = JSON.stringify({ configs, allowedModelUids });
|
|
110516
|
+
return {
|
|
110517
|
+
envelope: buildModelListEnvelope(facts, undefined, body),
|
|
110518
|
+
exitCode: EXIT.ok,
|
|
110519
|
+
bodyText: body
|
|
110520
|
+
};
|
|
110521
|
+
}
|
|
110522
|
+
async function runDevinRawModelList(deps) {
|
|
110523
|
+
const secrets = newSecrets();
|
|
110524
|
+
const facts = { provider: "devin", tokenRefreshed: false };
|
|
110525
|
+
return runModelListWith(deps, facts, secrets, () => determineDevin(deps, secrets, facts));
|
|
110526
|
+
}
|
|
110527
|
+
var DEVIN_SESSION_TOKEN_PREFIX = "devin-session-token$";
|
|
110528
|
+
var init_subscription_devin_models = __esm(() => {
|
|
110529
|
+
init_devin_credentials();
|
|
110530
|
+
init_devin_models();
|
|
110531
|
+
init_devin_request();
|
|
110532
|
+
init_subscription_antigravity_session();
|
|
110533
|
+
init_subscription_models_command();
|
|
110534
|
+
});
|
|
110535
|
+
|
|
110407
110536
|
// src/subscription-models-command.ts
|
|
110408
110537
|
var exports_subscription_models_command = {};
|
|
110409
110538
|
__export(exports_subscription_models_command, {
|
|
@@ -110413,11 +110542,15 @@ __export(exports_subscription_models_command, {
|
|
|
110413
110542
|
SECRET_GUARD_MESSAGE: () => SECRET_GUARD_MESSAGE,
|
|
110414
110543
|
SUBSCRIPTION_MODELS_USAGE: () => SUBSCRIPTION_MODELS_USAGE,
|
|
110415
110544
|
SUBSCRIPTION_MODEL_LIST_SCHEMA: () => SUBSCRIPTION_MODEL_LIST_SCHEMA,
|
|
110545
|
+
buildModelListEnvelope: () => buildModelListEnvelope,
|
|
110416
110546
|
defaultSubscriptionModelsDeps: () => defaultSubscriptionModelsDeps,
|
|
110417
110547
|
exitCodeFor: () => exitCodeFor,
|
|
110548
|
+
modelListFail: () => modelListFail,
|
|
110549
|
+
modelListFailureOf: () => modelListFailureOf,
|
|
110418
110550
|
parseSubscriptionModelsArgs: () => parseSubscriptionModelsArgs,
|
|
110419
110551
|
reportSubscriptionInternalError: () => reportSubscriptionInternalError,
|
|
110420
110552
|
runAntigravityRawModelList: () => runAntigravityRawModelList,
|
|
110553
|
+
runModelListWith: () => runModelListWith,
|
|
110421
110554
|
subscriptionModelsCommand: () => subscriptionModelsCommand
|
|
110422
110555
|
});
|
|
110423
110556
|
function exitCodeFor(failure) {
|
|
@@ -110444,11 +110577,11 @@ function defaultSubscriptionModelsDeps() {
|
|
|
110444
110577
|
function parseSubscriptionModelsArgs(argv) {
|
|
110445
110578
|
const positionals = argv.filter((a) => !a.startsWith("-"));
|
|
110446
110579
|
const flags = argv.filter((a) => a.startsWith("-"));
|
|
110447
|
-
const positionalsOk = positionals.length === 2 && positionals[0] === "models" && positionals[1]
|
|
110580
|
+
const positionalsOk = positionals.length === 2 && positionals[0] === "models" && MODEL_LIST_PROVIDERS.includes(positionals[1]);
|
|
110448
110581
|
const flagsOk = flags.length === 2 && flags.includes("--raw") && flags.includes("--json");
|
|
110449
|
-
return positionalsOk && flagsOk ? { ok: true, provider:
|
|
110582
|
+
return positionalsOk && flagsOk ? { ok: true, provider: positionals[1] } : { ok: false };
|
|
110450
110583
|
}
|
|
110451
|
-
function
|
|
110584
|
+
function buildModelListEnvelope(facts, failure, body) {
|
|
110452
110585
|
return {
|
|
110453
110586
|
schema: SUBSCRIPTION_MODEL_LIST_SCHEMA,
|
|
110454
110587
|
...facts.provider !== undefined ? { provider: facts.provider } : {},
|
|
@@ -110459,25 +110592,27 @@ function buildEnvelope(facts, failure, body) {
|
|
|
110459
110592
|
...facts.clientVersionSource !== undefined ? { clientVersionSource: facts.clientVersionSource } : {},
|
|
110460
110593
|
tokenRefreshed: facts.tokenRefreshed,
|
|
110461
110594
|
...facts.httpStatus !== undefined ? { httpStatus: facts.httpStatus } : {},
|
|
110595
|
+
...facts.teamSettingsUrl !== undefined ? { teamSettingsUrl: facts.teamSettingsUrl } : {},
|
|
110596
|
+
...facts.teamSettingsHttpStatus !== undefined ? { teamSettingsHttpStatus: facts.teamSettingsHttpStatus } : {},
|
|
110462
110597
|
...failure ? { failure } : {},
|
|
110463
110598
|
...body !== undefined ? { body } : {}
|
|
110464
110599
|
};
|
|
110465
110600
|
}
|
|
110466
|
-
function
|
|
110601
|
+
function modelListFailureOf(cls, code, httpStatus) {
|
|
110467
110602
|
return failureOf2(cls, code, httpStatus);
|
|
110468
110603
|
}
|
|
110469
110604
|
function listFailure(value, status) {
|
|
110470
110605
|
const models = value !== null && typeof value === "object" && !Array.isArray(value) ? value.models : undefined;
|
|
110471
110606
|
if (models === null || typeof models !== "object" || Array.isArray(models)) {
|
|
110472
|
-
return
|
|
110607
|
+
return modelListFailureOf("list_incomplete", "models_absent", status);
|
|
110473
110608
|
}
|
|
110474
110609
|
if (Object.keys(models).length === 0) {
|
|
110475
|
-
return
|
|
110610
|
+
return modelListFailureOf("list_incomplete", "models_empty", status);
|
|
110476
110611
|
}
|
|
110477
110612
|
return null;
|
|
110478
110613
|
}
|
|
110479
|
-
function
|
|
110480
|
-
return { envelope:
|
|
110614
|
+
function modelListFail(facts, failure) {
|
|
110615
|
+
return { envelope: buildModelListEnvelope(facts, failure), exitCode: exitCodeFor(failure) };
|
|
110481
110616
|
}
|
|
110482
110617
|
function lineOf(envelope) {
|
|
110483
110618
|
return `${JSON.stringify(envelope)}
|
|
@@ -110489,15 +110624,16 @@ function safeStderr(deps, line) {
|
|
|
110489
110624
|
} catch {}
|
|
110490
110625
|
}
|
|
110491
110626
|
async function writeInternalFailure(deps, facts, code, stderrLine, secrets) {
|
|
110492
|
-
const failure =
|
|
110627
|
+
const failure = modelListFailureOf("internal", code);
|
|
110493
110628
|
safeStderr(deps, `${stderrLine}
|
|
110494
110629
|
`);
|
|
110495
|
-
|
|
110496
|
-
if (
|
|
110497
|
-
|
|
110498
|
-
await deps.writeStdout(line);
|
|
110499
|
-
} catch {}
|
|
110630
|
+
let line = lineOf(buildModelListEnvelope(facts, failure));
|
|
110631
|
+
if (secrets && containsSecret(secrets, line)) {
|
|
110632
|
+
line = lineOf(buildModelListEnvelope({ tokenRefreshed: facts.tokenRefreshed }, failure));
|
|
110500
110633
|
}
|
|
110634
|
+
try {
|
|
110635
|
+
await deps.writeStdout(line);
|
|
110636
|
+
} catch {}
|
|
110501
110637
|
return exitCodeFor(failure);
|
|
110502
110638
|
}
|
|
110503
110639
|
async function reportSubscriptionInternalError(deps) {
|
|
@@ -110516,34 +110652,35 @@ function classifyAvailableModels(facts, raw, timedOut) {
|
|
|
110516
110652
|
facts.clientVersion = raw.clientVersion;
|
|
110517
110653
|
facts.clientVersionSource = raw.clientVersionSource;
|
|
110518
110654
|
const networkCode = timedOut ? "fetch_available_models_timeout" : "fetch_available_models_network_error";
|
|
110519
|
-
if (raw.kind === "unreachable")
|
|
110520
|
-
return
|
|
110655
|
+
if (raw.kind === "unreachable") {
|
|
110656
|
+
return modelListFail(facts, modelListFailureOf("network", networkCode));
|
|
110657
|
+
}
|
|
110521
110658
|
facts.fetchedAt = raw.fetchedAt;
|
|
110522
110659
|
facts.httpStatus = raw.status;
|
|
110523
110660
|
if (raw.status < 200 || raw.status >= 300) {
|
|
110524
|
-
return { envelope:
|
|
110661
|
+
return { envelope: buildModelListEnvelope(facts), exitCode: EXIT.ok };
|
|
110525
110662
|
}
|
|
110526
110663
|
switch (raw.body.kind) {
|
|
110527
110664
|
case "json": {
|
|
110528
110665
|
const incomplete = listFailure(raw.body.value, raw.status);
|
|
110529
110666
|
if (incomplete)
|
|
110530
|
-
return
|
|
110667
|
+
return modelListFail(facts, incomplete);
|
|
110531
110668
|
return {
|
|
110532
|
-
envelope:
|
|
110669
|
+
envelope: buildModelListEnvelope(facts, undefined, raw.body.text),
|
|
110533
110670
|
exitCode: EXIT.ok,
|
|
110534
110671
|
bodyText: raw.body.text
|
|
110535
110672
|
};
|
|
110536
110673
|
}
|
|
110537
110674
|
case "not-json":
|
|
110538
|
-
return
|
|
110675
|
+
return modelListFail(facts, modelListFailureOf("list_incomplete", "body_not_json", raw.status));
|
|
110539
110676
|
default:
|
|
110540
|
-
return
|
|
110677
|
+
return modelListFail(facts, modelListFailureOf("network", networkCode, raw.status));
|
|
110541
110678
|
}
|
|
110542
110679
|
}
|
|
110543
|
-
async function
|
|
110680
|
+
async function determineAntigravity(deps, secrets, facts) {
|
|
110544
110681
|
const session = await establishAntigravitySession(deps, secrets, facts);
|
|
110545
110682
|
if (!session.ok)
|
|
110546
|
-
return
|
|
110683
|
+
return modelListFail(facts, session.failure);
|
|
110547
110684
|
const { accessToken, projectId } = session;
|
|
110548
110685
|
const controller = new AbortController;
|
|
110549
110686
|
const timer = setTimeout(() => controller.abort(), deps.httpTimeoutMs);
|
|
@@ -110557,14 +110694,12 @@ async function determine(deps, secrets, facts) {
|
|
|
110557
110694
|
clearTimeout(timer);
|
|
110558
110695
|
}
|
|
110559
110696
|
}
|
|
110560
|
-
async function
|
|
110561
|
-
const secrets = newSecrets();
|
|
110562
|
-
const facts = { provider: "antigravity", tokenRefreshed: false };
|
|
110697
|
+
async function runModelListWith(deps, facts, secrets, determine) {
|
|
110563
110698
|
let line;
|
|
110564
110699
|
let exitCode;
|
|
110565
110700
|
let bodyText;
|
|
110566
110701
|
try {
|
|
110567
|
-
const result = await determine(
|
|
110702
|
+
const result = await determine();
|
|
110568
110703
|
line = lineOf(result.envelope);
|
|
110569
110704
|
exitCode = result.exitCode;
|
|
110570
110705
|
bodyText = result.bodyText;
|
|
@@ -110583,27 +110718,53 @@ async function runAntigravityRawModelList(deps) {
|
|
|
110583
110718
|
}
|
|
110584
110719
|
return exitCode;
|
|
110585
110720
|
}
|
|
110721
|
+
async function runAntigravityRawModelList(deps) {
|
|
110722
|
+
const secrets = newSecrets();
|
|
110723
|
+
const facts = { provider: "antigravity", tokenRefreshed: false };
|
|
110724
|
+
return runModelListWith(deps, facts, secrets, () => determineAntigravity(deps, secrets, facts));
|
|
110725
|
+
}
|
|
110586
110726
|
async function subscriptionModelsCommand(argv, deps = defaultSubscriptionModelsDeps()) {
|
|
110587
|
-
|
|
110727
|
+
const args = parseSubscriptionModelsArgs(argv);
|
|
110728
|
+
if (!args.ok) {
|
|
110588
110729
|
return writeInternalFailure(deps, { tokenRefreshed: false }, "usage", SUBSCRIPTION_MODELS_USAGE);
|
|
110589
110730
|
}
|
|
110731
|
+
if (args.provider === "devin") {
|
|
110732
|
+
let devin;
|
|
110733
|
+
try {
|
|
110734
|
+
devin = await Promise.resolve().then(() => (init_subscription_devin_models(), exports_subscription_devin_models));
|
|
110735
|
+
} catch {
|
|
110736
|
+
return writeInternalFailure(deps, { provider: args.provider, tokenRefreshed: false }, "internal_error", INTERNAL_ERROR_MESSAGE);
|
|
110737
|
+
}
|
|
110738
|
+
return devin.runDevinRawModelList(deps);
|
|
110739
|
+
}
|
|
110590
110740
|
return runAntigravityRawModelList(deps);
|
|
110591
110741
|
}
|
|
110592
|
-
var SUBSCRIPTION_MODEL_LIST_SCHEMA = "claudish.subscription-model-list.v1", FAILURE_CODES, EXIT, SUBSCRIPTION_MODELS_USAGE = "usage: claudish subscription models antigravity --raw --json", SECRET_GUARD_MESSAGE = "[claudish subscription] refused: output contained a credential value", INTERNAL_ERROR_MESSAGE = "[claudish subscription] internal error";
|
|
110742
|
+
var SUBSCRIPTION_MODEL_LIST_SCHEMA = "claudish.subscription-model-list.v1", FAILURE_CODES, EXIT, SUBSCRIPTION_MODELS_USAGE = "usage: claudish subscription models antigravity|devin --raw --json", SECRET_GUARD_MESSAGE = "[claudish subscription] refused: output contained a credential value", INTERNAL_ERROR_MESSAGE = "[claudish subscription] internal error", MODEL_LIST_PROVIDERS;
|
|
110593
110743
|
var init_subscription_models_command = __esm(() => {
|
|
110594
110744
|
init_antigravity_token();
|
|
110595
110745
|
init_antigravity_user();
|
|
110596
110746
|
init_subscription_antigravity_session();
|
|
110597
110747
|
FAILURE_CODES = {
|
|
110598
|
-
credential_missing: SESSION_FAILURE_CODES.credential_missing,
|
|
110748
|
+
credential_missing: [...SESSION_FAILURE_CODES.credential_missing, "devin_credential_absent"],
|
|
110599
110749
|
keychain_locked: SESSION_FAILURE_CODES.keychain_locked,
|
|
110600
110750
|
mint_failed: SESSION_FAILURE_CODES.mint_failed,
|
|
110601
110751
|
network: [
|
|
110602
110752
|
...SESSION_FAILURE_CODES.network,
|
|
110603
110753
|
"fetch_available_models_network_error",
|
|
110604
|
-
"fetch_available_models_timeout"
|
|
110754
|
+
"fetch_available_models_timeout",
|
|
110755
|
+
"model_configs_network_error",
|
|
110756
|
+
"model_configs_timeout",
|
|
110757
|
+
"team_settings_network_error",
|
|
110758
|
+
"team_settings_timeout"
|
|
110759
|
+
],
|
|
110760
|
+
list_incomplete: [
|
|
110761
|
+
"models_absent",
|
|
110762
|
+
"models_empty",
|
|
110763
|
+
"body_not_json",
|
|
110764
|
+
"configs_empty",
|
|
110765
|
+
"team_settings_http_error",
|
|
110766
|
+
"allowed_uids_empty"
|
|
110605
110767
|
],
|
|
110606
|
-
list_incomplete: ["models_absent", "models_empty", "body_not_json"],
|
|
110607
110768
|
internal: ["usage", "secret_guard", "internal_error"]
|
|
110608
110769
|
};
|
|
110609
110770
|
EXIT = {
|
|
@@ -110616,6 +110777,7 @@ var init_subscription_models_command = __esm(() => {
|
|
|
110616
110777
|
network: 6,
|
|
110617
110778
|
list_incomplete: 7
|
|
110618
110779
|
};
|
|
110780
|
+
MODEL_LIST_PROVIDERS = ["antigravity", "devin"];
|
|
110619
110781
|
});
|
|
110620
110782
|
|
|
110621
110783
|
// src/subscription-dispatch.ts
|
|
@@ -110643,6 +110805,232 @@ function probeLoadFailureLine() {
|
|
|
110643
110805
|
var SUBSCRIPTION_PROBE_SCHEMA = "claudish.subscription-probe.v1";
|
|
110644
110806
|
var init_subscription_dispatch = () => {};
|
|
110645
110807
|
|
|
110808
|
+
// src/subscription-secret-guard.ts
|
|
110809
|
+
import { readFile as readFile8, stat as stat2 } from "fs/promises";
|
|
110810
|
+
function redactSecretValues(text, secrets) {
|
|
110811
|
+
let out = text;
|
|
110812
|
+
const values = [...secrets.values].sort((a, b) => b.length - a.length);
|
|
110813
|
+
for (const value of values) {
|
|
110814
|
+
for (const form of new Set([value, value.replaceAll("/", "\\/")])) {
|
|
110815
|
+
if (out.includes(form))
|
|
110816
|
+
out = out.replaceAll(form, REDACTED2);
|
|
110817
|
+
}
|
|
110818
|
+
}
|
|
110819
|
+
return { text: out, redacted: out !== text };
|
|
110820
|
+
}
|
|
110821
|
+
function jsonUnescapedForms(text) {
|
|
110822
|
+
const forms = [];
|
|
110823
|
+
if (text === undefined)
|
|
110824
|
+
return { forms, complete: true };
|
|
110825
|
+
let current = text;
|
|
110826
|
+
for (let round = 0;round < MAX_UNESCAPE_ROUNDS; round++) {
|
|
110827
|
+
const next = current.replace(JSON_ESCAPE, (_match, hex, letter) => hex !== undefined ? String.fromCharCode(Number.parseInt(hex, 16)) : SIMPLE_ESCAPES[letter]);
|
|
110828
|
+
if (next === current)
|
|
110829
|
+
return { forms, complete: true };
|
|
110830
|
+
forms.push(next);
|
|
110831
|
+
current = next;
|
|
110832
|
+
}
|
|
110833
|
+
return { forms, complete: false };
|
|
110834
|
+
}
|
|
110835
|
+
async function readCapped(res, cap) {
|
|
110836
|
+
if (!res.body)
|
|
110837
|
+
return "";
|
|
110838
|
+
const reader = res.body.getReader();
|
|
110839
|
+
const chunks = [];
|
|
110840
|
+
let size = 0;
|
|
110841
|
+
for (;; ) {
|
|
110842
|
+
const { done, value } = await reader.read();
|
|
110843
|
+
if (done)
|
|
110844
|
+
break;
|
|
110845
|
+
size += value.byteLength;
|
|
110846
|
+
if (size > cap) {
|
|
110847
|
+
await reader.cancel().catch(() => {});
|
|
110848
|
+
return null;
|
|
110849
|
+
}
|
|
110850
|
+
chunks.push(value);
|
|
110851
|
+
}
|
|
110852
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
110853
|
+
}
|
|
110854
|
+
async function defaultReadRequestFile(path) {
|
|
110855
|
+
const info = await stat2(path);
|
|
110856
|
+
if (!info.isFile())
|
|
110857
|
+
throw new Error("not a file");
|
|
110858
|
+
if (info.size > PROBE_REQUEST_FILE_CAP_BYTES)
|
|
110859
|
+
return null;
|
|
110860
|
+
return readFile8(path, "utf8");
|
|
110861
|
+
}
|
|
110862
|
+
async function readRequestFileChecked(read, path) {
|
|
110863
|
+
let text;
|
|
110864
|
+
try {
|
|
110865
|
+
text = await read(path);
|
|
110866
|
+
} catch {
|
|
110867
|
+
return { ok: false, code: "request_file_unreadable" };
|
|
110868
|
+
}
|
|
110869
|
+
if (text === null)
|
|
110870
|
+
return { ok: false, code: "request_file_invalid" };
|
|
110871
|
+
let value;
|
|
110872
|
+
try {
|
|
110873
|
+
value = JSON.parse(text);
|
|
110874
|
+
} catch {
|
|
110875
|
+
return { ok: false, code: "request_file_invalid" };
|
|
110876
|
+
}
|
|
110877
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
110878
|
+
return { ok: false, code: "request_file_invalid" };
|
|
110879
|
+
}
|
|
110880
|
+
return { ok: true, text, value };
|
|
110881
|
+
}
|
|
110882
|
+
var PROBE_BODY_CAP_BYTES, PROBE_REQUEST_FILE_CAP_BYTES, REDACTED2 = "[REDACTED]", JSON_ESCAPE, SIMPLE_ESCAPES, MAX_UNESCAPE_ROUNDS = 64;
|
|
110883
|
+
var init_subscription_secret_guard = __esm(() => {
|
|
110884
|
+
PROBE_BODY_CAP_BYTES = 512 * 1024;
|
|
110885
|
+
PROBE_REQUEST_FILE_CAP_BYTES = 8 * 1024 * 1024;
|
|
110886
|
+
JSON_ESCAPE = /\\(?:u([0-9a-fA-F]{4})|(["\\/bfnrt]))/g;
|
|
110887
|
+
SIMPLE_ESCAPES = {
|
|
110888
|
+
'"': '"',
|
|
110889
|
+
"\\": "\\",
|
|
110890
|
+
"/": "/",
|
|
110891
|
+
b: "\b",
|
|
110892
|
+
f: "\f",
|
|
110893
|
+
n: `
|
|
110894
|
+
`,
|
|
110895
|
+
r: "\r",
|
|
110896
|
+
t: "\t"
|
|
110897
|
+
};
|
|
110898
|
+
});
|
|
110899
|
+
|
|
110900
|
+
// src/subscription-devin-probe.ts
|
|
110901
|
+
var exports_subscription_devin_probe = {};
|
|
110902
|
+
__export(exports_subscription_devin_probe, {
|
|
110903
|
+
DEVIN_PROBE_REQUEST_FILE_MESSAGE: () => DEVIN_PROBE_REQUEST_FILE_MESSAGE,
|
|
110904
|
+
buildDevinProbePayload: () => buildDevinProbePayload,
|
|
110905
|
+
runDevinProbe: () => runDevinProbe
|
|
110906
|
+
});
|
|
110907
|
+
function buildDevinProbePayload(model, claudeRequest) {
|
|
110908
|
+
const adapter = new DevinAPIFormat(model);
|
|
110909
|
+
adapter.setResponseWireFormat("connect-proto");
|
|
110910
|
+
adapter.reset();
|
|
110911
|
+
const messages = adapter.convertMessages(claudeRequest, filterIdentity);
|
|
110912
|
+
const tools = adapter.convertTools(claudeRequest);
|
|
110913
|
+
const payload = adapter.buildPayload(claudeRequest, messages, tools);
|
|
110914
|
+
adapter.prepareRequest(payload, claudeRequest);
|
|
110915
|
+
payload.modelUid = model;
|
|
110916
|
+
delete payload.effort;
|
|
110917
|
+
return { payload, toolNameMap: adapter.getToolNameMap() };
|
|
110918
|
+
}
|
|
110919
|
+
async function decodeDevinAnswer(res, facts, model, toolNameMap) {
|
|
110920
|
+
const head = await sniffDevinStreamHead(res);
|
|
110921
|
+
if (head.kind !== "clean") {
|
|
110922
|
+
facts.streamError = { code: head.code, retryable: head.kind === "retryable" };
|
|
110923
|
+
return JSON.stringify({ error: { code: head.code, message: head.message } });
|
|
110924
|
+
}
|
|
110925
|
+
let apiError;
|
|
110926
|
+
let measured;
|
|
110927
|
+
let endOfStream = false;
|
|
110928
|
+
const sse = createDevinConnectStream({}, head.response, {
|
|
110929
|
+
modelName: model,
|
|
110930
|
+
toolNameMap,
|
|
110931
|
+
onTokenUpdate: (input, output) => {
|
|
110932
|
+
measured = { input_tokens: input, output_tokens: output };
|
|
110933
|
+
},
|
|
110934
|
+
onServedModel: (uid) => {
|
|
110935
|
+
facts.servedModel = uid;
|
|
110936
|
+
},
|
|
110937
|
+
onApiError: (code, message) => {
|
|
110938
|
+
apiError ??= { code, message };
|
|
110939
|
+
},
|
|
110940
|
+
onEndOfStream: () => {
|
|
110941
|
+
endOfStream = true;
|
|
110942
|
+
}
|
|
110943
|
+
});
|
|
110944
|
+
const collected = await collectSseMessage(sse, model);
|
|
110945
|
+
if (apiError) {
|
|
110946
|
+
facts.streamError = {
|
|
110947
|
+
code: apiError.code,
|
|
110948
|
+
retryable: classifyDevinStreamError(apiError.code, apiError.message) === "retryable"
|
|
110949
|
+
};
|
|
110950
|
+
} else if (collected.streamError || !endOfStream) {
|
|
110951
|
+
return null;
|
|
110952
|
+
}
|
|
110953
|
+
const { streamError: _internal, ...message } = collected;
|
|
110954
|
+
if (measured)
|
|
110955
|
+
message.usage = measured;
|
|
110956
|
+
return JSON.stringify(message);
|
|
110957
|
+
}
|
|
110958
|
+
async function determineDevin2(deps, secrets, facts, args) {
|
|
110959
|
+
const request = await readRequestFileChecked(deps.readRequestFile, args.requestFile);
|
|
110960
|
+
if (!request.ok)
|
|
110961
|
+
return probeFail(facts, probeFailureOf("internal", request.code));
|
|
110962
|
+
const apiKey = readDevinSecrets(secrets);
|
|
110963
|
+
if (!apiKey) {
|
|
110964
|
+
return probeFail(facts, probeFailureOf("credential_missing", "devin_credential_absent"));
|
|
110965
|
+
}
|
|
110966
|
+
const { payload, toolNameMap } = buildDevinProbePayload(args.model, request.value);
|
|
110967
|
+
const body = encodeDevinRequest(payload, { apiKey });
|
|
110968
|
+
const url = `${readDevinServerUrl()}${CHAT_PATH}`;
|
|
110969
|
+
facts.sourceUrl = stripUserinfo(url);
|
|
110970
|
+
facts.clientVersion = DEVIN_CLI_VERSION;
|
|
110971
|
+
const controller = new AbortController;
|
|
110972
|
+
const timer = setTimeout(() => controller.abort(), deps.requestTimeoutMs);
|
|
110973
|
+
const networkFailure = () => probeFailureOf("network", controller.signal.aborted ? "get_chat_message_timeout" : "get_chat_message_network_error", facts.httpStatus);
|
|
110974
|
+
try {
|
|
110975
|
+
let res;
|
|
110976
|
+
try {
|
|
110977
|
+
res = await fetch(url, {
|
|
110978
|
+
method: "POST",
|
|
110979
|
+
headers: { ...devinAuthHeaders(apiKey), "content-type": CHAT_CONTENT_TYPE2 },
|
|
110980
|
+
body,
|
|
110981
|
+
signal: controller.signal,
|
|
110982
|
+
redirect: "manual"
|
|
110983
|
+
});
|
|
110984
|
+
} catch {
|
|
110985
|
+
return probeFail(facts, networkFailure());
|
|
110986
|
+
}
|
|
110987
|
+
facts.fetchedAt = new Date(deps.now()).toISOString();
|
|
110988
|
+
facts.httpStatus = res.status;
|
|
110989
|
+
if (!res.ok) {
|
|
110990
|
+
let text;
|
|
110991
|
+
try {
|
|
110992
|
+
text = await readCapped(res, PROBE_BODY_CAP_BYTES);
|
|
110993
|
+
} catch {
|
|
110994
|
+
return probeFail(facts, networkFailure());
|
|
110995
|
+
}
|
|
110996
|
+
if (text === null) {
|
|
110997
|
+
return probeFail(facts, probeFailureOf("list_incomplete", "body_too_large", res.status));
|
|
110998
|
+
}
|
|
110999
|
+
return probeBodyResult(facts, text, secrets, res.status);
|
|
111000
|
+
}
|
|
111001
|
+
const text = await decodeDevinAnswer(res, facts, args.model, toolNameMap);
|
|
111002
|
+
if (text === null)
|
|
111003
|
+
return probeFail(facts, networkFailure());
|
|
111004
|
+
if (Buffer.byteLength(text, "utf8") > PROBE_BODY_CAP_BYTES) {
|
|
111005
|
+
return probeFail(facts, probeFailureOf("list_incomplete", "body_too_large", res.status));
|
|
111006
|
+
}
|
|
111007
|
+
return probeBodyResult(facts, text, secrets, res.status);
|
|
111008
|
+
} finally {
|
|
111009
|
+
clearTimeout(timer);
|
|
111010
|
+
}
|
|
111011
|
+
}
|
|
111012
|
+
async function runDevinProbe(deps, args) {
|
|
111013
|
+
const secrets = newSecrets();
|
|
111014
|
+
const facts = { provider: "devin", model: args.model, tokenRefreshed: false };
|
|
111015
|
+
return runProbeWith(deps, facts, secrets, DEVIN_PROBE_REQUEST_FILE_MESSAGE, () => determineDevin2(deps, secrets, facts, args));
|
|
111016
|
+
}
|
|
111017
|
+
var DEVIN_PROBE_REQUEST_FILE_MESSAGE = "[claudish subscription] the request file must hold one JSON object (an Anthropic Messages request)", CHAT_CONTENT_TYPE2 = "application/connect+proto";
|
|
111018
|
+
var init_subscription_devin_probe = __esm(() => {
|
|
111019
|
+
init_devin_api_format();
|
|
111020
|
+
init_devin_credential();
|
|
111021
|
+
init_collect_sse_message();
|
|
111022
|
+
init_devin_stream_head_sniffer();
|
|
111023
|
+
init_openai_compat();
|
|
111024
|
+
init_devin_connect();
|
|
111025
|
+
init_devin_credentials();
|
|
111026
|
+
init_devin_request();
|
|
111027
|
+
init_devin2();
|
|
111028
|
+
init_subscription_antigravity_session();
|
|
111029
|
+
init_subscription_devin_models();
|
|
111030
|
+
init_subscription_probe_command();
|
|
111031
|
+
init_subscription_secret_guard();
|
|
111032
|
+
});
|
|
111033
|
+
|
|
110646
111034
|
// src/subscription-probe-command.ts
|
|
110647
111035
|
var exports_subscription_probe_command = {};
|
|
110648
111036
|
__export(exports_subscription_probe_command, {
|
|
@@ -110655,33 +111043,30 @@ __export(exports_subscription_probe_command, {
|
|
|
110655
111043
|
REDACTED: () => REDACTED2,
|
|
110656
111044
|
SUBSCRIPTION_PROBE_SCHEMA: () => SUBSCRIPTION_PROBE_SCHEMA,
|
|
110657
111045
|
SUBSCRIPTION_PROBE_USAGE: () => SUBSCRIPTION_PROBE_USAGE,
|
|
111046
|
+
buildProbeEnvelope: () => buildProbeEnvelope,
|
|
110658
111047
|
buildProbeEnvelopeBody: () => buildProbeEnvelopeBody,
|
|
110659
111048
|
defaultSubscriptionProbeDeps: () => defaultSubscriptionProbeDeps,
|
|
110660
111049
|
isSubscriptionProbeArgv: () => isSubscriptionProbeArgv,
|
|
110661
111050
|
jsonUnescapedForms: () => jsonUnescapedForms,
|
|
110662
111051
|
parseSubscriptionProbeArgs: () => parseSubscriptionProbeArgs,
|
|
111052
|
+
probeBodyResult: () => probeBodyResult,
|
|
110663
111053
|
probeExitCodeFor: () => probeExitCodeFor,
|
|
111054
|
+
probeFail: () => probeFail,
|
|
111055
|
+
probeFailureOf: () => probeFailureOf,
|
|
111056
|
+
readCapped: () => readCapped,
|
|
110664
111057
|
redactSecretValues: () => redactSecretValues,
|
|
110665
111058
|
reportProbeInternalError: () => reportProbeInternalError,
|
|
110666
111059
|
runAntigravityProbe: () => runAntigravityProbe,
|
|
111060
|
+
runProbeWith: () => runProbeWith,
|
|
110667
111061
|
subscriptionProbeCommand: () => subscriptionProbeCommand
|
|
110668
111062
|
});
|
|
110669
111063
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
110670
|
-
import { readFile as readFile8, stat as stat2 } from "fs/promises";
|
|
110671
111064
|
function probeExitCodeFor(failure) {
|
|
110672
111065
|
if (failure.class === "internal") {
|
|
110673
111066
|
return CALLER_CODES.includes(failure.code) ? PROBE_EXIT.usage : PROBE_EXIT.internal;
|
|
110674
111067
|
}
|
|
110675
111068
|
return PROBE_EXIT[failure.class];
|
|
110676
111069
|
}
|
|
110677
|
-
async function defaultReadRequestFile(path) {
|
|
110678
|
-
const info = await stat2(path);
|
|
110679
|
-
if (!info.isFile())
|
|
110680
|
-
throw new Error("not a file");
|
|
110681
|
-
if (info.size > PROBE_REQUEST_FILE_CAP_BYTES)
|
|
110682
|
-
return null;
|
|
110683
|
-
return readFile8(path, "utf8");
|
|
110684
|
-
}
|
|
110685
111070
|
function defaultSubscriptionProbeDeps() {
|
|
110686
111071
|
return {
|
|
110687
111072
|
platform: () => process.platform,
|
|
@@ -110725,15 +111110,20 @@ function parseSubscriptionProbeArgs(argv) {
|
|
|
110725
111110
|
}
|
|
110726
111111
|
const model = values.get("--model");
|
|
110727
111112
|
const requestFile = values.get("--request-file");
|
|
110728
|
-
if (positionals.length !== 2 || positionals[0] !== "probe" || positionals[1]
|
|
111113
|
+
if (positionals.length !== 2 || positionals[0] !== "probe" || !PROBE_PROVIDERS.includes(positionals[1]) || switches.size !== 2 || model === undefined || requestFile === undefined) {
|
|
110729
111114
|
return { ok: false };
|
|
110730
111115
|
}
|
|
110731
|
-
return {
|
|
111116
|
+
return {
|
|
111117
|
+
ok: true,
|
|
111118
|
+
provider: positionals[1],
|
|
111119
|
+
model,
|
|
111120
|
+
requestFile
|
|
111121
|
+
};
|
|
110732
111122
|
}
|
|
110733
|
-
function
|
|
111123
|
+
function probeFailureOf(cls, code, httpStatus) {
|
|
110734
111124
|
return failureOf2(cls, code, httpStatus);
|
|
110735
111125
|
}
|
|
110736
|
-
function
|
|
111126
|
+
function buildProbeEnvelope(facts, failure, body) {
|
|
110737
111127
|
return {
|
|
110738
111128
|
schema: SUBSCRIPTION_PROBE_SCHEMA,
|
|
110739
111129
|
...facts.provider !== undefined ? { provider: facts.provider } : {},
|
|
@@ -110745,12 +111135,27 @@ function buildEnvelope2(facts, failure, body) {
|
|
|
110745
111135
|
...facts.clientVersionSource !== undefined ? { clientVersionSource: facts.clientVersionSource } : {},
|
|
110746
111136
|
tokenRefreshed: facts.tokenRefreshed,
|
|
110747
111137
|
...facts.httpStatus !== undefined ? { httpStatus: facts.httpStatus } : {},
|
|
111138
|
+
...facts.servedModel !== undefined ? { servedModel: facts.servedModel } : {},
|
|
111139
|
+
...facts.streamError !== undefined ? { streamError: facts.streamError } : {},
|
|
110748
111140
|
...failure ? { failure } : {},
|
|
110749
111141
|
...body ? { bodyRedacted: body.redacted, body: body.text } : {}
|
|
110750
111142
|
};
|
|
110751
111143
|
}
|
|
110752
|
-
function
|
|
110753
|
-
return { envelope:
|
|
111144
|
+
function probeFail(facts, failure) {
|
|
111145
|
+
return { envelope: buildProbeEnvelope(facts, failure), exitCode: probeExitCodeFor(failure) };
|
|
111146
|
+
}
|
|
111147
|
+
function probeBodyResult(facts, text, secrets, status) {
|
|
111148
|
+
const body = redactSecretValues(text, secrets);
|
|
111149
|
+
const unescaped = jsonUnescapedForms(body.text);
|
|
111150
|
+
if (!unescaped.complete) {
|
|
111151
|
+
return probeFail(facts, probeFailureOf("list_incomplete", "body_unverifiable", status));
|
|
111152
|
+
}
|
|
111153
|
+
return {
|
|
111154
|
+
envelope: buildProbeEnvelope(facts, undefined, body),
|
|
111155
|
+
exitCode: PROBE_EXIT.ok,
|
|
111156
|
+
bodyText: body.text,
|
|
111157
|
+
bodyForms: unescaped.forms
|
|
111158
|
+
};
|
|
110754
111159
|
}
|
|
110755
111160
|
function lineOf2(envelope) {
|
|
110756
111161
|
return `${JSON.stringify(envelope)}
|
|
@@ -110762,62 +111167,41 @@ function safeStderr2(deps, line) {
|
|
|
110762
111167
|
} catch {}
|
|
110763
111168
|
}
|
|
110764
111169
|
async function writeInternalFailure2(deps, facts, code, stderrLine, secrets) {
|
|
110765
|
-
const failure =
|
|
111170
|
+
const failure = probeFailureOf("internal", code);
|
|
110766
111171
|
safeStderr2(deps, `${stderrLine}
|
|
110767
111172
|
`);
|
|
110768
|
-
let line = lineOf2(
|
|
111173
|
+
let line = lineOf2(buildProbeEnvelope(facts, failure));
|
|
110769
111174
|
if (secrets && containsSecret(secrets, line)) {
|
|
110770
|
-
line = lineOf2(
|
|
111175
|
+
line = lineOf2(buildProbeEnvelope({ tokenRefreshed: facts.tokenRefreshed }, failure));
|
|
110771
111176
|
}
|
|
110772
111177
|
try {
|
|
110773
111178
|
await deps.writeStdout(line);
|
|
110774
111179
|
} catch {}
|
|
110775
111180
|
return probeExitCodeFor(failure);
|
|
110776
111181
|
}
|
|
110777
|
-
function
|
|
110778
|
-
let
|
|
110779
|
-
const values = [...secrets.values].sort((a, b) => b.length - a.length);
|
|
110780
|
-
for (const value of values) {
|
|
110781
|
-
for (const form of new Set([value, value.replaceAll("/", "\\/")])) {
|
|
110782
|
-
if (out.includes(form))
|
|
110783
|
-
out = out.replaceAll(form, REDACTED2);
|
|
110784
|
-
}
|
|
110785
|
-
}
|
|
110786
|
-
return { text: out, redacted: out !== text };
|
|
110787
|
-
}
|
|
110788
|
-
function jsonUnescapedForms(text) {
|
|
110789
|
-
const forms = [];
|
|
110790
|
-
if (text === undefined)
|
|
110791
|
-
return { forms, complete: true };
|
|
110792
|
-
let current = text;
|
|
110793
|
-
for (let round = 0;round < MAX_UNESCAPE_ROUNDS; round++) {
|
|
110794
|
-
const next = current.replace(JSON_ESCAPE, (_match, hex, letter) => hex !== undefined ? String.fromCharCode(Number.parseInt(hex, 16)) : SIMPLE_ESCAPES[letter]);
|
|
110795
|
-
if (next === current)
|
|
110796
|
-
return { forms, complete: true };
|
|
110797
|
-
forms.push(next);
|
|
110798
|
-
current = next;
|
|
110799
|
-
}
|
|
110800
|
-
return { forms, complete: false };
|
|
110801
|
-
}
|
|
110802
|
-
async function readRequest(deps, path) {
|
|
110803
|
-
let text;
|
|
111182
|
+
async function runProbeWith(deps, facts, secrets, requestFileMessage, determine) {
|
|
111183
|
+
let result;
|
|
110804
111184
|
try {
|
|
110805
|
-
|
|
111185
|
+
result = await determine();
|
|
110806
111186
|
} catch {
|
|
110807
|
-
return {
|
|
111187
|
+
return writeInternalFailure2(deps, { provider: facts.provider, model: facts.model, tokenRefreshed: facts.tokenRefreshed }, "internal_error", INTERNAL_ERROR_MESSAGE2, secrets);
|
|
111188
|
+
}
|
|
111189
|
+
if (result.envelope.failure?.code === "request_file_unreadable" || result.envelope.failure?.code === "request_file_invalid") {
|
|
111190
|
+
safeStderr2(deps, `${requestFileMessage}
|
|
111191
|
+
`);
|
|
111192
|
+
}
|
|
111193
|
+
const line = lineOf2(result.envelope);
|
|
111194
|
+
if (containsSecret(secrets, line, result.bodyText, ...result.bodyForms ?? [])) {
|
|
111195
|
+
return writeInternalFailure2(deps, facts, "secret_guard", SECRET_GUARD_MESSAGE2, secrets);
|
|
110808
111196
|
}
|
|
110809
|
-
if (text === null)
|
|
110810
|
-
return { ok: false, code: "request_file_invalid" };
|
|
110811
|
-
let value;
|
|
110812
111197
|
try {
|
|
110813
|
-
|
|
111198
|
+
await deps.writeStdout(line);
|
|
110814
111199
|
} catch {
|
|
110815
|
-
|
|
110816
|
-
|
|
110817
|
-
|
|
110818
|
-
return { ok: false, code: "request_file_invalid" };
|
|
111200
|
+
safeStderr2(deps, `${INTERNAL_ERROR_MESSAGE2}
|
|
111201
|
+
`);
|
|
111202
|
+
return PROBE_EXIT.internal;
|
|
110819
111203
|
}
|
|
110820
|
-
return
|
|
111204
|
+
return result.exitCode;
|
|
110821
111205
|
}
|
|
110822
111206
|
function buildProbeEnvelopeBody(model, projectId, tierId, requestText, userPromptId = randomUUID12()) {
|
|
110823
111207
|
const head = JSON.stringify({ model, project: projectId, user_prompt_id: userPromptId });
|
|
@@ -110827,32 +111211,13 @@ function buildProbeEnvelopeBody(model, projectId, tierId, requestText, userPromp
|
|
|
110827
111211
|
function createActivityRequestId3() {
|
|
110828
111212
|
return Math.random().toString(36).substring(7);
|
|
110829
111213
|
}
|
|
110830
|
-
async function
|
|
110831
|
-
|
|
110832
|
-
return "";
|
|
110833
|
-
const reader = res.body.getReader();
|
|
110834
|
-
const chunks = [];
|
|
110835
|
-
let size = 0;
|
|
110836
|
-
for (;; ) {
|
|
110837
|
-
const { done, value } = await reader.read();
|
|
110838
|
-
if (done)
|
|
110839
|
-
break;
|
|
110840
|
-
size += value.byteLength;
|
|
110841
|
-
if (size > cap) {
|
|
110842
|
-
await reader.cancel().catch(() => {});
|
|
110843
|
-
return null;
|
|
110844
|
-
}
|
|
110845
|
-
chunks.push(value);
|
|
110846
|
-
}
|
|
110847
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
110848
|
-
}
|
|
110849
|
-
async function determine2(deps, secrets, facts, args) {
|
|
110850
|
-
const request = await readRequest(deps, args.requestFile);
|
|
111214
|
+
async function determineAntigravity2(deps, secrets, facts, args) {
|
|
111215
|
+
const request = await readRequestFileChecked(deps.readRequestFile, args.requestFile);
|
|
110851
111216
|
if (!request.ok)
|
|
110852
|
-
return
|
|
111217
|
+
return probeFail(facts, probeFailureOf("internal", request.code));
|
|
110853
111218
|
const session = await establishAntigravitySession(deps, secrets, facts);
|
|
110854
111219
|
if (!session.ok)
|
|
110855
|
-
return
|
|
111220
|
+
return probeFail(facts, session.failure);
|
|
110856
111221
|
const url = `${antigravityHost()}/v1internal:generateContent`;
|
|
110857
111222
|
const version = resolveAntigravityClientVersion();
|
|
110858
111223
|
facts.sourceUrl = stripUserinfo(url);
|
|
@@ -110860,7 +111225,7 @@ async function determine2(deps, secrets, facts, args) {
|
|
|
110860
111225
|
facts.clientVersionSource = version.source;
|
|
110861
111226
|
const controller = new AbortController;
|
|
110862
111227
|
const timer = setTimeout(() => controller.abort(), deps.requestTimeoutMs);
|
|
110863
|
-
const networkFailure = () =>
|
|
111228
|
+
const networkFailure = () => probeFailureOf("network", controller.signal.aborted ? "generate_content_timeout" : "generate_content_network_error", facts.httpStatus);
|
|
110864
111229
|
try {
|
|
110865
111230
|
let res;
|
|
110866
111231
|
try {
|
|
@@ -110877,7 +111242,7 @@ async function determine2(deps, secrets, facts, args) {
|
|
|
110877
111242
|
redirect: "manual"
|
|
110878
111243
|
});
|
|
110879
111244
|
} catch {
|
|
110880
|
-
return
|
|
111245
|
+
return probeFail(facts, networkFailure());
|
|
110881
111246
|
}
|
|
110882
111247
|
facts.fetchedAt = new Date(deps.now()).toISOString();
|
|
110883
111248
|
facts.httpStatus = res.status;
|
|
@@ -110885,28 +111250,18 @@ async function determine2(deps, secrets, facts, args) {
|
|
|
110885
111250
|
try {
|
|
110886
111251
|
text = await readCapped(res, PROBE_BODY_CAP_BYTES);
|
|
110887
111252
|
} catch {
|
|
110888
|
-
return
|
|
111253
|
+
return probeFail(facts, networkFailure());
|
|
110889
111254
|
}
|
|
110890
111255
|
if (text === null)
|
|
110891
|
-
return
|
|
111256
|
+
return probeFail(facts, probeFailureOf("list_incomplete", "body_too_large", res.status));
|
|
110892
111257
|
if (res.ok) {
|
|
110893
111258
|
try {
|
|
110894
111259
|
JSON.parse(text);
|
|
110895
111260
|
} catch {
|
|
110896
|
-
return
|
|
111261
|
+
return probeFail(facts, probeFailureOf("list_incomplete", "body_not_json", res.status));
|
|
110897
111262
|
}
|
|
110898
111263
|
}
|
|
110899
|
-
|
|
110900
|
-
const unescaped = jsonUnescapedForms(body.text);
|
|
110901
|
-
if (!unescaped.complete) {
|
|
110902
|
-
return fail2(facts, failureOf4("list_incomplete", "body_unverifiable", res.status));
|
|
110903
|
-
}
|
|
110904
|
-
return {
|
|
110905
|
-
envelope: buildEnvelope2(facts, undefined, body),
|
|
110906
|
-
exitCode: PROBE_EXIT.ok,
|
|
110907
|
-
bodyText: body.text,
|
|
110908
|
-
bodyForms: unescaped.forms
|
|
110909
|
-
};
|
|
111264
|
+
return probeBodyResult(facts, text, secrets, res.status);
|
|
110910
111265
|
} finally {
|
|
110911
111266
|
clearTimeout(timer);
|
|
110912
111267
|
}
|
|
@@ -110914,28 +111269,7 @@ async function determine2(deps, secrets, facts, args) {
|
|
|
110914
111269
|
async function runAntigravityProbe(deps, args) {
|
|
110915
111270
|
const secrets = newSecrets();
|
|
110916
111271
|
const facts = { provider: "antigravity", model: args.model, tokenRefreshed: false };
|
|
110917
|
-
|
|
110918
|
-
try {
|
|
110919
|
-
result = await determine2(deps, secrets, facts, args);
|
|
110920
|
-
} catch {
|
|
110921
|
-
return writeInternalFailure2(deps, { provider: facts.provider, model: facts.model, tokenRefreshed: facts.tokenRefreshed }, "internal_error", INTERNAL_ERROR_MESSAGE2, secrets);
|
|
110922
|
-
}
|
|
110923
|
-
if (result.envelope.failure?.code === "request_file_unreadable" || result.envelope.failure?.code === "request_file_invalid") {
|
|
110924
|
-
safeStderr2(deps, `${PROBE_REQUEST_FILE_MESSAGE}
|
|
110925
|
-
`);
|
|
110926
|
-
}
|
|
110927
|
-
const line = lineOf2(result.envelope);
|
|
110928
|
-
if (containsSecret(secrets, line, result.bodyText, ...result.bodyForms ?? [])) {
|
|
110929
|
-
return writeInternalFailure2(deps, facts, "secret_guard", SECRET_GUARD_MESSAGE2, secrets);
|
|
110930
|
-
}
|
|
110931
|
-
try {
|
|
110932
|
-
await deps.writeStdout(line);
|
|
110933
|
-
} catch {
|
|
110934
|
-
safeStderr2(deps, `${INTERNAL_ERROR_MESSAGE2}
|
|
110935
|
-
`);
|
|
110936
|
-
return PROBE_EXIT.internal;
|
|
110937
|
-
}
|
|
110938
|
-
return result.exitCode;
|
|
111272
|
+
return runProbeWith(deps, facts, secrets, PROBE_REQUEST_FILE_MESSAGE, () => determineAntigravity2(deps, secrets, facts, args));
|
|
110939
111273
|
}
|
|
110940
111274
|
async function reportProbeInternalError(deps) {
|
|
110941
111275
|
let resolved;
|
|
@@ -110953,23 +111287,36 @@ async function subscriptionProbeCommand(argv, deps = defaultSubscriptionProbeDep
|
|
|
110953
111287
|
if (!args.ok) {
|
|
110954
111288
|
return writeInternalFailure2(deps, { tokenRefreshed: false }, "usage", SUBSCRIPTION_PROBE_USAGE);
|
|
110955
111289
|
}
|
|
111290
|
+
if (args.provider === "devin") {
|
|
111291
|
+
let devin;
|
|
111292
|
+
try {
|
|
111293
|
+
devin = await Promise.resolve().then(() => (init_subscription_devin_probe(), exports_subscription_devin_probe));
|
|
111294
|
+
} catch {
|
|
111295
|
+
return writeInternalFailure2(deps, { provider: args.provider, model: args.model, tokenRefreshed: false }, "internal_error", INTERNAL_ERROR_MESSAGE2);
|
|
111296
|
+
}
|
|
111297
|
+
return devin.runDevinProbe(deps, args);
|
|
111298
|
+
}
|
|
110956
111299
|
return runAntigravityProbe(deps, args);
|
|
110957
111300
|
}
|
|
110958
|
-
var PROBE_FAILURE_CODES, PROBE_EXIT, CALLER_CODES,
|
|
111301
|
+
var PROBE_FAILURE_CODES, PROBE_EXIT, CALLER_CODES, SUBSCRIPTION_PROBE_USAGE = "usage: claudish subscription probe antigravity|devin --model <wire-model-id> --request-file <path> --raw --json", PROBE_REQUEST_FILE_MESSAGE = "[claudish subscription] the request file must hold one JSON object (the inner Gemini request)", SECRET_GUARD_MESSAGE2 = "[claudish subscription] refused: output contained a credential value", INTERNAL_ERROR_MESSAGE2 = "[claudish subscription] internal error", PROBE_PROVIDERS;
|
|
110959
111302
|
var init_subscription_probe_command = __esm(() => {
|
|
110960
111303
|
init_antigravity_token();
|
|
110961
111304
|
init_antigravity_user();
|
|
110962
111305
|
init_subscription_antigravity_session();
|
|
110963
111306
|
init_subscription_dispatch();
|
|
111307
|
+
init_subscription_secret_guard();
|
|
110964
111308
|
init_subscription_dispatch();
|
|
111309
|
+
init_subscription_secret_guard();
|
|
110965
111310
|
PROBE_FAILURE_CODES = {
|
|
110966
|
-
credential_missing: SESSION_FAILURE_CODES.credential_missing,
|
|
111311
|
+
credential_missing: [...SESSION_FAILURE_CODES.credential_missing, "devin_credential_absent"],
|
|
110967
111312
|
keychain_locked: SESSION_FAILURE_CODES.keychain_locked,
|
|
110968
111313
|
mint_failed: SESSION_FAILURE_CODES.mint_failed,
|
|
110969
111314
|
network: [
|
|
110970
111315
|
...SESSION_FAILURE_CODES.network,
|
|
110971
111316
|
"generate_content_network_error",
|
|
110972
|
-
"generate_content_timeout"
|
|
111317
|
+
"generate_content_timeout",
|
|
111318
|
+
"get_chat_message_network_error",
|
|
111319
|
+
"get_chat_message_timeout"
|
|
110973
111320
|
],
|
|
110974
111321
|
list_incomplete: ["body_too_large", "body_not_json", "body_unverifiable"],
|
|
110975
111322
|
internal: [
|
|
@@ -110995,20 +111342,7 @@ var init_subscription_probe_command = __esm(() => {
|
|
|
110995
111342
|
"request_file_unreadable",
|
|
110996
111343
|
"request_file_invalid"
|
|
110997
111344
|
];
|
|
110998
|
-
|
|
110999
|
-
PROBE_REQUEST_FILE_CAP_BYTES = 8 * 1024 * 1024;
|
|
111000
|
-
JSON_ESCAPE = /\\(?:u([0-9a-fA-F]{4})|(["\\/bfnrt]))/g;
|
|
111001
|
-
SIMPLE_ESCAPES = {
|
|
111002
|
-
'"': '"',
|
|
111003
|
-
"\\": "\\",
|
|
111004
|
-
"/": "/",
|
|
111005
|
-
b: "\b",
|
|
111006
|
-
f: "\f",
|
|
111007
|
-
n: `
|
|
111008
|
-
`,
|
|
111009
|
-
r: "\r",
|
|
111010
|
-
t: "\t"
|
|
111011
|
-
};
|
|
111345
|
+
PROBE_PROVIDERS = ["antigravity", "devin"];
|
|
111012
111346
|
});
|
|
111013
111347
|
|
|
111014
111348
|
// src/account-forecast-render.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudish",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.16.1",
|
|
4
4
|
"description": "Run Claude Code with any model - OpenRouter, Ollama, LM Studio & local models",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -71,10 +71,10 @@
|
|
|
71
71
|
"ai"
|
|
72
72
|
],
|
|
73
73
|
"optionalDependencies": {
|
|
74
|
-
"@claudish/magmux-darwin-arm64": "14.
|
|
75
|
-
"@claudish/magmux-darwin-x64": "14.
|
|
76
|
-
"@claudish/magmux-linux-arm64": "14.
|
|
77
|
-
"@claudish/magmux-linux-x64": "14.
|
|
74
|
+
"@claudish/magmux-darwin-arm64": "14.16.1",
|
|
75
|
+
"@claudish/magmux-darwin-x64": "14.16.1",
|
|
76
|
+
"@claudish/magmux-linux-arm64": "14.16.1",
|
|
77
|
+
"@claudish/magmux-linux-x64": "14.16.1"
|
|
78
78
|
},
|
|
79
79
|
"author": "Jack Rudenko <i@madappgang.com>",
|
|
80
80
|
"license": "MIT",
|