dsh-multi-router 0.0.0-stage → 0.3.7

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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +249 -2
  3. package/THIRD_PARTY_NOTICES.md +135 -0
  4. package/cordis.patch.yml +3 -0
  5. package/lib/accounts.js +13 -0
  6. package/lib/accounts.js.map +7 -0
  7. package/lib/chunk-3VVQXFPV.js +449 -0
  8. package/lib/chunk-3VVQXFPV.js.map +7 -0
  9. package/lib/chunk-BIEDP6NW.js +885 -0
  10. package/lib/chunk-BIEDP6NW.js.map +7 -0
  11. package/lib/chunk-DNQPZMPF.js +33 -0
  12. package/lib/chunk-DNQPZMPF.js.map +7 -0
  13. package/lib/chunk-HZVBEBOD.js +208 -0
  14. package/lib/chunk-HZVBEBOD.js.map +7 -0
  15. package/lib/chunk-IEN3ZQFS.js +95 -0
  16. package/lib/chunk-IEN3ZQFS.js.map +7 -0
  17. package/lib/chunk-PD2DTHG2.js +116 -0
  18. package/lib/chunk-PD2DTHG2.js.map +7 -0
  19. package/lib/chunk-S5KMFAQM.js +50 -0
  20. package/lib/chunk-S5KMFAQM.js.map +7 -0
  21. package/lib/chunk-TEZ2FMEO.js +1091 -0
  22. package/lib/chunk-TEZ2FMEO.js.map +7 -0
  23. package/lib/chunk-VESY2B5O.js +5068 -0
  24. package/lib/chunk-VESY2B5O.js.map +7 -0
  25. package/lib/chunk-WNRHV24A.js +2022 -0
  26. package/lib/chunk-WNRHV24A.js.map +7 -0
  27. package/lib/cli.js +102 -0
  28. package/lib/cli.js.map +7 -0
  29. package/lib/client.js +1371 -0
  30. package/lib/client.js.map +7 -0
  31. package/lib/clients.js +16 -0
  32. package/lib/clients.js.map +7 -0
  33. package/lib/host.js +362 -0
  34. package/lib/index.js +362 -0
  35. package/lib/index.js.map +7 -0
  36. package/lib/manager.js +23 -0
  37. package/lib/manager.js.map +7 -0
  38. package/lib/region-DO3KAV7V.js +15 -0
  39. package/lib/region-DO3KAV7V.js.map +7 -0
  40. package/lib/router.js +14 -0
  41. package/lib/router.js.map +7 -0
  42. package/lib/shim.js +10 -0
  43. package/lib/shim.js.map +7 -0
  44. package/lib/upstream-42RS7F4H.js +25 -0
  45. package/lib/upstream-42RS7F4H.js.map +7 -0
  46. package/package.json +72 -4
  47. package/router.example.json +289 -0
  48. package/upstream-lock.json +17 -0
@@ -0,0 +1,95 @@
1
+ import {
2
+ TraeCredentialStore,
3
+ WORKBUDDY_VARIANTS,
4
+ WorkBuddyCredentialStore,
5
+ atRestKeyProviderFor,
6
+ readTraeIdentity
7
+ } from "./chunk-WNRHV24A.js";
8
+ import {
9
+ PROVIDERS,
10
+ writePrivateJson
11
+ } from "./chunk-HZVBEBOD.js";
12
+
13
+ // src/accounts.mjs
14
+ import { readFile } from "node:fs/promises";
15
+ var pick = (value, keys) => Object.fromEntries(keys.filter((k) => Object.hasOwn(value ?? {}, k)).map((k) => [k, value[k]]));
16
+ async function readPortableCredential(account) {
17
+ const raw = JSON.parse(await readFile(account.credentialFile, "utf8"));
18
+ if (account.provider.startsWith("qoder")) return raw.pat ? { document: pick(raw, ["version", "pat", "region"]) } : null;
19
+ if (!raw.credential?.accessToken) return null;
20
+ const document = { version: 1, credential: pick(raw.credential, ["accessToken", "refreshToken", "expiresAtMs", "refreshExpiresAtMs", "uid", "nickname", "enterpriseId", "domain", "userId", "accountName", "edition", "userRegion", "host"]) };
21
+ const identity = account.provider.startsWith("trae") ? pick(JSON.parse(await readFile(account.identityFile, "utf8")), ["machineId", "deviceId", "edition"]) : void 0;
22
+ if (identity && (!identity.machineId || !identity.deviceId || identity.edition !== raw.credential.edition)) throw new Error("Trae identity is incomplete");
23
+ return { document, identity };
24
+ }
25
+ async function importAccount({ provider, source, credentialFile, identityFile, edition, identitySource, document, identityDocument }) {
26
+ if (!PROVIDERS.includes(provider)) throw new Error("Unknown provider");
27
+ const inline = document !== void 0;
28
+ const raw = inline ? JSON.stringify(document) : await readFile(source, "utf8");
29
+ if (!inline) try {
30
+ document = JSON.parse(raw);
31
+ } catch {
32
+ }
33
+ if (document?.format === "dsh-multi-router-account" && document.provider !== provider) throw new Error("Portable credential provider does not match selected provider");
34
+ const text = (value) => typeof value === "string" && value.trim().length > 0 && value.length <= 32768;
35
+ if (provider.startsWith("workbuddy")) {
36
+ let credential;
37
+ if (document?.version === 1 && document.credential) credential = document.credential;
38
+ else if (inline) credential = document;
39
+ else {
40
+ const variant = WORKBUDDY_VARIANTS.find((v) => v.id === provider);
41
+ const store = new WorkBuddyCredentialStore({
42
+ desktopPath: source,
43
+ ownPath: `${credentialFile}.import-empty`,
44
+ variant,
45
+ keyProvider: atRestKeyProviderFor(variant),
46
+ refresh: async () => {
47
+ throw new Error("Import cannot refresh");
48
+ }
49
+ });
50
+ credential = await store.current();
51
+ }
52
+ if (!text(credential?.accessToken) || !text(credential.uid) || !Number.isFinite(credential.expiresAtMs) || credential.expiresAtMs <= 0 || typeof credential.domain !== "string" || /[\r\n]/.test(credential.domain)) throw new Error("No valid WorkBuddy credential in source");
53
+ const { regionOf } = await import("./upstream-42RS7F4H.js");
54
+ if (regionOf(credential.domain) === "global" !== (provider === "workbuddy-ai")) throw new Error("WorkBuddy credential region does not match provider");
55
+ await writePrivateJson(credentialFile, { version: 1, credential: { ...credential, source: "dsh" } });
56
+ } else if (provider.startsWith("trae")) {
57
+ let credential;
58
+ if (document?.version === 1 && document.credential) credential = document.credential;
59
+ else if (inline) credential = document;
60
+ else {
61
+ if (!["cn", "sg", "solo", "solo-sg"].includes(edition)) throw new Error("Trae desktop import needs --edition cn|sg|solo|solo-sg");
62
+ const store = new TraeCredentialStore({
63
+ storagePath: source,
64
+ edition,
65
+ ownPath: `${credentialFile}.import-empty`,
66
+ legacyOwnPath: `${credentialFile}.no-legacy`,
67
+ region: provider === "trae" ? "cn" : "ai",
68
+ refresh: async () => {
69
+ throw new Error("Import cannot refresh");
70
+ }
71
+ });
72
+ credential = await store.current();
73
+ }
74
+ if (!text(credential?.accessToken) || !text(credential.userId) || !Number.isFinite(credential.expiresAtMs) || credential.expiresAtMs <= 0 || !["cn", "sg", "solo", "solo-sg"].includes(credential.edition)) throw new Error("No valid Trae credential in source");
75
+ const { regionOfCredential, regionOfHost } = await import("./region-DO3KAV7V.js");
76
+ if (credential.host && !regionOfHost(credential.host)) throw new Error("Invalid Trae credential host");
77
+ if (regionOfCredential(credential) === "ai" !== (provider === "trae-global")) throw new Error("Trae credential region does not match provider");
78
+ const identity = identityDocument ?? document?.identity ?? (identitySource ? JSON.parse(await readFile(identitySource, "utf8")) : source ? await readTraeIdentity({ edition: credential.edition, path: source, source: "desktop" }) : void 0);
79
+ if (!text(identity?.machineId) || !text(identity.deviceId) || identity.edition !== credential.edition) throw new Error("Identity must belong to this Trae edition");
80
+ await writePrivateJson(identityFile, identity);
81
+ await writePrivateJson(credentialFile, { version: 1, credential: { ...credential, source: "dsh" } });
82
+ } else {
83
+ const region = provider === "qoder" ? "china" : "global";
84
+ const pat = typeof document?.pat === "string" ? document.pat.trim() : raw.trim();
85
+ if (!pat || document && typeof document.pat !== "string" || /\s/.test(pat)) throw new Error("Source must be a Qoder PAT text file or credential JSON");
86
+ if (document?.region && document.region !== region) throw new Error("Qoder PAT region does not match provider");
87
+ await writePrivateJson(credentialFile, { version: 2, pat, region, savedAt: (/* @__PURE__ */ new Date()).toISOString() });
88
+ }
89
+ }
90
+
91
+ export {
92
+ readPortableCredential,
93
+ importAccount
94
+ };
95
+ //# sourceMappingURL=chunk-IEN3ZQFS.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/accounts.mjs"],
4
+ "sourcesContent": ["import { readFile } from 'node:fs/promises';\nimport { WorkBuddyCredentialStore } from '../upstream/workbuddy/src/auth.ts';\nimport { WORKBUDDY_VARIANTS } from '../upstream/workbuddy/src/variants.ts';\nimport { atRestKeyProviderFor } from '../upstream/workbuddy/src/desktop-credential-protection.ts';\nimport { TraeCredentialStore } from '../upstream/trae/src/auth.ts';\nimport { readTraeIdentity } from '../upstream/trae/src/identity.ts';\nimport { PROVIDERS, writePrivateJson } from './config.mjs';\n\nconst pick = (value, keys) => Object.fromEntries(keys.filter(k => Object.hasOwn(value ?? {}, k)).map(k => [k, value[k]]));\n// Share the same portable fields with full encrypted configuration migration.\nexport async function readPortableCredential(account) {\n const raw = JSON.parse(await readFile(account.credentialFile, 'utf8'));\n if (account.provider.startsWith('qoder')) return raw.pat ? { document: pick(raw, ['version', 'pat', 'region']) } : null;\n if (!raw.credential?.accessToken) return null;\n const document = { version: 1, credential: pick(raw.credential, ['accessToken', 'refreshToken', 'expiresAtMs', 'refreshExpiresAtMs', 'uid', 'nickname', 'enterpriseId', 'domain', 'userId', 'accountName', 'edition', 'userRegion', 'host']) };\n const identity = account.provider.startsWith('trae') ? pick(JSON.parse(await readFile(account.identityFile, 'utf8')), ['machineId', 'deviceId', 'edition']) : undefined;\n if (identity && (!identity.machineId || !identity.deviceId || identity.edition !== raw.credential.edition)) throw new Error('Trae identity is incomplete');\n return { document, identity };\n}\n\n// Imports never refresh tokens or write to the provider's own files.\nexport async function importAccount({ provider, source, credentialFile, identityFile, edition, identitySource, document, identityDocument }) {\n if (!PROVIDERS.includes(provider)) throw new Error('Unknown provider');\n const inline = document !== undefined;\n const raw = inline ? JSON.stringify(document) : await readFile(source, 'utf8');\n if (!inline) try { document = JSON.parse(raw); } catch { /* A Qoder PAT can be a plain text file. */ }\n if (document?.format === 'dsh-multi-router-account' && document.provider !== provider) throw new Error('Portable credential provider does not match selected provider');\n const text = value => typeof value === 'string' && value.trim().length > 0 && value.length <= 32768;\n if (provider.startsWith('workbuddy')) {\n let credential;\n if (document?.version === 1 && document.credential) credential = document.credential;\n else if (inline) credential = document;\n else {\n const variant = WORKBUDDY_VARIANTS.find(v => v.id === provider);\n const store = new WorkBuddyCredentialStore({ desktopPath: source, ownPath: `${credentialFile}.import-empty`,\n variant, keyProvider: atRestKeyProviderFor(variant), refresh: async () => { throw new Error('Import cannot refresh'); } });\n credential = await store.current();\n }\n if (!text(credential?.accessToken) || !text(credential.uid) || !Number.isFinite(credential.expiresAtMs) || credential.expiresAtMs <= 0 || typeof credential.domain !== 'string' || /[\\r\\n]/.test(credential.domain)) throw new Error('No valid WorkBuddy credential in source');\n // Use the upstream region resolver rather than guessing regional domains.\n const { regionOf } = await import('../upstream/workbuddy/src/upstream.ts');\n if ((regionOf(credential.domain) === 'global') !== (provider === 'workbuddy-ai')) throw new Error('WorkBuddy credential region does not match provider');\n await writePrivateJson(credentialFile, { version: 1, credential: { ...credential, source: 'dsh' } });\n } else if (provider.startsWith('trae')) {\n let credential;\n if (document?.version === 1 && document.credential) credential = document.credential;\n else if (inline) credential = document;\n else {\n if (!['cn', 'sg', 'solo', 'solo-sg'].includes(edition)) throw new Error('Trae desktop import needs --edition cn|sg|solo|solo-sg');\n const store = new TraeCredentialStore({ storagePath: source, edition, ownPath: `${credentialFile}.import-empty`,\n legacyOwnPath: `${credentialFile}.no-legacy`, region: provider === 'trae' ? 'cn' : 'ai',\n refresh: async () => { throw new Error('Import cannot refresh'); } });\n credential = await store.current();\n }\n if (!text(credential?.accessToken) || !text(credential.userId) || !Number.isFinite(credential.expiresAtMs) || credential.expiresAtMs <= 0 || !['cn', 'sg', 'solo', 'solo-sg'].includes(credential.edition)) throw new Error('No valid Trae credential in source');\n const { regionOfCredential, regionOfHost } = await import('../upstream/trae/src/region.ts');\n if (credential.host && !regionOfHost(credential.host)) throw new Error('Invalid Trae credential host');\n if ((regionOfCredential(credential) === 'ai') !== (provider === 'trae-global')) throw new Error('Trae credential region does not match provider');\n const identity = identityDocument ?? document?.identity ?? (identitySource ? JSON.parse(await readFile(identitySource, 'utf8'))\n : source ? await readTraeIdentity({ edition: credential.edition, path: source, source: 'desktop' }) : undefined);\n if (!text(identity?.machineId) || !text(identity.deviceId) || identity.edition !== credential.edition) throw new Error('Identity must belong to this Trae edition');\n await writePrivateJson(identityFile, identity);\n await writePrivateJson(credentialFile, { version: 1, credential: { ...credential, source: 'dsh' } });\n } else {\n const region = provider === 'qoder' ? 'china' : 'global';\n const pat = typeof document?.pat === 'string' ? document.pat.trim() : raw.trim();\n if (!pat || (document && typeof document.pat !== 'string') || /\\s/.test(pat)) throw new Error('Source must be a Qoder PAT text file or credential JSON');\n if (document?.region && document.region !== region) throw new Error('Qoder PAT region does not match provider');\n await writePrivateJson(credentialFile, { version: 2, pat, region, savedAt: new Date().toISOString() });\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;AAAA,SAAS,gBAAgB;AAQzB,IAAM,OAAO,CAAC,OAAO,SAAS,OAAO,YAAY,KAAK,OAAO,OAAK,OAAO,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,OAAK,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;AAExH,eAAsB,uBAAuB,SAAS;AACpD,QAAM,MAAM,KAAK,MAAM,MAAM,SAAS,QAAQ,gBAAgB,MAAM,CAAC;AACrE,MAAI,QAAQ,SAAS,WAAW,OAAO,EAAG,QAAO,IAAI,MAAM,EAAE,UAAU,KAAK,KAAK,CAAC,WAAW,OAAO,QAAQ,CAAC,EAAE,IAAI;AACnH,MAAI,CAAC,IAAI,YAAY,YAAa,QAAO;AACzC,QAAM,WAAW,EAAE,SAAS,GAAG,YAAY,KAAK,IAAI,YAAY,CAAC,eAAe,gBAAgB,eAAe,sBAAsB,OAAO,YAAY,gBAAgB,UAAU,UAAU,eAAe,WAAW,cAAc,MAAM,CAAC,EAAE;AAC7O,QAAM,WAAW,QAAQ,SAAS,WAAW,MAAM,IAAI,KAAK,KAAK,MAAM,MAAM,SAAS,QAAQ,cAAc,MAAM,CAAC,GAAG,CAAC,aAAa,YAAY,SAAS,CAAC,IAAI;AAC9J,MAAI,aAAa,CAAC,SAAS,aAAa,CAAC,SAAS,YAAY,SAAS,YAAY,IAAI,WAAW,SAAU,OAAM,IAAI,MAAM,6BAA6B;AACzJ,SAAO,EAAE,UAAU,SAAS;AAC9B;AAGA,eAAsB,cAAc,EAAE,UAAU,QAAQ,gBAAgB,cAAc,SAAS,gBAAgB,UAAU,iBAAiB,GAAG;AAC3I,MAAI,CAAC,UAAU,SAAS,QAAQ,EAAG,OAAM,IAAI,MAAM,kBAAkB;AACrE,QAAM,SAAS,aAAa;AAC5B,QAAM,MAAM,SAAS,KAAK,UAAU,QAAQ,IAAI,MAAM,SAAS,QAAQ,MAAM;AAC7E,MAAI,CAAC,OAAQ,KAAI;AAAE,eAAW,KAAK,MAAM,GAAG;AAAA,EAAG,QAAQ;AAAA,EAA8C;AACrG,MAAI,UAAU,WAAW,8BAA8B,SAAS,aAAa,SAAU,OAAM,IAAI,MAAM,+DAA+D;AACtK,QAAM,OAAO,WAAS,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,KAAK,MAAM,UAAU;AAC9F,MAAI,SAAS,WAAW,WAAW,GAAG;AACpC,QAAI;AACJ,QAAI,UAAU,YAAY,KAAK,SAAS,WAAY,cAAa,SAAS;AAAA,aACjE,OAAQ,cAAa;AAAA,SACzB;AACH,YAAM,UAAU,mBAAmB,KAAK,OAAK,EAAE,OAAO,QAAQ;AAC9D,YAAM,QAAQ,IAAI,yBAAyB;AAAA,QAAE,aAAa;AAAA,QAAQ,SAAS,GAAG,cAAc;AAAA,QAC1F;AAAA,QAAS,aAAa,qBAAqB,OAAO;AAAA,QAAG,SAAS,YAAY;AAAE,gBAAM,IAAI,MAAM,uBAAuB;AAAA,QAAG;AAAA,MAAE,CAAC;AAC3H,mBAAa,MAAM,MAAM,QAAQ;AAAA,IACnC;AACA,QAAI,CAAC,KAAK,YAAY,WAAW,KAAK,CAAC,KAAK,WAAW,GAAG,KAAK,CAAC,OAAO,SAAS,WAAW,WAAW,KAAK,WAAW,eAAe,KAAK,OAAO,WAAW,WAAW,YAAY,SAAS,KAAK,WAAW,MAAM,EAAG,OAAM,IAAI,MAAM,yCAAyC;AAE9Q,UAAM,EAAE,SAAS,IAAI,MAAM,OAAO,wBAAuC;AACzE,QAAK,SAAS,WAAW,MAAM,MAAM,cAAe,aAAa,gBAAiB,OAAM,IAAI,MAAM,qDAAqD;AACvJ,UAAM,iBAAiB,gBAAgB,EAAE,SAAS,GAAG,YAAY,EAAE,GAAG,YAAY,QAAQ,MAAM,EAAE,CAAC;AAAA,EACrG,WAAW,SAAS,WAAW,MAAM,GAAG;AACtC,QAAI;AACJ,QAAI,UAAU,YAAY,KAAK,SAAS,WAAY,cAAa,SAAS;AAAA,aACjE,OAAQ,cAAa;AAAA,SACzB;AACH,UAAI,CAAC,CAAC,MAAM,MAAM,QAAQ,SAAS,EAAE,SAAS,OAAO,EAAG,OAAM,IAAI,MAAM,wDAAwD;AAChI,YAAM,QAAQ,IAAI,oBAAoB;AAAA,QAAE,aAAa;AAAA,QAAQ;AAAA,QAAS,SAAS,GAAG,cAAc;AAAA,QAC9F,eAAe,GAAG,cAAc;AAAA,QAAc,QAAQ,aAAa,SAAS,OAAO;AAAA,QACnF,SAAS,YAAY;AAAE,gBAAM,IAAI,MAAM,uBAAuB;AAAA,QAAG;AAAA,MAAE,CAAC;AACtE,mBAAa,MAAM,MAAM,QAAQ;AAAA,IACnC;AACA,QAAI,CAAC,KAAK,YAAY,WAAW,KAAK,CAAC,KAAK,WAAW,MAAM,KAAK,CAAC,OAAO,SAAS,WAAW,WAAW,KAAK,WAAW,eAAe,KAAK,CAAC,CAAC,MAAM,MAAM,QAAQ,SAAS,EAAE,SAAS,WAAW,OAAO,EAAG,OAAM,IAAI,MAAM,oCAAoC;AAChQ,UAAM,EAAE,oBAAoB,aAAa,IAAI,MAAM,OAAO,sBAAgC;AAC1F,QAAI,WAAW,QAAQ,CAAC,aAAa,WAAW,IAAI,EAAG,OAAM,IAAI,MAAM,8BAA8B;AACrG,QAAK,mBAAmB,UAAU,MAAM,UAAW,aAAa,eAAgB,OAAM,IAAI,MAAM,gDAAgD;AAChJ,UAAM,WAAW,oBAAoB,UAAU,aAAa,iBAAiB,KAAK,MAAM,MAAM,SAAS,gBAAgB,MAAM,CAAC,IAC1H,SAAS,MAAM,iBAAiB,EAAE,SAAS,WAAW,SAAS,MAAM,QAAQ,QAAQ,UAAU,CAAC,IAAI;AACxG,QAAI,CAAC,KAAK,UAAU,SAAS,KAAK,CAAC,KAAK,SAAS,QAAQ,KAAK,SAAS,YAAY,WAAW,QAAS,OAAM,IAAI,MAAM,2CAA2C;AAClK,UAAM,iBAAiB,cAAc,QAAQ;AAC7C,UAAM,iBAAiB,gBAAgB,EAAE,SAAS,GAAG,YAAY,EAAE,GAAG,YAAY,QAAQ,MAAM,EAAE,CAAC;AAAA,EACrG,OAAO;AACL,UAAM,SAAS,aAAa,UAAU,UAAU;AAChD,UAAM,MAAM,OAAO,UAAU,QAAQ,WAAW,SAAS,IAAI,KAAK,IAAI,IAAI,KAAK;AAC/E,QAAI,CAAC,OAAQ,YAAY,OAAO,SAAS,QAAQ,YAAa,KAAK,KAAK,GAAG,EAAG,OAAM,IAAI,MAAM,yDAAyD;AACvJ,QAAI,UAAU,UAAU,SAAS,WAAW,OAAQ,OAAM,IAAI,MAAM,0CAA0C;AAC9G,UAAM,iBAAiB,gBAAgB,EAAE,SAAS,GAAG,KAAK,QAAQ,UAAS,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC;AAAA,EACvG;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,116 @@
1
+ import {
2
+ hostIsLoopback,
3
+ originIsLoopback
4
+ } from "./chunk-DNQPZMPF.js";
5
+ import {
6
+ RouteError
7
+ } from "./chunk-3VVQXFPV.js";
8
+ import {
9
+ REASONING_EFFORTS
10
+ } from "./chunk-HZVBEBOD.js";
11
+
12
+ // src/shim.mjs
13
+ import { createServer } from "node:http";
14
+ import { randomBytes, timingSafeEqual } from "node:crypto";
15
+ import { Readable } from "node:stream";
16
+ import { pipeline } from "node:stream/promises";
17
+ function createShim(router, { modelPrefix = "" } = {}) {
18
+ const secret = randomBytes(32).toString("base64url");
19
+ const server = createServer((req, res) => {
20
+ void handle(req, res);
21
+ });
22
+ const ready = new Promise((resolve, reject) => {
23
+ server.once("listening", resolve);
24
+ server.once("error", reject);
25
+ });
26
+ server.listen(0, "127.0.0.1");
27
+ const json = (res, status, value) => {
28
+ res.writeHead(status, { "Content-Type": "application/json" });
29
+ res.end(JSON.stringify(value));
30
+ };
31
+ async function handle(req, res) {
32
+ const controller = new AbortController();
33
+ req.on("aborted", () => controller.abort());
34
+ res.on("close", () => {
35
+ if (!res.writableEnded) controller.abort();
36
+ });
37
+ const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(router.config.requestTimeoutMs)]);
38
+ try {
39
+ if (!hostIsLoopback(req.headers.host) || !originIsLoopback(req.headers.origin)) throw new RouteError(403, "loopback_required");
40
+ const bearer = Buffer.from((req.headers.authorization ?? "").replace(/^Bearer\s+/i, ""));
41
+ const expected = Buffer.from(secret);
42
+ if (bearer.length !== expected.length || !timingSafeEqual(bearer, expected)) throw new RouteError(401, "unauthorized");
43
+ if (req.method === "GET" && req.url === "/router/status") {
44
+ json(res, 200, router.status());
45
+ return;
46
+ }
47
+ if (req.method === "GET" && req.url === "/v1/models") {
48
+ json(res, 200, { object: "list", data: Object.keys(router.config.routes).filter((id) => modelPrefix ? id.startsWith(modelPrefix) : !id.includes("::")).map((id) => ({ id: modelPrefix ? id.slice(modelPrefix.length) : id, object: "model", created: 0, owned_by: "multi-router" })) });
49
+ return;
50
+ }
51
+ if (req.method !== "POST" || req.url !== "/v1/chat/completions") throw new RouteError(404, "not_found");
52
+ if (!/^application\/json(?:\s*;|$)/i.test(req.headers["content-type"] ?? "")) throw new RouteError(415, "json_required");
53
+ let size = 0;
54
+ const chunks = [];
55
+ for await (const chunk of req) {
56
+ size += chunk.length;
57
+ if (size > 32 * 1024 * 1024) throw new RouteError(413, "request_too_large");
58
+ chunks.push(chunk);
59
+ signal.throwIfAborted();
60
+ }
61
+ let input;
62
+ try {
63
+ input = JSON.parse(Buffer.concat(chunks).toString("utf8"));
64
+ } catch {
65
+ throw new RouteError(400, "invalid_json");
66
+ }
67
+ if (!input || typeof input !== "object" || Array.isArray(input)) throw new RouteError(400, "invalid_request");
68
+ const effort = req.headers["x-multi-router-reasoning"];
69
+ if (effort !== void 0) {
70
+ if (effort !== "default" && !REASONING_EFFORTS.includes(effort)) throw new RouteError(400, "invalid_reasoning_effort");
71
+ input.reasoning_effort = effort === "default" ? null : effort;
72
+ }
73
+ const selected = await router.chat({ ...input, model: modelPrefix + input.model }, signal);
74
+ signal.throwIfAborted();
75
+ res.writeHead(200, {
76
+ "Content-Type": "text/event-stream; charset=utf-8",
77
+ "Cache-Control": "no-cache",
78
+ "X-Accel-Buffering": "no",
79
+ "X-Router-Account": selected.account,
80
+ "X-Router-Provider": selected.provider
81
+ });
82
+ await pipeline(Readable.fromWeb(selected.response.body), res, { signal });
83
+ } catch (error) {
84
+ if (res.destroyed) return;
85
+ if (res.headersSent) {
86
+ res.destroy();
87
+ return;
88
+ }
89
+ const status = error instanceof RouteError ? error.status : signal.aborted ? 504 : 500;
90
+ json(res, status, { error: {
91
+ type: error instanceof RouteError ? error.code : signal.aborted ? "request_timeout" : "internal_error",
92
+ message: error instanceof RouteError ? error.message : "Router request failed",
93
+ hint: error instanceof RouteError ? error.hint : void 0,
94
+ attempts: error instanceof RouteError ? error.attempts : []
95
+ } });
96
+ }
97
+ }
98
+ return {
99
+ ready,
100
+ token: () => secret,
101
+ baseUrl: () => `http://127.0.0.1:${server.address().port}`,
102
+ close: async () => {
103
+ await ready.catch(() => {
104
+ });
105
+ await new Promise((resolve) => {
106
+ server.close(resolve);
107
+ server.closeAllConnections();
108
+ });
109
+ }
110
+ };
111
+ }
112
+
113
+ export {
114
+ createShim
115
+ };
116
+ //# sourceMappingURL=chunk-PD2DTHG2.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/shim.mjs"],
4
+ "sourcesContent": ["import { createServer } from 'node:http';\nimport { randomBytes, timingSafeEqual } from 'node:crypto';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\nimport { hostIsLoopback, originIsLoopback } from '../upstream/workbuddy/src/loopback.ts';\nimport { RouteError } from './router.mjs';\nimport { REASONING_EFFORTS } from './reasoning.mjs';\n\nexport function createShim(router, { modelPrefix = '' } = {}) {\n const secret = randomBytes(32).toString('base64url');\n const server = createServer((req, res) => { void handle(req, res); });\n const ready = new Promise((resolve, reject) => {\n server.once('listening', resolve); server.once('error', reject);\n });\n server.listen(0, '127.0.0.1');\n const json = (res, status, value) => {\n res.writeHead(status, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(value));\n };\n async function handle(req, res) {\n const controller = new AbortController();\n req.on('aborted', () => controller.abort());\n res.on('close', () => { if (!res.writableEnded) controller.abort(); });\n const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(router.config.requestTimeoutMs)]);\n try {\n if (!hostIsLoopback(req.headers.host) || !originIsLoopback(req.headers.origin)) throw new RouteError(403, 'loopback_required');\n const bearer = Buffer.from((req.headers.authorization ?? '').replace(/^Bearer\\s+/i, ''));\n const expected = Buffer.from(secret);\n if (bearer.length !== expected.length || !timingSafeEqual(bearer, expected)) throw new RouteError(401, 'unauthorized');\n if (req.method === 'GET' && req.url === '/router/status') { json(res, 200, router.status()); return; }\n if (req.method === 'GET' && req.url === '/v1/models') {\n json(res, 200, { object: 'list', data: Object.keys(router.config.routes).filter(id => modelPrefix ? id.startsWith(modelPrefix) : !id.includes('::'))\n .map(id => ({ id: modelPrefix ? id.slice(modelPrefix.length) : id, object: 'model', created: 0, owned_by: 'multi-router' })) }); return;\n }\n if (req.method !== 'POST' || req.url !== '/v1/chat/completions') throw new RouteError(404, 'not_found');\n if (!/^application\\/json(?:\\s*;|$)/i.test(req.headers['content-type'] ?? '')) throw new RouteError(415, 'json_required');\n let size = 0; const chunks = [];\n for await (const chunk of req) {\n size += chunk.length;\n if (size > 32 * 1024 * 1024) throw new RouteError(413, 'request_too_large');\n chunks.push(chunk); signal.throwIfAborted();\n }\n let input;\n try { input = JSON.parse(Buffer.concat(chunks).toString('utf8')); }\n catch { throw new RouteError(400, 'invalid_json'); }\n if (!input || typeof input !== 'object' || Array.isArray(input)) throw new RouteError(400, 'invalid_request');\n const effort = req.headers['x-multi-router-reasoning'];\n if (effort !== undefined) {\n if (effort !== 'default' && !REASONING_EFFORTS.includes(effort)) throw new RouteError(400, 'invalid_reasoning_effort');\n input.reasoning_effort = effort === 'default' ? null : effort;\n }\n const selected = await router.chat({ ...input, model: modelPrefix + input.model }, signal);\n signal.throwIfAborted();\n res.writeHead(200, { 'Content-Type': 'text/event-stream; charset=utf-8', 'Cache-Control': 'no-cache',\n 'X-Accel-Buffering': 'no', 'X-Router-Account': selected.account, 'X-Router-Provider': selected.provider });\n await pipeline(Readable.fromWeb(selected.response.body), res, { signal });\n } catch (error) {\n if (res.destroyed) return;\n if (res.headersSent) { res.destroy(); return; }\n const status = error instanceof RouteError ? error.status : signal.aborted ? 504 : 500;\n json(res, status, { error: { type: error instanceof RouteError ? error.code : signal.aborted ? 'request_timeout' : 'internal_error',\n message: error instanceof RouteError ? error.message : 'Router request failed',\n hint: error instanceof RouteError ? error.hint : undefined,\n attempts: error instanceof RouteError ? error.attempts : [] } });\n }\n }\n return { ready, token: () => secret, baseUrl: () => `http://127.0.0.1:${server.address().port}`,\n close: async () => { await ready.catch(() => {}); await new Promise(resolve => { server.close(resolve); server.closeAllConnections(); }); } };\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,50 @@
1
+ // upstream/trae/src/region.ts
2
+ var REGION_GATEWAYS = {
3
+ cn: {
4
+ chat: "https://trae-api-cn.mchost.guru",
5
+ remote: "https://solo.trae.cn/api/remote/v1",
6
+ pay: "https://api.trae.cn"
7
+ },
8
+ ai: {
9
+ chat: "https://coresg-normal.trae.ai",
10
+ remote: "https://coresg-normal.trae.ai/api/remote/v1",
11
+ pay: "https://growsg-normal.trae.ai"
12
+ }
13
+ };
14
+ function regionOfEdition(edition) {
15
+ return edition === "sg" || edition === "solo-sg" ? "ai" : "cn";
16
+ }
17
+ function regionOfUserRegion(value) {
18
+ const raw = typeof value === "object" && value !== null && !Array.isArray(value) ? value["region"] : value;
19
+ if (typeof raw !== "string") return void 0;
20
+ const lowered = raw.trim().toLowerCase();
21
+ if (lowered === "cn") return "cn";
22
+ if (lowered === "sg" || lowered === "ai") return "ai";
23
+ return void 0;
24
+ }
25
+ function regionOfHost(host) {
26
+ if (host === void 0) return void 0;
27
+ const trimmed = host.trim();
28
+ if (trimmed === "") return void 0;
29
+ let hostname;
30
+ try {
31
+ hostname = new URL(trimmed.startsWith("http") ? trimmed : `https://${trimmed}`).hostname;
32
+ } catch {
33
+ return void 0;
34
+ }
35
+ if (hostname === "trae.ai" || hostname.endsWith(".trae.ai")) return "ai";
36
+ if (hostname === "trae.cn" || hostname.endsWith(".trae.cn") || hostname.endsWith(".trae.com.cn")) return "cn";
37
+ return void 0;
38
+ }
39
+ function regionOfCredential(credential) {
40
+ return regionOfUserRegion(credential.userRegion) ?? regionOfHost(credential.host) ?? regionOfEdition(credential.edition);
41
+ }
42
+
43
+ export {
44
+ REGION_GATEWAYS,
45
+ regionOfEdition,
46
+ regionOfUserRegion,
47
+ regionOfHost,
48
+ regionOfCredential
49
+ };
50
+ //# sourceMappingURL=chunk-S5KMFAQM.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../upstream/trae/src/region.ts"],
4
+ "sourcesContent": ["/**\n * Region model shared by every layer: two storage buckets (`cn` | `ai`) keyed\n * by the credential's own claim, plus the per-region gateway constants.\n *\n * Evidence: docs/INTL_SG_EVIDENCE.md (2026-09-15). The desktop installs of\n * both regions expose the same wire shapes; only the gateways, rosters, and\n * the refresh contract differ. `userRegion.region` ('CN' | 'SG', observed in\n * both cases; the desktop logs also spell it lowercase 'sg') is the\n * authoritative claim, the credential host suffix is the fallback, and the\n * edition label is the last resort.\n *\n * @module dsh-connect-trae/region\n */\n\nimport type { TraeEdition } from './paths.ts'\n\n/** Storage/routing bucket: the CN service or the international (ai) service. */\nexport type TraeRegion = 'cn' | 'ai'\n\n/** One region's upstream gateway bases. */\nexport interface TraeRegionGateways {\n /** Chat + agent API base (the `llm_utils_chat` / raw-chat family). */\n readonly chat: string\n /** SOLO remote model directory base (`/api/remote/v1`). */\n readonly remote: string\n /** Pay/status API base; the credential's own host is preferred when known. */\n readonly pay: string\n}\n\n/**\n * Verified gateway bases per region (docs/INTL_SG_EVIDENCE.md \u00A72).\n * The AI gateway is shared by both international installs (Trae desktop and\n * TRAE SOLO); its mchost shards (`api16/api22-normal-alisg.mchost.guru`)\n * answer the same bytes but stay internal \u2014 `coresg-normal.trae.ai` is the\n * single stable entry point.\n */\nexport const REGION_GATEWAYS: Readonly<Record<TraeRegion, TraeRegionGateways>> = {\n cn: {\n chat: 'https://trae-api-cn.mchost.guru',\n remote: 'https://solo.trae.cn/api/remote/v1',\n pay: 'https://api.trae.cn',\n },\n ai: {\n chat: 'https://coresg-normal.trae.ai',\n remote: 'https://coresg-normal.trae.ai/api/remote/v1',\n pay: 'https://growsg-normal.trae.ai',\n },\n}\n\n/** Region for an edition label: the international installs belong to `ai`. */\nexport function regionOfEdition(edition: TraeEdition): TraeRegion {\n return edition === 'sg' || edition === 'solo-sg' ? 'ai' : 'cn'\n}\n\n/**\n * Region from the credential's `userRegion` claim. The desktop storage spells\n * it as an object (`{\"region\":\"CN\",\"_aiRegion\":\"CN\"}`); the app logs also\n * spell the bare value lowercase (`\"sg\"`). Both are accepted, case-blind.\n */\nexport function regionOfUserRegion(value: unknown): TraeRegion | undefined {\n const raw = typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)['region']\n : value\n if (typeof raw !== 'string') return undefined\n const lowered = raw.trim().toLowerCase()\n if (lowered === 'cn') return 'cn'\n if (lowered === 'sg' || lowered === 'ai') return 'ai'\n return undefined\n}\n\n/** Region from a credential host (`.trae.ai` \u2192 ai, `.trae.cn`/`.trae.com.cn` \u2192 cn). */\nexport function regionOfHost(host: string | undefined): TraeRegion | undefined {\n if (host === undefined) return undefined\n const trimmed = host.trim()\n if (trimmed === '') return undefined\n let hostname: string\n try {\n hostname = new URL(trimmed.startsWith('http') ? trimmed : `https://${trimmed}`).hostname\n } catch {\n return undefined\n }\n if (hostname === 'trae.ai' || hostname.endsWith('.trae.ai')) return 'ai'\n if (hostname === 'trae.cn' || hostname.endsWith('.trae.cn') || hostname.endsWith('.trae.com.cn')) return 'cn'\n return undefined\n}\n\n/**\n * Region of a credential: the `userRegion` claim wins, the host suffix is the\n * fallback, and the edition label is the last resort. Every level is derived\n * from data the credential itself carries, so no user configuration is needed.\n */\nexport function regionOfCredential(credential: {\n edition: TraeEdition\n host?: string\n userRegion?: string\n}): TraeRegion {\n return regionOfUserRegion(credential.userRegion)\n ?? regionOfHost(credential.host)\n ?? regionOfEdition(credential.edition)\n}\n"],
5
+ "mappings": ";AAoCO,IAAM,kBAAoE;AAAA,EAC/E,IAAI;AAAA,IACF,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,KAAK;AAAA,EACP;AAAA,EACA,IAAI;AAAA,IACF,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,KAAK;AAAA,EACP;AACF;AAGO,SAAS,gBAAgB,SAAkC;AAChE,SAAO,YAAY,QAAQ,YAAY,YAAY,OAAO;AAC5D;AAOO,SAAS,mBAAmB,OAAwC;AACzE,QAAM,MAAM,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAC1E,MAAkC,QAAQ,IAC3C;AACJ,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,UAAU,IAAI,KAAK,EAAE,YAAY;AACvC,MAAI,YAAY,KAAM,QAAO;AAC7B,MAAI,YAAY,QAAQ,YAAY,KAAM,QAAO;AACjD,SAAO;AACT;AAGO,SAAS,aAAa,MAAkD;AAC7E,MAAI,SAAS,OAAW,QAAO;AAC/B,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,YAAY,GAAI,QAAO;AAC3B,MAAI;AACJ,MAAI;AACF,eAAW,IAAI,IAAI,QAAQ,WAAW,MAAM,IAAI,UAAU,WAAW,OAAO,EAAE,EAAE;AAAA,EAClF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,aAAa,aAAa,SAAS,SAAS,UAAU,EAAG,QAAO;AACpE,MAAI,aAAa,aAAa,SAAS,SAAS,UAAU,KAAK,SAAS,SAAS,cAAc,EAAG,QAAO;AACzG,SAAO;AACT;AAOO,SAAS,mBAAmB,YAIpB;AACb,SAAO,mBAAmB,WAAW,UAAU,KAC1C,aAAa,WAAW,IAAI,KAC5B,gBAAgB,WAAW,OAAO;AACzC;",
6
+ "names": []
7
+ }