@vibedev-si/dsh-vibedev 0.2.1 → 0.2.3

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 CHANGED
@@ -29,7 +29,7 @@
29
29
 
30
30
  - **VibeDev 应用**:已内置本插件,不用另装。VibeDev 账号就是应用的主账号:侧边栏最底下一行和设置里的第一项都是它,DeepSeek 账号作为第二个账号显示在它上方。
31
31
 
32
- 新版本在 npm 上发布后的 24 小时内,只写包名可能装到上一个版本(pnpm 默认不装发布未满一天的版本),想马上用新版就写明版本号,例如 `@vibedev-si/dsh-vibedev@0.2.1`。
32
+ 新版本在 npm 上发布后的 24 小时内,只写包名可能装到上一个版本(pnpm 默认不装发布未满一天的版本),想马上用新版就写明版本号,例如 `@vibedev-si/dsh-vibedev@0.2.3`。
33
33
 
34
34
  ## 登录 VibeDev
35
35
 
@@ -37,6 +37,8 @@
37
37
 
38
38
  「设置 → VibeDev 账号」里能看到当前账号、余额和 VibeDev 模型的数量,可以充值、查看用量明细、退出登录。登录会自动续期;退出后 VibeDev 的模型会从选择器里消失,DeepSeek 账号不受影响。
39
39
 
40
+ 0.2.3 起修复了重启后再次要求登录的问题:启动时凭据服务未就绪或临时读取失败,会在服务就绪后恢复,不把空结果永久缓存;正常关闭应用保留登录。过去写到本地备用文件的会话在宿主凭据服务可用后迁入,宿主已有记录优先,显式退出会清理备用会话。VibeDev 应用内置的插件需要随应用升级到修复版;单独安装在 DeepSeek Harness 中的插件可直接升级。
41
+
40
42
  ## VibeDev 的模型
41
43
 
42
44
  - 模型列表来自 VibeDev 网关,按你的账号下发,所以**未登录时不显示**,登录后立即出现,不用重启。
@@ -128,10 +130,12 @@ Sign in to VibeDev in DeepSeek Harness, next to the DeepSeek account:
128
130
 
129
131
  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.jzsaas.com](https://vibedev.jzsaas.com). This plugin was renamed from `dsh-media`.
130
132
 
131
- **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.1`): pnpm does not install versions younger than a day by default.
133
+ **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.3`): pnpm does not install versions younger than a day by default.
132
134
 
133
135
  **Signing in**: choose "Sign in to VibeDev" at the bottom of the sidebar or in Settings → VibeDev account. The VibeDev sign-in page opens in your system browser: you type your password on VibeDev's own page, never in the plugin. Settings → VibeDev account shows the account, the balance and how many VibeDev models are listed, and lets you top up, see usage and sign out.
134
136
 
137
+ **Sign-in persistence (0.2.3)**: an early empty or failed startup read is retried when storage becomes ready, and committed credential changes restore the account and model route. A late read cannot overwrite a newly saved sign-in. Normal app exit keeps the sign-in. A session saved in the local fallback is migrated into an available host store without overwriting an existing host record; signing out clears the fallback as well. A built-in copy is upgraded by updating VibeDev, while a separately installed plugin can be upgraded directly.
138
+
135
139
  **Models**: the list comes from the VibeDev gateway for your account, so nothing is listed before you sign in; models appear right after, without a restart. Using a VibeDev model while signed out, or after the sign-in has ended, asks you to sign in; a used-up balance says so with the top-up link. These messages are separate from the DeepSeek account's. An expired access token is renewed and the request sent again, without repeating any output.
136
140
 
137
141
  **Web search**: the plugin registers the web search provider `vibedev-gateway`, signed in with your VibeDev account. Registering does not select it: DeepSeek Harness keeps its own search unless the profile sets `searchProvider: vibedev-gateway` on the `web` row (the VibeDev app does). Searching is free, but the gateway refuses every request, searches included, while the balance is zero.
@@ -150,6 +154,12 @@ Everything is paid from your VibeDev balance; the DeepSeek account stays DeepSee
150
154
 
151
155
  **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`.
152
156
 
157
+ ## Maintainer releases
158
+
159
+ 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.
160
+
161
+ Configure npm Trusted Publisher with GitHub owner `VibeDev-Si`, repository `dsh-vibedev`, workflow `publish.yml`, no environment, and **Allow npm publish**. Enable **Allow npm dist-tag** as well to use the manual `dist-tag` operation for `latest` or `next`. No npm token is stored in this repository. Trusted publishing does not authorize `npm deprecate`; that maintenance operation still uses interactive authentication.
162
+
153
163
  ## License
154
164
 
155
165
  MIT
@@ -1,4 +1,4 @@
1
- THIRD-PARTY NOTICES · @vibedev-si/dsh-vibedev 0.2.1
1
+ THIRD-PARTY NOTICES · @vibedev-si/dsh-vibedev 0.2.3
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.
@@ -36,6 +36,7 @@ export declare class CredentialChain {
36
36
  */
37
37
  reject(credential: GatewayCredential): Promise<void>;
38
38
  }
39
+ export declare const GRANT_KEY: import("@deepseek-ai/dsh-credentials").CredentialKey;
39
40
  /**
40
41
  * Grant storage: the host's credential store when one is loaded (a grant
41
42
  * record the store keeps verbatim), else a private file in the state
@@ -88,7 +88,9 @@ export declare function openInBrowser(url: string): Promise<boolean>;
88
88
  export declare class PluginLogin {
89
89
  private readonly options;
90
90
  private grant;
91
- private loaded;
91
+ private reading;
92
+ private revision;
93
+ private readFailureReported;
92
94
  private refreshing;
93
95
  private pending;
94
96
  private readonly listeners;
@@ -98,7 +100,10 @@ export declare class PluginLogin {
98
100
  * @param options - gateway origin, storage and test seams.
99
101
  */
100
102
  constructor(options: PluginLoginOptions);
103
+ private readStored;
101
104
  private load;
105
+ /** Re-read after the credential provider becomes ready or its record changes, without starting a browser sign-in. */
106
+ restore(): Promise<void>;
102
107
  /** Take a grant as current and tell listeners when it changed. */
103
108
  private adopt;
104
109
  private save;
package/lib/index.js CHANGED
@@ -569,27 +569,77 @@ function grantStorage(stateDir, credentials) {
569
569
  const deviceFile = join(stateDir, "device.json");
570
570
  let device;
571
571
  const readFileGrant = async () => {
572
- const parsed = await readFile(sessionFile, "utf8").then((text) => JSON.parse(text), () => void 0);
572
+ let text;
573
+ try {
574
+ text = await readFile(sessionFile, "utf8");
575
+ } catch (error) {
576
+ if (error.code === "ENOENT") return void 0;
577
+ throw error;
578
+ }
579
+ const parsed = JSON.parse(text);
573
580
  return isGrant(parsed) ? parsed : void 0;
574
581
  };
575
582
  const writeFileGrant = (grant) => writeFileAtomic(sessionFile, new TextEncoder().encode(grant === void 0 ? "null" : JSON.stringify(grant)), { mode: 384 });
583
+ const clearFileGrant = async () => {
584
+ try {
585
+ await stat(sessionFile);
586
+ } catch (error) {
587
+ if (error.code === "ENOENT") return;
588
+ throw error;
589
+ }
590
+ await writeFileGrant(void 0);
591
+ };
592
+ const clearFileGrantQuietly = async () => {
593
+ try {
594
+ await clearFileGrant();
595
+ } catch {}
596
+ };
597
+ const readHostGrant = async (store) => {
598
+ const stored = await store.readRecord(GRANT_KEY);
599
+ if (stored !== void 0) {
600
+ const grant = stored.kind === "grant" && isGrant(stored.payload) ? stored.payload : void 0;
601
+ if (grant !== void 0) await clearFileGrantQuietly();
602
+ return grant;
603
+ }
604
+ try {
605
+ if ((await stat(sessionFile)).size <= 4) return void 0;
606
+ } catch (error) {
607
+ if (error.code === "ENOENT") return void 0;
608
+ throw error;
609
+ }
610
+ let adopted;
611
+ await store.modifyRecord(GRANT_KEY, async (current) => {
612
+ if (current !== void 0) {
613
+ adopted = current.kind === "grant" && isGrant(current.payload) ? current.payload : void 0;
614
+ return;
615
+ }
616
+ adopted = await readFileGrant();
617
+ return adopted === void 0 ? void 0 : {
618
+ kind: "grant",
619
+ payload: adopted
620
+ };
621
+ });
622
+ if (adopted !== void 0) await clearFileGrantQuietly();
623
+ return adopted;
624
+ };
576
625
  return {
577
626
  async read() {
578
627
  const store = credentials();
579
- if (store !== void 0) {
580
- const stored = await store.readRecord(GRANT_KEY);
581
- return stored?.kind === "grant" && isGrant(stored.payload) ? stored.payload : void 0;
582
- }
583
- return readFileGrant();
628
+ return store === void 0 ? readFileGrant() : readHostGrant(store);
584
629
  },
585
630
  async write(grant) {
586
631
  const store = credentials();
587
632
  if (store !== void 0) {
588
- if (grant === void 0) await store.deleteRecord(GRANT_KEY);
589
- else await store.modifyRecord(GRANT_KEY, () => Promise.resolve({
590
- kind: "grant",
591
- payload: grant
592
- }));
633
+ if (grant === void 0) {
634
+ await clearFileGrant();
635
+ await store.deleteRecord(GRANT_KEY);
636
+ } else {
637
+ await store.modifyRecord(GRANT_KEY, () => Promise.resolve({
638
+ kind: "grant",
639
+ payload: grant
640
+ }));
641
+ await clearFileGrantQuietly();
642
+ }
593
643
  return;
594
644
  }
595
645
  await writeFileGrant(grant);
@@ -597,6 +647,7 @@ function grantStorage(stateDir, credentials) {
597
647
  async update(change) {
598
648
  const store = credentials();
599
649
  if (store !== void 0) {
650
+ await readHostGrant(store);
600
651
  let remove = false;
601
652
  await store.modifyRecord(GRANT_KEY, async (current) => {
602
653
  const result = await change(current?.kind === "grant" && isGrant(current.payload) ? current.payload : void 0);
@@ -606,7 +657,10 @@ function grantStorage(stateDir, credentials) {
606
657
  };
607
658
  remove = result.kind === "delete";
608
659
  });
609
- if (remove) await store.deleteRecord(GRANT_KEY);
660
+ if (remove) {
661
+ await clearFileGrant();
662
+ await store.deleteRecord(GRANT_KEY);
663
+ }
610
664
  return;
611
665
  }
612
666
  const result = await change(await readFileGrant());
@@ -709,7 +763,9 @@ const PAGE = (title, body) => `<!doctype html><html><head><meta charset="utf-8">
709
763
  var PluginLogin = class {
710
764
  options;
711
765
  grant;
712
- loaded;
766
+ reading;
767
+ revision = 0;
768
+ readFailureReported = false;
713
769
  refreshing;
714
770
  pending;
715
771
  listeners = /* @__PURE__ */ new Set();
@@ -723,14 +779,36 @@ var PluginLogin = class {
723
779
  this.now = options.now ?? Date.now;
724
780
  this.origin = options.origin.trim().replace(/\/+$/, "");
725
781
  }
782
+ readStored(notify) {
783
+ if (this.reading !== void 0) return this.reading;
784
+ const revision = this.revision;
785
+ this.reading = this.options.storage.read().then((grant) => {
786
+ this.readFailureReported = false;
787
+ if (this.revision !== revision) return;
788
+ if (notify) this.adopt(grant);
789
+ else {
790
+ this.grant = grant;
791
+ this.revision++;
792
+ }
793
+ }, () => {
794
+ if (!this.readFailureReported) this.options.log?.("dsh-vibedev: stored sign-in is temporarily unreadable; it will be checked again");
795
+ this.readFailureReported = true;
796
+ }).finally(() => {
797
+ this.reading = void 0;
798
+ });
799
+ return this.reading;
800
+ }
726
801
  load() {
727
- this.loaded ??= this.options.storage.read().then((grant) => {
728
- this.grant = grant;
729
- }, () => void 0);
730
- return this.loaded;
802
+ return this.grant === void 0 ? this.readStored(false) : Promise.resolve();
803
+ }
804
+ /** Re-read after the credential provider becomes ready or its record changes, without starting a browser sign-in. */
805
+ async restore() {
806
+ await this.reading;
807
+ await this.readStored(true);
731
808
  }
732
809
  /** Take a grant as current and tell listeners when it changed. */
733
810
  adopt(grant) {
811
+ this.revision++;
734
812
  const changed = this.grant?.accessToken !== grant?.accessToken;
735
813
  this.grant = grant;
736
814
  if (!changed) return;
@@ -7168,6 +7246,15 @@ function apply(ctx, config) {
7168
7246
  client: config.client.trim() || "vibedev-plugin",
7169
7247
  openBrowser: (url) => config.openBrowserOnSignIn ? openInBrowser(url) : Promise.resolve(false)
7170
7248
  });
7249
+ ctx.inject(["credentials"], (scoped) => {
7250
+ scoped.on("credentials/record-updated", (key) => {
7251
+ if (key === GRANT_KEY) login.restore();
7252
+ });
7253
+ scoped.effect(async () => {
7254
+ await login.restore();
7255
+ return () => {};
7256
+ }, "dsh-vibedev: restore stored sign-in");
7257
+ });
7171
7258
  const chain = new CredentialChain({
7172
7259
  origin,
7173
7260
  plugin: login,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibedev-si/dsh-vibedev",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "VibeDev 账号与模型:在 DeepSeek Harness 里登录 VibeDev,与 DeepSeek 账号并存,使用 VibeDev 网关的模型(Claude、GPT 等),并生成图片、视频、音乐和播客 · VibeDev account and models for DeepSeek Harness: sign in to VibeDev next to the DeepSeek account and use the VibeDev gateway's models (Claude, GPT and more), plus image, video, music and podcast generation",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",