dsh-agy 0.2.7 → 0.2.9
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/lib/{accounts-D5LlcqCO.mjs → accounts-Um8N9I56.mjs} +146 -31
- package/lib/{accounts-D5LlcqCO.mjs.map → accounts-Um8N9I56.mjs.map} +1 -1
- package/lib/cli/index.mjs +4 -4
- package/lib/{exchange-D1sCdzuN.mjs → exchange-Bdql4kf3.mjs} +2 -2
- package/lib/{exchange-D1sCdzuN.mjs.map → exchange-Bdql4kf3.mjs.map} +1 -1
- package/lib/{import-BPKii1UF.mjs → import-BkqCMX7m.mjs} +3 -3
- package/lib/{import-BPKii1UF.mjs.map → import-BkqCMX7m.mjs.map} +1 -1
- package/lib/index.mjs +2 -2
- package/lib/{models-ExG7kJs1.mjs → models-F-1cWO0f.mjs} +2 -2
- package/lib/{models-ExG7kJs1.mjs.map → models-F-1cWO0f.mjs.map} +1 -1
- package/lib/{plugin-common-BkcyXO5Y.mjs → plugin-common-Di4n-1eR.mjs} +4 -10
- package/lib/plugin-common-Di4n-1eR.mjs.map +1 -0
- package/lib/{translate-nK61WoOC.mjs → translate-CE9tNXiV.mjs} +9 -6
- package/lib/translate-CE9tNXiV.mjs.map +1 -0
- package/lib/web/plugin.mjs +6 -6
- package/package.json +5 -4
- package/lib/plugin-common-BkcyXO5Y.mjs.map +0 -1
- package/lib/translate-nK61WoOC.mjs.map +0 -1
|
@@ -4,6 +4,7 @@ import { createCipheriv, createDecipheriv, createHash, randomBytes, randomUUID }
|
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { dirname, join, resolve } from "node:path";
|
|
6
6
|
import lockfile from "proper-lockfile";
|
|
7
|
+
import { Document, isMap, isScalar, parseDocument } from "yaml";
|
|
7
8
|
//#region src/types.ts
|
|
8
9
|
/**
|
|
9
10
|
* Host-independent authentication error. The adapter maps `kind` to the DSH
|
|
@@ -1092,7 +1093,7 @@ var AgySessionManager = class AgySessionManager {
|
|
|
1092
1093
|
try {
|
|
1093
1094
|
const auth = await this.accessTokenFor(account);
|
|
1094
1095
|
if (!auth) return null;
|
|
1095
|
-
const { fetchAvailableModels } = await import("./models-
|
|
1096
|
+
const { fetchAvailableModels } = await import("./models-F-1cWO0f.mjs").then((n) => n.r);
|
|
1096
1097
|
const quotas = ingestFamilyQuotas(await fetchAvailableModels(auth.access, account.projectId, (input, init) => {
|
|
1097
1098
|
const timeout = AbortSignal.timeout(AgySessionManager.QUOTA_FETCH_TIMEOUT_MS);
|
|
1098
1099
|
const signal = init?.signal ? AbortSignal.any([init.signal, timeout]) : timeout;
|
|
@@ -1243,7 +1244,7 @@ var AgySessionManager = class AgySessionManager {
|
|
|
1243
1244
|
}
|
|
1244
1245
|
const key = this.accountKey(picked.account);
|
|
1245
1246
|
if (!picked.account.projectId && !this.projectRetryFailed.has(key)) try {
|
|
1246
|
-
const { loadCodeAssist } = await import("./exchange-
|
|
1247
|
+
const { loadCodeAssist } = await import("./exchange-Bdql4kf3.mjs").then((n) => n.n);
|
|
1247
1248
|
const { projectId } = await loadCodeAssist(auth.access);
|
|
1248
1249
|
if (projectId) {
|
|
1249
1250
|
await this.store.mutate((s) => {
|
|
@@ -1338,7 +1339,7 @@ var AgySessionManager = class AgySessionManager {
|
|
|
1338
1339
|
ok: false,
|
|
1339
1340
|
error: "No agy account configured — run `dsh-agy login` first."
|
|
1340
1341
|
};
|
|
1341
|
-
const { toAgyRequestBody } = await import("./translate-
|
|
1342
|
+
const { toAgyRequestBody } = await import("./translate-CE9tNXiV.mjs").then((n) => n.n);
|
|
1342
1343
|
const { fetchAgyFirstOk } = await Promise.resolve().then(() => constants_exports);
|
|
1343
1344
|
const { parseAgySse } = await import("./parse-Bs4rPkHA.mjs").then((n) => n.n);
|
|
1344
1345
|
const body = toAgyRequestBody({
|
|
@@ -1528,34 +1529,86 @@ function resolveDshHome() {
|
|
|
1528
1529
|
function homedir() {
|
|
1529
1530
|
return process.env.HOME ?? process.env.USERPROFILE ?? ".";
|
|
1530
1531
|
}
|
|
1532
|
+
/** A credentials reference name must be a POSIX-style identifier. */
|
|
1533
|
+
const REF_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
1534
|
+
/** The document's non-reference sections. */
|
|
1535
|
+
const SECTION_KEYS = ["version", "records"];
|
|
1536
|
+
/** Every top-level key DSH's credentials provider admits; anything else is rejected. */
|
|
1537
|
+
const DOCUMENT_TOP_LEVEL = /* @__PURE__ */ new Set([...SECTION_KEYS, "refs"]);
|
|
1531
1538
|
/**
|
|
1532
|
-
*
|
|
1533
|
-
*
|
|
1534
|
-
|
|
1539
|
+
* Describe one YAML parse failure without quoting the source, which holds
|
|
1540
|
+
* secrets. The parser's own message embeds the offending line.
|
|
1541
|
+
*/
|
|
1542
|
+
function describeYamlError(error) {
|
|
1543
|
+
const at = error.linePos?.[0];
|
|
1544
|
+
return `${error.code ?? "parse-error"}${at ? ` at line ${at.line}, column ${at.col}` : ""}`;
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Parse the credentials document and locate its `refs` section.
|
|
1548
|
+
*
|
|
1549
|
+
* The document is owned by DSH's credentials provider, which serializes it with
|
|
1550
|
+
* the `yaml` package. Values are folded across physical lines (a long
|
|
1551
|
+
* single-quoted scalar folds at whitespace) and `records` payloads nest
|
|
1552
|
+
* arbitrary keys, so this MUST be a real parse rather than a line scan: a
|
|
1553
|
+
* line-based reader cannot represent folding, and it lets a `records` payload
|
|
1554
|
+
* shadow a real top-level reference.
|
|
1555
|
+
*
|
|
1556
|
+
* Deliberately permissive: it validates only the shape it consumes, so the read
|
|
1557
|
+
* path keeps working on a document written by an older build. What may be
|
|
1558
|
+
* rewritten is enforced separately, in the writer.
|
|
1559
|
+
*/
|
|
1560
|
+
function parseCredentialsDocument(text, file, context) {
|
|
1561
|
+
const document = parseDocument(text, {
|
|
1562
|
+
prettyErrors: true,
|
|
1563
|
+
uniqueKeys: true
|
|
1564
|
+
});
|
|
1565
|
+
if (document.errors.length > 0) throw new Error(`${context}: cannot parse ${file}: ` + document.errors.map(describeYamlError).join("; "));
|
|
1566
|
+
const root = document.contents;
|
|
1567
|
+
if (!isMap(root)) throw new Error(`${context}: ${file} must be a mapping`);
|
|
1568
|
+
const refs = root.get("refs");
|
|
1569
|
+
if (refs !== void 0 && refs !== null && !isMap(refs)) throw new Error(`${context}: "refs" in ${file} must be a mapping`);
|
|
1570
|
+
return {
|
|
1571
|
+
document,
|
|
1572
|
+
root,
|
|
1573
|
+
refs: isMap(refs) ? refs : void 0,
|
|
1574
|
+
flat: !root.has("version")
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
/**
|
|
1578
|
+
* Read the references from the DSH credentials document.
|
|
1579
|
+
*
|
|
1580
|
+
* Only `refs` holds references, and a `records` payload must never be able to
|
|
1581
|
+
* shadow one. Two compatibility cases matter:
|
|
1582
|
+
*
|
|
1583
|
+
* - A pre-release "flat" document keeps its references at the top level; the
|
|
1584
|
+
* provider still reads that layout, so we do too.
|
|
1585
|
+
* - Version 0.2.7 of this plugin appended the master key as a *top-level* key.
|
|
1586
|
+
* The provider rejects such a document, but the key still decrypts an
|
|
1587
|
+
* existing account store, so it is read as a fallback. Dropping it would
|
|
1588
|
+
* silently mint a new master key and strand those accounts.
|
|
1589
|
+
*
|
|
1590
|
+
* Non-string and empty values are skipped, mirroring the provider.
|
|
1535
1591
|
*/
|
|
1536
1592
|
function readCredentialsDocument(file) {
|
|
1537
|
-
if (!existsSync(file)) return /* @__PURE__ */ new Map();
|
|
1538
1593
|
const entries = /* @__PURE__ */ new Map();
|
|
1594
|
+
if (!existsSync(file)) return entries;
|
|
1539
1595
|
const text = readFileSync(file, "utf8");
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
if (!
|
|
1545
|
-
const
|
|
1546
|
-
|
|
1547
|
-
if (
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
throw new Error(`readCredentialsDocument: invalid quoted value for "${key}" in ${file}`);
|
|
1551
|
-
}
|
|
1552
|
-
else if (rawValue.startsWith("'")) {
|
|
1553
|
-
if (!rawValue.endsWith("'") || rawValue.length < 2) throw new Error(`readCredentialsDocument: unterminated single-quoted value for "${key}" in ${file}`);
|
|
1554
|
-
value = rawValue.slice(1, -1).replace(/''/g, "'");
|
|
1555
|
-
} else value = rawValue;
|
|
1556
|
-
if (value.length === 0) continue;
|
|
1596
|
+
if (text.trim().length === 0) return entries;
|
|
1597
|
+
const { root, refs, flat } = parseCredentialsDocument(text, file, "readCredentialsDocument");
|
|
1598
|
+
const section = flat ? root : refs;
|
|
1599
|
+
if (section) for (const item of section.items) {
|
|
1600
|
+
if (!isScalar(item.key) || !isScalar(item.value)) continue;
|
|
1601
|
+
const key = item.key.value;
|
|
1602
|
+
const value = item.value.value;
|
|
1603
|
+
if (typeof key !== "string" || !REF_NAME.test(key)) continue;
|
|
1604
|
+
if (flat && SECTION_KEYS.includes(key)) continue;
|
|
1605
|
+
if (typeof value !== "string" || value.length === 0) continue;
|
|
1557
1606
|
entries.set(key, value);
|
|
1558
1607
|
}
|
|
1608
|
+
if (!flat && !entries.has("AGY_MASTER_KEY")) {
|
|
1609
|
+
const legacy = root.get(MASTER_KEY_REF);
|
|
1610
|
+
if (typeof legacy === "string" && legacy.length > 0) entries.set(MASTER_KEY_REF, legacy);
|
|
1611
|
+
}
|
|
1559
1612
|
return entries;
|
|
1560
1613
|
}
|
|
1561
1614
|
/**
|
|
@@ -1579,21 +1632,83 @@ function loadMasterKey(dshHome) {
|
|
|
1579
1632
|
assertOwnerOnly(file);
|
|
1580
1633
|
return readCredentialsDocument(file).get(MASTER_KEY_REF);
|
|
1581
1634
|
}
|
|
1635
|
+
/** Top-level keys DSH's credentials provider admits; anything else is rejected. */
|
|
1636
|
+
/**
|
|
1637
|
+
* Render the version-1 layout for a pre-release "flat" document — a non-empty
|
|
1638
|
+
* top-level mapping of reference names to non-empty string scalars, with no
|
|
1639
|
+
* `version` key and no document directives. The original lines are nested
|
|
1640
|
+
* verbatim under `refs:`, so comments, blank lines, and each value's spelling
|
|
1641
|
+
* survive byte for byte. Returns undefined for anything the recognizer
|
|
1642
|
+
* declines, which the caller must then refuse to rewrite.
|
|
1643
|
+
*
|
|
1644
|
+
* Nesting whole lines is safe for block scalars: an explicit indent indicator
|
|
1645
|
+
* (`|2`) is absolute and the implied one is detected relative to the key, so
|
|
1646
|
+
* shifting every line by the same two spaces preserves the parsed value.
|
|
1647
|
+
*/
|
|
1648
|
+
function renderFlatLayoutMigration(text) {
|
|
1649
|
+
for (const line of text.split("\n")) if (/^(%|---|\.\.\.)/.test(line)) return void 0;
|
|
1650
|
+
const document = parseDocument(text, {
|
|
1651
|
+
prettyErrors: true,
|
|
1652
|
+
uniqueKeys: true
|
|
1653
|
+
});
|
|
1654
|
+
if (document.errors.length > 0) return void 0;
|
|
1655
|
+
const flat = document.contents;
|
|
1656
|
+
if (!isMap(flat) || flat.items.length === 0) return void 0;
|
|
1657
|
+
for (const pair of flat.items) {
|
|
1658
|
+
if (!isScalar(pair.key) || typeof pair.key.value !== "string") return void 0;
|
|
1659
|
+
if (DOCUMENT_TOP_LEVEL.has(pair.key.value) || !REF_NAME.test(pair.key.value)) return void 0;
|
|
1660
|
+
if (!isScalar(pair.value) || typeof pair.value.value !== "string") return void 0;
|
|
1661
|
+
if (pair.value.value.length === 0) return void 0;
|
|
1662
|
+
}
|
|
1663
|
+
return `version: 1\nrefs:\n${text.split("\n").map((line) => line.length === 0 ? line : ` ${line}`).join("\n")}${text.endsWith("\n") ? "" : "\n"}`;
|
|
1664
|
+
}
|
|
1665
|
+
/**
|
|
1666
|
+
* Render the credentials document with {@link MASTER_KEY_REF} added under `refs`.
|
|
1667
|
+
*
|
|
1668
|
+
* The key MUST be nested under `refs`: DSH's credentials provider rejects any
|
|
1669
|
+
* other top-level key, and a document it rejects makes *every* credential in
|
|
1670
|
+
* the file unreadable, not just ours. Editing the parsed document keeps the
|
|
1671
|
+
* comments and formatting of every untouched entry; a document this build
|
|
1672
|
+
* cannot prove it understands is never rewritten.
|
|
1673
|
+
*/
|
|
1674
|
+
function renderWithMasterKey(existingText, masterKey, file) {
|
|
1675
|
+
const fresh = () => new Document({
|
|
1676
|
+
version: 1,
|
|
1677
|
+
refs: { [MASTER_KEY_REF]: masterKey }
|
|
1678
|
+
}).toString();
|
|
1679
|
+
if (existingText.trim().length === 0) return fresh();
|
|
1680
|
+
const { document, root, refs, flat } = parseCredentialsDocument(existingText, file, "persistMasterKey");
|
|
1681
|
+
if (flat) {
|
|
1682
|
+
const migrated = renderFlatLayoutMigration(existingText);
|
|
1683
|
+
if (migrated === void 0) throw new Error(`persistMasterKey: ${file} is not a credentials document this build can prove it understands; refusing to rewrite it`);
|
|
1684
|
+
const next = parseDocument(migrated);
|
|
1685
|
+
next.setIn(["refs", MASTER_KEY_REF], masterKey);
|
|
1686
|
+
return next.toString();
|
|
1687
|
+
}
|
|
1688
|
+
for (const key of root.items) {
|
|
1689
|
+
if (!isScalar(key.key) || typeof key.key.value !== "string") continue;
|
|
1690
|
+
if (!DOCUMENT_TOP_LEVEL.has(key.key.value)) throw new Error(`persistMasterKey: ${file} has unknown top-level key "${key.key.value}"; refusing to rewrite it`);
|
|
1691
|
+
}
|
|
1692
|
+
const version = root.get("version");
|
|
1693
|
+
if (version !== 1) throw new Error(`persistMasterKey: ${file} declares version ${JSON.stringify(version)}; refusing to rewrite it`);
|
|
1694
|
+
if (refs) document.setIn(["refs", MASTER_KEY_REF], masterKey);
|
|
1695
|
+
else document.set("refs", { [MASTER_KEY_REF]: masterKey });
|
|
1696
|
+
return document.toString();
|
|
1697
|
+
}
|
|
1582
1698
|
/**
|
|
1583
1699
|
* Generate and persist a fresh master key (0600) in the DSH credentials document.
|
|
1584
1700
|
*
|
|
1585
|
-
*
|
|
1586
|
-
*
|
|
1587
|
-
*
|
|
1588
|
-
*
|
|
1701
|
+
* The key is added under `refs` by editing the parsed document, then written
|
|
1702
|
+
* with an atomic tmp+rename, so every other credential — and the comments and
|
|
1703
|
+
* formatting DSH services rely on — survives. Never rewrite the whole file from
|
|
1704
|
+
* a rebuilt view: that would silently drop entries this build does not model.
|
|
1589
1705
|
*/
|
|
1590
1706
|
function persistMasterKey(dshHome, masterKey) {
|
|
1591
1707
|
const file = join(dshHome, ".credentials.yaml");
|
|
1592
1708
|
mkdirSync(dirname(file), { recursive: true });
|
|
1593
1709
|
const existingText = existsSync(file) ? readFileSync(file, "utf8") : "";
|
|
1594
1710
|
if (existingText.length > 0 && readCredentialsDocument(file).has("AGY_MASTER_KEY")) throw new Error(`persistMasterKey: ${MASTER_KEY_REF} already exists in ${file}`);
|
|
1595
|
-
const
|
|
1596
|
-
const next = existingText.length > 0 && !existingText.endsWith("\n") ? `${existingText}\n${entry}\n` : `${existingText}${entry}\n`;
|
|
1711
|
+
const next = renderWithMasterKey(existingText, masterKey, file);
|
|
1597
1712
|
const tmp = `${file}.tmp-masterkey`;
|
|
1598
1713
|
writeFileSync(tmp, next, { mode: 384 });
|
|
1599
1714
|
renameSync(tmp, file);
|
|
@@ -1832,4 +1947,4 @@ var JsonAccountStore = class {
|
|
|
1832
1947
|
//#endregion
|
|
1833
1948
|
export { AgyPoolBlockedError as A, OAUTH_TOKEN_URL as C, getAgyBootstrapUserAgent as D, getAgyBootstrapClientMetadata as E, resolveAgyClientCredentials as O, OAUTH_AUTHORIZE_URL as S, fetchAgyFirstOk as T, deriveAntigravitySessionId as _, deriveKey as a, AGY_ENDPOINT_FALLBACKS as b, resolveDshHome as c, isAgyDisabled as d, generateFingerprint as f, resolveAntigravityVersionBounded as g, resolveAntigravityVersion as h, createAesGcmCodec as i, AgyAuthError as k, resolveMasterKeyCodec as l, calculateTokenExpiry as m, maskProxyUrl as n, loadMasterKey as o, recordFingerprintVersion as p, MASTER_KEY_REF as r, persistMasterKey as s, JsonAccountStore as t, AgySessionManager as u, generateAntigravityRequestId as v, OAUTH_USERINFO_URL as w, AGY_SCOPES as x, AGY_DEFAULT_REDIRECT_URI as y };
|
|
1834
1949
|
|
|
1835
|
-
//# sourceMappingURL=accounts-
|
|
1950
|
+
//# sourceMappingURL=accounts-Um8N9I56.mjs.map
|