dsh-tokenlens 1.3.7 → 1.3.9
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/CHANGELOG.md +6 -4
- package/PROTOCOL.md +7 -4
- package/README.en.md +3 -1
- package/README.md +9 -6
- package/client.js +100 -7
- package/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,18 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
**版本记录约定**:同一发布日只保留一个章节,标题取当日**最后一个**已发布版本号;当日多次发布的改动合并到该章节,并在标题下注明当日实际发布过的版本号(npm 上仍可安装这些独立版本)。若当日已有发布,后续小改动不再单独增加版本号,而是并入下一篇日志。这样日志按天规整、不出现同日多个小版本。
|
|
6
6
|
|
|
7
|
-
## 1.3.
|
|
7
|
+
## 1.3.9 — 2026-11-02
|
|
8
8
|
|
|
9
|
-
(当日已发布版本:v1.3.5 / v1.3.6 / v1.3.7)
|
|
9
|
+
(当日已发布版本:v1.3.5 / v1.3.6 / v1.3.7 / v1.3.8 / v1.3.9)
|
|
10
10
|
|
|
11
11
|
- feat(ui): 供应商管理页可为 **Open Design** 填写「工作区 ID」并持久化到插件配置——此前 workspaceId 只能在 profile patch 或 `OPENDESIGN_WORKSPACE_ID` 里设置,用推理 Key 配好供应商后额度卡片仍报错却无处可改
|
|
12
|
+
- feat(ui): **Control Key 也能在弹窗里直接填**(v1.3.9)——Open Design 卡片新增密码型输入框,粘贴保存即写入插件自己的 store;输入框是只写的(Host 只回 `hasKey` / `keySource`,secret 永不下发浏览器),保存成功后自动清空并改用「已保存;要替换请重新粘贴」占位提示。小白用户不再需要手改 `.credentials.yaml`
|
|
13
|
+
- feat(ui): 「工作区 ID」输入框支持**直接粘贴整条控制台地址**——`https://open-design.ai/cloud/dashboard?workspaceId=ws_x&tab=billing` 会自动提取出 `ws_x`,避免用户复制整条 URL 后被校验拒绝
|
|
12
14
|
- feat(ui): Open Design 额度失败时的引导——详情弹窗的错误条说明「额度查询需要工作区 ID 与 Control Key(不是推理 Key)」,并提供「去配置」按钮直接跳到供应商管理页
|
|
13
15
|
- feat(host): Host 侧错误文案更具体(区分缺 workspaceId / 缺 Control Key,并指向管理页);`providerUpdate` 接受 `workspaceId`,校验为 1–120 位标识符字符(字母、数字、`. _ : @ -`),路径穿越/空格/超长一律拒绝
|
|
14
16
|
- feat(wire): `providers[]` 仅对声明了 workspace 的适配器(Open Design)下发 `workspaceId` 字段;其余供应商不下发,客户端据此决定是否渲染输入框(旧 Host 无此字段 ⇒ 不渲染,自然降级)
|
|
15
17
|
- feat(ui): Open Design 缺配置时 chip 不再按 `missing-key` 静默隐藏,而是显示「待配置」并可点进详情看引导——否则用户既看不到提示也没有入口
|
|
16
18
|
- fix(tool): Agent 工具 `tokenlens_usage` 的 `updateCheck` 动作此前虽在枚举里却会落到用量读取分支,返回当前供应商的用量或错误;现在正确执行更新检查并返回 `UpdateInfo`
|
|
17
|
-
- docs: 中英 README 补充 **workspace ID 与 Control Key
|
|
18
|
-
- 测试:280 →
|
|
19
|
+
- docs: 中英 README 补充 **workspace ID 与 Control Key 到底在哪里取**——控制台的「套餐与余额」页没有独立的 ID 字段,workspace ID 取自地址栏的 `?workspaceId=`(成员邀请预览页也会显示「工作空间 ID」),而 `~/.amr/config.json` 只存 `controlKey` / `runtimeKey` / `apiUrl` / `linkUrl`、不含 workspace ID;Control Key 是桌面端 / `vela login` 写入该文件的 `profiles.prod.controlKey`,用推理 Key 冒充会得到 401
|
|
20
|
+
- 测试:280 → 289(workspace ID 校验/持久化/清空与 URL 提取、管理页字段渲染、Control Key 只写保存与不回声、缺 workspaceId 的 Host 报错与引导按钮、待配置 chip、工具 updateCheck 分发)
|
|
19
21
|
|
|
20
22
|
## 1.3.4 — 2026-11-01
|
|
21
23
|
|
package/PROTOCOL.md
CHANGED
|
@@ -116,10 +116,13 @@ row at the end of the capacity card and appends the amount to the chip tooltip.
|
|
|
116
116
|
**workspace id** (the console URL's `?workspaceId=` value) and a **Control Key**
|
|
117
117
|
(the `profiles.prod.controlKey` the desktop app / `vela login` writes to
|
|
118
118
|
`~/.amr/config.json`). The inference key from the Cloud API Keys page answers
|
|
119
|
-
HTTP 401 on the billing endpoints. Missing either value is a `missing-key` error
|
|
120
|
-
|
|
121
|
-
`providers.json`,
|
|
122
|
-
`
|
|
119
|
+
HTTP 401 on the billing endpoints. Missing either value is a `missing-key` error.
|
|
120
|
+
Since v1.3.9 both may be entered on the management page: `workspaceId` is
|
|
121
|
+
persisted in `providers.json`, and the Control Key is written through the same
|
|
122
|
+
`apiKey` override field the store already supported (write-only — the Host never
|
|
123
|
+
returns it, only `hasKey`/`keySource`). The environment/credential sources
|
|
124
|
+
(`OPENDESIGN_CONTROL_KEY`, `VELA_CONTROL_KEY`, `OPENDESIGN_WORKSPACE_ID`) still
|
|
125
|
+
work and the page override wins.
|
|
123
126
|
|
|
124
127
|
Rules:
|
|
125
128
|
|
package/README.en.md
CHANGED
|
@@ -111,9 +111,11 @@ Open Design (OpenDesign) is the exception: its read-only billing API needs a **w
|
|
|
111
111
|
|
|
112
112
|
**Where the two values come from.** Neither is on the Cloud API Keys page.
|
|
113
113
|
|
|
114
|
-
- *Workspace ID* — the console
|
|
114
|
+
- *Workspace ID* — the console has **no dedicated field for it**; it appears in the address bar: open 套餐与余额 or 成员 and read `open-design.ai/cloud/dashboard?workspaceId=<the id>`. A member-invite preview page also lists it as 「工作空间 ID」. `~/.amr/config.json` stores only the keys/URLs, not the workspace id.
|
|
115
115
|
- *Control Key* — a session credential that the Open Design desktop app / `vela` CLI writes to `~/.amr/config.json` when you sign in (`profiles.prod.controlKey`). Sign in once with the desktop app, or run `vela login` and approve the browser prompt, then copy that value (or point the plugin at it). Pasting the inference key instead makes the quota call answer HTTP 401.
|
|
116
116
|
|
|
117
|
+
Both values can be entered right in the plugin (simplest for non-technical users): usage dialog → 管理 → 供应商配置 → expand **Open Design**. The workspace-ID field also accepts a whole pasted console URL (`...?workspaceId=xxx` is extracted), and the Control Key field is a write-only password input — saving it stores the secret in the plugin's own store, and the Host never echoes it back (only `hasKey`). Press 测试连接 afterwards. Alternatively put `OPENDESIGN_CONTROL_KEY` in `$DSH_HOME/.credentials.yaml` `refs:` (or the environment) and the workspace id in `OPENDESIGN_WORKSPACE_ID`.
|
|
118
|
+
|
|
117
119
|
See the [Chinese README](README.md) for the full configuration reference, preview and provider tables.
|
|
118
120
|
|
|
119
121
|
## Supported providers and harnesses
|
package/README.md
CHANGED
|
@@ -201,11 +201,11 @@ dsh plugin --profile <profile> remove dsh-tokenlens
|
|
|
201
201
|
|
|
202
202
|
**1. workspace ID**
|
|
203
203
|
|
|
204
|
-
|
|
204
|
+
这个控制台**没有单独一栏显示工作区 ID**(截图里的「套餐与余额」页只有额度、余额、充值),它只出现在两处:
|
|
205
205
|
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
-
|
|
206
|
+
- **地址栏**:在 Cloud 控制台里点进「套餐与余额」或「成员」等任意页面,地址会变成 `open-design.ai/cloud/dashboard?workspaceId=<这一串>`——`workspaceId=` 后面、`&` 之前的整段就是。这是最可靠的取法。
|
|
207
|
+
- **成员邀请预览页**:生成一条成员邀请并打开它,确认页会把「工作空间 ID」作为一条信息列出来。
|
|
208
|
+
- 装了桌面端的话,本机 `~/.amr/config.json` 只保存 `controlKey` / `runtimeKey` / `apiUrl` / `linkUrl`,**不含** workspace ID,别在这里找。
|
|
209
209
|
|
|
210
210
|
**2. Control Key**
|
|
211
211
|
|
|
@@ -231,8 +231,11 @@ Control Key 是 **Open Design 桌面端/CLI 登录时写入本机的会话凭据
|
|
|
231
231
|
|
|
232
232
|
拿到后两种放法:
|
|
233
233
|
|
|
234
|
-
-
|
|
235
|
-
-
|
|
234
|
+
- **最简单(推荐给不折腾的用户)**:全部在插件里填——用量弹窗 →「管理」→「供应商配置」→ 展开 **Open Design**:
|
|
235
|
+
- 「工作区 ID」:直接粘贴控制台地址整条也行(`...?workspaceId=xxx` 会自动提取出 ID);
|
|
236
|
+
- 「Control Key」:粘贴后保存,输入框是密码型、保存后不再显示(Host 只回 `hasKey`,secret 永不下发浏览器)。
|
|
237
|
+
保存后点「测试连接」,出现 5 小时 / 7 天百分比与钱包余额即成功。
|
|
238
|
+
- **或写配置文件**:Control Key 写进 `$DSH_HOME/.credentials.yaml` 的 `refs:` 下 `OPENDESIGN_CONTROL_KEY: <control key>`(或环境变量 `OPENDESIGN_CONTROL_KEY` / `VELA_CONTROL_KEY`);workspace ID 用 `OPENDESIGN_WORKSPACE_ID` 环境变量或 profile 配置。
|
|
236
239
|
|
|
237
240
|
> 这也解释了最容易踩的坑:**用推理 Key 填 Control Key** 会让额度查询返回 401。插件已经区分这两种凭据,缺哪一项都会指名道姓地提示。
|
|
238
241
|
|
package/client.js
CHANGED
|
@@ -331,8 +331,12 @@ window.__ModuleLoader__.load({
|
|
|
331
331
|
providerConsoleURLPlaceholder: '留空则使用内置默认地址',
|
|
332
332
|
providerWorkspaceId: '工作区 ID(Workspace ID)',
|
|
333
333
|
providerWorkspaceIdHint: 'Open Design Cloud 工作区标识;额度查询需要',
|
|
334
|
-
providerWorkspaceIdPlaceholder: '
|
|
334
|
+
providerWorkspaceIdPlaceholder: '粘贴工作区 ID,或直接粘贴控制台地址(自动提取)',
|
|
335
335
|
providerWorkspaceIdTooLong: '工作区 ID 最多 120 个字符',
|
|
336
|
+
providerControlKey: 'Control Key',
|
|
337
|
+
providerControlKeyHint: '本机 Open Design 登录后写在 ~/.amr/config.json 的 profiles.prod.controlKey;不是 Cloud API Keys 页的推理 Key',
|
|
338
|
+
providerControlKeyPlaceholder: '粘贴 Control Key(保存后不再显示)',
|
|
339
|
+
providerControlKeySaved: '已保存;要替换请重新粘贴',
|
|
336
340
|
configureProvider: '去配置',
|
|
337
341
|
errorOpenDesignConfig: 'Open Design 额度查询需要工作区 ID 与 Control Key(不是推理 Key)',
|
|
338
342
|
providerSave: '保存',
|
|
@@ -582,8 +586,12 @@ window.__ModuleLoader__.load({
|
|
|
582
586
|
providerConsoleURL: 'Console URL',
|
|
583
587
|
providerWorkspaceId: 'Workspace ID',
|
|
584
588
|
providerWorkspaceIdHint: 'The Open Design Cloud workspace identifier the quota query is scoped to',
|
|
585
|
-
providerWorkspaceIdPlaceholder: '
|
|
589
|
+
providerWorkspaceIdPlaceholder: 'Paste the workspace ID, or the console URL (it is extracted)',
|
|
586
590
|
providerWorkspaceIdTooLong: 'The workspace ID may be at most 120 characters',
|
|
591
|
+
providerControlKey: 'Control Key',
|
|
592
|
+
providerControlKeyHint: 'Written to ~/.amr/config.json as profiles.prod.controlKey after signing in to Open Design; not the inference key from the Cloud API Keys page',
|
|
593
|
+
providerControlKeyPlaceholder: 'Paste the Control Key (never shown again after saving)',
|
|
594
|
+
providerControlKeySaved: 'Saved; paste again to replace it',
|
|
587
595
|
configureProvider: 'Configure',
|
|
588
596
|
errorOpenDesignConfig: 'Open Design quota needs a workspace ID and a Control Key (not an inference key)',
|
|
589
597
|
providerConsoleURLPlaceholder: 'Leave empty for the built-in default',
|
|
@@ -2329,6 +2337,10 @@ window.__ModuleLoader__.load({
|
|
|
2329
2337
|
const clearedDraft = hasField('consoleUrl')
|
|
2330
2338
|
const resetNames = !clearedDraft && (hasField('shortLabel') || hasField('label'))
|
|
2331
2339
|
const resetWorkspace = hasField('workspaceId')
|
|
2340
|
+
// v1.3.9: a saved secret is never echoed back, so the typed value is
|
|
2341
|
+
// dropped after the Host confirms it (the placeholder then reports
|
|
2342
|
+
// "already saved" through the answered hasKey flag).
|
|
2343
|
+
const resetSecret = hasField('apiKey')
|
|
2332
2344
|
const rowTest = { ...current.rowTest }
|
|
2333
2345
|
delete rowTest[providerId]
|
|
2334
2346
|
// Adopt the answered list; a Host that omits it must not blank the page.
|
|
@@ -2336,7 +2348,7 @@ window.__ModuleLoader__.load({
|
|
|
2336
2348
|
let nextDrafts = current.drafts
|
|
2337
2349
|
if (clearedDraft) {
|
|
2338
2350
|
nextDrafts = mapWithout(current.drafts, providerId)
|
|
2339
|
-
} else if (resetNames || resetWorkspace) {
|
|
2351
|
+
} else if (resetNames || resetWorkspace || resetSecret) {
|
|
2340
2352
|
const draft = current.drafts[providerId]
|
|
2341
2353
|
const answered = adoptsList
|
|
2342
2354
|
? parsed.providers.find((entry) => entry.providerId === providerId)
|
|
@@ -2355,6 +2367,7 @@ window.__ModuleLoader__.load({
|
|
|
2355
2367
|
? answered.workspaceId
|
|
2356
2368
|
: draft.workspaceId
|
|
2357
2369
|
}
|
|
2370
|
+
if (resetSecret) next.apiKey = ''
|
|
2358
2371
|
nextDrafts = mapWith(current.drafts, providerId, next)
|
|
2359
2372
|
}
|
|
2360
2373
|
}
|
|
@@ -2843,6 +2856,9 @@ window.__ModuleLoader__.load({
|
|
|
2843
2856
|
shortLabel: provider.shortLabel,
|
|
2844
2857
|
label: provider.label,
|
|
2845
2858
|
workspaceId: typeof provider.workspaceId === 'string' ? provider.workspaceId : '',
|
|
2859
|
+
// A secret is never seeded from the wire: it starts empty and only
|
|
2860
|
+
// carries what the user just typed (v1.3.9).
|
|
2861
|
+
apiKey: '',
|
|
2846
2862
|
}
|
|
2847
2863
|
}
|
|
2848
2864
|
|
|
@@ -2869,6 +2885,10 @@ window.__ModuleLoader__.load({
|
|
|
2869
2885
|
const wireWorkspaceId = typeof provider.workspaceId === 'string' ? provider.workspaceId : ''
|
|
2870
2886
|
const workspaceId = typeof source.workspaceId === 'string' ? source.workspaceId.trim() : wireWorkspaceId
|
|
2871
2887
|
if (workspaceId !== wireWorkspaceId) update.workspaceId = workspaceId
|
|
2888
|
+
// v1.3.9: the Control Key is write-only. A typed value is sent once; an
|
|
2889
|
+
// empty draft means "leave the stored secret alone", never "clear it".
|
|
2890
|
+
const apiKey = typeof source.apiKey === 'string' ? source.apiKey.trim() : ''
|
|
2891
|
+
if (apiKey !== '') update.apiKey = apiKey
|
|
2872
2892
|
}
|
|
2873
2893
|
return update
|
|
2874
2894
|
}
|
|
@@ -2893,21 +2913,43 @@ window.__ModuleLoader__.load({
|
|
|
2893
2913
|
shortLabel: provider.shortLabel,
|
|
2894
2914
|
label: provider.label,
|
|
2895
2915
|
workspaceId: typeof provider.workspaceId === 'string' ? provider.workspaceId : '',
|
|
2916
|
+
apiKey: '',
|
|
2896
2917
|
}),
|
|
2897
2918
|
})
|
|
2898
2919
|
}
|
|
2899
2920
|
|
|
2900
|
-
/**
|
|
2921
|
+
/**
|
|
2922
|
+
* Accept a pasted console URL and pull the workspace id out of it (v1.3.9):
|
|
2923
|
+
* beginners copy the whole address bar, so `…?workspaceId=abc&x=1` must
|
|
2924
|
+
* reduce to `abc` instead of being rejected for containing slashes.
|
|
2925
|
+
*/
|
|
2926
|
+
function extractWorkspaceId(raw) {
|
|
2927
|
+
const text = typeof raw === 'string' ? raw.trim() : ''
|
|
2928
|
+
if (text === '') return ''
|
|
2929
|
+
const match = /[?&]workspaceId=([^&#\s]+)/i.exec(text)
|
|
2930
|
+
if (match === null) return text
|
|
2931
|
+
try {
|
|
2932
|
+
return decodeURIComponent(match[1])
|
|
2933
|
+
} catch {
|
|
2934
|
+
return match[1]
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
/** Record one typed editor value (names, the display-only console URL, the workspace id, a typed secret). */
|
|
2901
2939
|
function updateProviderDraft(provider, field, value) {
|
|
2902
|
-
if (field !== 'consoleUrl' && field !== 'shortLabel' && field !== 'label' && field !== 'workspaceId')
|
|
2903
|
-
|
|
2940
|
+
if (field !== 'consoleUrl' && field !== 'shortLabel' && field !== 'label' && field !== 'workspaceId' && field !== 'apiKey') {
|
|
2941
|
+
return
|
|
2942
|
+
}
|
|
2943
|
+
if ((field === 'workspaceId' || field === 'apiKey') && provider.supportsWorkspaceId !== true) return
|
|
2904
2944
|
if (provider === null || provider === undefined) return
|
|
2905
2945
|
const current = store.get().management
|
|
2906
2946
|
const draft = providerDraftOf(provider, current)
|
|
2907
2947
|
patchManagement({
|
|
2908
2948
|
drafts: mapWith(current.drafts, provider.providerId, {
|
|
2909
2949
|
...draft,
|
|
2910
|
-
|
|
2950
|
+
// v1.3.9: a pasted console URL is reduced to its workspace id as the
|
|
2951
|
+
// user types, so the save never posts a URL or gets rejected.
|
|
2952
|
+
[field]: field === 'workspaceId' ? extractWorkspaceId(value) : typeof value === 'string' ? value : '',
|
|
2911
2953
|
}),
|
|
2912
2954
|
})
|
|
2913
2955
|
}
|
|
@@ -5943,6 +5985,43 @@ window.__ModuleLoader__.load({
|
|
|
5943
5985
|
return (typeof value === 'string' ? value.trim() : '').length
|
|
5944
5986
|
}
|
|
5945
5987
|
|
|
5988
|
+
/**
|
|
5989
|
+
* One write-only credential field (v1.3.9).
|
|
5990
|
+
*
|
|
5991
|
+
* The Host never sends a stored secret back, so the input always starts empty
|
|
5992
|
+
* and its placeholder reports whether a value is already saved. Typing a new
|
|
5993
|
+
* value stages `{apiKey}` on the draft; the save button then sends it once,
|
|
5994
|
+
* and a successful save clears it again (the Host keeps the secret).
|
|
5995
|
+
*/
|
|
5996
|
+
function buildProviderSecretField(provider, management, t, options) {
|
|
5997
|
+
const providerId = provider.providerId
|
|
5998
|
+
const draft = providerDraftOf(provider, management)
|
|
5999
|
+
const disabled = management.busy[providerId] !== undefined
|
|
6000
|
+
const value = typeof draft[options.field] === 'string' ? draft[options.field] : ''
|
|
6001
|
+
return h(
|
|
6002
|
+
'div',
|
|
6003
|
+
{ key: options.key, className: 'dsh-tokenlens-field' },
|
|
6004
|
+
h(
|
|
6005
|
+
'div',
|
|
6006
|
+
{ className: 'dsh-tokenlens-field-head' },
|
|
6007
|
+
h('span', { className: 'dsh-tokenlens-field-label' }, options.label),
|
|
6008
|
+
h('span', { className: 'dsh-tokenlens-field-hint' }, options.hint),
|
|
6009
|
+
),
|
|
6010
|
+
h('input', {
|
|
6011
|
+
type: 'password',
|
|
6012
|
+
className: `dsh-tokenlens-input ${options.className}`,
|
|
6013
|
+
[options.dataAttr]: providerId,
|
|
6014
|
+
value,
|
|
6015
|
+
placeholder: options.placeholder,
|
|
6016
|
+
disabled,
|
|
6017
|
+
autoComplete: 'off',
|
|
6018
|
+
spellCheck: false,
|
|
6019
|
+
'aria-label': options.label,
|
|
6020
|
+
onChange: (event) => updateProviderDraft(provider, options.field, event?.target?.value),
|
|
6021
|
+
}),
|
|
6022
|
+
)
|
|
6023
|
+
}
|
|
6024
|
+
|
|
5946
6025
|
/**
|
|
5947
6026
|
* One editable name field (v0.12.0): a text input capped at the Host limit,
|
|
5948
6027
|
* with a permanent limit hint and an inline error while the draft is longer
|
|
@@ -6072,6 +6151,20 @@ window.__ModuleLoader__.load({
|
|
|
6072
6151
|
: buildDshUnmanagedRow(provider, t)
|
|
6073
6152
|
const fields = [
|
|
6074
6153
|
sourceRow,
|
|
6154
|
+
// v1.3.9: Open Design's Control Key cannot come from the DSH model
|
|
6155
|
+
// settings (that route holds the inference key, which the billing API
|
|
6156
|
+
// rejects), so this one provider gets a write-only credential input.
|
|
6157
|
+
provider.supportsWorkspaceId === true
|
|
6158
|
+
? buildProviderSecretField(provider, management, t, {
|
|
6159
|
+
key: 'apiKey',
|
|
6160
|
+
className: 'dsh-tokenlens-controlkey-input',
|
|
6161
|
+
dataAttr: 'data-provider-controlkey',
|
|
6162
|
+
field: 'apiKey',
|
|
6163
|
+
placeholder: provider.hasKey === true ? t('providerControlKeySaved') : t('providerControlKeyPlaceholder'),
|
|
6164
|
+
label: t('providerControlKey'),
|
|
6165
|
+
hint: t('providerControlKeyHint'),
|
|
6166
|
+
})
|
|
6167
|
+
: null,
|
|
6075
6168
|
buildProviderNameField(provider, management, t, {
|
|
6076
6169
|
key: 'shortLabel',
|
|
6077
6170
|
className: 'dsh-tokenlens-shortlabel-input',
|
package/index.js
CHANGED
|
@@ -76,7 +76,7 @@ export const inject = []
|
|
|
76
76
|
export const PLUGIN_NAME = 'dsh-tokenlens'
|
|
77
77
|
|
|
78
78
|
/** Plugin version; mirrors package.json (kept here so diagnostics need no fs read). */
|
|
79
|
-
export const PLUGIN_VERSION = '1.3.
|
|
79
|
+
export const PLUGIN_VERSION = '1.3.9'
|
|
80
80
|
|
|
81
81
|
/** Documented defaults; every field of the raw row config is optional. */
|
|
82
82
|
const DEFAULTS = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-tokenlens",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "dsh-TokenLens (词元透镜) — provider-aware plan capacity plus local token statistics (today/7d/30d/all-time, by harness/provider/model/workspace) for DSH.",
|
|
6
6
|
"license": "MIT",
|