dsh-grok-subscription 1.0.5 → 1.0.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/README.en.md +9 -2
- package/README.md +9 -2
- package/lib/client.js +7 -2
- package/lib/index.js +139 -18
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -25,7 +25,7 @@ Models, reasoning effort, and weekly quota all stay inside DSH.
|
|
|
25
25
|
|
|
26
26
|
## Three-step start
|
|
27
27
|
|
|
28
|
-
1. **Install the plugin.** Run the command below; to select a candidate version, enter the full `package@version`, for example `dsh-grok-subscription@1.0.
|
|
28
|
+
1. **Install the plugin.** Run the command below; to select a candidate version, enter the full `package@version`, for example `dsh-grok-subscription@1.0.6`.
|
|
29
29
|
|
|
30
30
|
```sh
|
|
31
31
|
dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
|
|
@@ -43,6 +43,7 @@ Restart `dsh web` after installing or upgrading, otherwise the old `lib/` keeps
|
|
|
43
43
|
| **Direct subscription routing** | Reuse the official Grok Build CLI session — no `XAI_API_KEY` needed |
|
|
44
44
|
| **Real model catalog** | Read the models the account can actually use (such as `grok-4.7`, `grok-4.6`, `grok-4.5`) from `/v1/models-v2`; nothing is exposed while signed out |
|
|
45
45
|
| **Reasoning effort** | The model picker offers `low` / `medium` / `high` / `xhigh`, defaulting to `high`, matching the Codex interaction |
|
|
46
|
+
| **Image input** | `grok-4.7` / `grok-4.7-fast` / `grok-4.6` accept pasted images, normalized to the host's pixel and byte budget by its attachment service |
|
|
46
47
|
| **Composer quota** | A badge beside the model picker shows weekly remaining percentage; hover or click for "Weekly quota · N% left · resets M/D HH:mm" |
|
|
47
48
|
| **Usage panel (experimental)** | Settings shows the used and remaining percentages the backend reports, and never invents numbers when the read fails |
|
|
48
49
|
| **Credentials stay local** | Only the short-lived access token from `~/.grok/auth.json` is read on the host; no token is handed to browser RPC |
|
|
@@ -90,7 +91,7 @@ dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
|
|
|
90
91
|
You can also install the version published on npm:
|
|
91
92
|
|
|
92
93
|
```sh
|
|
93
|
-
dsh plugin --profile web add dsh-grok-subscription@1.0.
|
|
94
|
+
dsh plugin --profile web add dsh-grok-subscription@1.0.6
|
|
94
95
|
```
|
|
95
96
|
|
|
96
97
|
DSH handles target selection, the profile lock, dependency resolution, and bundle activation.
|
|
@@ -155,6 +156,12 @@ Restart DSH manually afterwards, then:
|
|
|
155
156
|
|
|
156
157
|
The badge appears only when the current session's provider is `grok-build` and the usage read succeeds; hover or click for "Weekly quota · N% left · resets M/D HH:mm". It reflects only the weekly quota the backend returns, and when the read fails the badge is simply hidden — chat is unaffected.
|
|
157
158
|
|
|
159
|
+
### Image input
|
|
160
|
+
|
|
161
|
+
`grok-4.7`, `grok-4.7-fast` and `grok-4.6` accept image input: paste or attach an image in the composer and ask about it. Images are normalized to the host's pixel and byte budget by its attachment service before they are sent, and an over-budget image degrades to a text description rather than being dropped silently.
|
|
162
|
+
|
|
163
|
+
`grok-4.5` is **deliberately excluded**. Given a solid red image and asked its colour it answered "green", and it described a yellow square as a "circle with a black outline" — it confabulates instead of looking. The plugin therefore does not advertise image input for it, so it cannot return confident nonsense.
|
|
164
|
+
|
|
158
165
|
### Reasoning effort
|
|
159
166
|
|
|
160
167
|
After selecting a Grok model, the model menu exposes a reasoning effort submenu: `low` / `medium` / `high` / `xhigh`, defaulting to `high`. The levels come from `model.reasoning` metadata (`efforts` + `defaultEffort`), so the interaction matches Codex in DSH; the levels actually available depend on the account's model catalog.
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
## 三步开始
|
|
27
27
|
|
|
28
|
-
1. **安装插件**:在终端运行下面的命令;指定候选版本时填写完整的 `包名@版本`,例如 `dsh-grok-subscription@1.0.
|
|
28
|
+
1. **安装插件**:在终端运行下面的命令;指定候选版本时填写完整的 `包名@版本`,例如 `dsh-grok-subscription@1.0.6`。
|
|
29
29
|
|
|
30
30
|
```sh
|
|
31
31
|
dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
| **订阅直连** | 复用官方 Grok Build CLI 会话,不需要 `XAI_API_KEY` |
|
|
44
44
|
| **真实模型目录** | 登录后从 `/v1/models-v2` 读取账号实际可用的模型(如 `grok-4.7`、`grok-4.6`、`grok-4.5`);未登录时不暴露任何模型 |
|
|
45
45
|
| **推理档位** | 模型选择器内置 `low` / `medium` / `high` / `xhigh` 子菜单,默认 `high`,与 Codex 的交互一致 |
|
|
46
|
+
| **图片输入** | `grok-4.7` / `grok-4.7-fast` / `grok-4.6` 可直接粘贴图片提问,图片由宿主的附件服务按像素与体积预算归一化 |
|
|
46
47
|
| **输入框额度** | 模型选择器旁的徽章直接显示每周剩余比例,悬停或点击查看「每周额度 剩余 N% · 重置于 M/D HH:mm」 |
|
|
47
48
|
| **用量面板(实验性)** | 设置页显示服务端返回的已用与剩余百分比,读取失败时不猜数字、不虚构额度 |
|
|
48
49
|
| **凭据留在本机** | 只在主机侧读取 `~/.grok/auth.json` 的短期 access token;不会把 token 交给浏览器 RPC |
|
|
@@ -90,7 +91,7 @@ dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
|
|
|
90
91
|
也可以按 npm 上的已发布版本安装:
|
|
91
92
|
|
|
92
93
|
```sh
|
|
93
|
-
dsh plugin --profile web add dsh-grok-subscription@1.0.
|
|
94
|
+
dsh plugin --profile web add dsh-grok-subscription@1.0.6
|
|
94
95
|
```
|
|
95
96
|
|
|
96
97
|
目标选择、profile 锁、依赖解析和 bundle 激活均由 DSH 负责。
|
|
@@ -155,6 +156,12 @@ dsh --profile web --dump-config
|
|
|
155
156
|
|
|
156
157
|
仅当当前会话的 provider 为 `grok-build` 且用量读取成功时显示徽章;悬停或点击可查看「每周额度 剩余 N% · 重置于 M/D HH:mm」。徽章只反映服务端返回的每周额度;读取失败时徽章不显示,聊天不受影响。
|
|
157
158
|
|
|
159
|
+
### 图片输入
|
|
160
|
+
|
|
161
|
+
`grok-4.7`、`grok-4.7-fast` 与 `grok-4.6` 支持图片输入:在输入框粘贴或附加图片即可提问。图片由宿主的附件服务按像素与体积预算归一化后发送,超预算时降级为文本说明,而不是静默丢弃。
|
|
162
|
+
|
|
163
|
+
`grok-4.5` **不在支持列表内**。实测给它一张纯红图片并询问颜色,它回答 "green",还把黄色方块描述成「带黑边的圆」——它在编造而不是看图。插件因此不为它声明图片能力,避免输出看起来自信却完全错误的答案。
|
|
164
|
+
|
|
158
165
|
### 推理档位
|
|
159
166
|
|
|
160
167
|
选择 Grok 模型后,模型菜单里会出现推理档位子菜单:`low` / `medium` / `high` / `xhigh`,默认 `high`。档位通过 `model.reasoning`(`efforts` + `defaultEffort`)元数据提供,因此与 Codex 在 DSH 中的交互一致;具体可用档位以账号模型目录为准。
|
package/lib/client.js
CHANGED
|
@@ -37,7 +37,7 @@ var import_react2 = require("react");
|
|
|
37
37
|
var zh = {
|
|
38
38
|
nav: "Grok \u8BA2\u9605",
|
|
39
39
|
title: "Grok \u8BA2\u9605",
|
|
40
|
-
subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 1.0.
|
|
40
|
+
subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 1.0.6",
|
|
41
41
|
signedIn: "\u5DF2\u767B\u5F55",
|
|
42
42
|
signedOut: "\u672A\u767B\u5F55",
|
|
43
43
|
account: "\u8D26\u6237",
|
|
@@ -87,7 +87,7 @@ var zh = {
|
|
|
87
87
|
var en = {
|
|
88
88
|
nav: "Grok Subscription",
|
|
89
89
|
title: "Grok Subscription",
|
|
90
|
-
subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 1.0.
|
|
90
|
+
subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 1.0.6",
|
|
91
91
|
signedIn: "Signed in",
|
|
92
92
|
signedOut: "Signed out",
|
|
93
93
|
account: "Account",
|
|
@@ -173,6 +173,11 @@ var MAX_REQUEST_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
|
173
173
|
var REQUEST_IMAGE_PIXEL_BUDGET = 2048 * 2048;
|
|
174
174
|
var REQUEST_IMAGE_MAX_BYTES = 1024 * 1024;
|
|
175
175
|
var FALLBACK_MODEL_IDS = Object.freeze(["grok-4.7", "grok-4.6", "grok-4.5"]);
|
|
176
|
+
var IMAGE_INPUT_MODEL_IDS = Object.freeze([
|
|
177
|
+
"grok-4.7",
|
|
178
|
+
"grok-4.7-build-fast",
|
|
179
|
+
"grok-4.6"
|
|
180
|
+
]);
|
|
176
181
|
|
|
177
182
|
// src/client-settings-style.js
|
|
178
183
|
var SETTINGS_STYLE = `
|
package/lib/index.js
CHANGED
|
@@ -29,14 +29,19 @@ var MAX_REQUEST_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
|
29
29
|
var REQUEST_IMAGE_PIXEL_BUDGET = 2048 * 2048;
|
|
30
30
|
var REQUEST_IMAGE_MAX_BYTES = 1024 * 1024;
|
|
31
31
|
var FALLBACK_MODEL_IDS = Object.freeze(["grok-4.7", "grok-4.6", "grok-4.5"]);
|
|
32
|
+
var IMAGE_INPUT_MODEL_IDS = Object.freeze([
|
|
33
|
+
"grok-4.7",
|
|
34
|
+
"grok-4.7-build-fast",
|
|
35
|
+
"grok-4.6"
|
|
36
|
+
]);
|
|
32
37
|
var IMPORT_TIMEOUT_MS = 2500;
|
|
33
38
|
var CREDENTIALS_IO_TIMEOUT_MS = 1500;
|
|
34
39
|
var RPC_HANDLER_TIMEOUT_MS = 8e3;
|
|
35
40
|
|
|
36
41
|
// src/session.js
|
|
37
|
-
import { existsSync as
|
|
42
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
38
43
|
import { spawn } from "node:child_process";
|
|
39
|
-
import { join as
|
|
44
|
+
import { join as join3 } from "node:path";
|
|
40
45
|
|
|
41
46
|
// src/auth-file.js
|
|
42
47
|
import { lstatSync, openSync, readFileSync, closeSync, constants as fsConstants } from "node:fs";
|
|
@@ -337,6 +342,9 @@ function mergeCatalog(live) {
|
|
|
337
342
|
}
|
|
338
343
|
return live.map((model) => ({ ...model, source: "live" }));
|
|
339
344
|
}
|
|
345
|
+
function supportsImageInput(id) {
|
|
346
|
+
return typeof id === "string" && IMAGE_INPUT_MODEL_IDS.includes(id);
|
|
347
|
+
}
|
|
340
348
|
function toPiModels(models) {
|
|
341
349
|
return models.map((model) => {
|
|
342
350
|
const efforts = model.reasoningEfforts ?? ["low", "medium", "high", "xhigh"];
|
|
@@ -357,7 +365,7 @@ function toPiModels(models) {
|
|
|
357
365
|
baseUrl: PROXY_BASE_URL,
|
|
358
366
|
reasoning: model.reasoning !== false,
|
|
359
367
|
thinkingLevelMap,
|
|
360
|
-
input: ["text"],
|
|
368
|
+
input: supportsImageInput(model.id) ? ["text", "image"] : ["text"],
|
|
361
369
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
362
370
|
contextWindow: model.contextWindow ?? 5e5,
|
|
363
371
|
maxTokens: model.maxTokens ?? 5e5,
|
|
@@ -711,6 +719,11 @@ async function fetchBillingUsage(accessToken, options = {}) {
|
|
|
711
719
|
}
|
|
712
720
|
|
|
713
721
|
// src/adapter.js
|
|
722
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
|
|
723
|
+
import { createRequire } from "node:module";
|
|
724
|
+
import { homedir as homedir2 } from "node:os";
|
|
725
|
+
import { dirname, join as join2 } from "node:path";
|
|
726
|
+
import { pathToFileURL } from "node:url";
|
|
714
727
|
function withImportTimeout(promise, ms, message) {
|
|
715
728
|
let timer;
|
|
716
729
|
return Promise.race([
|
|
@@ -722,18 +735,115 @@ function withImportTimeout(promise, ms, message) {
|
|
|
722
735
|
if (timer) clearTimeout(timer);
|
|
723
736
|
});
|
|
724
737
|
}
|
|
738
|
+
function hostModuleRoots(env = process.env, argv1 = process.argv[1]) {
|
|
739
|
+
const roots = [];
|
|
740
|
+
const push = (dir) => {
|
|
741
|
+
if (typeof dir === "string" && dir && !roots.includes(dir)) roots.push(dir);
|
|
742
|
+
};
|
|
743
|
+
const override = env.DSH_GROK_PEER_ROOT;
|
|
744
|
+
if (typeof override === "string" && override.trim()) push(override.trim());
|
|
745
|
+
if (typeof argv1 === "string" && argv1) {
|
|
746
|
+
let dir = dirname(argv1);
|
|
747
|
+
for (let depth = 0; depth < 6 && dir && dir !== dirname(dir); depth++) {
|
|
748
|
+
push(join2(dir, "node_modules"));
|
|
749
|
+
dir = dirname(dir);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
const prefix = env.NPM_CONFIG_PREFIX;
|
|
753
|
+
if (typeof prefix === "string" && prefix.trim()) push(join2(prefix.trim(), "lib", "node_modules"));
|
|
754
|
+
push(join2(homedir2(), ".local", "lib", "node_modules"));
|
|
755
|
+
const dshHome = typeof env.DSH_HOME === "string" && env.DSH_HOME.trim() ? env.DSH_HOME.trim() : join2(homedir2(), ".dsh");
|
|
756
|
+
push(join2(dshHome, "profiles", "node_modules"));
|
|
757
|
+
return roots;
|
|
758
|
+
}
|
|
759
|
+
function splitSpecifier(specifier) {
|
|
760
|
+
const parts = specifier.split("/");
|
|
761
|
+
const take = specifier.startsWith("@") ? 2 : 1;
|
|
762
|
+
return {
|
|
763
|
+
name: parts.slice(0, take).join("/"),
|
|
764
|
+
subpath: parts.length > take ? `./${parts.slice(take).join("/")}` : "."
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
function pickExportTarget(value) {
|
|
768
|
+
if (typeof value === "string") return value;
|
|
769
|
+
if (!value || typeof value !== "object") return void 0;
|
|
770
|
+
for (const condition of ["import", "module", "default", "node"]) {
|
|
771
|
+
if (condition in value) {
|
|
772
|
+
const picked = pickExportTarget(value[condition]);
|
|
773
|
+
if (picked) return picked;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return void 0;
|
|
777
|
+
}
|
|
778
|
+
function resolveExportsTarget(exportsField, subpath) {
|
|
779
|
+
if (typeof exportsField === "string") return subpath === "." ? exportsField : void 0;
|
|
780
|
+
if (!exportsField || typeof exportsField !== "object") return void 0;
|
|
781
|
+
const keys = Object.keys(exportsField);
|
|
782
|
+
const isSubpathMap = keys.some((key) => key === "." || key.startsWith("./"));
|
|
783
|
+
if (!isSubpathMap) return subpath === "." ? pickExportTarget(exportsField) : void 0;
|
|
784
|
+
if (subpath in exportsField) return pickExportTarget(exportsField[subpath]);
|
|
785
|
+
for (const key of keys) {
|
|
786
|
+
const star = key.indexOf("*");
|
|
787
|
+
if (star === -1) continue;
|
|
788
|
+
const prefix = key.slice(0, star);
|
|
789
|
+
const suffix = key.slice(star + 1);
|
|
790
|
+
if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix)) continue;
|
|
791
|
+
const matched = subpath.slice(prefix.length, subpath.length - suffix.length);
|
|
792
|
+
const target = pickExportTarget(exportsField[key]);
|
|
793
|
+
if (target) return target.replace("*", matched);
|
|
794
|
+
}
|
|
795
|
+
return void 0;
|
|
796
|
+
}
|
|
797
|
+
function resolveHostSpecifierManually(root, specifier) {
|
|
798
|
+
const { name: name2, subpath } = splitSpecifier(specifier);
|
|
799
|
+
for (const pkgDir of [join2(root, name2), join2(root, "node_modules", name2)]) {
|
|
800
|
+
const manifest = join2(pkgDir, "package.json");
|
|
801
|
+
if (!existsSync2(manifest)) continue;
|
|
802
|
+
let pkg;
|
|
803
|
+
try {
|
|
804
|
+
pkg = JSON.parse(readFileSync3(manifest, "utf8"));
|
|
805
|
+
} catch {
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
const target = pkg.exports !== void 0 ? resolveExportsTarget(pkg.exports, subpath) : subpath === "." ? pkg.module ?? pkg.main ?? "index.js" : void 0;
|
|
809
|
+
if (typeof target !== "string" || !target) continue;
|
|
810
|
+
const file = join2(pkgDir, target);
|
|
811
|
+
if (existsSync2(file)) return file;
|
|
812
|
+
}
|
|
813
|
+
return void 0;
|
|
814
|
+
}
|
|
815
|
+
function resolveHostSpecifier(specifier, roots = hostModuleRoots()) {
|
|
816
|
+
for (const root of roots) {
|
|
817
|
+
try {
|
|
818
|
+
const resolved = createRequire(join2(root, "noop.js")).resolve(specifier);
|
|
819
|
+
if (typeof resolved === "string" && resolved) return resolved;
|
|
820
|
+
} catch {
|
|
821
|
+
}
|
|
822
|
+
const manual = resolveHostSpecifierManually(root, specifier);
|
|
823
|
+
if (manual) return manual;
|
|
824
|
+
}
|
|
825
|
+
return void 0;
|
|
826
|
+
}
|
|
725
827
|
async function optionalImport(specifier, options = {}) {
|
|
726
828
|
const timeoutMs = typeof options.timeoutMs === "number" && options.timeoutMs > 0 ? options.timeoutMs : IMPORT_TIMEOUT_MS;
|
|
727
|
-
const
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
829
|
+
const injected = typeof options.importFn === "function" ? options.importFn : void 0;
|
|
830
|
+
const importFn = injected ?? ((id) => import(id));
|
|
831
|
+
const candidates = [specifier];
|
|
832
|
+
if (!injected && !specifier.startsWith(".") && !specifier.startsWith("/") && !specifier.startsWith("file:")) {
|
|
833
|
+
const resolved = resolveHostSpecifier(specifier, options.hostRoots ?? hostModuleRoots());
|
|
834
|
+
if (resolved) candidates.push(pathToFileURL(resolved).href);
|
|
835
|
+
}
|
|
836
|
+
for (const candidate of candidates) {
|
|
837
|
+
try {
|
|
838
|
+
return await withImportTimeout(
|
|
839
|
+
Promise.resolve(importFn(candidate)),
|
|
840
|
+
timeoutMs,
|
|
841
|
+
`Import timed out after ${timeoutMs}ms: ${specifier}`
|
|
842
|
+
);
|
|
843
|
+
} catch {
|
|
844
|
+
}
|
|
736
845
|
}
|
|
846
|
+
return void 0;
|
|
737
847
|
}
|
|
738
848
|
function textOf(content) {
|
|
739
849
|
if (typeof content === "string") return content;
|
|
@@ -1286,7 +1396,11 @@ async function createGrokBuildAdapter(session, options = {}) {
|
|
|
1286
1396
|
env: async () => void 0,
|
|
1287
1397
|
fileExists: async () => false
|
|
1288
1398
|
})
|
|
1289
|
-
})
|
|
1399
|
+
}),
|
|
1400
|
+
// The host's durable attachment store. Without it PiAiAdapter rejects any
|
|
1401
|
+
// request carrying an image ("requires the durable attachment service"),
|
|
1402
|
+
// and with it images are normalized to the configured budget for us.
|
|
1403
|
+
resolveAttachments: typeof options.resolveAttachments === "function" ? () => options.resolveAttachments() : void 0
|
|
1290
1404
|
});
|
|
1291
1405
|
return {
|
|
1292
1406
|
adapter,
|
|
@@ -1298,17 +1412,17 @@ async function createGrokBuildAdapter(session, options = {}) {
|
|
|
1298
1412
|
}
|
|
1299
1413
|
|
|
1300
1414
|
// src/session.js
|
|
1301
|
-
function resolveGrokBin(env = process.env, exists =
|
|
1415
|
+
function resolveGrokBin(env = process.env, exists = existsSync3) {
|
|
1302
1416
|
if (typeof env.DSH_GROK_BIN === "string" && env.DSH_GROK_BIN.trim()) return env.DSH_GROK_BIN.trim();
|
|
1303
|
-
const local =
|
|
1417
|
+
const local = join3(grokHome(env), "bin", "grok");
|
|
1304
1418
|
if (exists(local)) return local;
|
|
1305
1419
|
return "grok";
|
|
1306
1420
|
}
|
|
1307
|
-
function grokCliAvailable(env = process.env, exists =
|
|
1421
|
+
function grokCliAvailable(env = process.env, exists = existsSync3) {
|
|
1308
1422
|
const bin = resolveGrokBin(env, exists);
|
|
1309
1423
|
if (bin.includes("/") || bin.includes("\\")) return exists(bin);
|
|
1310
1424
|
const delimiter = process.platform === "win32" ? ";" : ":";
|
|
1311
|
-
return String(env.PATH ?? "").split(delimiter).some((dir) => dir && exists(
|
|
1425
|
+
return String(env.PATH ?? "").split(delimiter).some((dir) => dir && exists(join3(dir, bin)));
|
|
1312
1426
|
}
|
|
1313
1427
|
function spawnGrokLogin(options = {}) {
|
|
1314
1428
|
const spawnFn = options.spawn ?? spawn;
|
|
@@ -1973,8 +2087,15 @@ async function deferredBoot(ctx, session, notifyCatalogChange, options = {}) {
|
|
|
1973
2087
|
}
|
|
1974
2088
|
if (options.skipUpgrade) return;
|
|
1975
2089
|
const createAsync = options.createAsync ?? createGrokBuildAdapter;
|
|
2090
|
+
const resolveAttachments = () => {
|
|
2091
|
+
try {
|
|
2092
|
+
return typeof ctx.get === "function" ? ctx.get("attachments") : void 0;
|
|
2093
|
+
} catch {
|
|
2094
|
+
return void 0;
|
|
2095
|
+
}
|
|
2096
|
+
};
|
|
1976
2097
|
try {
|
|
1977
|
-
const created = await createAsync(session);
|
|
2098
|
+
const created = await createAsync(session, { ...options.adapterOptions, resolveAttachments });
|
|
1978
2099
|
if (created.kind === "pi-ai") {
|
|
1979
2100
|
if (created.note) ctx.logger?.warn?.(created.note);
|
|
1980
2101
|
ctx.llm.registerAdapter([PROVIDER_ID], created.adapter);
|