dsh-grok-provider 0.1.4 → 0.1.6
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 +16 -0
- package/README.en.md +13 -11
- package/README.md +13 -11
- package/SECURITY.md +6 -2
- package/dist/client/client.js +3 -2
- package/dist/host/index.mjs +6 -6
- package/dist/internal/account-dashboard.mjs +27 -1
- package/dist/internal/auth-controller.mjs +196 -45
- package/dist/internal/official-auth-driver.mjs +23 -3
- package/dist/internal/official-cli-auth.mjs +84 -23
- package/dist/internal/official-cli-verifier.mjs +36 -5
- package/dist/internal/provider-runtime.mjs +53 -11
- package/dist/internal/responses-request-compiler.mjs +7 -4
- package/dist/internal/responses-request.mjs +19 -12
- package/docs/01-product-requirements.md +4 -2
- package/docs/03-security-threat-model.md +11 -8
- package/docs/04-harness-contract.md +7 -1
- package/docs/05-test-plan.md +12 -6
- package/docs/06-release-plan.md +11 -10
- package/docs/09-implementation-status.md +35 -10
- package/docs/10-release-checklist.md +32 -4
- package/docs/11-capability-roadmap.md +38 -18
- package/docs/12-upstream-image-input-evidence.md +8 -6
- package/docs/README.md +8 -6
- package/docs/adr/0006-account-dashboard.md +7 -5
- package/docs/adr/0008-image-input-request-compiler.md +1 -1
- package/docs/releases/v0.1.5.md +53 -0
- package/docs/releases/v0.1.6.md +55 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.6 - 2026-08-28
|
|
4
|
+
|
|
5
|
+
- Accept schema-valid provider-neutral user and system history containing private reasoning blocks, omit that reasoning from the Grok wire, and preserve adjacent visible text and ordered image input; malformed or oversized omitted blocks still fail closed before attachment I/O.
|
|
6
|
+
- Keep encrypted reasoning replay restricted to valid same-provider assistant history and retain the one-level tool-result text/image boundary with stable generic-error classification for unsupported tool-result blocks.
|
|
7
|
+
- Give executable resolution, read-only verification, `--version`, `login --help`, and the final official CLI action independent deadlines so Windows cold starts cannot consume the browser-login budget before `login --oauth` launches; process-tree teardown is independently bounded and late caller cancellation cannot be reported as success.
|
|
8
|
+
- Make executable metadata verification and direct-process completion observe cancellation; bound whole-tree teardown separately, and quarantine the authentication driver after cleanup failure until the Host restarts or the subprocess driver is replaced. Login start, logout, and credential refresh share controller-owned single-flight, cancellation, shutdown, and driver-generation guards so stale operations cannot dispatch or report success after replacement, without changing the fixed official path, fixed argv, scrubbed environment, output bounds, no-shell policy, or post-login credential validation.
|
|
9
|
+
- Keep Web/X Search, image generation, new server-tool events, arbitrary URL downloads, API-key mode, and custom endpoints out of this maintenance release; the proposed Search slice moves to `0.1.7`.
|
|
10
|
+
|
|
11
|
+
## 0.1.5 - 2026-08-28
|
|
12
|
+
|
|
13
|
+
- Bind Trusted Publisher runs to the exact stable tag ref and peeled commit, require one non-draft/non-prerelease GitHub Release asset with the exact package filename, and pin the publishing runtime to Node.js `24.19.0`.
|
|
14
|
+
- Project model input modalities into the account dashboard so exact `grok-4.6` visibly advertises image input while text-only models do not.
|
|
15
|
+
- Make provider installation transactional: partial authentication and adapter registrations are rolled back when a later installation step fails, while successful disposal remains idempotent and best-effort.
|
|
16
|
+
- Correct release-state and streaming-deadline documentation without changing authentication, endpoints, image compilation, or Responses wire behavior.
|
|
17
|
+
- Keep Web/X Search, image generation, URL downloads, API-key mode, and new SSE event handling outside this maintenance release.
|
|
18
|
+
|
|
3
19
|
## 0.1.4 - 2026-08-28
|
|
4
20
|
|
|
5
21
|
- Add an asynchronous Responses request compiler for bounded jpeg/png image input from the optional Harness attachment store.
|
package/README.en.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Use an already authenticated official Grok Build account from DeepSeek Harness, with dynamic model discovery, streaming reasoning, tool calls, and an account quota/model capability dashboard.
|
|
6
6
|
|
|
7
|
-
> Unofficial community project; not affiliated with xAI or DeepSeek Harness. The current source version is `0.1.
|
|
7
|
+
> Unofficial community project; not affiliated with xAI or DeepSeek Harness. The current source version is `0.1.6`; published stable versions are identified by the npm Registry `latest` tag. The project no longer publishes prereleases; stable defects are fixed in a new incremented stable version.
|
|
8
8
|
|
|
9
9
|
## What it provides
|
|
10
10
|
|
|
@@ -39,10 +39,10 @@ The official CLI opens a browser on first use. The provider supports only the of
|
|
|
39
39
|
|
|
40
40
|
### 2. Install the provider
|
|
41
41
|
|
|
42
|
-
After `0.1.
|
|
42
|
+
After `0.1.6` is published, install that exact version from npm:
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
|
-
dsh plugin --profile web add dsh-grok-provider@0.1.
|
|
45
|
+
dsh plugin --profile web add dsh-grok-provider@0.1.6
|
|
46
46
|
dsh web
|
|
47
47
|
```
|
|
48
48
|
|
|
@@ -63,7 +63,7 @@ The Web settings page shows:
|
|
|
63
63
|
|
|
64
64
|
- login state and sign-in, cancel, and logout actions;
|
|
65
65
|
- used/remaining quota and the real billing-period reset time;
|
|
66
|
-
- account-visible models, context windows, reasoning efforts, streaming and tool capabilities.
|
|
66
|
+
- account-visible models, context windows, reasoning efforts, and image-input, streaming, and tool capabilities.
|
|
67
67
|
|
|
68
68
|
When protobuf-backed billing includes a complete weekly/monthly period but omits a zero-valued percentage, the page restores “0% used / 100% remaining.” Other incomplete responses remain unknown.
|
|
69
69
|
|
|
@@ -112,8 +112,8 @@ Uninstalling the provider does not remove the official Grok CLI or directly modi
|
|
|
112
112
|
|
|
113
113
|
## Sources and discovery
|
|
114
114
|
|
|
115
|
-
- npm `0.1.
|
|
116
|
-
- GitHub `0.1.
|
|
115
|
+
- npm `0.1.6` page (available after publication): [dsh-grok-provider@0.1.6](https://www.npmjs.com/package/dsh-grok-provider/v/0.1.6)
|
|
116
|
+
- GitHub `0.1.6` release and integrity values (available after publication): [v0.1.6](https://github.com/yoshino-xiao7/dsh-grok-provider/releases/tag/v0.1.6)
|
|
117
117
|
- GitHub community discovery: the repository carries the DeepSeek Harness-recommended `dsh-plugin` and `dsh` topics
|
|
118
118
|
- YukiRyou managed source: [deepseek-yukiryou-plugin-catalog](https://github.com/yoshino-xiao7/deepseek-yukiryou-plugin-catalog), still pinned to the real-device-accepted `dsh-grok-provider@0.1.0` and marking only `darwin-arm64`
|
|
119
119
|
|
|
@@ -121,17 +121,17 @@ Directory inclusion is not an endorsement by xAI or DeepSeek Harness. [Listing P
|
|
|
121
121
|
|
|
122
122
|
## Compatibility and scope
|
|
123
123
|
|
|
124
|
-
| Item | `0.1.
|
|
124
|
+
| Item | `0.1.6` status |
|
|
125
125
|
| --- | --- |
|
|
126
126
|
| DeepSeek Harness | Exact support for `0.1.1-rc.2` |
|
|
127
127
|
| Node.js | `>=24.19.0` |
|
|
128
128
|
| macOS arm64 | Real-network and isolated Harness acceptance completed |
|
|
129
|
-
| Windows x64 | Code and Windows CI supported;
|
|
129
|
+
| Windows x64 | Code, slow-fake, and Windows CI supported; by owner decision, real browser launch is checked against the exact Registry version after publication |
|
|
130
130
|
| macOS x64 / Linux | Unsupported |
|
|
131
131
|
| Grok CLI | No full-version lock; official path, `login --oauth` capability, and production OIDC credential contract are enforced |
|
|
132
132
|
| Models | Every account catalog model whose backend has a strict codec in this release |
|
|
133
133
|
|
|
134
|
-
`0.1.4`
|
|
134
|
+
`0.1.6` preserves the `0.1.4` image boundary: image input is enabled only for exact `grok-4.6`, while `grok-4.5` and every other dynamically discovered model remain text-only. The account dashboard projects capability badges from that same catalog, so only image-capable models show “Image input.” Images must be verified JPEG/PNG projections from the Harness attachment service. Ordinary user content and images nested one level inside a tool result are supported, with `detail:"high"` fixed to the official xAI Responses image example; URLs, filesystem paths, file IDs, and caller-supplied data URLs are rejected. Private reasoning in ordinary user/system history is omitted while adjacent visible text remains ordered, so `subagent-settled` history and same-message reasoning no longer block image requests.
|
|
135
135
|
|
|
136
136
|
Each projected image is limited to 4 MiB, 16,777,216 pixels, and 8192px per side. A request retains at most eight images and 8 MiB of projected image bytes. When a limit is exceeded, the globally oldest images are offloaded to Harness text placeholders; the final JSON remains capped at 16 MiB. Web/X Search, image generation, arbitrary downloads, API-key mode, multiple accounts, enterprise OIDC, ACP, and Headless agent wrapping remain out of scope; see the [capability roadmap](docs/11-capability-roadmap.md).
|
|
137
137
|
|
|
@@ -219,8 +219,10 @@ Read the [contributing guide](CONTRIBUTING.md) before filing an issue or PR. Cha
|
|
|
219
219
|
- [x] Publish the `0.1.1` documentation and release-process correction
|
|
220
220
|
- [x] Publish the `0.1.2` Windows CLI compatibility correction
|
|
221
221
|
- [x] Publish the `0.1.3` cross-provider tool-history compatibility correction
|
|
222
|
-
- [x] `0.1.4`: image input only for exact `grok-4.6`; red/blue user
|
|
223
|
-
- [
|
|
222
|
+
- [x] Publish `0.1.4`: image input only for exact `grok-4.6`; red/blue user/tool-result Proxy gates and final Harness attachment revalidation passed, while `grok-4.5` fails closed as text-only
|
|
223
|
+
- [x] Publish `0.1.5`: maintenance for release binding, dashboard capability badges, and transactional Provider Runtime installation; the unique artifact, dual-platform CI, signatures, and SLSA provenance are verified
|
|
224
|
+
- [ ] Publish `0.1.6`: image-history reasoning compatibility and per-stage official-CLI deadline repair for Windows
|
|
225
|
+
- [ ] Independent `0.1.7` slice: opt-in, default-off Web Search / X Search
|
|
224
226
|
- [ ] A subsequent slice: opt-in image generation (inline results only, committed through Harness attachments)
|
|
225
227
|
- [ ] Complete independent Windows x64 acceptance and publish a later stable fix if needed
|
|
226
228
|
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
让 DeepSeek Harness 使用你已登录的官方 Grok Build 账号:动态模型发现、流式推理、工具调用,以及账号额度与模型能力面板。
|
|
6
6
|
|
|
7
|
-
> 非官方社区项目,与 xAI 或 DeepSeek Harness 官方无隶属关系。当前源码版本为 `0.1.
|
|
7
|
+
> 非官方社区项目,与 xAI 或 DeepSeek Harness 官方无隶属关系。当前源码版本为 `0.1.6`;npm 已发布稳定版本以 Registry 的 `latest` 标签为准。项目不再发行预发行版;正式版缺陷通过新的递增稳定版本修复。
|
|
8
8
|
|
|
9
9
|
## 它解决什么问题
|
|
10
10
|
|
|
@@ -39,10 +39,10 @@ grok models
|
|
|
39
39
|
|
|
40
40
|
### 2. 安装 Provider
|
|
41
41
|
|
|
42
|
-
`0.1.
|
|
42
|
+
`0.1.6` 发布后,从 npm 安装该精确版本:
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
|
-
dsh plugin --profile web add dsh-grok-provider@0.1.
|
|
45
|
+
dsh plugin --profile web add dsh-grok-provider@0.1.6
|
|
46
46
|
dsh web
|
|
47
47
|
```
|
|
48
48
|
|
|
@@ -63,7 +63,7 @@ Web 设置页展示:
|
|
|
63
63
|
|
|
64
64
|
- 当前登录状态及登录、取消、退出操作;
|
|
65
65
|
- 已使用/剩余额度和真实周期重置时间;
|
|
66
|
-
- 当前账号可见模型、上下文窗口、reasoning
|
|
66
|
+
- 当前账号可见模型、上下文窗口、reasoning 档位,以及图片输入、streaming 与 tool capability。
|
|
67
67
|
|
|
68
68
|
当 protobuf-backed billing 返回完整的 weekly/monthly 周期但省略零值百分比时,页面会恢复为“已使用 0% / 剩余 100%”;其他不完整响应保持未知,不伪造额度。
|
|
69
69
|
|
|
@@ -112,8 +112,8 @@ dsh web
|
|
|
112
112
|
|
|
113
113
|
## 项目来源与发现
|
|
114
114
|
|
|
115
|
-
- npm `0.1.
|
|
116
|
-
- GitHub `0.1.
|
|
115
|
+
- npm `0.1.6` 页面(发布后可用):[dsh-grok-provider@0.1.6](https://www.npmjs.com/package/dsh-grok-provider/v/0.1.6)
|
|
116
|
+
- GitHub `0.1.6` 发行版与校验值(发布后可用):[v0.1.6](https://github.com/yoshino-xiao7/dsh-grok-provider/releases/tag/v0.1.6)
|
|
117
117
|
- GitHub 社区发现:仓库已添加 DeepSeek Harness 官方推荐的 `dsh-plugin` 与 `dsh` Topics
|
|
118
118
|
- YukiRyou 受管来源:[deepseek-yukiryou-plugin-catalog](https://github.com/yoshino-xiao7/deepseek-yukiryou-plugin-catalog),当前仍锁定已完成真机验收的 `dsh-grok-provider@0.1.0`,且只标记 `darwin-arm64`
|
|
119
119
|
|
|
@@ -121,17 +121,17 @@ dsh web
|
|
|
121
121
|
|
|
122
122
|
## 兼容性与范围
|
|
123
123
|
|
|
124
|
-
| 项目 | `0.1.
|
|
124
|
+
| 项目 | `0.1.6` 状态 |
|
|
125
125
|
| --- | --- |
|
|
126
126
|
| DeepSeek Harness | 精确支持 `0.1.1-rc.2` |
|
|
127
127
|
| Node.js | `>=24.19.0` |
|
|
128
128
|
| macOS arm64 | 已完成真实网络与隔离 Harness 验收 |
|
|
129
|
-
| Windows x64 |
|
|
129
|
+
| Windows x64 | 代码、slow-fake 与 Windows CI 支持;按仓库所有者决定,发布 Registry 精确版本后验证真实浏览器弹出 |
|
|
130
130
|
| macOS x64 / Linux | 不支持 |
|
|
131
131
|
| Grok CLI | 不锁完整版本;严格校验官方路径、`login --oauth` 能力与生产 OIDC 凭据契约 |
|
|
132
132
|
| 模型 | 当前账号目录中 backend 已被严格 codec 支持的全部模型 |
|
|
133
133
|
|
|
134
|
-
`0.1.4`
|
|
134
|
+
`0.1.6` 沿用 `0.1.4` 的图片边界:只为精确的 `grok-4.6` 开启图片输入;`grok-4.5` 与其他动态发现的模型继续按 text-only 处理。账户面板从同一模型目录投影能力标签,因此只有图片模型显示“图片输入”。图片只能来自 Harness attachment service 的已验证 JPEG/PNG 投影,支持普通用户内容和一层工具结果中的图片,并按 xAI 官方 Responses 图片示例固定使用 `detail:"high"`;不接受 URL、文件路径、file ID 或调用方预制的 data URL。普通 user/system 历史中的私有 reasoning 会被省略并保留相邻可见文本,因此 `subagent-settled` 历史和同消息 reasoning 不再阻断图片请求。
|
|
135
135
|
|
|
136
136
|
每张投影图片最多 4 MiB、16,777,216 像素且任一边不超过 8192px;每次请求最多保留 8 张、投影字节合计最多 8 MiB。超限时按全局最旧优先移除图片并保留 Harness 的文本占位,最终 JSON 仍受 16 MiB 上限约束。Web/X Search、图片生成、任意文件下载、API Key 模式、多账号、企业 OIDC、ACP 与 Headless agent 封装仍不在本版本范围内;后续切片见[能力路线图](docs/11-capability-roadmap.md)。
|
|
137
137
|
|
|
@@ -219,8 +219,10 @@ npm run pack:check
|
|
|
219
219
|
- [x] 发布 `0.1.1` 文档与发布流程修正版
|
|
220
220
|
- [x] 发布 `0.1.2` Windows CLI 兼容性修正版
|
|
221
221
|
- [x] 发布 `0.1.3` 跨 Provider 工具调用历史兼容性修正版
|
|
222
|
-
- [x] `0.1.4`:仅精确 `grok-4.6` 图片输入;user/tool-result 红蓝语义 Proxy 门禁与最终 Harness attachment
|
|
223
|
-
- [
|
|
222
|
+
- [x] 发布 `0.1.4`:仅精确 `grok-4.6` 图片输入;user/tool-result 红蓝语义 Proxy 门禁与最终 Harness attachment 复验通过,`grok-4.5` 失败关闭为 text-only
|
|
223
|
+
- [x] 发布 `0.1.5`:发布链路、账户面板能力标签与 Provider Runtime 安装回滚维护版;唯一制品、双平台 CI、签名与 SLSA provenance 均已验证
|
|
224
|
+
- [ ] 发布 `0.1.6`:图片历史 reasoning 兼容与 Windows 官方 CLI 分阶段 deadline 修复版
|
|
225
|
+
- [ ] `0.1.7` 独立切片:默认关闭、用户分别开启的 Web Search / X Search
|
|
224
226
|
- [ ] 再后续独立切片:默认关闭的图片生成(只收内联结果,提交 Harness attachment)
|
|
225
227
|
- [ ] 完成 Windows x64 独立真机验收并按需发布后续稳定修复版
|
|
226
228
|
|
package/SECURITY.md
CHANGED
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
## 支持范围
|
|
6
6
|
|
|
7
|
-
本安全策略对应源码版本 `0.1.
|
|
7
|
+
本安全策略对应源码版本 `0.1.6`;当前已发布稳定版本以 npm Registry 的 `latest` 标签为准。DeepSeek Harness、Node.js 与操作系统按发布线明确维护;Grok CLI 不使用完整版本字符串作为信任门禁,而是严格校验官方默认路径、命令能力、生产 OIDC 凭据契约和固定服务端协议。macOS arm64 已完成真实验收;Windows x64 有代码、slow-fake 与 CI 覆盖,外部浏览器弹出由仓库所有者在 Registry 精确 `0.1.6` 发布后验证。
|
|
8
|
+
|
|
9
|
+
`0.1.6` 沿用 `0.1.4` 的图片边界:只为精确 `grok-4.6` 提供有界图片输入;`grok-4.5` 与所有其他模型保持 text-only。普通 user/system 历史中的私有 reasoning 会被省略并保留相邻可见 text/image;只有有效的同 Provider assistant 历史可进入加密 reasoning replay,一层 tool-result 仍只接受公开 text/image。图片只能来自 Harness attachment service 的已验证 JPEG/PNG 投影,以 `detail:"high"` 发送,并受单图字节、像素、边长、数量、总字节与最终 JSON 上限约束;URL、路径、file ID 和调用方预制 data URL 都会被拒绝。
|
|
10
|
+
|
|
11
|
+
CLI executable 解析、只读验证、`--version`、`login --help` 与最终 action 各自拥有 deadline;direct-process completion 观察同一取消,whole-tree teardown 另有有界 wait。登录 starting、confirmed logout 与 credential refresh 共享 controller-owned single-flight、shutdown fence 和 driver-generation 门禁;清理失败会隔离当前认证 driver 直到 Host 重启或 subprocess driver replacement,旧代际操作不能迟发 CLI 动作或报告成功。固定路径/argv、最小环境、无 shell、输出上限、取消和登录后凭据重验边界不变;本版不新增认证模式、Search、生成或 endpoint。项目不再发行预发行版,安全或兼容性缺陷使用新的递增稳定版本修复。
|
|
8
12
|
|
|
9
13
|
## 私下报告漏洞
|
|
10
14
|
|
|
@@ -32,4 +36,4 @@
|
|
|
32
36
|
|
|
33
37
|
---
|
|
34
38
|
|
|
35
|
-
English summary: GitHub Private vulnerability reporting is enabled and is the preferred reporting channel. Version `0.1.
|
|
39
|
+
English summary: GitHub Private vulnerability reporting is enabled and is the preferred reporting channel. Version `0.1.6` preserves bounded JPEG/PNG attachment input with `detail:"high"` only for exact `grok-4.6`; `grok-4.5` and all other models remain text-only. It omits schema-valid private reasoning from ordinary user/system history while preserving visible text/images, gives each official-CLI preparation/action stage an independent deadline, and bounds direct-process/tree settlement. Login start, confirmed logout, and credential refresh share controller-owned single-flight, a shutdown fence, and driver-generation guards. Cleanup failure quarantines the current authentication driver until Host restart or subprocess-driver replacement, and stale operations cannot dispatch or report success. Fixed path/argv, no-shell, credential, origin, and URL-rejection boundaries remain unchanged; no Search, generation, new auth mode, or custom endpoint is added. Never send credentials, identity data, prompts, tool arguments, cookies, image bytes, or unreviewed diagnostics.
|
package/dist/client/client.js
CHANGED
|
@@ -21,7 +21,7 @@ window.__ModuleLoader__.load({
|
|
|
21
21
|
usageUnknown: "上游未提供使用比例", quotaUnavailable: "暂时无法读取 Grok Build 额度。",
|
|
22
22
|
modelsTitle: "当前账号可用的模型", modelsDescription: "模型来自 Grok Build 动态目录;Harness 模型选择器会显示这里列出的全部模型。",
|
|
23
23
|
modelsUnavailable: "暂时无法读取模型目录。", noModels: "当前账号没有返回可用模型。",
|
|
24
|
-
context: "上下文", reasoning: "推理档位", defaultEffort: "默认", text: "文本输入", streaming: "流式输出", tools: "工具调用",
|
|
24
|
+
context: "上下文", reasoning: "推理档位", defaultEffort: "默认", text: "文本输入", image: "图片输入", streaming: "流式输出", tools: "工具调用",
|
|
25
25
|
lastUpdated: "数据更新时间",
|
|
26
26
|
},
|
|
27
27
|
en: {
|
|
@@ -38,7 +38,7 @@ window.__ModuleLoader__.load({
|
|
|
38
38
|
usageUnknown: "Usage percentage was not provided", quotaUnavailable: "Grok Build quota is temporarily unavailable.",
|
|
39
39
|
modelsTitle: "Models available to this account", modelsDescription: "Models come from the live Grok Build catalog; every model listed here remains visible in the Harness model selector.",
|
|
40
40
|
modelsUnavailable: "The model catalog is temporarily unavailable.", noModels: "This account returned no available models.",
|
|
41
|
-
context: "Context", reasoning: "Reasoning", defaultEffort: "default", text: "Text input", streaming: "Streaming", tools: "Tool calling",
|
|
41
|
+
context: "Context", reasoning: "Reasoning", defaultEffort: "default", text: "Text input", image: "Image input", streaming: "Streaming", tools: "Tool calling",
|
|
42
42
|
lastUpdated: "Updated",
|
|
43
43
|
},
|
|
44
44
|
}
|
|
@@ -158,6 +158,7 @@ window.__ModuleLoader__.load({
|
|
|
158
158
|
React.createElement("div", { className: "dsh-grok-badges" },
|
|
159
159
|
React.createElement("span", { className: "dsh-grok-badge", "data-accent": true }, `${t("context")} ${formatContext(model.contextWindow)}`),
|
|
160
160
|
model.capabilities?.textInput && React.createElement("span", { className: "dsh-grok-badge" }, t("text")),
|
|
161
|
+
model.capabilities?.imageInput && React.createElement("span", { className: "dsh-grok-badge" }, t("image")),
|
|
161
162
|
model.capabilities?.streaming && React.createElement("span", { className: "dsh-grok-badge" }, t("streaming")),
|
|
162
163
|
model.capabilities?.functionTools && React.createElement("span", { className: "dsh-grok-badge" }, t("tools"))),
|
|
163
164
|
effortText && React.createElement("div", { className: "dsh-grok-efforts" }, `${t("reasoning")}:${effortText}`))
|
package/dist/host/index.mjs
CHANGED
|
@@ -83,19 +83,19 @@ export function apply(ctx) {
|
|
|
83
83
|
homeDir,
|
|
84
84
|
verifyExecutable: verifyOfficialCliExecutable,
|
|
85
85
|
})
|
|
86
|
-
const refresh = async () => {
|
|
87
|
-
const outcome = await officialAuth.refresh()
|
|
88
|
-
if (outcome.kind !== "succeeded") throw new UnsupportedCredentialError()
|
|
89
|
-
}
|
|
90
|
-
refreshOfficialCredential = refresh
|
|
91
86
|
const removeDriver = authController.installDriver(createOfficialAuthDriver({
|
|
92
87
|
officialAuth,
|
|
93
88
|
credentialSource: officialSource,
|
|
94
89
|
}))
|
|
90
|
+
const refresh = async () => {
|
|
91
|
+
const outcome = await authController.refresh()
|
|
92
|
+
if (outcome.kind !== "succeeded") throw new UnsupportedCredentialError()
|
|
93
|
+
}
|
|
94
|
+
refreshOfficialCredential = refresh
|
|
95
95
|
return async () => {
|
|
96
|
-
await authController.shutdown()
|
|
97
96
|
if (refreshOfficialCredential === refresh) refreshOfficialCredential = undefined
|
|
98
97
|
removeDriver()
|
|
98
|
+
await authController.shutdown()
|
|
99
99
|
}
|
|
100
100
|
})
|
|
101
101
|
|
|
@@ -40,10 +40,36 @@ function projectModel(model) {
|
|
|
40
40
|
...(typeof model.description === "string" ? { description: model.description } : {}),
|
|
41
41
|
contextWindow: model.context.contextWindow,
|
|
42
42
|
...(model.reasoning === undefined ? {} : { reasoning: projectReasoning(model.reasoning) }),
|
|
43
|
-
capabilities:
|
|
43
|
+
capabilities: projectCapabilities(model.inputModalities),
|
|
44
44
|
})
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
function projectCapabilities(inputModalities) {
|
|
48
|
+
if (
|
|
49
|
+
!Array.isArray(inputModalities) ||
|
|
50
|
+
Object.getPrototypeOf(inputModalities) !== Array.prototype
|
|
51
|
+
) throw new TypeError("Invalid Grok dashboard input modalities")
|
|
52
|
+
const length = inputModalities.length
|
|
53
|
+
if (
|
|
54
|
+
length === 0 ||
|
|
55
|
+
length > 2 ||
|
|
56
|
+
Reflect.ownKeys(inputModalities).length !== length + 1
|
|
57
|
+
) throw new TypeError("Invalid Grok dashboard input modalities")
|
|
58
|
+
|
|
59
|
+
let textInput = false
|
|
60
|
+
let imageInput = false
|
|
61
|
+
for (let index = 0; index < length; index += 1) {
|
|
62
|
+
const descriptor = Object.getOwnPropertyDescriptor(inputModalities, String(index))
|
|
63
|
+
if (descriptor === undefined || !("value" in descriptor)) {
|
|
64
|
+
throw new TypeError("Invalid Grok dashboard input modalities")
|
|
65
|
+
}
|
|
66
|
+
if (descriptor.value === "text" && !textInput) textInput = true
|
|
67
|
+
else if (descriptor.value === "image" && !imageInput) imageInput = true
|
|
68
|
+
else throw new TypeError("Invalid Grok dashboard input modalities")
|
|
69
|
+
}
|
|
70
|
+
return Object.freeze({ textInput, imageInput, streaming: true, functionTools: true })
|
|
71
|
+
}
|
|
72
|
+
|
|
47
73
|
function projectReasoning(reasoning) {
|
|
48
74
|
if (!isPlainObject(reasoning) || !Array.isArray(reasoning.efforts)) {
|
|
49
75
|
throw new TypeError("Invalid Grok dashboard reasoning capability")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const OUTCOMES = new Set(["succeeded", "cancelled", "failed"])
|
|
1
|
+
const OUTCOMES = new Set(["succeeded", "cancelled", "failed", "cleanup-failed"])
|
|
2
2
|
|
|
3
3
|
export class AuthLoginBusyError extends Error {
|
|
4
4
|
constructor() {
|
|
@@ -25,9 +25,12 @@ export function createAuthController({ registry, randomUUID, now = () => new Dat
|
|
|
25
25
|
|
|
26
26
|
let installed
|
|
27
27
|
let active
|
|
28
|
+
let refreshing
|
|
29
|
+
let shutdownWait
|
|
28
30
|
let state
|
|
29
31
|
let confirmation
|
|
30
|
-
let closing
|
|
32
|
+
let closing
|
|
33
|
+
let quarantined = false
|
|
31
34
|
if (driver !== undefined) install(driver)
|
|
32
35
|
|
|
33
36
|
function install(nextDriver) {
|
|
@@ -36,8 +39,14 @@ export function createAuthController({ registry, randomUUID, now = () => new Dat
|
|
|
36
39
|
}
|
|
37
40
|
const token = Object.freeze({})
|
|
38
41
|
installed = { driver: nextDriver, token }
|
|
42
|
+
quarantined = false
|
|
43
|
+
confirmation = undefined
|
|
39
44
|
return () => {
|
|
40
|
-
if (installed?.token === token)
|
|
45
|
+
if (installed?.token === token) {
|
|
46
|
+
installed = undefined
|
|
47
|
+
quarantined = false
|
|
48
|
+
confirmation = undefined
|
|
49
|
+
}
|
|
41
50
|
}
|
|
42
51
|
}
|
|
43
52
|
|
|
@@ -47,42 +56,51 @@ export function createAuthController({ registry, randomUUID, now = () => new Dat
|
|
|
47
56
|
async status() {
|
|
48
57
|
return Object.freeze({
|
|
49
58
|
...await registry.status(),
|
|
50
|
-
driver: installed !== undefined,
|
|
59
|
+
driver: installed !== undefined && !quarantined,
|
|
51
60
|
...(state === undefined ? {} : { session: Object.freeze({ ...state }) }),
|
|
52
61
|
})
|
|
53
62
|
},
|
|
54
63
|
|
|
55
64
|
async beginLogin() {
|
|
56
|
-
if (active !== undefined || closing
|
|
57
|
-
|
|
65
|
+
if (active !== undefined || refreshing !== undefined || closing || shutdownWait !== undefined) {
|
|
66
|
+
throw new AuthLoginBusyError()
|
|
67
|
+
}
|
|
68
|
+
if (installed === undefined || quarantined) throw new AuthDriverUnavailableError()
|
|
58
69
|
|
|
59
70
|
const sessionId = createId(randomUUID, "Invalid Grok auth session id")
|
|
60
71
|
const abortController = new AbortController()
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
started = await installed.driver.begin({ signal: abortController.signal })
|
|
64
|
-
} catch {
|
|
65
|
-
state = { state: "failed", sessionId }
|
|
66
|
-
registry.invalidate()
|
|
67
|
-
throw new AuthDriverUnavailableError()
|
|
68
|
-
}
|
|
69
|
-
if (
|
|
70
|
-
!isPlainObject(started) ||
|
|
71
|
-
Object.keys(started).length !== 1 ||
|
|
72
|
-
!started.completion ||
|
|
73
|
-
typeof started.completion.then !== "function"
|
|
74
|
-
) throw new TypeError("Invalid Grok auth driver session")
|
|
75
|
-
|
|
72
|
+
const driverRegistration = installed
|
|
76
73
|
const publicState = Object.freeze({ sessionId, state: "running" })
|
|
77
74
|
state = publicState
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
confirmation = undefined
|
|
76
|
+
let resolveStarted
|
|
77
|
+
let rejectStarted
|
|
78
|
+
const startedReady = new Promise((resolve, reject) => {
|
|
79
|
+
resolveStarted = resolve
|
|
80
|
+
rejectStarted = reject
|
|
81
|
+
})
|
|
82
|
+
const settled = startedReady.then(
|
|
83
|
+
(started) => Promise.resolve(started.completion).then(
|
|
84
|
+
(outcome) => normalizeOutcome(outcome),
|
|
85
|
+
() => Object.freeze({ kind: abortController.signal.aborted ? "cancelled" : "failed" }),
|
|
86
|
+
),
|
|
80
87
|
() => Object.freeze({ kind: abortController.signal.aborted ? "cancelled" : "failed" }),
|
|
81
88
|
).then((outcome) => {
|
|
82
|
-
|
|
83
|
-
|
|
89
|
+
const registrationIsCurrent = installed?.token === driverRegistration.token
|
|
90
|
+
const cleanupFailed = outcome.kind === "cleanup-failed"
|
|
91
|
+
if (cleanupFailed && registrationIsCurrent) {
|
|
92
|
+
quarantined = true
|
|
93
|
+
confirmation = undefined
|
|
94
|
+
}
|
|
95
|
+
const publicOutcome = cleanupFailed || !registrationIsCurrent
|
|
96
|
+
? Object.freeze({ kind: "failed" })
|
|
97
|
+
: abortController.signal.aborted
|
|
98
|
+
? Object.freeze({ kind: "cancelled" })
|
|
99
|
+
: outcome
|
|
100
|
+
state = { state: publicOutcome.kind, sessionId }
|
|
101
|
+
if (active === session) active = undefined
|
|
84
102
|
registry.invalidate()
|
|
85
|
-
return
|
|
103
|
+
return publicOutcome
|
|
86
104
|
})
|
|
87
105
|
|
|
88
106
|
const session = Object.freeze({
|
|
@@ -106,6 +124,26 @@ export function createAuthController({ registry, randomUUID, now = () => new Dat
|
|
|
106
124
|
},
|
|
107
125
|
})
|
|
108
126
|
active = session
|
|
127
|
+
|
|
128
|
+
let started
|
|
129
|
+
try {
|
|
130
|
+
started = await driverRegistration.driver.begin({ signal: abortController.signal })
|
|
131
|
+
} catch {
|
|
132
|
+
rejectStarted(new AuthDriverUnavailableError())
|
|
133
|
+
await settled
|
|
134
|
+
throw new AuthDriverUnavailableError()
|
|
135
|
+
}
|
|
136
|
+
if (
|
|
137
|
+
!isPlainObject(started) ||
|
|
138
|
+
Object.keys(started).length !== 1 ||
|
|
139
|
+
!started.completion ||
|
|
140
|
+
typeof started.completion.then !== "function"
|
|
141
|
+
) {
|
|
142
|
+
rejectStarted(new TypeError("Invalid Grok auth driver session"))
|
|
143
|
+
await settled
|
|
144
|
+
throw new TypeError("Invalid Grok auth driver session")
|
|
145
|
+
}
|
|
146
|
+
resolveStarted(started)
|
|
109
147
|
return session
|
|
110
148
|
},
|
|
111
149
|
|
|
@@ -116,25 +154,102 @@ export function createAuthController({ registry, randomUUID, now = () => new Dat
|
|
|
116
154
|
},
|
|
117
155
|
|
|
118
156
|
async shutdown() {
|
|
119
|
-
if (
|
|
120
|
-
active.
|
|
121
|
-
|
|
122
|
-
|
|
157
|
+
if (shutdownWait !== undefined) return shutdownWait
|
|
158
|
+
const operations = [active, refreshing, closing].filter((operation) => operation !== undefined)
|
|
159
|
+
if (operations.length === 0) return false
|
|
160
|
+
let resolveShutdown
|
|
161
|
+
let rejectShutdown
|
|
162
|
+
const wait = new Promise((resolve, reject) => {
|
|
163
|
+
resolveShutdown = resolve
|
|
164
|
+
rejectShutdown = reject
|
|
165
|
+
})
|
|
166
|
+
shutdownWait = wait
|
|
167
|
+
for (const operation of operations) operation.cancel()
|
|
168
|
+
Promise.all(operations.map((operation) => operation.wait())).then(
|
|
169
|
+
() => resolveShutdown(true),
|
|
170
|
+
rejectShutdown,
|
|
171
|
+
)
|
|
172
|
+
try {
|
|
173
|
+
return await wait
|
|
174
|
+
} finally {
|
|
175
|
+
if (shutdownWait === wait) shutdownWait = undefined
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
async refresh({ signal } = {}) {
|
|
180
|
+
if (active !== undefined || refreshing !== undefined || closing || shutdownWait !== undefined) {
|
|
181
|
+
throw new AuthLoginBusyError()
|
|
182
|
+
}
|
|
183
|
+
if (installed === undefined || quarantined || typeof installed.driver.refresh !== "function") {
|
|
184
|
+
throw new AuthDriverUnavailableError()
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const driverRegistration = installed
|
|
188
|
+
const abortController = new AbortController()
|
|
189
|
+
const abortFromCaller = () => abortController.abort(signal.reason)
|
|
190
|
+
if (signal?.aborted) abortFromCaller()
|
|
191
|
+
else signal?.addEventListener("abort", abortFromCaller, { once: true })
|
|
192
|
+
confirmation = undefined
|
|
193
|
+
|
|
194
|
+
let operation
|
|
195
|
+
const settled = Promise.resolve().then(() => {
|
|
196
|
+
if (installed?.token !== driverRegistration.token || quarantined) {
|
|
197
|
+
throw new AuthDriverUnavailableError()
|
|
198
|
+
}
|
|
199
|
+
abortController.signal.throwIfAborted()
|
|
200
|
+
return driverRegistration.driver.refresh({ signal: abortController.signal })
|
|
201
|
+
}).then(
|
|
202
|
+
(outcome) => normalizeOutcome(outcome),
|
|
203
|
+
() => Object.freeze({ kind: abortController.signal.aborted ? "cancelled" : "failed" }),
|
|
204
|
+
).then((outcome) => {
|
|
205
|
+
const registrationIsCurrent = installed?.token === driverRegistration.token
|
|
206
|
+
const cleanupFailed = outcome.kind === "cleanup-failed"
|
|
207
|
+
if (cleanupFailed && registrationIsCurrent) {
|
|
208
|
+
quarantined = true
|
|
209
|
+
confirmation = undefined
|
|
210
|
+
}
|
|
211
|
+
if (!registrationIsCurrent || quarantined || cleanupFailed) {
|
|
212
|
+
return Object.freeze({ kind: "failed" })
|
|
213
|
+
}
|
|
214
|
+
if (abortController.signal.aborted) return Object.freeze({ kind: "cancelled" })
|
|
215
|
+
return outcome
|
|
216
|
+
}).finally(() => {
|
|
217
|
+
signal?.removeEventListener("abort", abortFromCaller)
|
|
218
|
+
if (refreshing === operation) refreshing = undefined
|
|
219
|
+
registry.invalidate()
|
|
220
|
+
})
|
|
221
|
+
operation = Object.freeze({
|
|
222
|
+
driverToken: driverRegistration.token,
|
|
223
|
+
cancel() {
|
|
224
|
+
abortController.abort()
|
|
225
|
+
},
|
|
226
|
+
wait() {
|
|
227
|
+
return settled
|
|
228
|
+
},
|
|
229
|
+
})
|
|
230
|
+
refreshing = operation
|
|
231
|
+
return settled
|
|
123
232
|
},
|
|
124
233
|
|
|
125
234
|
async logout({ signal } = {}) {
|
|
126
|
-
if (closing
|
|
127
|
-
|
|
235
|
+
if (closing !== undefined || refreshing !== undefined || shutdownWait !== undefined) {
|
|
236
|
+
throw new AuthLoginBusyError()
|
|
237
|
+
}
|
|
238
|
+
if (installed === undefined || quarantined || typeof installed.driver.logout !== "function") {
|
|
128
239
|
throw new AuthDriverUnavailableError()
|
|
129
240
|
}
|
|
130
241
|
const currentTime = now()
|
|
131
242
|
if (!(currentTime instanceof Date) || !Number.isFinite(currentTime.getTime())) {
|
|
132
243
|
throw new TypeError("Invalid Grok auth controller clock")
|
|
133
244
|
}
|
|
134
|
-
if (
|
|
245
|
+
if (
|
|
246
|
+
confirmation === undefined ||
|
|
247
|
+
confirmation.driverToken !== installed.token ||
|
|
248
|
+
confirmation.expiresAt <= currentTime.getTime()
|
|
249
|
+
) {
|
|
135
250
|
const confirmationId = createId(randomUUID, "Invalid Grok logout confirmation id")
|
|
136
251
|
const expiresAt = currentTime.getTime() + 30_000
|
|
137
|
-
confirmation = { confirmationId, expiresAt }
|
|
252
|
+
confirmation = { confirmationId, expiresAt, driverToken: installed.token }
|
|
138
253
|
return Object.freeze({
|
|
139
254
|
kind: "confirmation-required",
|
|
140
255
|
confirmationId,
|
|
@@ -143,23 +258,59 @@ export function createAuthController({ registry, randomUUID, now = () => new Dat
|
|
|
143
258
|
}
|
|
144
259
|
|
|
145
260
|
confirmation = undefined
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
261
|
+
const driverRegistration = installed
|
|
262
|
+
const loginOperation = active
|
|
263
|
+
loginOperation?.cancel()
|
|
264
|
+
const abortController = new AbortController()
|
|
265
|
+
const abortFromCaller = () => abortController.abort(signal.reason)
|
|
266
|
+
if (signal?.aborted) abortFromCaller()
|
|
267
|
+
else signal?.addEventListener("abort", abortFromCaller, { once: true })
|
|
268
|
+
|
|
269
|
+
let operation
|
|
270
|
+
const settled = Promise.resolve().then(async () => {
|
|
271
|
+
if (loginOperation !== undefined) {
|
|
272
|
+
await loginOperation.wait()
|
|
273
|
+
}
|
|
274
|
+
if (installed?.token !== driverRegistration.token || quarantined) {
|
|
275
|
+
return Object.freeze({ kind: "failed" })
|
|
151
276
|
}
|
|
152
277
|
let outcome
|
|
153
278
|
try {
|
|
154
|
-
|
|
279
|
+
abortController.signal.throwIfAborted()
|
|
280
|
+
outcome = normalizeOutcome(await driverRegistration.driver.logout({
|
|
281
|
+
signal: abortController.signal,
|
|
282
|
+
}))
|
|
155
283
|
} catch {
|
|
156
|
-
outcome = Object.freeze({ kind: signal
|
|
284
|
+
outcome = Object.freeze({ kind: abortController.signal.aborted ? "cancelled" : "failed" })
|
|
285
|
+
}
|
|
286
|
+
const registrationIsCurrent = installed?.token === driverRegistration.token
|
|
287
|
+
const cleanupFailed = outcome.kind === "cleanup-failed"
|
|
288
|
+
if (cleanupFailed && registrationIsCurrent) {
|
|
289
|
+
quarantined = true
|
|
290
|
+
confirmation = undefined
|
|
157
291
|
}
|
|
292
|
+
const publicOutcome = cleanupFailed || !registrationIsCurrent || quarantined
|
|
293
|
+
? Object.freeze({ kind: "failed" })
|
|
294
|
+
: abortController.signal.aborted
|
|
295
|
+
? Object.freeze({ kind: "cancelled" })
|
|
296
|
+
: outcome
|
|
297
|
+
return publicOutcome
|
|
298
|
+
}).finally(() => {
|
|
299
|
+
signal?.removeEventListener("abort", abortFromCaller)
|
|
300
|
+
if (closing === operation) closing = undefined
|
|
158
301
|
registry.invalidate()
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
302
|
+
})
|
|
303
|
+
operation = Object.freeze({
|
|
304
|
+
driverToken: driverRegistration.token,
|
|
305
|
+
cancel() {
|
|
306
|
+
abortController.abort()
|
|
307
|
+
},
|
|
308
|
+
wait() {
|
|
309
|
+
return settled
|
|
310
|
+
},
|
|
311
|
+
})
|
|
312
|
+
closing = operation
|
|
313
|
+
return settled
|
|
163
314
|
},
|
|
164
315
|
})
|
|
165
316
|
}
|