@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/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
- **媒体生成**:Agent 可以生成图片、视频、音乐和播客,并把语音转成文字,结果保存在工作区的 `media/` 目录下;
|
|
7
7
|
- **网页搜索**:提供走 VibeDev 网关的网页搜索,见下面的「网页搜索」。
|
|
8
8
|
|
|
9
|
-
费用都从你的 VibeDev 余额里扣,DeepSeek 账号照常由 DeepSeek Harness 自己管理,两边互不影响。还没有 VibeDev 账号的话,可以在 [
|
|
9
|
+
费用都从你的 VibeDev 余额里扣,DeepSeek 账号照常由 DeepSeek Harness 自己管理,两边互不影响。还没有 VibeDev 账号的话,可以在 [VibeDev](https://api.vibedev.studio) 注册。
|
|
10
10
|
|
|
11
11
|
本插件由 `dsh-media` 改名而来,见下面的「从 dsh-media 升级」。
|
|
12
12
|
|
|
@@ -43,6 +43,12 @@
|
|
|
43
43
|
|
|
44
44
|
## VibeDev 的模型
|
|
45
45
|
|
|
46
|
+
正式默认 API 为 `https://api.vibedev.studio`。从旧版升级后须重新登录,才会获取本站模型;登录仍使用 S256 PKCE、state、随机 localhost 回调和 app token,不使用共享 HMAC secret。充值、注册、用量链接跟随所选 API origin,网页搜索也使用该 API 的 `/v1/vibedev/web-search`。
|
|
47
|
+
|
|
48
|
+
所有网关的凭据键、设备记录和媒体任务目录均按规范化 gateway origin 隔离,只接受带匹配 origin 标记的登录记录。原 `dsh-media/vibedev-session` 和未标注的数据目录保留在本地,但不再读取、导入或自动恢复、重新提交其中的旧视频、音频与上传状态。DeepSeek 等其它账号、聊天、工作区及历史媒体 URL 不改写;已有美国 origin 登录记录可继续使用。
|
|
49
|
+
|
|
50
|
+
媒体创建、完成、刷新请求走所选 API;上传和下载使用服务器返回的完整签名 URL,保留 host、path、query,不携带 VibeDev app token。返回签名链接时下载不再先探测旧 `/content` 接口;未返回链接的旧接口仍可直接返回文件。API 和下载请求拒绝重定向,不把失败或503当作跨区域重发理由。现有同站同键重试、SSE、心跳和取消语义保留。默认不读取开发密钥,旧 `VIBEDEV_GATEWAY_API_KEY` 变量不再使用;开发调试需显式指定本站专用环境变量。本地验证版不解除服务器503挡板,不发送真实模型或付费媒体测试。
|
|
51
|
+
|
|
46
52
|
- 模型列表来自 VibeDev 网关,按你的账号下发,所以**未登录时不显示**,登录后立即出现,不用重启。
|
|
47
53
|
- 模型在选择器里的「VibeDev」组;默认模型仍是 DeepSeek Harness 原来的,想用 VibeDev 的模型就在选择器里选。
|
|
48
54
|
- 没登录就用了 VibeDev 的模型,或登录已失效时,对话里会提示到侧边栏或设置里登录;余额不足时会提示并给出充值链接。这些提示和 DeepSeek 账号的登录、充值提示是分开的。
|
|
@@ -126,12 +132,14 @@ npm run build
|
|
|
126
132
|
|
|
127
133
|
## English
|
|
128
134
|
|
|
135
|
+
**American API default**: production defaults to `https://api.vibedev.studio`. Upgrades from unscoped historical state require a new app-token login before listing models. Every origin has separate credentials, devices and durable media tasks; grants must carry a matching origin. Historical unmarked files and the old credential record stay intact but are never read, imported, resumed or resubmitted. Existing American-origin grants remain usable. Other accounts, chats, workspaces and historical media URLs are unchanged. API control requests, account web links and web search follow the selected origin; uploads/downloads preserve the server's complete signed URL without an app token. Redirects and cross-region fallback are refused. S256 PKCE, state, random localhost callback, SSE, heartbeats, cancellation and same-origin idempotency remain. Developer-key fallback is disabled by default and the old shared environment variable is ignored; development requires an explicitly named gateway-specific variable. Local verification neither removes the server's 503 gate nor sends real model or paid media tests.
|
|
136
|
+
|
|
129
137
|
Sign in to VibeDev in DeepSeek Harness, next to the DeepSeek account:
|
|
130
138
|
|
|
131
139
|
- **VibeDev models**: once signed in, the model picker gains a "VibeDev" group (Claude, GPT and the other models of the VibeDev gateway) next to DeepSeek's;
|
|
132
140
|
- **Media generation**: the agent generates images, videos, music and podcasts and transcribes speech; results are saved in the workspace under `media/`.
|
|
133
141
|
|
|
134
|
-
Everything is paid from your VibeDev balance; the DeepSeek account stays DeepSeek Harness's own, and the two do not affect each other. To create an account, register at [
|
|
142
|
+
Everything is paid from your VibeDev balance; the DeepSeek account stays DeepSeek Harness's own, and the two do not affect each other. To create an account, register at [VibeDev](https://api.vibedev.studio). This plugin was renamed from `dsh-media`.
|
|
135
143
|
|
|
136
144
|
**Install**: install `@vibedev-si/dsh-vibedev` by package name on the Plugins page (the `@vibedev-si/` scope is required), or from the [VibeDev Plugin Center](https://github.com/VibeDev-Si/dsh-ecosystem); from the command line, `dsh plugin add @vibedev-si/dsh-vibedev`, or `dsh plugin --profile desktop add @vibedev-si/dsh-vibedev` for the desktop app (quit it fully first). The VibeDev app ships with the plugin built in, and there the VibeDev account is the main account (the bottom row of the sidebar and the first Settings section), with the DeepSeek account as the second one. Within 24 hours of a release, name the version (e.g. `@vibedev-si/dsh-vibedev@0.2.5`): pnpm does not install versions younger than a day by default.
|
|
137
145
|
|
|
@@ -159,6 +167,12 @@ Everything is paid from your VibeDev balance; the DeepSeek account stays DeepSee
|
|
|
159
167
|
|
|
160
168
|
**Third-party code**: the model route in `src/llm/` is ported from the VibeDev app; `context.ts`, `replay.ts` and `stream.ts` are copied from DeepSeek Harness's `@deepseek-ai/dsh-llm-pi-ai` (MIT). The licences of the bundled pi-ai, Anthropic SDK, OpenAI SDK and their dependencies are in `THIRD-PARTY-NOTICES.txt`.
|
|
161
169
|
|
|
170
|
+
## Login diagnostics (0.2.8-us.3) · 登录诊断
|
|
171
|
+
|
|
172
|
+
登录状态区分等待浏览器、交换登录凭据、保存凭据与成功、失败、取消、过期;设置页面和新版 VibeDev Next 欢迎页显示网络、超时、授权拒绝、服务不可用、响应异常、本地存储或回调错误。折叠的诊断信息及 Host 日志只记录尝试编号、阶段、目标 origin、HTTP 状态和受控网络错误码,不记录账号密码、授权码、PKCE verifier、token、响应正文或完整回调链接。保持 PKCE、state 与随机 localhost 回调;交换失败不会自动重发或切换网关。美国网页公开配置仍需服务维护方单独核对。
|
|
173
|
+
|
|
174
|
+
Login distinguishes waiting for the browser, exchanging credentials, saving credentials, and successful, failed, cancelled or expired outcomes. Settings and the updated VibeDev Next welcome page show classified failures. Collapsible support details and Host logs contain only the attempt reference, phase, origin, HTTP status and allowlisted network codes. Passwords, authorization codes, PKCE verifiers, tokens, response bodies and full callback URLs are excluded. PKCE, state and the random localhost callback remain; a failed exchange is never automatically repeated or sent to another gateway. The gateway operator must separately verify the American web page's public configuration.
|
|
175
|
+
|
|
162
176
|
## Maintainer releases
|
|
163
177
|
|
|
164
178
|
Build, typecheck and test locally, pack the tested archive, tag the matching source version, and publish a GitHub Release with `vibedev-si-dsh-vibedev-<version>.tgz` attached. `.github/workflows/publish.yml` publishes that exact archive with npm OIDC; it does not rebuild the package. The job checks the source/tag/archive identities and Release asset digest, refuses an existing version with different bytes, and skips an identical already-published version. It verifies the complete npm tarball after publication. For a manual retry of an unpublished version, run the workflow on the version's tag ref.
|
package/THIRD-PARTY-NOTICES.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
THIRD-PARTY NOTICES · @vibedev-si/dsh-vibedev 0.
|
|
1
|
+
THIRD-PARTY NOTICES · @vibedev-si/dsh-vibedev 0.3.0
|
|
2
2
|
|
|
3
3
|
This package bundles code from the projects below. Each is used under the licence that
|
|
4
4
|
follows its name; the licence texts are reproduced as the projects ship them.
|
package/client/client.js
CHANGED
|
@@ -131,10 +131,10 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
131
131
|
const body = await response.json().catch(() => void 0);
|
|
132
132
|
if (!response.ok || typeof body?.url !== "string") throw new Error(typeof body?.error?.message === "string" ? body.error.message : `HTTP ${response.status}`);
|
|
133
133
|
if (body.opened !== true) this.options.openWindow?.(body.url);
|
|
134
|
-
} catch
|
|
134
|
+
} catch {
|
|
135
135
|
this.set({ actionError: {
|
|
136
136
|
kind: "sign-in",
|
|
137
|
-
message:
|
|
137
|
+
message: "Sign-in request failed."
|
|
138
138
|
} });
|
|
139
139
|
} finally {
|
|
140
140
|
this.set({ busy: void 0 });
|
|
@@ -179,6 +179,71 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
179
179
|
}
|
|
180
180
|
};
|
|
181
181
|
//#endregion
|
|
182
|
+
//#region src/client/sign-in-presentation.ts
|
|
183
|
+
/** @param attempt - latest safe Host snapshot. @returns a locale key for a terminal login failure. */
|
|
184
|
+
function failureKey(attempt) {
|
|
185
|
+
if (attempt?.phase === "expired") return "signInExpired";
|
|
186
|
+
if (attempt?.phase !== "failed") return void 0;
|
|
187
|
+
const keys = {
|
|
188
|
+
network: "signInNetwork",
|
|
189
|
+
timeout: "signInTimeout",
|
|
190
|
+
"gateway-refused": "signInRefused",
|
|
191
|
+
"gateway-unavailable": "signInUnavailable",
|
|
192
|
+
protocol: "signInProtocol",
|
|
193
|
+
storage: "signInStorage",
|
|
194
|
+
callback: "signInCallback"
|
|
195
|
+
};
|
|
196
|
+
return attempt.errorCode !== void 0 && Object.hasOwn(keys, attempt.errorCode) ? keys[attempt.errorCode] : "signInUnknown";
|
|
197
|
+
}
|
|
198
|
+
/** @param attempt - latest snapshot. @returns support reference without query, body, credentials or raw error. */
|
|
199
|
+
function safeSupportDetails(attempt) {
|
|
200
|
+
if (attempt === void 0) return void 0;
|
|
201
|
+
const id = /^[a-zA-Z0-9_-]{1,80}$/.test(attempt.id) ? attempt.id : void 0;
|
|
202
|
+
let origin;
|
|
203
|
+
try {
|
|
204
|
+
const url = new URL(attempt.gatewayOrigin);
|
|
205
|
+
if (["https:", "http:"].includes(url.protocol) && !url.username && !url.password) origin = url.origin;
|
|
206
|
+
} catch {}
|
|
207
|
+
const stage = [
|
|
208
|
+
"listen",
|
|
209
|
+
"device",
|
|
210
|
+
"redeem",
|
|
211
|
+
"persist"
|
|
212
|
+
].includes(attempt.stage ?? "") ? attempt.stage : void 0;
|
|
213
|
+
return [
|
|
214
|
+
[
|
|
215
|
+
"network",
|
|
216
|
+
"timeout",
|
|
217
|
+
"gateway-refused",
|
|
218
|
+
"gateway-unavailable",
|
|
219
|
+
"protocol",
|
|
220
|
+
"storage",
|
|
221
|
+
"callback"
|
|
222
|
+
].includes(attempt.errorCode ?? "") ? attempt.errorCode : void 0,
|
|
223
|
+
stage,
|
|
224
|
+
Number.isInteger(attempt.httpStatus) && Number(attempt.httpStatus) >= 100 && Number(attempt.httpStatus) <= 599 ? `HTTP ${attempt.httpStatus}` : void 0,
|
|
225
|
+
[
|
|
226
|
+
"ECONNRESET",
|
|
227
|
+
"ECONNREFUSED",
|
|
228
|
+
"ETIMEDOUT",
|
|
229
|
+
"ENOTFOUND",
|
|
230
|
+
"EAI_AGAIN",
|
|
231
|
+
"ENETUNREACH",
|
|
232
|
+
"EHOSTUNREACH",
|
|
233
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
234
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
235
|
+
"UND_ERR_BODY_TIMEOUT",
|
|
236
|
+
"UND_ERR_SOCKET",
|
|
237
|
+
"CERT_HAS_EXPIRED",
|
|
238
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
239
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
240
|
+
"ERR_TLS_CERT_ALTNAME_INVALID"
|
|
241
|
+
].includes(attempt.networkCode ?? "") ? attempt.networkCode : void 0,
|
|
242
|
+
origin,
|
|
243
|
+
id
|
|
244
|
+
].filter((value) => value !== void 0).join(" · ");
|
|
245
|
+
}
|
|
246
|
+
//#endregion
|
|
182
247
|
//#region src/client/locales.ts
|
|
183
248
|
/**
|
|
184
249
|
* Copy for the dsh-vibedev pages (the plugin's settings page, the VibeDev
|
|
@@ -239,6 +304,18 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
239
304
|
signOut: "退出登录",
|
|
240
305
|
signOutFailed: "退出没有成功,请重试。",
|
|
241
306
|
signInFailed: "没能开始登录:{message}",
|
|
307
|
+
signInExchanging: "正在交换登录凭据…",
|
|
308
|
+
signInCommitting: "正在保存登录信息…",
|
|
309
|
+
signInExpired: "登录已超时,请重新登录。",
|
|
310
|
+
signInNetwork: "无法连接登录服务,请检查网络后重新登录。",
|
|
311
|
+
signInTimeout: "登录服务响应超时,请重新登录。",
|
|
312
|
+
signInRefused: "登录服务拒绝了本次授权,请重新发起登录。",
|
|
313
|
+
signInUnavailable: "登录服务暂时不可用,请稍后重试。",
|
|
314
|
+
signInProtocol: "登录服务返回内容异常,请将诊断信息提供给支持人员。",
|
|
315
|
+
signInStorage: "无法在本机保存登录信息,请检查应用数据目录的访问权限。",
|
|
316
|
+
signInCallback: "无法启动本地登录回调,请检查安全软件后重试。",
|
|
317
|
+
signInUnknown: "登录未完成,请重新登录。",
|
|
318
|
+
signInDiagnostic: "登录诊断信息",
|
|
242
319
|
loadFailed: "读取 VibeDev 账号状态失败,请稍后再试。",
|
|
243
320
|
sidebarSignedIn: "VibeDev 已登录",
|
|
244
321
|
sidebarSignedOut: "登录 VibeDev",
|
|
@@ -302,6 +379,18 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
302
379
|
signOut: "Sign out",
|
|
303
380
|
signOutFailed: "Signing out did not work. Try again.",
|
|
304
381
|
signInFailed: "Could not start the sign-in: {message}",
|
|
382
|
+
signInExchanging: "Exchanging sign-in credentials…",
|
|
383
|
+
signInCommitting: "Saving sign-in credentials…",
|
|
384
|
+
signInExpired: "Sign-in expired. Please sign in again.",
|
|
385
|
+
signInNetwork: "Could not connect to the sign-in service. Check your network and try signing in again.",
|
|
386
|
+
signInTimeout: "The sign-in service did not respond in time. Please sign in again.",
|
|
387
|
+
signInRefused: "The sign-in service rejected this authorization. Please start a new sign-in.",
|
|
388
|
+
signInUnavailable: "The sign-in service is temporarily unavailable. Please try again later.",
|
|
389
|
+
signInProtocol: "The sign-in service returned an invalid response. Please share the diagnostic details with support.",
|
|
390
|
+
signInStorage: "Could not save the sign-in locally. Check access to the app data folder.",
|
|
391
|
+
signInCallback: "Could not start the local sign-in callback. Check local security software and try again.",
|
|
392
|
+
signInUnknown: "Could not complete sign-in. Please sign in again.",
|
|
393
|
+
signInDiagnostic: "Sign-in diagnostic details",
|
|
305
394
|
loadFailed: "The VibeDev account could not be read. Try again later.",
|
|
306
395
|
sidebarSignedIn: "VibeDev signed in",
|
|
307
396
|
sidebarSignedOut: "Sign in to VibeDev",
|
|
@@ -402,6 +491,7 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
402
491
|
function statusDot(state) {
|
|
403
492
|
const view = state.view;
|
|
404
493
|
if (view === void 0) return "idle";
|
|
494
|
+
if (view.source === "none" && view.attempt?.phase === "failed") return "error";
|
|
405
495
|
if (view.pending !== void 0) return "ongoing";
|
|
406
496
|
return view.source === "none" ? "idle" : "done";
|
|
407
497
|
}
|
|
@@ -432,8 +522,8 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
432
522
|
let actions = null;
|
|
433
523
|
if (view === void 0) status = state.loadFailed ? t("loadFailed") : t("loading");
|
|
434
524
|
else if (view.pending !== void 0) {
|
|
435
|
-
status = t("signingIn");
|
|
436
|
-
details = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
525
|
+
status = view.attempt?.phase === "committing" ? t("signInCommitting") : view.attempt?.phase === "exchanging" ? t("signInExchanging") : t("signingIn");
|
|
526
|
+
details = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: view.attempt === void 0 || view.attempt.phase === "waiting-browser" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
437
527
|
style: styles$1.line,
|
|
438
528
|
children: t("pendingStatus")
|
|
439
529
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -443,10 +533,11 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
443
533
|
store.openPending();
|
|
444
534
|
},
|
|
445
535
|
children: t("pendingLink")
|
|
446
|
-
})] });
|
|
536
|
+
})] }) : null });
|
|
447
537
|
actions = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
448
538
|
variant: "outline",
|
|
449
539
|
size: "sm",
|
|
540
|
+
disabled: view.attempt?.phase === "committing",
|
|
450
541
|
onClick: () => {
|
|
451
542
|
store.cancel();
|
|
452
543
|
},
|
|
@@ -529,7 +620,9 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
529
620
|
}) : null
|
|
530
621
|
] });
|
|
531
622
|
}
|
|
532
|
-
const error = state.actionError === void 0 ? void 0 : state.actionError.kind === "sign-in" ?
|
|
623
|
+
const error = state.actionError === void 0 ? void 0 : state.actionError.kind === "sign-in" ? t("signInUnknown") : t("signOutFailed");
|
|
624
|
+
const loginFailure = view?.source === "none" ? failureKey(view.attempt) : void 0;
|
|
625
|
+
const supportDetails = loginFailure === void 0 ? void 0 : safeSupportDetails(view?.attempt);
|
|
533
626
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
534
627
|
style: styles$1.page,
|
|
535
628
|
"data-dsh-vibedev-account": true,
|
|
@@ -562,6 +655,15 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
562
655
|
style: styles$1.error,
|
|
563
656
|
role: "alert",
|
|
564
657
|
children: error
|
|
658
|
+
}),
|
|
659
|
+
loginFailure === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
660
|
+
style: styles$1.error,
|
|
661
|
+
role: "alert",
|
|
662
|
+
children: t(loginFailure)
|
|
663
|
+
}),
|
|
664
|
+
supportDetails === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
665
|
+
style: styles$1.quiet,
|
|
666
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("signInDiagnostic") }), supportDetails]
|
|
565
667
|
})
|
|
566
668
|
]
|
|
567
669
|
})
|
|
@@ -636,8 +738,6 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
636
738
|
* output folder, the pinned models, and how the account works. Edits are
|
|
637
739
|
* staged and written together by the save, like every settings page there.
|
|
638
740
|
*/
|
|
639
|
-
/** Where new users register and everyone tops up. */
|
|
640
|
-
const VIBEDEV_SITE = "https://vibedev.jzsaas.com";
|
|
641
741
|
const styles = {
|
|
642
742
|
section: {
|
|
643
743
|
padding: "12px 0",
|
|
@@ -714,11 +814,12 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
714
814
|
}
|
|
715
815
|
/**
|
|
716
816
|
* Render the settings page.
|
|
717
|
-
* @param props - the dictionary,
|
|
817
|
+
* @param props - the dictionary, form store, actions and selected gateway account view.
|
|
718
818
|
* @returns the page.
|
|
719
819
|
*/
|
|
720
|
-
function MediaSettingsPage({ t, store, actions }) {
|
|
820
|
+
function MediaSettingsPage({ t, store, actions, account }) {
|
|
721
821
|
const state = (0, react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
|
|
822
|
+
const site = (0, react.useSyncExternalStore)(account.subscribe, account.getSnapshot).view?.links.register;
|
|
722
823
|
if (state.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
723
824
|
style: styles.quiet,
|
|
724
825
|
role: "status",
|
|
@@ -801,9 +902,9 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
801
902
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
802
903
|
style: styles.hint,
|
|
803
904
|
children: t("accountHint")
|
|
804
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
905
|
+
}), site === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
805
906
|
style: styles.link,
|
|
806
|
-
href:
|
|
907
|
+
href: site,
|
|
807
908
|
target: "_blank",
|
|
808
909
|
rel: "noreferrer",
|
|
809
910
|
children: t("register")
|
|
@@ -960,15 +1061,16 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
960
1061
|
{
|
|
961
1062
|
type: "label",
|
|
962
1063
|
id: "pending",
|
|
963
|
-
text: t("pendingStatus")
|
|
1064
|
+
text: view?.attempt?.phase === "committing" ? t("signInCommitting") : view?.attempt?.phase === "exchanging" ? t("signInExchanging") : t("pendingStatus")
|
|
964
1065
|
},
|
|
965
|
-
{
|
|
1066
|
+
...view?.attempt === void 0 || view.attempt.phase === "waiting-browser" ? [{
|
|
966
1067
|
id: "open-pending",
|
|
967
1068
|
label: t("pendingLink")
|
|
968
|
-
},
|
|
1069
|
+
}] : [],
|
|
969
1070
|
{
|
|
970
1071
|
id: "cancel",
|
|
971
|
-
label: t("cancelSignIn")
|
|
1072
|
+
label: t("cancelSignIn"),
|
|
1073
|
+
disabled: view?.attempt?.phase === "committing"
|
|
972
1074
|
},
|
|
973
1075
|
{
|
|
974
1076
|
type: "separator",
|
|
@@ -1136,15 +1238,16 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
1136
1238
|
{
|
|
1137
1239
|
type: "label",
|
|
1138
1240
|
id: "pending",
|
|
1139
|
-
text: t("pendingStatus")
|
|
1241
|
+
text: view.attempt?.phase === "committing" ? t("signInCommitting") : view.attempt?.phase === "exchanging" ? t("signInExchanging") : t("pendingStatus")
|
|
1140
1242
|
},
|
|
1141
|
-
{
|
|
1243
|
+
...view.attempt === void 0 || view.attempt.phase === "waiting-browser" ? [{
|
|
1142
1244
|
id: "open-pending",
|
|
1143
1245
|
label: t("pendingLink")
|
|
1144
|
-
},
|
|
1246
|
+
}] : [],
|
|
1145
1247
|
{
|
|
1146
1248
|
id: "cancel",
|
|
1147
|
-
label: t("cancelSignIn")
|
|
1249
|
+
label: t("cancelSignIn"),
|
|
1250
|
+
disabled: view.attempt?.phase === "committing"
|
|
1148
1251
|
}
|
|
1149
1252
|
] : [
|
|
1150
1253
|
{
|
|
@@ -1331,7 +1434,8 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
1331
1434
|
}, (owner = {}) => owner.view === "summary" ? null : (0, react.createElement)(MediaSettingsPage, {
|
|
1332
1435
|
t,
|
|
1333
1436
|
store,
|
|
1334
|
-
actions
|
|
1437
|
+
actions,
|
|
1438
|
+
account
|
|
1335
1439
|
})));
|
|
1336
1440
|
let section;
|
|
1337
1441
|
const placeSection = () => {
|
|
@@ -1345,7 +1449,8 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
1345
1449
|
}, () => (0, react.createElement)(MediaSettingsPage, {
|
|
1346
1450
|
t,
|
|
1347
1451
|
store,
|
|
1348
|
-
actions
|
|
1452
|
+
actions,
|
|
1453
|
+
account
|
|
1349
1454
|
}))));
|
|
1350
1455
|
};
|
|
1351
1456
|
const unsubscribe = account.subscribe(placeSection);
|
package/lib/account/index.d.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import type { ConnectionFetchRoute } from '@deepseek-ai/dsh-client-connection';
|
|
21
21
|
import type { CredentialChain } from '../auth/credentials.js';
|
|
22
|
-
import type { PluginLogin } from '../auth/login.js';
|
|
22
|
+
import type { PluginLogin, SignInAttempt } from '../auth/login.js';
|
|
23
23
|
/** Path prefix of the plugin's Host routes. */
|
|
24
24
|
export declare const ACCOUNT_ROUTE_PREFIX = "/api/dsh-vibedev/account";
|
|
25
25
|
/** Where the active credential comes from. */
|
|
@@ -43,6 +43,8 @@ export interface AccountView {
|
|
|
43
43
|
readonly url: string;
|
|
44
44
|
readonly expiresAt: number;
|
|
45
45
|
};
|
|
46
|
+
/** Safe sign-in progress or the most recent terminal diagnostic. */
|
|
47
|
+
readonly attempt?: SignInAttempt;
|
|
46
48
|
/** VibeDev chat models the pickers list now, and why there are none. */
|
|
47
49
|
readonly models: {
|
|
48
50
|
readonly count: number;
|
|
@@ -36,13 +36,15 @@ export declare class CredentialChain {
|
|
|
36
36
|
*/
|
|
37
37
|
reject(credential: GatewayCredential): Promise<void>;
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
/** Credential identity for one gateway; never consult another gateway's record. */
|
|
40
|
+
export declare function gatewayGrantKey(origin: string): import("@deepseek-ai/dsh-credentials").CredentialKey;
|
|
40
41
|
/**
|
|
41
42
|
* Grant storage: the host's credential store when one is loaded (a grant
|
|
42
43
|
* record the store keeps verbatim), else a private file in the state
|
|
43
44
|
* directory. The device id is not a secret and lives in the state directory.
|
|
44
|
-
* @param
|
|
45
|
+
* @param root - the plugin state root; the storage selects an origin-specific directory itself.
|
|
45
46
|
* @param credentials - the host's credential store, when loaded.
|
|
47
|
+
* @param selectedOrigin - the issuing gateway; unmarked historical state is never adopted.
|
|
46
48
|
* @returns the storage.
|
|
47
49
|
*/
|
|
48
|
-
export declare function grantStorage(
|
|
50
|
+
export declare function grantStorage(root: string, credentials: () => CredentialProvider | undefined, selectedOrigin?: string): GrantStorage;
|
package/lib/auth/login.d.ts
CHANGED
|
@@ -12,8 +12,12 @@
|
|
|
12
12
|
* - `POST /api/v1/vibedev/app-token/revoke` (bearer or `{refresh_token}`): always 204
|
|
13
13
|
* @module dsh-vibedev/auth/login
|
|
14
14
|
*/
|
|
15
|
+
import type { SignInAttempt } from './sign-in-state.js';
|
|
16
|
+
export type { SignInAttempt } from './sign-in-state.js';
|
|
15
17
|
/** A stored plugin session. */
|
|
16
18
|
export interface PluginGrant {
|
|
19
|
+
/** Origin that issued this stored app grant; legacy unmarked grants belong to the domestic gateway only. */
|
|
20
|
+
readonly gatewayOrigin?: string;
|
|
17
21
|
readonly accessToken: string;
|
|
18
22
|
readonly refreshToken: string;
|
|
19
23
|
/** Epoch ms. */
|
|
@@ -57,7 +61,7 @@ export interface PluginLoginOptions {
|
|
|
57
61
|
readonly now?: () => number;
|
|
58
62
|
/** Open a URL in the system browser; resolves whether that was attempted successfully. */
|
|
59
63
|
readonly openBrowser?: (url: string) => Promise<boolean>;
|
|
60
|
-
/**
|
|
64
|
+
/** Sign-in deadline through credential exchange, before the atomic write starts. Defaults to 5 minutes. */
|
|
61
65
|
readonly signInTimeoutMs?: number;
|
|
62
66
|
/** The client name the gateway records for the sign-in (it picks the key the usage is billed to). Defaults to `vibedev-plugin`. */
|
|
63
67
|
readonly client?: string;
|
|
@@ -93,6 +97,7 @@ export declare class PluginLogin {
|
|
|
93
97
|
private readFailureReported;
|
|
94
98
|
private refreshing;
|
|
95
99
|
private pending;
|
|
100
|
+
private attempt;
|
|
96
101
|
private readonly listeners;
|
|
97
102
|
private readonly now;
|
|
98
103
|
private readonly origin;
|
|
@@ -106,6 +111,11 @@ export declare class PluginLogin {
|
|
|
106
111
|
restore(): Promise<void>;
|
|
107
112
|
/** Take a grant as current and tell listeners when it changed. */
|
|
108
113
|
private adopt;
|
|
114
|
+
private notify;
|
|
115
|
+
/** A copied safe snapshot, retained after completion until the next sign-in. */
|
|
116
|
+
signInAttempt(): SignInAttempt | undefined;
|
|
117
|
+
private logAttempt;
|
|
118
|
+
private transition;
|
|
109
119
|
private save;
|
|
110
120
|
/**
|
|
111
121
|
* Hear about sign-in and sign-out.
|
|
@@ -151,7 +161,7 @@ export declare class PluginLogin {
|
|
|
151
161
|
}): Promise<PendingSignIn & {
|
|
152
162
|
opened: boolean;
|
|
153
163
|
}>;
|
|
154
|
-
private
|
|
164
|
+
private beginSignIn;
|
|
155
165
|
/** Sign out: revoke the session at the gateway (best effort) and forget it. */
|
|
156
166
|
signOut(): Promise<void>;
|
|
157
167
|
/** Stop a waiting sign-in. */
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Safe host diagnostics: deliberately separate from authorization URLs and grants. */
|
|
2
|
+
export interface SignInAttempt {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly phase: 'preparing' | 'waiting-browser' | 'exchanging' | 'committing' | 'succeeded' | 'failed' | 'cancelled' | 'expired';
|
|
5
|
+
readonly expiresAt: number;
|
|
6
|
+
readonly gatewayOrigin: string;
|
|
7
|
+
readonly stage?: 'listen' | 'device' | 'redeem' | 'persist';
|
|
8
|
+
readonly errorCode?: 'network' | 'timeout' | 'gateway-refused' | 'gateway-unavailable' | 'protocol' | 'storage' | 'callback';
|
|
9
|
+
readonly httpStatus?: number;
|
|
10
|
+
readonly networkCode?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const REDEEM_PATH = "/api/v1/vibedev/link/redeem";
|
|
13
|
+
export type SignInFailure = Pick<SignInAttempt, 'errorCode' | 'httpStatus' | 'networkCode'>;
|
|
14
|
+
/** Read only fixed transport codes, never error text, stack, body or arbitrary fields. */
|
|
15
|
+
export declare function transportFailure(error: unknown): SignInFailure;
|
|
16
|
+
/** A fixed bilingual explanation suitable for exposed rejection messages. */
|
|
17
|
+
export declare function failureMessage(attempt: Pick<SignInAttempt, 'errorCode' | 'stage' | 'httpStatus'>): string;
|
|
18
|
+
/** Only our fixed explanation and escaped safe enum/status fields reach the browser. */
|
|
19
|
+
export declare function failurePage(attempt: SignInAttempt): string;
|
|
20
|
+
/** Explicit log allowlist; adding fields to the public attempt never expands log output. */
|
|
21
|
+
export declare function attemptLog(event: 'request' | 'phase' | 'response' | 'terminal', attempt: SignInAttempt): string;
|
package/lib/gateway/http.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface GatewayCredential {
|
|
|
21
21
|
}
|
|
22
22
|
/** Construction options; `fetch` and `sleep` are test seams. */
|
|
23
23
|
export interface GatewayHttpOptions {
|
|
24
|
-
/** Gateway origin, such as `https://vibedev.
|
|
24
|
+
/** Gateway origin, such as `https://api.vibedev.studio`. */
|
|
25
25
|
readonly origin: string;
|
|
26
26
|
/** The credential for the next request; undefined while nobody is signed in. */
|
|
27
27
|
readonly resolveCredential: () => Promise<GatewayCredential | undefined>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Production API origin. Storage URLs are returned by the server, not derived from this address. */
|
|
2
|
+
export declare const DEFAULT_GATEWAY_ORIGIN = "https://api.vibedev.studio";
|
|
3
|
+
/** Validate a bare HTTP(S) origin and canonicalize default ports, casing and trailing slash. */
|
|
4
|
+
export declare function normalizeGatewayOrigin(value: string): string;
|
|
5
|
+
/** Stable, filesystem-safe identity; ports and schemes remain separate boundaries. */
|
|
6
|
+
export declare function gatewayId(origin: string): string;
|
|
7
|
+
/** Every gateway uses its own namespace; never read the unmarked historical root. */
|
|
8
|
+
export declare function gatewayStateDirectory(root: string, origin: string): string;
|
|
9
|
+
/** Ignore the old shared key variable; development requires an explicitly named, gateway-specific variable. */
|
|
10
|
+
export declare function developmentKey(origin: string, name: string, env?: Readonly<Record<string, string | undefined>>): string | undefined;
|