@vibedev-si/dsh-vibedev 0.2.7 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -2
- package/THIRD-PARTY-NOTICES.txt +1 -1
- package/client/client.js +127 -22
- package/lib/account/index.d.ts +3 -1
- package/lib/auth/credentials.d.ts +5 -3
- package/lib/auth/login.d.ts +12 -2
- package/lib/auth/sign-in-state.d.ts +21 -0
- package/lib/gateway/http.d.ts +1 -1
- package/lib/gateway/origin.d.ts +10 -0
- package/lib/index.js +436 -141
- package/lib/tasks/tracker.d.ts +1 -1
- package/lib/tools/account.d.ts +0 -2
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -53,12 +53,14 @@ var AccountService = class {
|
|
|
53
53
|
const credential = await this.options.chain.resolve().catch(() => void 0);
|
|
54
54
|
const source = credential === void 0 ? "none" : credential.kind === "account" ? "host" : credential.kind;
|
|
55
55
|
const pending = this.options.login.pendingSignIn();
|
|
56
|
+
const attempt = this.options.login.signInAttempt();
|
|
56
57
|
const base = {
|
|
57
58
|
source,
|
|
58
59
|
models: this.options.models(),
|
|
59
60
|
links: this.links(),
|
|
60
61
|
primary: this.options.primary === true,
|
|
61
|
-
...pending === void 0 ? {} : { pending }
|
|
62
|
+
...pending === void 0 ? {} : { pending },
|
|
63
|
+
...attempt === void 0 ? {} : { attempt }
|
|
62
64
|
};
|
|
63
65
|
if (credential === void 0) return base;
|
|
64
66
|
const grantUser = credential.kind === "plugin" ? await this.options.login.user().catch(() => void 0) : void 0;
|
|
@@ -82,7 +84,8 @@ var AccountService = class {
|
|
|
82
84
|
authorization: `Bearer ${token}`,
|
|
83
85
|
"user-agent": this.options.userAgent
|
|
84
86
|
},
|
|
85
|
-
signal: AbortSignal.timeout(15e3)
|
|
87
|
+
signal: AbortSignal.timeout(15e3),
|
|
88
|
+
redirect: "error"
|
|
86
89
|
});
|
|
87
90
|
if (!response.ok) return {};
|
|
88
91
|
const body = record$8(await response.json().catch(() => void 0));
|
|
@@ -166,9 +169,11 @@ function json(status, body) {
|
|
|
166
169
|
});
|
|
167
170
|
}
|
|
168
171
|
function failure(error) {
|
|
172
|
+
const candidate = record$8(error)?.code;
|
|
173
|
+
const code = candidate === "SIGN_IN_REFUSED" || candidate === "SIGN_IN_TIMEOUT" || candidate === "SIGN_IN_CANCELLED" ? candidate : "INTERNAL";
|
|
169
174
|
return json(500, { error: {
|
|
170
|
-
code
|
|
171
|
-
message:
|
|
175
|
+
code,
|
|
176
|
+
message: code === "SIGN_IN_CANCELLED" ? "Sign-in was cancelled. 登录已取消。" : code === "SIGN_IN_TIMEOUT" ? "Sign-in expired. 登录已超时,请重新登录。" : "The account request could not be completed. 账号请求未完成,请查看登录诊断后重试。"
|
|
172
177
|
} });
|
|
173
178
|
}
|
|
174
179
|
/**
|
|
@@ -511,6 +516,32 @@ async function downloadTo(response, temporary, maxBytes, signal) {
|
|
|
511
516
|
}
|
|
512
517
|
}
|
|
513
518
|
//#endregion
|
|
519
|
+
//#region src/gateway/origin.ts
|
|
520
|
+
/** Origin boundaries for accounts and durable media state. No endpoint fallback is defined here. */
|
|
521
|
+
/** Production API origin. Storage URLs are returned by the server, not derived from this address. */
|
|
522
|
+
const DEFAULT_GATEWAY_ORIGIN = "https://api.vibedev.studio";
|
|
523
|
+
/** Validate a bare HTTP(S) origin and canonicalize default ports, casing and trailing slash. */
|
|
524
|
+
function normalizeGatewayOrigin(value) {
|
|
525
|
+
const url = new URL(value.trim());
|
|
526
|
+
if (!["https:", "http:"].includes(url.protocol) || url.username !== "" || url.password !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "") throw new Error("gatewayOrigin must be a bare HTTP(S) origin, without credentials, path, query or fragment.");
|
|
527
|
+
return url.origin;
|
|
528
|
+
}
|
|
529
|
+
/** Stable, filesystem-safe identity; ports and schemes remain separate boundaries. */
|
|
530
|
+
function gatewayId(origin) {
|
|
531
|
+
return createHash("sha256").update(normalizeGatewayOrigin(origin)).digest("hex");
|
|
532
|
+
}
|
|
533
|
+
/** Every gateway uses its own namespace; never read the unmarked historical root. */
|
|
534
|
+
function gatewayStateDirectory(root, origin) {
|
|
535
|
+
return join(root, "gateways", gatewayId(origin));
|
|
536
|
+
}
|
|
537
|
+
/** Ignore the old shared key variable; development requires an explicitly named, gateway-specific variable. */
|
|
538
|
+
function developmentKey(origin, name, env = process.env) {
|
|
539
|
+
normalizeGatewayOrigin(origin);
|
|
540
|
+
const key = name.trim();
|
|
541
|
+
if (key === "" || key === "VIBEDEV_GATEWAY_API_KEY") return void 0;
|
|
542
|
+
return env[key]?.trim() || void 0;
|
|
543
|
+
}
|
|
544
|
+
//#endregion
|
|
514
545
|
//#region src/auth/credentials.ts
|
|
515
546
|
/**
|
|
516
547
|
* Which VibeDev credential a gateway request carries, in order: the host's
|
|
@@ -555,7 +586,10 @@ var CredentialChain = class {
|
|
|
555
586
|
else if (credential.kind === "plugin") await this.options.plugin?.reject(credential.token);
|
|
556
587
|
}
|
|
557
588
|
};
|
|
558
|
-
|
|
589
|
+
/** Credential identity for one gateway; never consult another gateway's record. */
|
|
590
|
+
function gatewayGrantKey(origin) {
|
|
591
|
+
return credentialKey("dsh-vibedev", `gateway-${gatewayId(origin)}`);
|
|
592
|
+
}
|
|
559
593
|
function isGrant(value) {
|
|
560
594
|
const grant = value;
|
|
561
595
|
return typeof grant === "object" && grant !== null && typeof grant.accessToken === "string" && typeof grant.refreshToken === "string" && typeof grant.expiresAt === "number" && typeof grant.refreshExpiresAt === "number";
|
|
@@ -564,11 +598,23 @@ function isGrant(value) {
|
|
|
564
598
|
* Grant storage: the host's credential store when one is loaded (a grant
|
|
565
599
|
* record the store keeps verbatim), else a private file in the state
|
|
566
600
|
* directory. The device id is not a secret and lives in the state directory.
|
|
567
|
-
* @param
|
|
601
|
+
* @param root - the plugin state root; the storage selects an origin-specific directory itself.
|
|
568
602
|
* @param credentials - the host's credential store, when loaded.
|
|
603
|
+
* @param selectedOrigin - the issuing gateway; unmarked historical state is never adopted.
|
|
569
604
|
* @returns the storage.
|
|
570
605
|
*/
|
|
571
|
-
function grantStorage(
|
|
606
|
+
function grantStorage(root, credentials, selectedOrigin = DEFAULT_GATEWAY_ORIGIN) {
|
|
607
|
+
const origin = normalizeGatewayOrigin(selectedOrigin);
|
|
608
|
+
const key = gatewayGrantKey(origin);
|
|
609
|
+
const stateDir = gatewayStateDirectory(root, origin);
|
|
610
|
+
const readGrant = (value) => isGrant(value) && value.gatewayOrigin === origin ? value : void 0;
|
|
611
|
+
const bind = (grant) => {
|
|
612
|
+
if (grant.gatewayOrigin !== void 0 && grant.gatewayOrigin !== origin) throw new Error("Cannot store a grant issued by another gateway.");
|
|
613
|
+
return {
|
|
614
|
+
...grant,
|
|
615
|
+
gatewayOrigin: origin
|
|
616
|
+
};
|
|
617
|
+
};
|
|
572
618
|
const sessionFile = join(stateDir, "session.json");
|
|
573
619
|
const deviceFile = join(stateDir, "device.json");
|
|
574
620
|
let device;
|
|
@@ -581,9 +627,9 @@ function grantStorage(stateDir, credentials) {
|
|
|
581
627
|
throw error;
|
|
582
628
|
}
|
|
583
629
|
const parsed = JSON.parse(text);
|
|
584
|
-
return
|
|
630
|
+
return readGrant(parsed);
|
|
585
631
|
};
|
|
586
|
-
const writeFileGrant = (grant) => writeFileAtomic(sessionFile, new TextEncoder().encode(grant === void 0 ? "null" : JSON.stringify(grant)), { mode: 384 });
|
|
632
|
+
const writeFileGrant = (grant) => writeFileAtomic(sessionFile, new TextEncoder().encode(grant === void 0 ? "null" : JSON.stringify(bind(grant))), { mode: 384 });
|
|
587
633
|
const clearFileGrant = async () => {
|
|
588
634
|
try {
|
|
589
635
|
await stat(sessionFile);
|
|
@@ -599,9 +645,9 @@ function grantStorage(stateDir, credentials) {
|
|
|
599
645
|
} catch {}
|
|
600
646
|
};
|
|
601
647
|
const readHostGrant = async (store) => {
|
|
602
|
-
const stored = await store.readRecord(
|
|
648
|
+
const stored = await store.readRecord(key);
|
|
603
649
|
if (stored !== void 0) {
|
|
604
|
-
const grant = stored.kind === "grant"
|
|
650
|
+
const grant = stored.kind === "grant" ? readGrant(stored.payload) : void 0;
|
|
605
651
|
if (grant !== void 0) await clearFileGrantQuietly();
|
|
606
652
|
return grant;
|
|
607
653
|
}
|
|
@@ -612,15 +658,15 @@ function grantStorage(stateDir, credentials) {
|
|
|
612
658
|
throw error;
|
|
613
659
|
}
|
|
614
660
|
let adopted;
|
|
615
|
-
await store.modifyRecord(
|
|
661
|
+
await store.modifyRecord(key, async (current) => {
|
|
616
662
|
if (current !== void 0) {
|
|
617
|
-
adopted = current.kind === "grant"
|
|
663
|
+
adopted = current.kind === "grant" ? readGrant(current.payload) : void 0;
|
|
618
664
|
return;
|
|
619
665
|
}
|
|
620
666
|
adopted = await readFileGrant();
|
|
621
667
|
return adopted === void 0 ? void 0 : {
|
|
622
668
|
kind: "grant",
|
|
623
|
-
payload: adopted
|
|
669
|
+
payload: bind(adopted)
|
|
624
670
|
};
|
|
625
671
|
});
|
|
626
672
|
if (adopted !== void 0) await clearFileGrantQuietly();
|
|
@@ -636,11 +682,11 @@ function grantStorage(stateDir, credentials) {
|
|
|
636
682
|
if (store !== void 0) {
|
|
637
683
|
if (grant === void 0) {
|
|
638
684
|
await clearFileGrant();
|
|
639
|
-
await store.deleteRecord(
|
|
685
|
+
await store.deleteRecord(key);
|
|
640
686
|
} else {
|
|
641
|
-
await store.modifyRecord(
|
|
687
|
+
await store.modifyRecord(key, () => Promise.resolve({
|
|
642
688
|
kind: "grant",
|
|
643
|
-
payload: grant
|
|
689
|
+
payload: bind(grant)
|
|
644
690
|
}));
|
|
645
691
|
await clearFileGrantQuietly();
|
|
646
692
|
}
|
|
@@ -653,17 +699,17 @@ function grantStorage(stateDir, credentials) {
|
|
|
653
699
|
if (store !== void 0) {
|
|
654
700
|
await readHostGrant(store);
|
|
655
701
|
let remove = false;
|
|
656
|
-
await store.modifyRecord(
|
|
657
|
-
const result = await change(current?.kind === "grant"
|
|
702
|
+
await store.modifyRecord(key, async (current) => {
|
|
703
|
+
const result = await change(current?.kind === "grant" ? readGrant(current.payload) : void 0);
|
|
658
704
|
if (result.kind === "set") return {
|
|
659
705
|
kind: "grant",
|
|
660
|
-
payload: result.grant
|
|
706
|
+
payload: bind(result.grant)
|
|
661
707
|
};
|
|
662
708
|
remove = result.kind === "delete";
|
|
663
709
|
});
|
|
664
710
|
if (remove) {
|
|
665
711
|
await clearFileGrant();
|
|
666
|
-
await store.deleteRecord(
|
|
712
|
+
await store.deleteRecord(key);
|
|
667
713
|
}
|
|
668
714
|
return;
|
|
669
715
|
}
|
|
@@ -686,6 +732,100 @@ function grantStorage(stateDir, credentials) {
|
|
|
686
732
|
};
|
|
687
733
|
}
|
|
688
734
|
//#endregion
|
|
735
|
+
//#region src/auth/sign-in-state.ts
|
|
736
|
+
const REDEEM_PATH = "/api/v1/vibedev/link/redeem";
|
|
737
|
+
const NETWORK_CODES = /* @__PURE__ */ new Set([
|
|
738
|
+
"ECONNREFUSED",
|
|
739
|
+
"ECONNRESET",
|
|
740
|
+
"ECONNABORTED",
|
|
741
|
+
"ENOTFOUND",
|
|
742
|
+
"EAI_AGAIN",
|
|
743
|
+
"ENETUNREACH",
|
|
744
|
+
"EHOSTUNREACH",
|
|
745
|
+
"EPIPE",
|
|
746
|
+
"ETIMEDOUT",
|
|
747
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
748
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
749
|
+
"UND_ERR_BODY_TIMEOUT",
|
|
750
|
+
"UND_ERR_SOCKET",
|
|
751
|
+
"CERT_HAS_EXPIRED",
|
|
752
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
753
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
754
|
+
"ERR_TLS_CERT_ALTNAME_INVALID"
|
|
755
|
+
]);
|
|
756
|
+
const TIMEOUT_CODES = /* @__PURE__ */ new Set([
|
|
757
|
+
"ETIMEDOUT",
|
|
758
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
759
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
760
|
+
"UND_ERR_BODY_TIMEOUT"
|
|
761
|
+
]);
|
|
762
|
+
/** Read only fixed transport codes, never error text, stack, body or arbitrary fields. */
|
|
763
|
+
function transportFailure(error) {
|
|
764
|
+
let cause = error;
|
|
765
|
+
let timeout = false;
|
|
766
|
+
let networkCode;
|
|
767
|
+
for (let depth = 0; depth < 5 && typeof cause === "object" && cause !== null; depth++) {
|
|
768
|
+
const value = cause;
|
|
769
|
+
if (value.name === "TimeoutError") timeout = true;
|
|
770
|
+
if (typeof value.code === "string" && NETWORK_CODES.has(value.code)) {
|
|
771
|
+
networkCode ??= value.code;
|
|
772
|
+
if (TIMEOUT_CODES.has(value.code)) timeout = true;
|
|
773
|
+
}
|
|
774
|
+
cause = value.cause;
|
|
775
|
+
}
|
|
776
|
+
return {
|
|
777
|
+
errorCode: timeout ? "timeout" : "network",
|
|
778
|
+
...networkCode === void 0 ? {} : { networkCode }
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
const EXPLANATIONS = {
|
|
782
|
+
network: ["无法连接登录网关,请检查网络后重新登录。", "The sign-in gateway could not be reached. Check the connection and start sign-in again."],
|
|
783
|
+
timeout: ["登录等待或网关请求已超时,请重新登录。", "Sign-in or the gateway request timed out. Start sign-in again."],
|
|
784
|
+
"gateway-refused": ["登录网关拒绝了请求,请重新登录。", "The gateway refused the sign-in request. Start sign-in again."],
|
|
785
|
+
"gateway-unavailable": ["登录网关暂时不可用,请稍后重新登录。", "The sign-in gateway is temporarily unavailable. Start sign-in again later."],
|
|
786
|
+
protocol: ["登录网关返回了无效的凭据格式,请稍后重新登录。", "The gateway returned an invalid credential response. Start sign-in again later."],
|
|
787
|
+
storage: ["无法准备或保存本地登录凭据,请检查存储权限后重新登录。", "Local sign-in credentials could not be prepared or saved. Check storage permissions and start sign-in again."],
|
|
788
|
+
callback: ["无法启动或使用本地登录回调,请重新登录。", "The local sign-in callback could not be started or used. Start sign-in again."]
|
|
789
|
+
};
|
|
790
|
+
const STAGES = {
|
|
791
|
+
listen: ["本地回调监听", "Local callback listener"],
|
|
792
|
+
device: ["准备本地设备标识", "Preparing the local device identity"],
|
|
793
|
+
redeem: ["交换登录凭据", "Exchanging sign-in credentials"],
|
|
794
|
+
persist: ["保存本地凭据", "Saving local credentials"]
|
|
795
|
+
};
|
|
796
|
+
/** A fixed bilingual explanation suitable for exposed rejection messages. */
|
|
797
|
+
function failureMessage(attempt) {
|
|
798
|
+
const [chinese, english] = EXPLANATIONS[attempt.errorCode ?? "callback"];
|
|
799
|
+
const stage = attempt.stage === void 0 ? "" : ` ${STAGES[attempt.stage].join(" / ")}; stage=${attempt.stage}.`;
|
|
800
|
+
const status = attempt.httpStatus === void 0 ? "" : ` HTTP ${attempt.httpStatus}.`;
|
|
801
|
+
return `${english} ${chinese} errorCode=${attempt.errorCode ?? "callback"}.${stage}${status}`;
|
|
802
|
+
}
|
|
803
|
+
const escapeHtml = (text) => text.replace(/[&<>"']/g, (character) => ({
|
|
804
|
+
"&": "&",
|
|
805
|
+
"<": "<",
|
|
806
|
+
">": ">",
|
|
807
|
+
"\"": """,
|
|
808
|
+
"'": "'"
|
|
809
|
+
})[character]);
|
|
810
|
+
/** Only our fixed explanation and escaped safe enum/status fields reach the browser. */
|
|
811
|
+
function failurePage(attempt) {
|
|
812
|
+
return `<h1>Sign-in failed.</h1><p>${escapeHtml(failureMessage(attempt))}</p><p>请回到对话重新登录。</p>`;
|
|
813
|
+
}
|
|
814
|
+
/** Explicit log allowlist; adding fields to the public attempt never expands log output. */
|
|
815
|
+
function attemptLog(event, attempt) {
|
|
816
|
+
return `dsh-vibedev sign-in ${JSON.stringify({
|
|
817
|
+
event,
|
|
818
|
+
id: attempt.id,
|
|
819
|
+
phase: attempt.phase,
|
|
820
|
+
gatewayOrigin: attempt.gatewayOrigin,
|
|
821
|
+
path: REDEEM_PATH,
|
|
822
|
+
...attempt.stage === void 0 ? {} : { stage: attempt.stage },
|
|
823
|
+
...attempt.errorCode === void 0 ? {} : { errorCode: attempt.errorCode },
|
|
824
|
+
...attempt.httpStatus === void 0 ? {} : { httpStatus: attempt.httpStatus },
|
|
825
|
+
...attempt.networkCode === void 0 ? {} : { networkCode: attempt.networkCode }
|
|
826
|
+
})}`;
|
|
827
|
+
}
|
|
828
|
+
//#endregion
|
|
689
829
|
//#region src/auth/login.ts
|
|
690
830
|
/**
|
|
691
831
|
* The plugin's own VibeDev sign-in, for hosts whose account service does not
|
|
@@ -772,6 +912,7 @@ var PluginLogin = class {
|
|
|
772
912
|
readFailureReported = false;
|
|
773
913
|
refreshing;
|
|
774
914
|
pending;
|
|
915
|
+
attempt;
|
|
775
916
|
listeners = /* @__PURE__ */ new Set();
|
|
776
917
|
now;
|
|
777
918
|
origin;
|
|
@@ -781,7 +922,7 @@ var PluginLogin = class {
|
|
|
781
922
|
constructor(options) {
|
|
782
923
|
this.options = options;
|
|
783
924
|
this.now = options.now ?? Date.now;
|
|
784
|
-
this.origin = options.origin
|
|
925
|
+
this.origin = normalizeGatewayOrigin(options.origin);
|
|
785
926
|
}
|
|
786
927
|
readStored(notify) {
|
|
787
928
|
if (this.reading !== void 0) return this.reading;
|
|
@@ -816,10 +957,32 @@ var PluginLogin = class {
|
|
|
816
957
|
const changed = this.grant?.accessToken !== grant?.accessToken;
|
|
817
958
|
this.grant = grant;
|
|
818
959
|
if (!changed) return;
|
|
960
|
+
this.notify();
|
|
961
|
+
}
|
|
962
|
+
notify() {
|
|
819
963
|
for (const listener of this.listeners) try {
|
|
820
964
|
listener();
|
|
821
965
|
} catch {}
|
|
822
966
|
}
|
|
967
|
+
/** A copied safe snapshot, retained after completion until the next sign-in. */
|
|
968
|
+
signInAttempt() {
|
|
969
|
+
return this.attempt === void 0 ? void 0 : Object.freeze({ ...this.attempt });
|
|
970
|
+
}
|
|
971
|
+
logAttempt(event) {
|
|
972
|
+
if (this.attempt === void 0) return;
|
|
973
|
+
try {
|
|
974
|
+
this.options.log?.(attemptLog(event, this.attempt));
|
|
975
|
+
} catch {}
|
|
976
|
+
}
|
|
977
|
+
transition(id, change, event = "phase") {
|
|
978
|
+
if (this.attempt?.id !== id) return;
|
|
979
|
+
this.attempt = {
|
|
980
|
+
...this.attempt,
|
|
981
|
+
...change
|
|
982
|
+
};
|
|
983
|
+
this.logAttempt(event);
|
|
984
|
+
this.notify();
|
|
985
|
+
}
|
|
823
986
|
async save(grant) {
|
|
824
987
|
await this.options.storage.write(grant);
|
|
825
988
|
this.adopt(grant);
|
|
@@ -842,9 +1005,10 @@ var PluginLogin = class {
|
|
|
842
1005
|
}
|
|
843
1006
|
/** Whether a sign-in is waiting for the browser, and its link. */
|
|
844
1007
|
pendingSignIn() {
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
1008
|
+
const pending = this.pending;
|
|
1009
|
+
return pending?.url === void 0 ? void 0 : {
|
|
1010
|
+
url: pending.url,
|
|
1011
|
+
expiresAt: pending.expiresAt
|
|
848
1012
|
};
|
|
849
1013
|
}
|
|
850
1014
|
/**
|
|
@@ -900,7 +1064,7 @@ var PluginLogin = class {
|
|
|
900
1064
|
});
|
|
901
1065
|
const body = await response.json().catch(() => void 0);
|
|
902
1066
|
if (response.status === 401 || response.status === 403) {
|
|
903
|
-
this.options.log?.(`dsh-vibedev: the VibeDev session ended (${
|
|
1067
|
+
this.options.log?.(`dsh-vibedev: the VibeDev session ended (HTTP ${response.status}); signed out`);
|
|
904
1068
|
outcome = void 0;
|
|
905
1069
|
return { kind: "delete" };
|
|
906
1070
|
}
|
|
@@ -927,7 +1091,7 @@ var PluginLogin = class {
|
|
|
927
1091
|
})();
|
|
928
1092
|
return this.refreshing;
|
|
929
1093
|
}
|
|
930
|
-
post(path, json, token) {
|
|
1094
|
+
post(path, json, token, signal = AbortSignal.timeout(3e4)) {
|
|
931
1095
|
return (this.options.fetch ?? globalThis.fetch)(`${this.origin}${path}`, {
|
|
932
1096
|
method: "POST",
|
|
933
1097
|
headers: {
|
|
@@ -937,7 +1101,8 @@ var PluginLogin = class {
|
|
|
937
1101
|
...token === void 0 ? {} : { authorization: `Bearer ${token}` }
|
|
938
1102
|
},
|
|
939
1103
|
body: JSON.stringify(json),
|
|
940
|
-
|
|
1104
|
+
redirect: "error",
|
|
1105
|
+
signal
|
|
941
1106
|
});
|
|
942
1107
|
}
|
|
943
1108
|
/**
|
|
@@ -948,19 +1113,22 @@ var PluginLogin = class {
|
|
|
948
1113
|
* @returns the authorization link, whether a browser was opened, and the completion.
|
|
949
1114
|
*/
|
|
950
1115
|
async startSignIn(request = {}) {
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1116
|
+
if (this.pending !== void 0 && !this.pending.committing && this.pending.expiresAt <= this.now()) this.pending.cancel("expired");
|
|
1117
|
+
const active = this.pending ?? this.beginSignIn();
|
|
1118
|
+
const started = await active.ready;
|
|
1119
|
+
const opened = request.open === false || this.pending !== active ? false : await (this.options.openBrowser ?? openInBrowser)(started.url).catch(() => false);
|
|
1120
|
+
return {
|
|
1121
|
+
...started,
|
|
1122
|
+
opened
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
beginSignIn() {
|
|
961
1126
|
const verifier = base64url(randomBytes(32));
|
|
962
1127
|
const challenge = base64url(createHash("sha256").update(verifier).digest());
|
|
963
1128
|
const state = base64url(randomBytes(18));
|
|
1129
|
+
const id = randomUUID();
|
|
1130
|
+
const expiresAt = this.now() + (this.options.signInTimeoutMs ?? 3e5);
|
|
1131
|
+
const controller = new AbortController();
|
|
964
1132
|
let resolveDone;
|
|
965
1133
|
let rejectDone;
|
|
966
1134
|
const done = new Promise((resolve, reject) => {
|
|
@@ -968,8 +1136,73 @@ var PluginLogin = class {
|
|
|
968
1136
|
rejectDone = reject;
|
|
969
1137
|
});
|
|
970
1138
|
done.catch(() => void 0);
|
|
971
|
-
let
|
|
972
|
-
|
|
1139
|
+
let resolveReady;
|
|
1140
|
+
let rejectReady;
|
|
1141
|
+
const ready = new Promise((resolve, reject) => {
|
|
1142
|
+
resolveReady = resolve;
|
|
1143
|
+
rejectReady = reject;
|
|
1144
|
+
});
|
|
1145
|
+
ready.catch(() => void 0);
|
|
1146
|
+
let finished = false;
|
|
1147
|
+
let claimed = false;
|
|
1148
|
+
let server;
|
|
1149
|
+
let timer;
|
|
1150
|
+
const finish = () => {
|
|
1151
|
+
finished = true;
|
|
1152
|
+
clearTimeout(timer);
|
|
1153
|
+
server?.close();
|
|
1154
|
+
server?.closeAllConnections?.();
|
|
1155
|
+
if (this.pending === active) this.pending = void 0;
|
|
1156
|
+
};
|
|
1157
|
+
const fail = (failure, phase = "failed") => {
|
|
1158
|
+
const attempt = {
|
|
1159
|
+
...this.attempt,
|
|
1160
|
+
...failure,
|
|
1161
|
+
phase
|
|
1162
|
+
};
|
|
1163
|
+
const error = new MediaError(phase === "cancelled" ? "The sign-in was cancelled." : failureMessage(attempt), phase === "expired" ? "SIGN_IN_TIMEOUT" : phase === "cancelled" ? "SIGN_IN_CANCELLED" : "SIGN_IN_REFUSED", attempt.httpStatus === void 0 ? {} : { status: attempt.httpStatus });
|
|
1164
|
+
finish();
|
|
1165
|
+
controller.abort();
|
|
1166
|
+
this.transition(id, {
|
|
1167
|
+
...failure,
|
|
1168
|
+
phase
|
|
1169
|
+
}, "terminal");
|
|
1170
|
+
rejectReady(error);
|
|
1171
|
+
rejectDone(error);
|
|
1172
|
+
return error;
|
|
1173
|
+
};
|
|
1174
|
+
const active = {
|
|
1175
|
+
id,
|
|
1176
|
+
expiresAt,
|
|
1177
|
+
ready,
|
|
1178
|
+
done,
|
|
1179
|
+
committing: false,
|
|
1180
|
+
cancel: (phase) => {
|
|
1181
|
+
if (!finished && !active.committing) fail(phase === "expired" ? { errorCode: "timeout" } : {}, phase);
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1184
|
+
this.pending = active;
|
|
1185
|
+
this.attempt = {
|
|
1186
|
+
id,
|
|
1187
|
+
expiresAt,
|
|
1188
|
+
gatewayOrigin: this.origin,
|
|
1189
|
+
phase: "preparing",
|
|
1190
|
+
stage: "listen"
|
|
1191
|
+
};
|
|
1192
|
+
this.logAttempt("request");
|
|
1193
|
+
this.notify();
|
|
1194
|
+
const live = () => {
|
|
1195
|
+
if (finished || this.pending !== active) return false;
|
|
1196
|
+
if (!active.committing && this.now() >= expiresAt) {
|
|
1197
|
+
active.cancel("expired");
|
|
1198
|
+
return false;
|
|
1199
|
+
}
|
|
1200
|
+
return true;
|
|
1201
|
+
};
|
|
1202
|
+
if (!live()) return active;
|
|
1203
|
+
timer = setTimeout(() => active.cancel("expired"), Math.max(0, expiresAt - this.now()));
|
|
1204
|
+
timer.unref();
|
|
1205
|
+
const callback = (request, response) => {
|
|
973
1206
|
const url = new URL(request.url ?? "/", "http://127.0.0.1");
|
|
974
1207
|
if (url.pathname !== CALLBACK_PATH) {
|
|
975
1208
|
response.writeHead(404).end();
|
|
@@ -981,107 +1214,170 @@ var PluginLogin = class {
|
|
|
981
1214
|
"cache-control": "no-store"
|
|
982
1215
|
}).end(PAGE(title, body));
|
|
983
1216
|
};
|
|
984
|
-
if (
|
|
985
|
-
|
|
1217
|
+
if (request.method !== "GET") {
|
|
1218
|
+
response.setHeader("allow", "GET");
|
|
1219
|
+
answer(405, "VibeDev", "<h1>Use GET for sign-in.</h1><p>请使用浏览器登录回调。</p>");
|
|
986
1220
|
return;
|
|
987
1221
|
}
|
|
988
|
-
if (
|
|
1222
|
+
if (claimed || !live()) {
|
|
1223
|
+
answer(410, "VibeDev", "<h1>This sign-in link has already been used.</h1><p>此登录链接已使用过或已失效。</p>");
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
const states = url.searchParams.getAll("state");
|
|
1227
|
+
const codes = url.searchParams.getAll("code");
|
|
1228
|
+
const code = codes[0];
|
|
1229
|
+
if (states.length !== 1 || states[0] !== state || codes.length !== 1 || code === void 0 || code.trim() === "") {
|
|
989
1230
|
answer(400, "VibeDev", "<h1>Sign-in could not be completed.</h1><p>登录未完成:链接无效,请回到对话重新登录。</p>");
|
|
990
1231
|
return;
|
|
991
1232
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1233
|
+
claimed = true;
|
|
1234
|
+
this.transition(id, {
|
|
1235
|
+
phase: "exchanging",
|
|
1236
|
+
stage: "device"
|
|
1237
|
+
}, "request");
|
|
1238
|
+
(async () => {
|
|
1239
|
+
let grant;
|
|
1240
|
+
try {
|
|
1241
|
+
if (!live()) return;
|
|
1242
|
+
const deviceId = await this.options.storage.deviceId();
|
|
1243
|
+
if (!live()) return;
|
|
1244
|
+
this.transition(id, { stage: "redeem" }, "request");
|
|
1245
|
+
if (!live()) return;
|
|
1246
|
+
const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(3e4)]);
|
|
1247
|
+
const response = await this.post(REDEEM_PATH, {
|
|
1248
|
+
code,
|
|
1249
|
+
code_verifier: verifier,
|
|
1250
|
+
token_type: "app",
|
|
1251
|
+
client: this.options.client ?? "vibedev-plugin",
|
|
1252
|
+
device_id: deviceId,
|
|
1253
|
+
device_model: `${platform()}-${arch()}`.slice(0, 128),
|
|
1254
|
+
os_version: release().slice(0, 128)
|
|
1255
|
+
}, void 0, signal);
|
|
1256
|
+
if (!live()) {
|
|
1257
|
+
response.body?.cancel().catch(() => void 0);
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
this.transition(id, { httpStatus: response.status }, "response");
|
|
1261
|
+
if (!live()) {
|
|
1262
|
+
response.body?.cancel().catch(() => void 0);
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
if (!response.ok) {
|
|
1266
|
+
response.body?.cancel().catch(() => void 0);
|
|
1267
|
+
const errorCode = response.status === 429 || response.status >= 500 ? "gateway-unavailable" : response.status >= 400 && response.status < 500 ? "gateway-refused" : "protocol";
|
|
1268
|
+
answer(502, "VibeDev", failurePage({
|
|
1269
|
+
...this.attempt,
|
|
1270
|
+
errorCode
|
|
1271
|
+
}));
|
|
1272
|
+
fail({ errorCode });
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
let body;
|
|
1276
|
+
try {
|
|
1277
|
+
body = await response.json();
|
|
1278
|
+
} catch (error) {
|
|
1279
|
+
if (!live()) return;
|
|
1280
|
+
const transport = transportFailure(error);
|
|
1281
|
+
const failure = transport.networkCode !== void 0 || transport.errorCode === "timeout" ? transport : { errorCode: "protocol" };
|
|
1282
|
+
answer(502, "VibeDev", failurePage({
|
|
1283
|
+
...this.attempt,
|
|
1284
|
+
...failure
|
|
1285
|
+
}));
|
|
1286
|
+
fail(failure);
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
if (!live()) return;
|
|
1290
|
+
const envelope = record$6(body);
|
|
1291
|
+
const parsed = envelope !== void 0 && (envelope.code === void 0 || envelope.code === 0) && (envelope.data === void 0 || record$6(envelope.data) !== void 0) ? parseGrant(body, this.now()) : void 0;
|
|
1292
|
+
if (parsed === void 0) {
|
|
1293
|
+
answer(502, "VibeDev", failurePage({
|
|
1294
|
+
...this.attempt,
|
|
1295
|
+
errorCode: "protocol"
|
|
1296
|
+
}));
|
|
1297
|
+
fail({ errorCode: "protocol" });
|
|
1298
|
+
return;
|
|
1299
|
+
}
|
|
1300
|
+
grant = parsed;
|
|
1301
|
+
active.committing = true;
|
|
1302
|
+
clearTimeout(timer);
|
|
1303
|
+
this.transition(id, {
|
|
1304
|
+
phase: "committing",
|
|
1305
|
+
stage: "persist"
|
|
1306
|
+
});
|
|
1307
|
+
await this.save(grant);
|
|
1308
|
+
} catch (error) {
|
|
1309
|
+
if (finished || this.pending !== active) return;
|
|
1310
|
+
const failure = this.attempt?.stage === "device" || this.attempt?.stage === "persist" ? { errorCode: "storage" } : transportFailure(error);
|
|
1311
|
+
answer(502, "VibeDev", failurePage({
|
|
1312
|
+
...this.attempt,
|
|
1313
|
+
...failure
|
|
1314
|
+
}));
|
|
1315
|
+
fail(failure);
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
answer(200, "VibeDev", "<h1>Signed in to VibeDev.</h1><p>已登录 VibeDev,可以关闭此页面,回到对话继续。</p><p>You can close this tab and return to the conversation.</p>");
|
|
997
1319
|
finish();
|
|
1320
|
+
this.transition(id, { phase: "succeeded" }, "terminal");
|
|
998
1321
|
resolveDone(grant);
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1322
|
+
})();
|
|
1323
|
+
};
|
|
1324
|
+
try {
|
|
1325
|
+
server = createServer(callback);
|
|
1326
|
+
server.on("error", () => {
|
|
1327
|
+
if (finished || active.committing) return;
|
|
1328
|
+
this.transition(id, { stage: "listen" });
|
|
1329
|
+
if (!finished) fail({ errorCode: "callback" });
|
|
1003
1330
|
});
|
|
1004
|
-
});
|
|
1005
|
-
await new Promise((resolve, reject) => {
|
|
1006
|
-
server.once("error", reject);
|
|
1007
1331
|
server.listen(0, "127.0.0.1", () => {
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
const callback = `http://127.0.0.1:${server.address().port}${CALLBACK_PATH}`;
|
|
1013
|
-
const url = `${this.origin}/vibedev-link?${new URLSearchParams({
|
|
1014
|
-
callback,
|
|
1015
|
-
state,
|
|
1016
|
-
response_type: "code",
|
|
1017
|
-
code_challenge: challenge,
|
|
1018
|
-
code_challenge_method: "S256",
|
|
1019
|
-
client: this.options.client ?? "vibedev-plugin"
|
|
1020
|
-
}).toString()}`;
|
|
1021
|
-
const timeoutMs = this.options.signInTimeoutMs ?? 3e5;
|
|
1022
|
-
const timer = setTimeout(() => {
|
|
1023
|
-
if (settled) return;
|
|
1024
|
-
settled = true;
|
|
1025
|
-
finish();
|
|
1026
|
-
rejectDone(new MediaError("The VibeDev sign-in was not completed in time.", "SIGN_IN_TIMEOUT"));
|
|
1027
|
-
}, timeoutMs);
|
|
1028
|
-
timer.unref();
|
|
1029
|
-
const finish = () => {
|
|
1030
|
-
clearTimeout(timer);
|
|
1031
|
-
server.close();
|
|
1032
|
-
server.closeAllConnections?.();
|
|
1033
|
-
if (this.pending?.server === server) this.pending = void 0;
|
|
1034
|
-
};
|
|
1035
|
-
this.pending = {
|
|
1036
|
-
url,
|
|
1037
|
-
expiresAt: this.now() + timeoutMs,
|
|
1038
|
-
done,
|
|
1039
|
-
server,
|
|
1040
|
-
cancel: (reason) => {
|
|
1041
|
-
if (!settled) {
|
|
1042
|
-
settled = true;
|
|
1043
|
-
finish();
|
|
1044
|
-
rejectDone(reason);
|
|
1332
|
+
if (!live()) {
|
|
1333
|
+
server?.close();
|
|
1334
|
+
server?.closeAllConnections?.();
|
|
1335
|
+
return;
|
|
1045
1336
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
await this.save(grant);
|
|
1071
|
-
return grant;
|
|
1337
|
+
const port = (server?.address()).port;
|
|
1338
|
+
const url = `${this.origin}/vibedev-link?${new URLSearchParams({
|
|
1339
|
+
callback: `http://127.0.0.1:${port}${CALLBACK_PATH}`,
|
|
1340
|
+
state,
|
|
1341
|
+
response_type: "code",
|
|
1342
|
+
code_challenge: challenge,
|
|
1343
|
+
code_challenge_method: "S256",
|
|
1344
|
+
client: this.options.client ?? "vibedev-plugin"
|
|
1345
|
+
}).toString()}`;
|
|
1346
|
+
active.url = url;
|
|
1347
|
+
this.transition(id, {
|
|
1348
|
+
phase: "waiting-browser",
|
|
1349
|
+
stage: void 0
|
|
1350
|
+
});
|
|
1351
|
+
if (live()) resolveReady({
|
|
1352
|
+
url,
|
|
1353
|
+
expiresAt,
|
|
1354
|
+
done
|
|
1355
|
+
});
|
|
1356
|
+
});
|
|
1357
|
+
} catch {
|
|
1358
|
+
if (!finished) fail({ errorCode: "callback" });
|
|
1359
|
+
}
|
|
1360
|
+
return active;
|
|
1072
1361
|
}
|
|
1073
1362
|
/** Sign out: revoke the session at the gateway (best effort) and forget it. */
|
|
1074
1363
|
async signOut() {
|
|
1364
|
+
const attemptId = this.attempt?.id;
|
|
1365
|
+
const pending = this.pending;
|
|
1366
|
+
if (pending?.committing) await pending.done.catch(() => void 0);
|
|
1367
|
+
else pending?.cancel("cancelled");
|
|
1075
1368
|
await this.load();
|
|
1076
1369
|
const grant = this.grant;
|
|
1077
|
-
|
|
1370
|
+
if (grant !== void 0) await this.save(void 0);
|
|
1371
|
+
if (this.attempt?.id === attemptId && this.pending === void 0) {
|
|
1372
|
+
this.attempt = void 0;
|
|
1373
|
+
this.notify();
|
|
1374
|
+
}
|
|
1078
1375
|
if (grant === void 0) return;
|
|
1079
|
-
await this.save(void 0);
|
|
1080
1376
|
await this.post("/api/v1/vibedev/app-token/revoke", { refresh_token: grant.refreshToken }, grant.accessToken).then((response) => response.body?.cancel(), () => void 0);
|
|
1081
1377
|
}
|
|
1082
1378
|
/** Stop a waiting sign-in. */
|
|
1083
1379
|
dispose() {
|
|
1084
|
-
this.pending?.cancel(
|
|
1380
|
+
this.pending?.cancel("cancelled");
|
|
1085
1381
|
}
|
|
1086
1382
|
};
|
|
1087
1383
|
//#endregion
|
|
@@ -2495,7 +2791,12 @@ var GatewayAdapter = class extends LlmAdapter {
|
|
|
2495
2791
|
const token = credential.token;
|
|
2496
2792
|
let response;
|
|
2497
2793
|
const observingFetch = async (input, init) => {
|
|
2498
|
-
const
|
|
2794
|
+
const url = input instanceof Request ? input.url : String(input);
|
|
2795
|
+
if (new URL(url).origin !== snapshot.origin) throw new Error("Refusing a model request to another gateway origin.");
|
|
2796
|
+
const received = await fetch(input, {
|
|
2797
|
+
...init,
|
|
2798
|
+
redirect: "error"
|
|
2799
|
+
});
|
|
2499
2800
|
response = {
|
|
2500
2801
|
status: received.status,
|
|
2501
2802
|
headers: Object.fromEntries(received.headers.entries())
|
|
@@ -3141,7 +3442,7 @@ var GatewayHttp = class {
|
|
|
3141
3442
|
method: request.method ?? (body === void 0 ? "GET" : "POST"),
|
|
3142
3443
|
headers,
|
|
3143
3444
|
...body === void 0 ? {} : { body },
|
|
3144
|
-
redirect: "
|
|
3445
|
+
redirect: "error",
|
|
3145
3446
|
signal
|
|
3146
3447
|
});
|
|
3147
3448
|
} catch (error) {
|
|
@@ -4591,7 +4892,7 @@ const text$2 = (value) => typeof value === "string" && value.trim() !== "" ? val
|
|
|
4591
4892
|
const noPaths = { readPath: () => Promise.reject(new MediaError("The gateway returned an unexpected image reference.", "GATEWAY_BAD_RESPONSE")) };
|
|
4592
4893
|
/**
|
|
4593
4894
|
* Read the images of an `/v1/images/*` answer: `b64_json`, a `data:` URL, or a link.
|
|
4594
|
-
* @param runtime - for
|
|
4895
|
+
* @param runtime - for downloading the gateway's full signed result URLs.
|
|
4595
4896
|
* @param body - the parsed answer.
|
|
4596
4897
|
* @param signal - cancels downloads.
|
|
4597
4898
|
* @returns the images and the revised prompt, when the model returned one.
|
|
@@ -4611,7 +4912,7 @@ async function decodeImages(runtime, body, signal) {
|
|
|
4611
4912
|
else if (url?.startsWith("data:") === true) data = (await loadMedia(url, noPaths, MAX_OUTPUT_BYTES)).data;
|
|
4612
4913
|
else if (url !== void 0 && /^https?:\/\//i.test(url)) {
|
|
4613
4914
|
const response = await runtime.http.send(url, {
|
|
4614
|
-
anonymous:
|
|
4915
|
+
anonymous: true,
|
|
4615
4916
|
headers: { accept: "*/*" },
|
|
4616
4917
|
timeoutMs: 12e4,
|
|
4617
4918
|
signal
|
|
@@ -6516,19 +6817,15 @@ var TaskTracker = class {
|
|
|
6516
6817
|
missing
|
|
6517
6818
|
};
|
|
6518
6819
|
}
|
|
6519
|
-
/**
|
|
6820
|
+
/** Use a returned signed URL verbatim and without the app token; old gateways without a URL use `/content`. */
|
|
6520
6821
|
async fetchOutput(record, task, output, temporary) {
|
|
6521
6822
|
const spec = SPECS[record.kind];
|
|
6522
6823
|
const sources = [];
|
|
6523
|
-
if (record.kind === "video") sources.push({
|
|
6524
|
-
url: spec.content(task.id, output.index),
|
|
6525
|
-
anonymous: false
|
|
6526
|
-
});
|
|
6527
6824
|
if (output.url !== void 0) sources.push({
|
|
6528
6825
|
url: output.url,
|
|
6529
6826
|
anonymous: true
|
|
6530
6827
|
});
|
|
6531
|
-
|
|
6828
|
+
else sources.push({
|
|
6532
6829
|
url: spec.content(task.id, output.index),
|
|
6533
6830
|
anonymous: false
|
|
6534
6831
|
});
|
|
@@ -6561,8 +6858,6 @@ var TaskTracker = class {
|
|
|
6561
6858
|
* Settings → VibeDev 账号; this is the agent's way to the same sign-in.
|
|
6562
6859
|
* @module dsh-vibedev/tools/account
|
|
6563
6860
|
*/
|
|
6564
|
-
/** Where new users register. */
|
|
6565
|
-
const REGISTER_URL = "https://vibedev.jzsaas.com";
|
|
6566
6861
|
/**
|
|
6567
6862
|
* Build the tool.
|
|
6568
6863
|
* @param runtime - the media runtime.
|
|
@@ -6643,7 +6938,7 @@ function mediaAccountTool(runtime, chain, login) {
|
|
|
6643
6938
|
return {
|
|
6644
6939
|
source,
|
|
6645
6940
|
signInUrl: started.url,
|
|
6646
|
-
message: `${started.opened ? "Opened the VibeDev sign-in page in the user's browser." : "Could not open a browser."} If it did not appear, give the user this link: ${started.url} — new users can register on that page (or at ${
|
|
6941
|
+
message: `${started.opened ? "Opened the VibeDev sign-in page in the user's browser." : "Could not open a browser."} If it did not appear, give the user this link: ${started.url} — new users can register on that page (or at ${runtime.http.origin}). The sign-in completes in the background within ${minutes} minutes; once the user says they are done, retry the request.`
|
|
6647
6942
|
};
|
|
6648
6943
|
}
|
|
6649
6944
|
});
|
|
@@ -7263,10 +7558,10 @@ const Config = Schema.object({
|
|
|
7263
7558
|
musicModel: Schema.string().default("").volatile(),
|
|
7264
7559
|
podcastModel: Schema.string().default("").volatile(),
|
|
7265
7560
|
transcriptionModel: Schema.string().default("").volatile(),
|
|
7266
|
-
gatewayOrigin: Schema.string().default(
|
|
7561
|
+
gatewayOrigin: Schema.string().default(DEFAULT_GATEWAY_ORIGIN),
|
|
7267
7562
|
client: Schema.string().default("vibedev-plugin"),
|
|
7268
7563
|
primary: Schema.boolean().default(false),
|
|
7269
|
-
apiKeyEnv: Schema.string().default("
|
|
7564
|
+
apiKeyEnv: Schema.string().default(""),
|
|
7270
7565
|
stateDir: Schema.string().default("")
|
|
7271
7566
|
}).i18n({
|
|
7272
7567
|
"zh-CN": {
|
|
@@ -7308,13 +7603,13 @@ const Config = Schema.object({
|
|
|
7308
7603
|
});
|
|
7309
7604
|
const GUIDANCE = "VibeDev media tools: image_generate, video_generate, audio_generate (music or a podcast) and audio_transcribe create and transcribe media with the user's VibeDev account; media_models lists models and prices. Use them on your own judgment whenever generated media serves the request (illustrations, product shots, storyboard frames, short clips, background music, a podcast episode), not only when a tool is named. They spend the user's balance, so make what the request needs rather than speculative variations. Results are saved in the workspace under media/. Pass files the user attached in this chat as chat:1 (the most recent), chat:2, and so on. Video and audio run as background jobs: carry on and let the job notification report the result.";
|
|
7310
7605
|
function apply(ctx, config) {
|
|
7311
|
-
const origin = config.gatewayOrigin
|
|
7606
|
+
const origin = normalizeGatewayOrigin(config.gatewayOrigin);
|
|
7312
7607
|
const stateDir = stateDirectory(config.stateDir);
|
|
7313
7608
|
const userAgent = `vibedev-plugin/${version}`;
|
|
7314
7609
|
const warn = (message) => {
|
|
7315
7610
|
ctx.logger.warn(message);
|
|
7316
7611
|
};
|
|
7317
|
-
const storage = grantStorage(stateDir, () => ctx.get("credentials"));
|
|
7612
|
+
const storage = grantStorage(stateDir, () => ctx.get("credentials"), origin);
|
|
7318
7613
|
const login = new PluginLogin({
|
|
7319
7614
|
origin,
|
|
7320
7615
|
userAgent,
|
|
@@ -7325,7 +7620,7 @@ function apply(ctx, config) {
|
|
|
7325
7620
|
});
|
|
7326
7621
|
ctx.inject(["credentials"], (scoped) => {
|
|
7327
7622
|
scoped.on("credentials/record-updated", (key) => {
|
|
7328
|
-
if (key ===
|
|
7623
|
+
if (key === gatewayGrantKey(origin)) login.restore();
|
|
7329
7624
|
});
|
|
7330
7625
|
scoped.effect(async () => {
|
|
7331
7626
|
await login.restore();
|
|
@@ -7336,7 +7631,7 @@ function apply(ctx, config) {
|
|
|
7336
7631
|
origin,
|
|
7337
7632
|
plugin: login,
|
|
7338
7633
|
account: () => ctx.get("deepseekAccount"),
|
|
7339
|
-
apiKey: () =>
|
|
7634
|
+
apiKey: () => developmentKey(origin, config.apiKeyEnv)
|
|
7340
7635
|
});
|
|
7341
7636
|
const http = new GatewayHttp({
|
|
7342
7637
|
origin,
|
|
@@ -7344,7 +7639,7 @@ function apply(ctx, config) {
|
|
|
7344
7639
|
resolveCredential: () => chain.resolve(),
|
|
7345
7640
|
rejectToken: (credential) => chain.reject(credential)
|
|
7346
7641
|
});
|
|
7347
|
-
const store = new TaskStore(stateDir);
|
|
7642
|
+
const store = new TaskStore(gatewayStateDirectory(stateDir, origin));
|
|
7348
7643
|
const tracker = new TaskTracker({
|
|
7349
7644
|
http,
|
|
7350
7645
|
store,
|