dsh-any-deepseek 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -199,15 +199,21 @@ window.__ModuleLoader__.load({
199
199
  * Run one settings write and surface a refusal.
200
200
  *
201
201
  * A write can be refused for reasons the user can act on (read-only deployment, the settings
202
- * service not composed, a revision conflict) and for reasons they cannot (transport). Both are
203
- * facts. Silently discarding them is what makes a panel look alive while nothing is saved —
204
- * which is exactly how this plugin's writes were failing without a trace.
202
+ * service not composed, a revision conflict, a field the host considers non-volatile) and for
203
+ * reasons they cannot (transport). Both are facts, and silently discarding them is what makes
204
+ * a panel look alive while nothing is saved.
205
+ *
206
+ * 🔴 **The refusal message must NOT name causes we have not checked.** This used to read
207
+ * "只读部署,或设置服务未启用" — and the one time it actually fired, the real cause was
208
+ * neither: `current` was missing `.volatile()` in the schema, three layers away. Naming two
209
+ * unchecked causes sent the investigation in the wrong direction. A refusal is a refusal:
210
+ * say that, and let the host's own error speak for itself.
205
211
  * @param run - the write.
206
212
  * @param context - what the user was trying to do.
207
213
  */
208
214
  const write = (run, context = "保存失败") => {
209
215
  run().then((accepted) => {
210
- if (!accepted) setError(`${context}:宿主拒绝了这个修改(只读部署,或设置服务未启用)`);
216
+ if (!accepted) setError(`${context}:宿主拒绝了这个修改`);
211
217
  else setError(null);
212
218
  }, (cause) => {
213
219
  setError(`${context}:${cause instanceof Error ? cause.message : String(cause)}`);
package/lib/config.d.ts CHANGED
@@ -101,20 +101,39 @@ export interface PluginConfigInput {
101
101
  /**
102
102
  * Plugin configuration as the plugin reads it (schema OUTPUT).
103
103
  *
104
- * `channels` parses to a reference wrapper — created even when the user set nothing, so the
105
- * field is always present. Read it through {@link unwrapChannels}.
104
+ * `channels` and `current` parse to reference wrappers — created even when the user set
105
+ * nothing, so the fields are always present. Read them through {@link unwrap}.
106
+ *
107
+ * 🔴 **`current` is volatile too, and that is not cosmetic** — see the `.volatile()` note on
108
+ * `PluginConfigSchema`.
106
109
  */
107
110
  export interface PluginConfigValue {
108
111
  channels: Volatile<Record<string, ChannelConfigValue>>;
109
- current: string;
112
+ current: Volatile<string>;
110
113
  usage: UsageConfigValue;
111
114
  fallback: string[];
112
115
  }
113
116
  /**
114
117
  * Plugin configuration schema.
115
118
  *
116
- * `channels` is volatile: the panel can only render and edit a field the harness sees as a
117
- * reference, and the runtime value arrives as a wrapper whose `.get()` returns the document.
119
+ * ## Why EVERY panel-writable field must be `.volatile()`
120
+ *
121
+ * The host projects an entry's Config into a settings form only for volatile fields, and its
122
+ * write path **refuses** any path outside one:
123
+ *
124
+ * ```js
125
+ * for (const path of paths) if (path.length && !isVolatilePath(schema, path))
126
+ * throw new Error(`Config field "${path.join('.')}" is not volatile`)
127
+ * ```
128
+ *
129
+ * `channels` was volatile from the start, so channel edits worked. `current` was **not**, so
130
+ * every click on "设为当前" was rejected by the host — and the host's client-side form turns
131
+ * that refusal into a bare `false`, which the panel reported as "read-only deployment or the
132
+ * settings service is not enabled". **That message named two causes it had never checked.**
133
+ * The real cause was a schema annotation three layers away.
134
+ *
135
+ * ⇒ Rule: a field the panel writes must be `.volatile()`, and the runtime side must
136
+ * `unwrap()` it. Adding a writable field without both halves reproduces this bug silently.
118
137
  */
119
138
  export declare const PluginConfigSchema: SchemaOf<PluginConfigInput, PluginConfigValue>;
120
139
  /**
package/lib/index.js CHANGED
@@ -72,12 +72,28 @@ const UsageConfigSchema = z.object({
72
72
  /**
73
73
  * Plugin configuration schema.
74
74
  *
75
- * `channels` is volatile: the panel can only render and edit a field the harness sees as a
76
- * reference, and the runtime value arrives as a wrapper whose `.get()` returns the document.
75
+ * ## Why EVERY panel-writable field must be `.volatile()`
76
+ *
77
+ * The host projects an entry's Config into a settings form only for volatile fields, and its
78
+ * write path **refuses** any path outside one:
79
+ *
80
+ * ```js
81
+ * for (const path of paths) if (path.length && !isVolatilePath(schema, path))
82
+ * throw new Error(`Config field "${path.join('.')}" is not volatile`)
83
+ * ```
84
+ *
85
+ * `channels` was volatile from the start, so channel edits worked. `current` was **not**, so
86
+ * every click on "设为当前" was rejected by the host — and the host's client-side form turns
87
+ * that refusal into a bare `false`, which the panel reported as "read-only deployment or the
88
+ * settings service is not enabled". **That message named two causes it had never checked.**
89
+ * The real cause was a schema annotation three layers away.
90
+ *
91
+ * ⇒ Rule: a field the panel writes must be `.volatile()`, and the runtime side must
92
+ * `unwrap()` it. Adding a writable field without both halves reproduces this bug silently.
77
93
  */
78
94
  const PluginConfigSchema = z.object({
79
95
  channels: z.dict(ChannelConfigSchema).default({}).volatile().description("渠道表:每行一个中转渠道"),
80
- current: z.string().default("").description("当前使用的渠道 id"),
96
+ current: z.string().default("").volatile().description("当前使用的渠道 id"),
81
97
  usage: UsageConfigSchema.default({ enabled: true }),
82
98
  fallback: z.array(z.string()).default([]).description("预留:故障转移顺序(v1 未实现)")
83
99
  });
@@ -1592,8 +1608,8 @@ function apply(ctx, config) {
1592
1608
  return enabledChannels(resolveChannels(map), map);
1593
1609
  };
1594
1610
  const current = () => {
1595
- const live = ctx.fiber?.config ?? config;
1596
- return typeof live.current === "string" ? live.current : void 0;
1611
+ const value = unwrap((ctx.fiber?.config ?? config).current);
1612
+ return typeof value === "string" ? value : void 0;
1597
1613
  };
1598
1614
  const adapter = new AnyDeepSeekAdapter({
1599
1615
  channels,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-any-deepseek",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "One DeepSeek V4.1 Flash provider for DeepSeek Harness: paste a key per channel, switch channels manually, token usage booked per real upstream.",
5
5
  "license": "MIT",
6
6
  "repository": {