dsh-grok-provider 0.1.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/CONTRIBUTING.md +51 -0
  3. package/LICENSE +21 -0
  4. package/README.en.md +200 -0
  5. package/README.md +200 -0
  6. package/SECURITY.md +35 -0
  7. package/dist/client/client.js +215 -0
  8. package/dist/host/index.mjs +147 -0
  9. package/dist/internal/account-dashboard.mjs +67 -0
  10. package/dist/internal/auth-controller.mjs +184 -0
  11. package/dist/internal/auth-registry.mjs +64 -0
  12. package/dist/internal/auth-rpc.mjs +118 -0
  13. package/dist/internal/billing-summary.mjs +105 -0
  14. package/dist/internal/credential-source.mjs +168 -0
  15. package/dist/internal/grok-adapter.mjs +160 -0
  16. package/dist/internal/grok-command-handler.mjs +62 -0
  17. package/dist/internal/grok-command.mjs +17 -0
  18. package/dist/internal/grok-transport.mjs +279 -0
  19. package/dist/internal/model-catalog.mjs +142 -0
  20. package/dist/internal/official-auth-driver.mjs +40 -0
  21. package/dist/internal/official-cli-auth.mjs +231 -0
  22. package/dist/internal/official-cli-verifier.mjs +62 -0
  23. package/dist/internal/official-credential-loader.mjs +64 -0
  24. package/dist/internal/provider-runtime.mjs +43 -0
  25. package/dist/internal/responses-codec.mjs +357 -0
  26. package/dist/internal/responses-request.mjs +246 -0
  27. package/dist/internal/responses-sse.mjs +123 -0
  28. package/docs/01-product-requirements.md +128 -0
  29. package/docs/02-architecture-options.md +180 -0
  30. package/docs/03-security-threat-model.md +243 -0
  31. package/docs/04-harness-contract.md +325 -0
  32. package/docs/05-test-plan.md +224 -0
  33. package/docs/06-release-plan.md +182 -0
  34. package/docs/07-decision-gate.md +76 -0
  35. package/docs/08-upstream-cli-1.0.5-evidence.md +98 -0
  36. package/docs/09-implementation-status.md +37 -0
  37. package/docs/README.md +60 -0
  38. package/docs/adr/0001-auth-and-transport-route.md +86 -0
  39. package/docs/adr/0002-v0.1-scope.md +46 -0
  40. package/docs/adr/0003-dual-authentication.md +77 -0
  41. package/docs/adr/0004-dynamic-model-catalog.md +34 -0
  42. package/docs/adr/0005-official-cli-only-authentication.md +36 -0
  43. package/docs/adr/0006-account-dashboard.md +84 -0
  44. package/grok-provider.patch.yml +3 -0
  45. package/package.json +92 -0
  46. package/types/index.d.ts +9 -0
@@ -0,0 +1,215 @@
1
+ /* global document, window */
2
+
3
+ window.__ModuleLoader__.load({
4
+ id: "dsh-grok-provider",
5
+ factory: (require) => {
6
+ const module = { exports: {} }
7
+ const React = require("react")
8
+ const namespace = "settings.grok"
9
+ const dictionaries = {
10
+ zh: {
11
+ nav: "Grok Build", title: "Grok Build",
12
+ description: "管理官方 Grok Build 登录、账户额度与当前账号可用模型。敏感凭据不会发送到设置页面。",
13
+ loopback: "账户管理只允许在本机页面使用。", loading: "正在读取账户状态…", unavailable: "账户服务暂时不可用。",
14
+ ready: "已登录", notReady: "未登录", driverMissing: "官方 CLI 登录能力不可用",
15
+ running: "登录进行中", succeeded: "登录成功", failed: "登录失败", cancelled: "登录已取消",
16
+ login: "通过浏览器登录", cancel: "取消登录", logout: "退出登录", confirmLogout: "再次点击确认退出",
17
+ officialBody: "登录与凭据刷新由本机官方 Grok CLI 完成。退出会同时影响使用同一 Grok 凭据的其他应用。",
18
+ usage: "使用额度", grokBuild: "Grok Build", refresh: "刷新", refreshing: "刷新中…",
19
+ weekly: "每周额度", monthly: "每月额度", currentPeriod: "当前额度周期",
20
+ remaining: "剩余", used: "已使用", resetTime: "重置时间", resetUnknown: "上游未提供重置时间",
21
+ usageUnknown: "上游未提供使用比例", quotaUnavailable: "暂时无法读取 Grok Build 额度。",
22
+ modelsTitle: "当前账号可用的模型", modelsDescription: "模型来自 Grok Build 动态目录;Harness 模型选择器会显示这里列出的全部模型。",
23
+ modelsUnavailable: "暂时无法读取模型目录。", noModels: "当前账号没有返回可用模型。",
24
+ context: "上下文", reasoning: "推理档位", defaultEffort: "默认", text: "文本输入", streaming: "流式输出", tools: "工具调用",
25
+ lastUpdated: "数据更新时间",
26
+ },
27
+ en: {
28
+ nav: "Grok Build", title: "Grok Build",
29
+ description: "Manage official Grok Build sign-in, account quota, and models available to this account. Sensitive credentials never reach this page.",
30
+ loopback: "Account management is available only from the local page.", loading: "Reading account status…", unavailable: "The account service is temporarily unavailable.",
31
+ ready: "Signed in", notReady: "Signed out", driverMissing: "Official CLI sign-in unavailable",
32
+ running: "Sign-in in progress", succeeded: "Signed in", failed: "Sign-in failed", cancelled: "Sign-in cancelled",
33
+ login: "Sign in with browser", cancel: "Cancel sign-in", logout: "Sign out", confirmLogout: "Click again to confirm sign-out",
34
+ officialBody: "The official local Grok CLI owns sign-in and credential refresh. Signing out also affects other apps sharing the same Grok credential.",
35
+ usage: "Usage quota", grokBuild: "Grok Build", refresh: "Refresh", refreshing: "Refreshing…",
36
+ weekly: "Weekly quota", monthly: "Monthly quota", currentPeriod: "Current quota period",
37
+ remaining: "remaining", used: "Used", resetTime: "Resets", resetUnknown: "Reset time was not provided",
38
+ usageUnknown: "Usage percentage was not provided", quotaUnavailable: "Grok Build quota is temporarily unavailable.",
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
+ 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",
42
+ lastUpdated: "Updated",
43
+ },
44
+ }
45
+
46
+ const css = `
47
+ .dsh-grok-page{box-sizing:border-box;width:min(920px,100%);padding:8px 4px 40px;color:var(--dsw-alias-label-primary)}
48
+ .dsh-grok-page *{box-sizing:border-box}.dsh-grok-page h2{margin:0;font-size:21px;line-height:31px}.dsh-grok-description{max-width:760px;margin:5px 0 18px;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:21px}
49
+ .dsh-grok-stack{display:grid;gap:14px}.dsh-grok-panel{overflow:hidden;border:1px solid rgb(190 93 138 / 28%);border-radius:18px;background:linear-gradient(145deg,rgb(190 93 138 / 8%),rgb(190 93 138 / 3%) 55%,var(--dsw-alias-bg-layer-1));box-shadow:0 1px 2px rgb(20 20 30 / 3%)}
50
+ .dsh-grok-panel-inner{padding:20px}.dsh-grok-account-head{display:flex;align-items:center;justify-content:space-between;gap:16px}.dsh-grok-state{display:flex;min-width:0;align-items:center;gap:10px;font-size:17px;font-weight:650}.dsh-grok-dot{width:10px;height:10px;flex:0 0 auto;border-radius:50%;background:var(--dsw-alias-label-tertiary,#909090)}.dsh-grok-dot[data-good="true"]{background:var(--dsw-static-green-500,#1ebf68);box-shadow:0 0 0 4px rgb(30 191 104 / 10%)}.dsh-grok-dot[data-running="true"]{background:#d99b2b;box-shadow:0 0 0 4px rgb(217 155 43 / 12%)}
51
+ .dsh-grok-button{height:36px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;padding:0 14px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);cursor:pointer;font:inherit;font-size:12px}.dsh-grok-button:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsh-grok-button:disabled{cursor:default;opacity:.45}.dsh-grok-primary{border-color:var(--dsw-static-deepseek-500,#4d6bfe);color:#fff;background:var(--dsw-static-deepseek-500,#4d6bfe)}
52
+ .dsh-grok-auth-note{margin:13px 0 0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:19px}.dsh-grok-actions{display:flex;flex-wrap:wrap;gap:7px}
53
+ .dsh-grok-divider{height:1px;margin:18px 0;background:rgb(190 93 138 / 20%)}.dsh-grok-section-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.dsh-grok-section-head h3{margin:0;font-size:15px;line-height:23px}.dsh-grok-section-head .dsh-grok-button{height:30px;padding:0 11px}
54
+ .dsh-grok-quota{margin-top:15px}.dsh-grok-quota-row{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:8px;color:var(--dsw-alias-label-secondary);font-size:13px}.dsh-grok-quota-name{color:var(--dsw-alias-label-primary);font-weight:600}.dsh-grok-progress{height:9px;overflow:hidden;border-radius:999px;background:rgb(190 93 138 / 13%)}.dsh-grok-progress-fill{height:100%;border-radius:inherit;background:linear-gradient(90deg,#d7448a,#ea1768);transition:width .25s ease}.dsh-grok-quota-meta{display:flex;flex-wrap:wrap;justify-content:space-between;gap:7px;margin-top:9px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}.dsh-grok-muted{margin:12px 0 0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:19px}
55
+ .dsh-grok-models-head{padding:20px 20px 0}.dsh-grok-models-head h3{margin:0;font-size:16px;line-height:24px}.dsh-grok-models-head p{margin:5px 0 0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:19px}.dsh-grok-model-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;padding:16px 20px 20px}.dsh-grok-model{min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:13px;padding:14px;background:var(--dsw-alias-bg-layer-1)}.dsh-grok-model-title{display:flex;align-items:baseline;justify-content:space-between;gap:8px}.dsh-grok-model-title strong{overflow-wrap:anywhere;font-size:14px}.dsh-grok-model-id{overflow-wrap:anywhere;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px}.dsh-grok-model-description{margin:5px 0 10px;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:17px}.dsh-grok-badges{display:flex;flex-wrap:wrap;gap:5px}.dsh-grok-badge{border-radius:999px;padding:2px 8px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover);font-size:10px;line-height:18px}.dsh-grok-badge[data-accent="true"]{color:#a9326b;background:rgb(215 68 138 / 11%)}.dsh-grok-efforts{margin-top:10px;color:var(--dsw-alias-label-secondary);font-size:10px;line-height:17px}.dsh-grok-footer{padding:0 20px 16px;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));font-size:10px}
56
+ .dsh-grok-notice{padding:13px 14px;border:1px solid rgb(229 72 77 / 28%);border-radius:11px;color:var(--dsw-alias-label-secondary);background:rgb(229 72 77 / 7%);font-size:12px;line-height:19px}.dsh-grok-error{margin:10px 0 0;color:var(--dsw-static-red-500,#e5484d);font-size:12px}
57
+ @media(max-width:680px){.dsh-grok-panel-inner,.dsh-grok-models-head{padding:16px}.dsh-grok-model-grid{grid-template-columns:1fr;padding:14px 16px 16px}.dsh-grok-account-head{align-items:flex-start;flex-direction:column}.dsh-grok-quota-row{align-items:flex-start;flex-direction:column;gap:3px}}
58
+ `
59
+ if (!document.querySelector('style[data-plugin-css="dsh-grok-provider"]')) {
60
+ const style = document.createElement("style")
61
+ style.dataset.pluginCss = "dsh-grok-provider"
62
+ style.textContent = css
63
+ document.head.appendChild(style)
64
+ }
65
+
66
+ function GrokSettings({ connection, t }) {
67
+ const [status, setStatus] = React.useState()
68
+ const [dashboard, setDashboard] = React.useState()
69
+ const [busy, setBusy] = React.useState(false)
70
+ const [error, setError] = React.useState(false)
71
+ const [dashboardError, setDashboardError] = React.useState(false)
72
+ const [confirmLogout, setConfirmLogout] = React.useState(false)
73
+ const call = React.useCallback(async (endpoint, payload = {}) => {
74
+ const result = await connection.rpc.call("/grok-auth", endpoint, payload)
75
+ if (!result || result.ok !== true) throw new Error("Grok account RPC failed")
76
+ return result.value
77
+ }, [connection])
78
+ const refreshDashboard = React.useCallback(async () => {
79
+ try {
80
+ const value = await call("dashboard")
81
+ setDashboard(value.dashboard)
82
+ setDashboardError(false)
83
+ } catch { setDashboardError(true) }
84
+ }, [call])
85
+ const refreshStatus = React.useCallback(async ({ includeDashboard = false } = {}) => {
86
+ try {
87
+ const value = await call("status")
88
+ setStatus(value.status)
89
+ setError(false)
90
+ if (value.status.available === true && includeDashboard) await refreshDashboard()
91
+ if (value.status.available !== true) setDashboard(undefined)
92
+ } catch { setError(true) }
93
+ }, [call, refreshDashboard])
94
+ React.useEffect(() => { refreshStatus({ includeDashboard: true }) }, [refreshStatus])
95
+ React.useEffect(() => {
96
+ if (status?.session?.state !== "running") return undefined
97
+ const timer = window.setInterval(() => refreshStatus(), 1_000)
98
+ return () => window.clearInterval(timer)
99
+ }, [refreshStatus, status])
100
+ React.useEffect(() => {
101
+ if (status?.available === true && status?.session?.state === "succeeded") refreshDashboard()
102
+ }, [refreshDashboard, status?.available, status?.session?.state])
103
+ const act = async (operation) => {
104
+ setBusy(true); setError(false)
105
+ try { await operation() } catch { setError(true) } finally { setBusy(false) }
106
+ }
107
+ const login = () => act(async () => {
108
+ await call("login"); setConfirmLogout(false); await refreshStatus()
109
+ })
110
+ const cancel = () => act(async () => {
111
+ const sessionId = status?.session?.sessionId
112
+ if (typeof sessionId !== "string") throw new Error("No active Grok login")
113
+ await call("cancel", { sessionId }); await refreshStatus({ includeDashboard: true })
114
+ })
115
+ const logout = () => act(async () => {
116
+ const value = await call("logout")
117
+ if (value.kind === "logout-confirmation-required") setConfirmLogout(true)
118
+ else { setConfirmLogout(false); setDashboard(undefined); await refreshStatus() }
119
+ })
120
+ const manualRefresh = () => act(async () => refreshStatus({ includeDashboard: true }))
121
+
122
+ if (!connection.isLoopback) return React.createElement("section", { className: "dsh-grok-page" },
123
+ React.createElement("h2", null, t("title")), React.createElement("p", { className: "dsh-grok-notice" }, t("loopback")))
124
+ if (!status && !error) return React.createElement("section", { className: "dsh-grok-page" }, t("loading"))
125
+
126
+ const driver = status?.driver === true
127
+ const available = status?.available === true
128
+ const sessionState = status?.session?.state
129
+ const stateLabel = !driver ? t("driverMissing") : sessionState === "running" ? t("running")
130
+ : sessionState === "succeeded" && available ? t("succeeded") : sessionState === "failed" ? t("failed")
131
+ : sessionState === "cancelled" ? t("cancelled") : available ? t("ready") : t("notReady")
132
+
133
+ const renderQuota = () => {
134
+ if (!available) return React.createElement("p", { className: "dsh-grok-muted" }, t("notReady"))
135
+ if (!dashboard && !dashboardError) return React.createElement("p", { className: "dsh-grok-muted" }, t("refreshing"))
136
+ const quota = dashboard?.quota
137
+ if (dashboardError || quota?.state !== "ready") return React.createElement("p", { className: "dsh-grok-muted" }, t("quotaUnavailable"))
138
+ const hasPercent = typeof quota.remainingPercent === "number"
139
+ const periodLabel = quota.periodKind === "weekly" ? t("weekly") : quota.periodKind === "monthly" ? t("monthly") : t("currentPeriod")
140
+ return React.createElement("div", { className: "dsh-grok-quota" },
141
+ React.createElement("div", { className: "dsh-grok-quota-row" },
142
+ React.createElement("span", { className: "dsh-grok-quota-name" }, t("grokBuild")),
143
+ React.createElement("span", null, hasPercent ? `${t("remaining")} ${formatPercent(quota.remainingPercent)}` : t("usageUnknown"))),
144
+ hasPercent && React.createElement("div", { className: "dsh-grok-progress", role: "progressbar", "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": quota.remainingPercent },
145
+ React.createElement("div", { className: "dsh-grok-progress-fill", style: { width: `${quota.remainingPercent}%` } })),
146
+ React.createElement("div", { className: "dsh-grok-quota-meta" },
147
+ React.createElement("span", null, hasPercent ? `${periodLabel} · ${t("used")} ${formatPercent(quota.usedPercent)}` : periodLabel),
148
+ React.createElement("span", null, quota.resetsAt ? `${t("resetTime")}:${formatDateTime(quota.resetsAt)}` : t("resetUnknown"))))
149
+ }
150
+
151
+ const renderModel = (model) => {
152
+ const effortText = model.reasoning?.efforts?.map((effort) => effort.id === model.reasoning.defaultEffort
153
+ ? `${effort.name} (${t("defaultEffort")})` : effort.name).join(" · ")
154
+ return React.createElement("article", { className: "dsh-grok-model", key: model.id },
155
+ React.createElement("div", { className: "dsh-grok-model-title" },
156
+ React.createElement("strong", null, model.name), React.createElement("span", { className: "dsh-grok-model-id" }, model.id)),
157
+ model.description && React.createElement("p", { className: "dsh-grok-model-description" }, model.description),
158
+ React.createElement("div", { className: "dsh-grok-badges" },
159
+ React.createElement("span", { className: "dsh-grok-badge", "data-accent": true }, `${t("context")} ${formatContext(model.contextWindow)}`),
160
+ model.capabilities?.textInput && React.createElement("span", { className: "dsh-grok-badge" }, t("text")),
161
+ model.capabilities?.streaming && React.createElement("span", { className: "dsh-grok-badge" }, t("streaming")),
162
+ model.capabilities?.functionTools && React.createElement("span", { className: "dsh-grok-badge" }, t("tools"))),
163
+ effortText && React.createElement("div", { className: "dsh-grok-efforts" }, `${t("reasoning")}:${effortText}`))
164
+ }
165
+
166
+ const models = dashboard?.models
167
+ return React.createElement("section", { className: "dsh-grok-page" },
168
+ React.createElement("h2", null, t("title")), React.createElement("p", { className: "dsh-grok-description" }, t("description")),
169
+ React.createElement("div", { className: "dsh-grok-stack" },
170
+ React.createElement("article", { className: "dsh-grok-panel" }, React.createElement("div", { className: "dsh-grok-panel-inner" },
171
+ React.createElement("div", { className: "dsh-grok-account-head" },
172
+ React.createElement("div", { className: "dsh-grok-state" }, React.createElement("span", { className: "dsh-grok-dot", "data-good": available, "data-running": sessionState === "running" }), stateLabel),
173
+ React.createElement("div", { className: "dsh-grok-actions" },
174
+ sessionState === "running"
175
+ ? React.createElement("button", { className: "dsh-grok-button", type: "button", disabled: busy, onClick: cancel }, t("cancel"))
176
+ : !available && React.createElement("button", { className: "dsh-grok-button dsh-grok-primary", type: "button", disabled: busy || !driver, onClick: login }, t("login")),
177
+ available && React.createElement("button", { className: "dsh-grok-button", type: "button", disabled: busy || !driver, onClick: logout }, t(confirmLogout ? "confirmLogout" : "logout")))),
178
+ React.createElement("p", { className: "dsh-grok-auth-note" }, t("officialBody")),
179
+ React.createElement("div", { className: "dsh-grok-divider" }),
180
+ React.createElement("div", { className: "dsh-grok-section-head" }, React.createElement("h3", null, t("usage")),
181
+ React.createElement("button", { className: "dsh-grok-button", type: "button", disabled: busy || !available, onClick: manualRefresh }, busy ? t("refreshing") : t("refresh"))),
182
+ renderQuota(), error && React.createElement("p", { className: "dsh-grok-error" }, t("unavailable")))),
183
+ React.createElement("article", { className: "dsh-grok-panel" },
184
+ React.createElement("div", { className: "dsh-grok-models-head" }, React.createElement("h3", null, t("modelsTitle")), React.createElement("p", null, t("modelsDescription"))),
185
+ React.createElement("div", { className: "dsh-grok-model-grid" },
186
+ !available ? React.createElement("p", { className: "dsh-grok-muted" }, t("notReady"))
187
+ : dashboardError || models?.state === "unavailable" ? React.createElement("p", { className: "dsh-grok-muted" }, t("modelsUnavailable"))
188
+ : !models ? React.createElement("p", { className: "dsh-grok-muted" }, t("refreshing"))
189
+ : models.items.length === 0 ? React.createElement("p", { className: "dsh-grok-muted" }, t("noModels"))
190
+ : models.items.map(renderModel)),
191
+ dashboard?.fetchedAt && React.createElement("div", { className: "dsh-grok-footer" }, `${t("lastUpdated")}:${formatDateTime(dashboard.fetchedAt)}`))))
192
+ }
193
+
194
+ function formatPercent(value) { return `${new Intl.NumberFormat(undefined, { maximumFractionDigits: 2 }).format(value)}%` }
195
+ function formatDateTime(value) { return new Intl.DateTimeFormat(undefined, { dateStyle: "medium", timeStyle: "short" }).format(new Date(value)) }
196
+ function formatContext(value) {
197
+ if (value >= 1_000_000) return `${new Intl.NumberFormat(undefined, { maximumFractionDigits: 1 }).format(value / 1_000_000)}M`
198
+ if (value >= 1_000) return `${new Intl.NumberFormat(undefined, { maximumFractionDigits: 0 }).format(value / 1_000)}K`
199
+ return new Intl.NumberFormat().format(value)
200
+ }
201
+
202
+ const inject = ["slots", "locale", "connection"]
203
+ function apply(ctx) {
204
+ ctx.effect(() => ctx.locale.register(namespace, dictionaries), "dsh-grok: dictionaries")
205
+ const t = ctx.locale.bind(namespace)
206
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
207
+ name: "settings.section", id: "grok-auth", order: 45, label: () => t("nav"), locale: namespace,
208
+ inject: () => ({ connection: ctx.connection, t }),
209
+ }, GrokSettings))
210
+ }
211
+ module.exports.inject = inject
212
+ module.exports.apply = apply
213
+ return module.exports
214
+ },
215
+ })
@@ -0,0 +1,147 @@
1
+ import os from "node:os"
2
+ import path from "node:path"
3
+ import { randomUUID } from "node:crypto"
4
+
5
+ import { LlmError, attributionHeaders } from "@deepseek-ai/dsh-llm"
6
+ import Schema from "@deepseek-ai/schemastery"
7
+
8
+ import {
9
+ CredentialFileTooLargeError,
10
+ GROK_CLI_1_0_5_AUTH_CONTRACT,
11
+ UnsupportedCredentialError,
12
+ createCredentialSource,
13
+ } from "../internal/credential-source.mjs"
14
+ import { AuthModeUnavailableError } from "../internal/auth-registry.mjs"
15
+ import { createAccountDashboard } from "../internal/account-dashboard.mjs"
16
+ import { createAuthController } from "../internal/auth-controller.mjs"
17
+ import { createAuthRpcHandler } from "../internal/auth-rpc.mjs"
18
+ import { createGrokAdapter } from "../internal/grok-adapter.mjs"
19
+ import { createGrokCommandHandler } from "../internal/grok-command-handler.mjs"
20
+ import { GrokTransportError, createGrokTransport } from "../internal/grok-transport.mjs"
21
+ import {
22
+ OfficialCredentialFileError,
23
+ createOfficialCredentialLoader,
24
+ } from "../internal/official-credential-loader.mjs"
25
+ import { createOfficialCliAuth } from "../internal/official-cli-auth.mjs"
26
+ import { createOfficialAuthDriver } from "../internal/official-auth-driver.mjs"
27
+ import { verifyOfficialCliExecutable } from "../internal/official-cli-verifier.mjs"
28
+ import { installProviderRuntime } from "../internal/provider-runtime.mjs"
29
+
30
+ export const name = "llm-grok"
31
+ export const inject = ["llm"]
32
+
33
+ export const Config = Schema.object({})
34
+
35
+ export function apply(ctx) {
36
+ const platform = process.platform
37
+ if (platform !== "darwin" && platform !== "win32") {
38
+ throw new TypeError("dsh-grok-provider supports macOS and Windows in version 0.1.0")
39
+ }
40
+
41
+ const homeDir = os.homedir()
42
+ let refreshOfficialCredential
43
+ const officialSource = createCredentialSource({
44
+ contract: GROK_CLI_1_0_5_AUTH_CONTRACT,
45
+ load: createOfficialCredentialLoader({
46
+ authPath: path.join(homeDir, ".grok", "auth.json"),
47
+ platform,
48
+ }),
49
+ now: () => new Date(),
50
+ refresh: async () => {
51
+ if (refreshOfficialCredential === undefined) throw new UnsupportedCredentialError()
52
+ await refreshOfficialCredential()
53
+ },
54
+ })
55
+ const runtime = installProviderRuntime({
56
+ llm: ctx.llm,
57
+ officialSource,
58
+ createTransport: (credentialSource) => createGrokTransport({
59
+ credentialSource,
60
+ fetch: globalThis.fetch,
61
+ attributionHeaders,
62
+ clientIdentifier: "dsh-grok-provider",
63
+ clientVersion: "1.0.5",
64
+ }),
65
+ createAdapter: ({ getGeneration }) => createGrokAdapter({
66
+ getGeneration,
67
+ mapError: mapLlmError,
68
+ }),
69
+ })
70
+ const authController = createAuthController({
71
+ registry: runtime.auth,
72
+ randomUUID,
73
+ })
74
+ const commandHandler = createGrokCommandHandler({ controller: authController })
75
+ const dashboard = createAccountDashboard({
76
+ listModels: () => runtime.adapter.listModels("grok"),
77
+ getBilling: ({ signal } = {}) => runtime.auth.getGeneration().transport.getBilling({ signal }),
78
+ now: () => new Date(),
79
+ })
80
+ const authRpcHandler = createAuthRpcHandler({ controller: authController, dashboard })
81
+
82
+ ctx.inject(["subprocess"], (subprocessCtx) => {
83
+ const officialAuth = createOfficialCliAuth({
84
+ subprocess: subprocessCtx.subprocess,
85
+ platform,
86
+ homeDir,
87
+ verifyExecutable: verifyOfficialCliExecutable,
88
+ })
89
+ const refresh = async () => {
90
+ const outcome = await officialAuth.refresh()
91
+ if (outcome.kind !== "succeeded") throw new UnsupportedCredentialError()
92
+ }
93
+ refreshOfficialCredential = refresh
94
+ const removeDriver = authController.installDriver(createOfficialAuthDriver({
95
+ officialAuth,
96
+ credentialSource: officialSource,
97
+ }))
98
+ return async () => {
99
+ await authController.shutdown()
100
+ if (refreshOfficialCredential === refresh) refreshOfficialCredential = undefined
101
+ removeDriver()
102
+ }
103
+ })
104
+
105
+ ctx.inject(["commands"], (commandsCtx) => commandsCtx.commands.register({
106
+ name: "grok",
107
+ description: "Manage Grok Build authentication",
108
+ input: { hint: "status|login|cancel|logout" },
109
+ recordInput: false,
110
+ handler: commandHandler,
111
+ }))
112
+ ctx.inject(["connection"], (connectionCtx) => connectionCtx.connection.rpc.handle(
113
+ "/grok-auth",
114
+ authRpcHandler,
115
+ { authority: "loopback" },
116
+ ))
117
+
118
+ ctx.effect(() => () => runtime.dispose(), "llm-grok runtime")
119
+ }
120
+
121
+ function mapLlmError(error) {
122
+ if (error instanceof LlmError) return error
123
+ if (error?.name === "AbortError") {
124
+ return new LlmError("The Grok Build request was cancelled", "ABORTED", { cause: error })
125
+ }
126
+ if (
127
+ error instanceof AuthModeUnavailableError ||
128
+ error instanceof UnsupportedCredentialError ||
129
+ error instanceof CredentialFileTooLargeError ||
130
+ error instanceof OfficialCredentialFileError ||
131
+ (error instanceof GrokTransportError && (error.status === 401 || error.status === 403))
132
+ ) {
133
+ return new LlmError("Grok authentication is required", "AUTH", {
134
+ cause: error,
135
+ ...(error.status === undefined ? {} : { status: error.status }),
136
+ })
137
+ }
138
+ if (error instanceof GrokTransportError) {
139
+ return new LlmError("The Grok Build request failed", error.status === 429 ? "RATE_LIMIT" : "PROVIDER_ERROR", {
140
+ cause: error,
141
+ ...(error.status === undefined ? {} : { status: error.status }),
142
+ })
143
+ }
144
+ return new LlmError("The Grok provider rejected an invalid or unsupported response", "INVALID_RESPONSE", {
145
+ cause: error,
146
+ })
147
+ }
@@ -0,0 +1,67 @@
1
+ import { parseBillingSummary } from "./billing-summary.mjs"
2
+
3
+ export function createAccountDashboard({ listModels, getBilling, now }) {
4
+ if (typeof listModels !== "function" || typeof getBilling !== "function" || typeof now !== "function") {
5
+ throw new TypeError("Invalid Grok account dashboard dependencies")
6
+ }
7
+
8
+ return async function getAccountDashboard({ signal } = {}) {
9
+ const [modelResult, quotaResult] = await Promise.allSettled([
10
+ Promise.resolve().then(() => listModels({ signal })).then((models) => models.map(projectModel)),
11
+ Promise.resolve().then(() => getBilling({ signal })).then(parseBillingSummary),
12
+ ])
13
+ const currentTime = now()
14
+ if (!(currentTime instanceof Date) || !Number.isFinite(currentTime.getTime())) {
15
+ throw new TypeError("Invalid Grok account dashboard clock")
16
+ }
17
+ return Object.freeze({
18
+ fetchedAt: currentTime.toISOString(),
19
+ models: modelResult.status === "fulfilled"
20
+ ? Object.freeze({ state: "ready", items: Object.freeze(modelResult.value) })
21
+ : Object.freeze({ state: "unavailable", items: Object.freeze([]) }),
22
+ quota: quotaResult.status === "fulfilled"
23
+ ? quotaResult.value
24
+ : Object.freeze({ state: "unavailable" }),
25
+ })
26
+ }
27
+ }
28
+
29
+ function projectModel(model) {
30
+ if (
31
+ !isPlainObject(model) ||
32
+ typeof model.id !== "string" ||
33
+ typeof model.name !== "string" ||
34
+ !isPlainObject(model.context) ||
35
+ !Number.isSafeInteger(model.context.contextWindow)
36
+ ) throw new TypeError("Invalid Grok dashboard model")
37
+ return Object.freeze({
38
+ id: model.id,
39
+ name: model.name,
40
+ ...(typeof model.description === "string" ? { description: model.description } : {}),
41
+ contextWindow: model.context.contextWindow,
42
+ ...(model.reasoning === undefined ? {} : { reasoning: projectReasoning(model.reasoning) }),
43
+ capabilities: Object.freeze({ textInput: true, streaming: true, functionTools: true }),
44
+ })
45
+ }
46
+
47
+ function projectReasoning(reasoning) {
48
+ if (!isPlainObject(reasoning) || !Array.isArray(reasoning.efforts)) {
49
+ throw new TypeError("Invalid Grok dashboard reasoning capability")
50
+ }
51
+ const efforts = reasoning.efforts.map((effort) => {
52
+ if (!isPlainObject(effort) || typeof effort.id !== "string" || typeof effort.name !== "string") {
53
+ throw new TypeError("Invalid Grok dashboard reasoning effort")
54
+ }
55
+ return Object.freeze({ id: effort.id, name: effort.name })
56
+ })
57
+ return Object.freeze({
58
+ efforts: Object.freeze(efforts),
59
+ ...(typeof reasoning.defaultEffort === "string" ? { defaultEffort: reasoning.defaultEffort } : {}),
60
+ })
61
+ }
62
+
63
+ function isPlainObject(value) {
64
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false
65
+ const prototype = Object.getPrototypeOf(value)
66
+ return prototype === Object.prototype || prototype === null
67
+ }
@@ -0,0 +1,184 @@
1
+ const OUTCOMES = new Set(["succeeded", "cancelled", "failed"])
2
+
3
+ export class AuthLoginBusyError extends Error {
4
+ constructor() {
5
+ super("A Grok login or logout is already running")
6
+ this.name = "AuthLoginBusyError"
7
+ }
8
+ }
9
+
10
+ export class AuthDriverUnavailableError extends Error {
11
+ constructor() {
12
+ super("The official Grok authentication driver is unavailable")
13
+ this.name = "AuthDriverUnavailableError"
14
+ }
15
+ }
16
+
17
+ export function createAuthController({ registry, randomUUID, now = () => new Date(), driver }) {
18
+ if (
19
+ !registry ||
20
+ typeof registry.invalidate !== "function" ||
21
+ typeof registry.status !== "function" ||
22
+ typeof randomUUID !== "function" ||
23
+ typeof now !== "function"
24
+ ) throw new TypeError("Invalid Grok auth controller dependencies")
25
+
26
+ let installed
27
+ let active
28
+ let state
29
+ let confirmation
30
+ let closing = false
31
+ if (driver !== undefined) install(driver)
32
+
33
+ function install(nextDriver) {
34
+ if (!nextDriver || typeof nextDriver.begin !== "function") {
35
+ throw new TypeError("Invalid Grok auth driver")
36
+ }
37
+ const token = Object.freeze({})
38
+ installed = { driver: nextDriver, token }
39
+ return () => {
40
+ if (installed?.token === token) installed = undefined
41
+ }
42
+ }
43
+
44
+ return Object.freeze({
45
+ installDriver: install,
46
+
47
+ async status() {
48
+ return Object.freeze({
49
+ ...await registry.status(),
50
+ driver: installed !== undefined,
51
+ ...(state === undefined ? {} : { session: Object.freeze({ ...state }) }),
52
+ })
53
+ },
54
+
55
+ async beginLogin() {
56
+ if (active !== undefined || closing) throw new AuthLoginBusyError()
57
+ if (installed === undefined) throw new AuthDriverUnavailableError()
58
+
59
+ const sessionId = createId(randomUUID, "Invalid Grok auth session id")
60
+ const abortController = new AbortController()
61
+ let started
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
+
76
+ const publicState = Object.freeze({ sessionId, state: "running" })
77
+ state = publicState
78
+ const settled = Promise.resolve(started.completion).then(
79
+ (outcome) => normalizeOutcome(outcome),
80
+ () => Object.freeze({ kind: abortController.signal.aborted ? "cancelled" : "failed" }),
81
+ ).then((outcome) => {
82
+ state = { state: outcome.kind, sessionId }
83
+ active = undefined
84
+ registry.invalidate()
85
+ return outcome
86
+ })
87
+
88
+ const session = Object.freeze({
89
+ public: publicState,
90
+ cancel() {
91
+ abortController.abort()
92
+ },
93
+ async wait({ signal } = {}) {
94
+ if (!signal) return settled
95
+ if (signal.aborted) {
96
+ abortController.abort(signal.reason)
97
+ return settled
98
+ }
99
+ const abort = () => abortController.abort(signal.reason)
100
+ signal.addEventListener("abort", abort, { once: true })
101
+ try {
102
+ return await settled
103
+ } finally {
104
+ signal.removeEventListener("abort", abort)
105
+ }
106
+ },
107
+ })
108
+ active = session
109
+ return session
110
+ },
111
+
112
+ cancel(sessionId) {
113
+ if (active === undefined || active.public.sessionId !== sessionId) return false
114
+ active.cancel()
115
+ return true
116
+ },
117
+
118
+ async shutdown() {
119
+ if (active === undefined) return false
120
+ active.cancel()
121
+ await active.wait()
122
+ return true
123
+ },
124
+
125
+ async logout({ signal } = {}) {
126
+ if (closing) throw new AuthLoginBusyError()
127
+ if (installed === undefined || typeof installed.driver.logout !== "function") {
128
+ throw new AuthDriverUnavailableError()
129
+ }
130
+ const currentTime = now()
131
+ if (!(currentTime instanceof Date) || !Number.isFinite(currentTime.getTime())) {
132
+ throw new TypeError("Invalid Grok auth controller clock")
133
+ }
134
+ if (confirmation === undefined || confirmation.expiresAt <= currentTime.getTime()) {
135
+ const confirmationId = createId(randomUUID, "Invalid Grok logout confirmation id")
136
+ const expiresAt = currentTime.getTime() + 30_000
137
+ confirmation = { confirmationId, expiresAt }
138
+ return Object.freeze({
139
+ kind: "confirmation-required",
140
+ confirmationId,
141
+ expiresAt: new Date(expiresAt).toISOString(),
142
+ })
143
+ }
144
+
145
+ confirmation = undefined
146
+ closing = true
147
+ try {
148
+ if (active !== undefined) {
149
+ active.cancel()
150
+ await active.wait()
151
+ }
152
+ let outcome
153
+ try {
154
+ outcome = normalizeOutcome(await installed.driver.logout({ signal }))
155
+ } catch {
156
+ outcome = Object.freeze({ kind: signal?.aborted ? "cancelled" : "failed" })
157
+ }
158
+ registry.invalidate()
159
+ return outcome
160
+ } finally {
161
+ closing = false
162
+ }
163
+ },
164
+ })
165
+ }
166
+
167
+ function normalizeOutcome(outcome) {
168
+ if (!isPlainObject(outcome) || !OUTCOMES.has(outcome.kind) || Object.keys(outcome).length !== 1) {
169
+ return Object.freeze({ kind: "failed" })
170
+ }
171
+ return Object.freeze({ kind: outcome.kind })
172
+ }
173
+
174
+ function createId(randomUUID, message) {
175
+ const value = randomUUID()
176
+ if (typeof value !== "string" || value.length === 0 || value.length > 128) throw new TypeError(message)
177
+ return value
178
+ }
179
+
180
+ function isPlainObject(value) {
181
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false
182
+ const prototype = Object.getPrototypeOf(value)
183
+ return prototype === Object.prototype || prototype === null
184
+ }