deepseek-web-retrieval 0.8.0 → 0.10.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.
- package/CHANGELOG.md +18 -1
- package/README.md +33 -16
- package/dist/cli.js +307 -174
- package/docs/adr/0001-refuse-drift-between-plan-and-apply.md +7 -3
- package/docs/adr/0002-shared-config-base-layer.md +5 -1
- package/docs/adr/0003-need-content-per-call-boolean.md +49 -0
- package/docs/agents/claude-code.md +16 -6
- package/docs/agents/codex.md +14 -8
- package/docs/agents/domain.md +2 -2
- package/docs/agents/issue-tracker.md +5 -5
- package/docs/agents/opencode-v2.md +29 -15
- package/docs/agents/pi.md +41 -12
- package/docs/configuration.md +24 -7
- package/docs/design/config-layering-design.md +10 -5
- package/docs/design/installer-design.md +57 -25
- package/docs/design/search-content-design.md +133 -0
- package/docs/integration.md +19 -16
- package/docs/research/control-matrix.md +39 -34
- package/docs/research/mcp-compatibility.md +3 -1
- package/docs/research/ori-question-sourcing.md +31 -20
- package/docs/research/result-quality-passthrough.md +9 -7
- package/docs/research/search-api-defaults.md +4 -2
- package/docs/research/search-inline-content.md +49 -9
- package/package.json +3 -2
- package/src/core/auth.ts +7 -0
- package/src/core/client.ts +98 -27
- package/src/core/config-summary.ts +3 -0
- package/src/core/constants.ts +30 -2
- package/src/core/descriptions.ts +17 -2
- package/src/core/env.ts +7 -1
- package/src/core/format.ts +35 -5
- package/src/core/options.ts +56 -0
- package/src/core/search.ts +106 -15
- package/src/core/types.ts +29 -0
- package/src/installer/apply.ts +4 -4
- package/src/installer/cli.ts +2 -2
- package/src/installer/config-command.ts +4 -3
- package/src/installer/hosts/claude-code.ts +3 -17
- package/src/installer/hosts/codex.ts +3 -17
- package/src/installer/hosts/index.ts +2 -2
- package/src/installer/hosts/opencode.ts +1 -1
- package/src/installer/hosts/pi.ts +14 -15
- package/src/installer/hosts/shared.ts +23 -0
- package/src/installer/ownership.ts +2 -2
- package/src/installer/paths.ts +2 -2
- package/src/installer/{state.ts → record.ts} +15 -15
- package/src/installer/types.ts +1 -1
- package/src/installer/uninstall.ts +4 -4
- package/src/mcp/serve.ts +1 -2
- package/src/mcp/server.ts +19 -19
- package/src/opencode/plugin.ts +15 -10
- package/src/pi/extension.ts +26 -17
- package/src/mcp/auth.ts +0 -11
package/dist/cli.js
CHANGED
|
@@ -16,10 +16,12 @@ var __export = (target, all) => {
|
|
|
16
16
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
17
|
|
|
18
18
|
// src/core/constants.ts
|
|
19
|
-
var
|
|
19
|
+
var DEFAULT_API_BASE_URL = "https://api.deepseek.com", SEARCH_ENDPOINT_PATH = "/v1/tools/search", FETCH_ENDPOINT_PATH = "/v1/tools/fetch", SEARCH_ENDPOINT, FETCH_ENDPOINT, DEFAULT_SEARCH_COUNT = 10, DEFAULT_MAX_RESULTS = 10, DEFAULT_MIN_RERANK = 0, DEFAULT_MIN_AUTHORITY = 0, DEFAULT_MAX_SUMMARY_CHARS = 1200, DEFAULT_SEARCH_CONTENT_MAX_CHARS = 1500, DEFAULT_SEARCH_CONTENT_TIMEOUT_MS = 60000, DEFAULT_FETCH_MAX_CHARS = 16000, MAX_FETCH_CHARS = 1e6, MAX_SEARCH_COUNT = 50, DEFAULT_TIMEOUT_MS = 30000, MIN_TIMEOUT_MS = 1000, MAX_TIMEOUT_MS = 120000, MAX_QUERY_CHARS = 128, MAX_ORI_QUESTION_CHARS = 128, MAX_URL_CHARS = 8192, MAX_ERROR_BODY_CHARS = 2000, MAX_RESPONSE_BYTES, FETCH_TRUNCATION_MARKER = `
|
|
20
20
|
|
|
21
|
-
[content truncated]`, DEFAULT_API_KEY_SOURCE = "auto", DEFAULT_MAX_RETRIES = 2, MAX_RETRIES = 5, DEFAULT_RETRY_BASE_DELAY_MS = 250, MAX_RETRY_DELAY_MS = 1e4, API_KEY_ENV = "DEEPSEEK_API_KEY", ENABLE_SEARCH_ENV = "DEEPSEEK_ENABLE_SEARCH", ENABLE_FETCH_ENV = "DEEPSEEK_ENABLE_FETCH", SEARCH_LOCALIZATION_ENV = "DEEPSEEK_SEARCH_LOCALIZATION";
|
|
21
|
+
[content truncated]`, DEFAULT_API_KEY_SOURCE = "auto", DEFAULT_MAX_RETRIES = 2, MAX_RETRIES = 5, DEFAULT_RETRY_BASE_DELAY_MS = 250, MAX_RETRY_DELAY_MS = 1e4, API_KEY_ENV = "DEEPSEEK_API_KEY", BASE_URL_ENV = "DEEPSEEK_BASE_URL", ENABLE_SEARCH_ENV = "DEEPSEEK_ENABLE_SEARCH", ENABLE_FETCH_ENV = "DEEPSEEK_ENABLE_FETCH", SEARCH_LOCALIZATION_ENV = "DEEPSEEK_SEARCH_LOCALIZATION";
|
|
22
22
|
var init_constants = __esm(() => {
|
|
23
|
+
SEARCH_ENDPOINT = `${DEFAULT_API_BASE_URL}${SEARCH_ENDPOINT_PATH}`;
|
|
24
|
+
FETCH_ENDPOINT = `${DEFAULT_API_BASE_URL}${FETCH_ENDPOINT_PATH}`;
|
|
23
25
|
MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
24
26
|
});
|
|
25
27
|
|
|
@@ -138,9 +140,15 @@ function resolvePluginOptions(input) {
|
|
|
138
140
|
search: resolveSearchOptions(input),
|
|
139
141
|
fetch: resolveFetchOptions(input),
|
|
140
142
|
auth: resolveAuthOptions(input),
|
|
141
|
-
retry: resolveRetryOptions(input)
|
|
143
|
+
retry: resolveRetryOptions(input),
|
|
144
|
+
baseUrl: resolveBaseUrl(input?.baseUrl)
|
|
142
145
|
};
|
|
143
146
|
}
|
|
147
|
+
function bothDisabledPrecheck(config, reason) {
|
|
148
|
+
if (config.search.enabled || config.fetch.enabled)
|
|
149
|
+
return;
|
|
150
|
+
return `Both tools are disabled (${reason}); at least one must stay enabled.`;
|
|
151
|
+
}
|
|
144
152
|
function resolveSearchOptions(input) {
|
|
145
153
|
const count = clampInt(input?.count, DEFAULT_SEARCH_COUNT, 1, 50);
|
|
146
154
|
return {
|
|
@@ -154,6 +162,8 @@ function resolveSearchOptions(input) {
|
|
|
154
162
|
language: optionalText(input?.language),
|
|
155
163
|
localization: localizationValue(input?.searchLocalization),
|
|
156
164
|
maxSummaryChars: clampInt(input?.maxSummaryChars, DEFAULT_MAX_SUMMARY_CHARS, 0, 1e6),
|
|
165
|
+
contentMaxChars: clampInt(input?.searchContentMaxChars, DEFAULT_SEARCH_CONTENT_MAX_CHARS, 0, 1e6),
|
|
166
|
+
contentTimeoutMs: clampInt(input?.searchContentTimeoutMs, DEFAULT_SEARCH_CONTENT_TIMEOUT_MS, MIN_TIMEOUT_MS, MAX_TIMEOUT_MS),
|
|
157
167
|
timeoutMs: clampInt(input?.searchTimeoutMs, DEFAULT_TIMEOUT_MS, MIN_TIMEOUT_MS, MAX_TIMEOUT_MS),
|
|
158
168
|
enabled: input?.searchEnabled !== false
|
|
159
169
|
};
|
|
@@ -180,6 +190,28 @@ function resolveRetryOptions(input) {
|
|
|
180
190
|
baseDelayMs: clampInt(input?.retryBaseDelayMs, DEFAULT_RETRY_BASE_DELAY_MS, 0, MAX_RETRY_DELAY_MS)
|
|
181
191
|
};
|
|
182
192
|
}
|
|
193
|
+
function resolveBaseUrl(value) {
|
|
194
|
+
if (typeof value !== "string")
|
|
195
|
+
return DEFAULT_API_BASE_URL;
|
|
196
|
+
const trimmed = value.trim();
|
|
197
|
+
if (!trimmed)
|
|
198
|
+
return DEFAULT_API_BASE_URL;
|
|
199
|
+
let parsed;
|
|
200
|
+
try {
|
|
201
|
+
parsed = new URL(trimmed);
|
|
202
|
+
} catch {
|
|
203
|
+
return DEFAULT_API_BASE_URL;
|
|
204
|
+
}
|
|
205
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
|
|
206
|
+
return DEFAULT_API_BASE_URL;
|
|
207
|
+
return trimmed.replace(/\/+$/, "");
|
|
208
|
+
}
|
|
209
|
+
function withPerCallCount(options, count) {
|
|
210
|
+
if (count === undefined || !Number.isFinite(count))
|
|
211
|
+
return options;
|
|
212
|
+
const clamped = Math.min(MAX_SEARCH_COUNT, Math.max(1, Math.trunc(count)));
|
|
213
|
+
return { ...options, count: clamped, maxResults: clamped };
|
|
214
|
+
}
|
|
183
215
|
function optionalText(value) {
|
|
184
216
|
if (typeof value !== "string")
|
|
185
217
|
return;
|
|
@@ -249,6 +281,8 @@ var init_config_summary = __esm(() => {
|
|
|
249
281
|
{ key: "language", read: (o) => o.search.language },
|
|
250
282
|
{ key: "searchLocalization", read: (o) => o.search.localization },
|
|
251
283
|
{ key: "maxSummaryChars", read: (o) => o.search.maxSummaryChars },
|
|
284
|
+
{ key: "searchContentMaxChars", read: (o) => o.search.contentMaxChars },
|
|
285
|
+
{ key: "searchContentTimeoutMs", read: (o) => o.search.contentTimeoutMs },
|
|
252
286
|
{ key: "searchTimeoutMs", read: (o) => o.search.timeoutMs },
|
|
253
287
|
{ key: "fetchMaxChars", read: (o) => o.fetch.maxChars },
|
|
254
288
|
{ key: "fetchAllowedDomains", read: (o) => o.fetch.allowedDomains },
|
|
@@ -256,7 +290,8 @@ var init_config_summary = __esm(() => {
|
|
|
256
290
|
{ key: "fetchTimeoutMs", read: (o) => o.fetch.timeoutMs },
|
|
257
291
|
{ key: "fetchBlockPrivateHosts", read: (o) => o.fetch.blockPrivateHosts },
|
|
258
292
|
{ key: "maxRetries", read: (o) => o.retry.maxRetries },
|
|
259
|
-
{ key: "retryBaseDelayMs", read: (o) => o.retry.baseDelayMs }
|
|
293
|
+
{ key: "retryBaseDelayMs", read: (o) => o.retry.baseDelayMs },
|
|
294
|
+
{ key: "baseUrl", read: (o) => o.baseUrl }
|
|
260
295
|
];
|
|
261
296
|
CONFIG_KEYS = VOCABULARY.map((entry) => entry.key);
|
|
262
297
|
});
|
|
@@ -3415,55 +3450,6 @@ var init_formats = __esm(() => {
|
|
|
3415
3450
|
init_toml();
|
|
3416
3451
|
});
|
|
3417
3452
|
|
|
3418
|
-
// src/installer/revert.ts
|
|
3419
|
-
import { existsSync as existsSync4, readFileSync as readFileSync7, rmdirSync, unlinkSync } from "node:fs";
|
|
3420
|
-
import { dirname as dirname2 } from "node:path";
|
|
3421
|
-
function pruneEmptyDir(dir) {
|
|
3422
|
-
try {
|
|
3423
|
-
rmdirSync(dir);
|
|
3424
|
-
} catch {}
|
|
3425
|
-
}
|
|
3426
|
-
function revertRecordedFile(file, host, opts) {
|
|
3427
|
-
const lines = [];
|
|
3428
|
-
if (!existsSync4(file.file)) {
|
|
3429
|
-
lines.push(`already absent: ${file.file}${opts.label}`);
|
|
3430
|
-
return lines;
|
|
3431
|
-
}
|
|
3432
|
-
const codec = codecFor(file.format, host);
|
|
3433
|
-
const text = readFileSync7(file.file, "utf8");
|
|
3434
|
-
if (file.format === "rules") {
|
|
3435
|
-
const edit2 = { file: file.file, format: "rules", block: file.blockApplied ?? "" };
|
|
3436
|
-
if (!codec.verify(text, edit2)) {
|
|
3437
|
-
lines.push(`no rules block${opts.label} in ${file.file}; left untouched`);
|
|
3438
|
-
return lines;
|
|
3439
|
-
}
|
|
3440
|
-
const updated = codec.revert(text, file).text;
|
|
3441
|
-
if (opts.whole && codec.isEffectivelyEmpty(updated)) {
|
|
3442
|
-
unlinkSync(file.file);
|
|
3443
|
-
pruneEmptyDir(dirname2(file.file));
|
|
3444
|
-
lines.push(`deleted ${file.file} (installer-created, now empty)`);
|
|
3445
|
-
} else {
|
|
3446
|
-
writeConfig(file.file, updated);
|
|
3447
|
-
lines.push(`removed rules block${opts.label} from ${file.file}`);
|
|
3448
|
-
}
|
|
3449
|
-
return lines;
|
|
3450
|
-
}
|
|
3451
|
-
const reverted = codec.revert(text, file);
|
|
3452
|
-
if (opts.whole && file.created && codec.isEffectivelyEmpty(reverted.text)) {
|
|
3453
|
-
unlinkSync(file.file);
|
|
3454
|
-
pruneEmptyDir(dirname2(file.file));
|
|
3455
|
-
lines.push(`deleted ${file.file} (installer-created, now empty)`);
|
|
3456
|
-
} else {
|
|
3457
|
-
writeConfig(file.file, reverted.text);
|
|
3458
|
-
lines.push(`reverted artifacts${opts.label} in ${file.file}`);
|
|
3459
|
-
}
|
|
3460
|
-
lines.push(...reverted.notes.map((note) => ` note: ${note}`));
|
|
3461
|
-
return lines;
|
|
3462
|
-
}
|
|
3463
|
-
var init_revert = __esm(() => {
|
|
3464
|
-
init_formats();
|
|
3465
|
-
});
|
|
3466
|
-
|
|
3467
3453
|
// src/installer/paths.ts
|
|
3468
3454
|
import { join as pathJoin } from "node:path";
|
|
3469
3455
|
function join2(...parts) {
|
|
@@ -3473,35 +3459,35 @@ function join2(...parts) {
|
|
|
3473
3459
|
function configHome(roots) {
|
|
3474
3460
|
return roots.configHome ?? join2(roots.home, ".config");
|
|
3475
3461
|
}
|
|
3476
|
-
function
|
|
3462
|
+
function recordDir(roots, scope) {
|
|
3477
3463
|
return scope === "global" ? join2(configHome(roots), "deepseek-web-retrieval") : join2(roots.cwd, ".deepseek-web-retrieval");
|
|
3478
3464
|
}
|
|
3479
3465
|
var init_paths = () => {};
|
|
3480
3466
|
|
|
3481
|
-
// src/installer/
|
|
3482
|
-
import { existsSync as
|
|
3467
|
+
// src/installer/record.ts
|
|
3468
|
+
import { existsSync as existsSync4, lstatSync as lstatSync2, readdirSync, readFileSync as readFileSync7, rmSync as rmSync2 } from "node:fs";
|
|
3483
3469
|
import { isAbsolute, join as join3 } from "node:path";
|
|
3484
3470
|
import { z } from "zod/v4";
|
|
3485
|
-
function
|
|
3486
|
-
return join3(
|
|
3471
|
+
function recordPathFor(input) {
|
|
3472
|
+
return join3(recordDir(input, input.scope), `install.${input.host}.json`);
|
|
3487
3473
|
}
|
|
3488
3474
|
function platformOf(roots) {
|
|
3489
3475
|
return roots.platform ?? process.platform;
|
|
3490
3476
|
}
|
|
3491
|
-
function
|
|
3477
|
+
function legacyGlobalRecordDir(roots) {
|
|
3492
3478
|
return join3(roots.home, ".config", "deepseek-web-retrieval");
|
|
3493
3479
|
}
|
|
3494
3480
|
function recordReadPath(input, platform = platformOf(input)) {
|
|
3495
|
-
const canonical =
|
|
3496
|
-
if (platform !== "win32" || input.scope !== "global" ||
|
|
3481
|
+
const canonical = recordPathFor(input);
|
|
3482
|
+
if (platform !== "win32" || input.scope !== "global" || existsSync4(canonical))
|
|
3497
3483
|
return canonical;
|
|
3498
|
-
const legacy = join3(
|
|
3499
|
-
return
|
|
3484
|
+
const legacy = join3(legacyGlobalRecordDir(input), `install.${input.host}.json`);
|
|
3485
|
+
return existsSync4(legacy) ? legacy : canonical;
|
|
3500
3486
|
}
|
|
3501
3487
|
function readRecordFile(file, host, scope) {
|
|
3502
3488
|
let text;
|
|
3503
3489
|
try {
|
|
3504
|
-
text =
|
|
3490
|
+
text = readFileSync7(file, "utf8");
|
|
3505
3491
|
} catch (error) {
|
|
3506
3492
|
if (error.code === "ENOENT") {
|
|
3507
3493
|
try {
|
|
@@ -3539,7 +3525,7 @@ function claimedByOtherScope(input, files) {
|
|
|
3539
3525
|
return files.filter((file) => record.files.some((candidate) => candidate.file === file));
|
|
3540
3526
|
}
|
|
3541
3527
|
function writeRecord(input, record) {
|
|
3542
|
-
const file =
|
|
3528
|
+
const file = recordPathFor(input);
|
|
3543
3529
|
atomicWrite(file, `${JSON.stringify(record, null, 2)}
|
|
3544
3530
|
`, 384);
|
|
3545
3531
|
}
|
|
@@ -3559,7 +3545,7 @@ function appendedByPrevious(record, path) {
|
|
|
3559
3545
|
}
|
|
3560
3546
|
function deleteRecord(input) {
|
|
3561
3547
|
const file = recordReadPath(input);
|
|
3562
|
-
if (
|
|
3548
|
+
if (existsSync4(file))
|
|
3563
3549
|
rmSync2(file);
|
|
3564
3550
|
}
|
|
3565
3551
|
function readRecordsFromDir(dir, scope) {
|
|
@@ -3591,18 +3577,18 @@ function hasRecordFiles(dir) {
|
|
|
3591
3577
|
function readingLegacyGlobalRecords(roots) {
|
|
3592
3578
|
if (platformOf(roots) !== "win32")
|
|
3593
3579
|
return false;
|
|
3594
|
-
if (hasRecordFiles(
|
|
3580
|
+
if (hasRecordFiles(recordDir(roots, "global")))
|
|
3595
3581
|
return false;
|
|
3596
|
-
return hasRecordFiles(
|
|
3582
|
+
return hasRecordFiles(legacyGlobalRecordDir(roots));
|
|
3597
3583
|
}
|
|
3598
3584
|
function listRecords(roots) {
|
|
3599
|
-
const global = readRecordsFromDir(
|
|
3600
|
-
const project = readRecordsFromDir(
|
|
3601
|
-
const legacy = readingLegacyGlobalRecords(roots) ? readRecordsFromDir(
|
|
3585
|
+
const global = readRecordsFromDir(recordDir(roots, "global"), "global");
|
|
3586
|
+
const project = readRecordsFromDir(recordDir(roots, "project"), "project");
|
|
3587
|
+
const legacy = readingLegacyGlobalRecords(roots) ? readRecordsFromDir(legacyGlobalRecordDir(roots), "global") : [];
|
|
3602
3588
|
return [...global, ...legacy, ...project];
|
|
3603
3589
|
}
|
|
3604
3590
|
var keyPath, tier, operation, recordSchema, InstallRecordError;
|
|
3605
|
-
var
|
|
3591
|
+
var init_record = __esm(() => {
|
|
3606
3592
|
init_paths();
|
|
3607
3593
|
init_persistence();
|
|
3608
3594
|
keyPath = z.array(z.string().min(1)).min(1);
|
|
@@ -3637,7 +3623,7 @@ var init_state = __esm(() => {
|
|
|
3637
3623
|
file;
|
|
3638
3624
|
kind;
|
|
3639
3625
|
constructor(file, kind, detail) {
|
|
3640
|
-
super(`${kind === "invalid" ? "Invalid install record" : "Cannot read install record"} at ${file} (${detail}). ` + "Restore a valid record from
|
|
3626
|
+
super(`${kind === "invalid" ? "Invalid install record" : "Cannot read install record"} at ${file} (${detail}). ` + "Restore a valid install record from your own copy or repair its permissions before retrying; configuration was not changed. Do not delete the install record to bypass this check.");
|
|
3641
3627
|
this.file = file;
|
|
3642
3628
|
this.kind = kind;
|
|
3643
3629
|
this.name = "InstallRecordError";
|
|
@@ -3645,6 +3631,55 @@ var init_state = __esm(() => {
|
|
|
3645
3631
|
};
|
|
3646
3632
|
});
|
|
3647
3633
|
|
|
3634
|
+
// src/installer/revert.ts
|
|
3635
|
+
import { existsSync as existsSync5, readFileSync as readFileSync8, rmdirSync, unlinkSync } from "node:fs";
|
|
3636
|
+
import { dirname as dirname2 } from "node:path";
|
|
3637
|
+
function pruneEmptyDir(dir) {
|
|
3638
|
+
try {
|
|
3639
|
+
rmdirSync(dir);
|
|
3640
|
+
} catch {}
|
|
3641
|
+
}
|
|
3642
|
+
function revertRecordedFile(file, host, opts) {
|
|
3643
|
+
const lines = [];
|
|
3644
|
+
if (!existsSync5(file.file)) {
|
|
3645
|
+
lines.push(`already absent: ${file.file}${opts.label}`);
|
|
3646
|
+
return lines;
|
|
3647
|
+
}
|
|
3648
|
+
const codec = codecFor(file.format, host);
|
|
3649
|
+
const text = readFileSync8(file.file, "utf8");
|
|
3650
|
+
if (file.format === "rules") {
|
|
3651
|
+
const edit2 = { file: file.file, format: "rules", block: file.blockApplied ?? "" };
|
|
3652
|
+
if (!codec.verify(text, edit2)) {
|
|
3653
|
+
lines.push(`no rules block${opts.label} in ${file.file}; left untouched`);
|
|
3654
|
+
return lines;
|
|
3655
|
+
}
|
|
3656
|
+
const updated = codec.revert(text, file).text;
|
|
3657
|
+
if (opts.whole && codec.isEffectivelyEmpty(updated)) {
|
|
3658
|
+
unlinkSync(file.file);
|
|
3659
|
+
pruneEmptyDir(dirname2(file.file));
|
|
3660
|
+
lines.push(`deleted ${file.file} (installer-created, now empty)`);
|
|
3661
|
+
} else {
|
|
3662
|
+
writeConfig(file.file, updated);
|
|
3663
|
+
lines.push(`removed rules block${opts.label} from ${file.file}`);
|
|
3664
|
+
}
|
|
3665
|
+
return lines;
|
|
3666
|
+
}
|
|
3667
|
+
const reverted = codec.revert(text, file);
|
|
3668
|
+
if (opts.whole && file.created && codec.isEffectivelyEmpty(reverted.text)) {
|
|
3669
|
+
unlinkSync(file.file);
|
|
3670
|
+
pruneEmptyDir(dirname2(file.file));
|
|
3671
|
+
lines.push(`deleted ${file.file} (installer-created, now empty)`);
|
|
3672
|
+
} else {
|
|
3673
|
+
writeConfig(file.file, reverted.text);
|
|
3674
|
+
lines.push(`reverted artifacts${opts.label} in ${file.file}`);
|
|
3675
|
+
}
|
|
3676
|
+
lines.push(...reverted.notes.map((note) => ` note: ${note}`));
|
|
3677
|
+
return lines;
|
|
3678
|
+
}
|
|
3679
|
+
var init_revert = __esm(() => {
|
|
3680
|
+
init_formats();
|
|
3681
|
+
});
|
|
3682
|
+
|
|
3648
3683
|
// src/installer/ownership.ts
|
|
3649
3684
|
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync9, realpathSync as realpathSync2, rmSync as rmSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3650
3685
|
import { basename as basename2, dirname as dirname3, join as join4, resolve } from "node:path";
|
|
@@ -3672,7 +3707,7 @@ function canonicalPath(file) {
|
|
|
3672
3707
|
return join4(canonicalPath(dirname3(absolute)), basename2(absolute));
|
|
3673
3708
|
}
|
|
3674
3709
|
function ownerOf(input) {
|
|
3675
|
-
return { recordPath: canonicalPath(
|
|
3710
|
+
return { recordPath: canonicalPath(recordPathFor(input)), scope: input.scope, cwd: canonicalPath(input.cwd) };
|
|
3676
3711
|
}
|
|
3677
3712
|
function claimPaths(input, files) {
|
|
3678
3713
|
return [
|
|
@@ -3748,8 +3783,8 @@ function releaseFileOwnership(input, files) {
|
|
|
3748
3783
|
releaseClaim(input, claim);
|
|
3749
3784
|
}
|
|
3750
3785
|
var init_ownership = __esm(() => {
|
|
3786
|
+
init_record();
|
|
3751
3787
|
init_revert();
|
|
3752
|
-
init_state();
|
|
3753
3788
|
});
|
|
3754
3789
|
|
|
3755
3790
|
// src/installer/operation-lock.ts
|
|
@@ -3808,7 +3843,7 @@ function applyPlan(plan, input) {
|
|
|
3808
3843
|
if (plan.errors.length > 0) {
|
|
3809
3844
|
throw new Error(`refusing to apply a plan with errors: ${plan.errors.map((error) => error.message).join("; ")}`);
|
|
3810
3845
|
}
|
|
3811
|
-
return withOperationLock([
|
|
3846
|
+
return withOperationLock([recordPathFor(input)], () => withOperationLock([...plan.edits, ...plan.reverts ?? []].map((file) => file.file), () => applyLocked(plan, input)));
|
|
3812
3847
|
}
|
|
3813
3848
|
function applyLocked(plan, input) {
|
|
3814
3849
|
const touched = [...plan.edits, ...plan.reverts ?? []];
|
|
@@ -3934,7 +3969,7 @@ function rollbackApply(input, previous, snapshots) {
|
|
|
3934
3969
|
writeRecord(input, previous);
|
|
3935
3970
|
else
|
|
3936
3971
|
deleteRecord(input);
|
|
3937
|
-
if (previous === undefined ? existsSync7(
|
|
3972
|
+
if (previous === undefined ? existsSync7(recordPathFor(input)) : !deepEqual(readRecord(input), previous)) {
|
|
3938
3973
|
throw new Error("restored install record differs from its previous state");
|
|
3939
3974
|
}
|
|
3940
3975
|
} catch (restoreError) {
|
|
@@ -4033,8 +4068,8 @@ var init_apply = __esm(() => {
|
|
|
4033
4068
|
init_value();
|
|
4034
4069
|
init_operation_lock();
|
|
4035
4070
|
init_ownership();
|
|
4071
|
+
init_record();
|
|
4036
4072
|
init_revert();
|
|
4037
|
-
init_state();
|
|
4038
4073
|
ApplyError = class ApplyError extends Error {
|
|
4039
4074
|
status;
|
|
4040
4075
|
constructor(status, message, cause) {
|
|
@@ -4050,6 +4085,12 @@ function apiKeyFromEnv(env = process.env) {
|
|
|
4050
4085
|
const key = env[API_KEY_ENV]?.trim();
|
|
4051
4086
|
return key ? key : undefined;
|
|
4052
4087
|
}
|
|
4088
|
+
function requireApiKey(env) {
|
|
4089
|
+
const apiKey = apiKeyFromEnv(env);
|
|
4090
|
+
if (!apiKey)
|
|
4091
|
+
throw new Error(MISSING_API_KEY_MESSAGE);
|
|
4092
|
+
return apiKey;
|
|
4093
|
+
}
|
|
4053
4094
|
var MISSING_API_KEY_MESSAGE;
|
|
4054
4095
|
var init_auth = __esm(() => {
|
|
4055
4096
|
init_constants();
|
|
@@ -4113,15 +4154,29 @@ function formatApiError(operation2, status, body) {
|
|
|
4113
4154
|
function formatTimeoutError(operation2, timeoutMs) {
|
|
4114
4155
|
return `${operation2} timed out after ${timeoutMs} ms`;
|
|
4115
4156
|
}
|
|
4116
|
-
function
|
|
4117
|
-
if (
|
|
4118
|
-
return
|
|
4119
|
-
|
|
4157
|
+
function searchHeader(context) {
|
|
4158
|
+
if (!context)
|
|
4159
|
+
return;
|
|
4160
|
+
if (context.effectiveQuery === context.inputQuery && context.notes.length === 0)
|
|
4161
|
+
return;
|
|
4162
|
+
const header = `Searched for: ${context.effectiveQuery}`;
|
|
4163
|
+
return context.notes.length > 0 ? `${header} — ${context.notes.join("; ")}` : header;
|
|
4164
|
+
}
|
|
4165
|
+
function formatSearchResults(results, context) {
|
|
4166
|
+
const header = searchHeader(context);
|
|
4167
|
+
if (results.length === 0) {
|
|
4168
|
+
return header ? `${header}
|
|
4169
|
+
|
|
4170
|
+
No results found.` : "No results found.";
|
|
4171
|
+
}
|
|
4172
|
+
const body = results.map((result, index) => {
|
|
4120
4173
|
const title = result.title?.trim() || result.url;
|
|
4121
4174
|
const marker = formatQualityMarker(result);
|
|
4122
4175
|
const lines = [`${index + 1}. [${escapeBrackets(title)}](${result.url})${marker ? ` ${marker}` : ""}`];
|
|
4123
4176
|
if (result.content) {
|
|
4124
|
-
lines.push(
|
|
4177
|
+
lines.push(result.contentKind === "page" ? result.content.split(`
|
|
4178
|
+
`).map((line) => ` ${line}`).join(`
|
|
4179
|
+
`) : ` ${result.content.replaceAll(`
|
|
4125
4180
|
`, " ")}`);
|
|
4126
4181
|
}
|
|
4127
4182
|
if (result.published !== undefined) {
|
|
@@ -4132,6 +4187,9 @@ function formatSearchResults(results) {
|
|
|
4132
4187
|
}).join(`
|
|
4133
4188
|
|
|
4134
4189
|
`);
|
|
4190
|
+
return header ? `${header}
|
|
4191
|
+
|
|
4192
|
+
${body}` : body;
|
|
4135
4193
|
}
|
|
4136
4194
|
function formatQualityMarker(result) {
|
|
4137
4195
|
const parts = [];
|
|
@@ -4212,17 +4270,62 @@ var init_url = __esm(() => {
|
|
|
4212
4270
|
});
|
|
4213
4271
|
|
|
4214
4272
|
// src/core/search.ts
|
|
4215
|
-
function
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
const
|
|
4220
|
-
|
|
4221
|
-
parts.push(include.map((domain) => `site:${domain}`).join(" OR "));
|
|
4273
|
+
function charCount(value) {
|
|
4274
|
+
return Array.from(value).length;
|
|
4275
|
+
}
|
|
4276
|
+
function operatorParts(options) {
|
|
4277
|
+
const blocked = normalizeDomains(options.blockedDomains).map((domain) => `-site:${domain}`);
|
|
4278
|
+
const include = normalizeDomains(options.includeDomains).map((domain) => `site:${domain}`);
|
|
4222
4279
|
const language = options.language?.trim().replace(/^lang:/i, "");
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4280
|
+
return {
|
|
4281
|
+
blocked,
|
|
4282
|
+
include: include.length ? include.join(" OR ") : undefined,
|
|
4283
|
+
language: language ? `lang:${language}` : undefined
|
|
4284
|
+
};
|
|
4285
|
+
}
|
|
4286
|
+
function assemble(body, parts, withInclude, withLang) {
|
|
4287
|
+
const segments = [body, ...parts.blocked];
|
|
4288
|
+
if (withInclude && parts.include)
|
|
4289
|
+
segments.push(parts.include);
|
|
4290
|
+
if (withLang && parts.language)
|
|
4291
|
+
segments.push(parts.language);
|
|
4292
|
+
return segments.filter((segment) => segment.length > 0).join(" ");
|
|
4293
|
+
}
|
|
4294
|
+
function bodyBudget(parts, withInclude, withLang, maxChars) {
|
|
4295
|
+
const fixed = [
|
|
4296
|
+
...parts.blocked,
|
|
4297
|
+
...withInclude && parts.include ? [parts.include] : [],
|
|
4298
|
+
...withLang && parts.language ? [parts.language] : []
|
|
4299
|
+
].join(" ");
|
|
4300
|
+
return maxChars - (fixed ? charCount(fixed) + 1 : 0);
|
|
4301
|
+
}
|
|
4302
|
+
function composeSearchQuery(query, options, maxChars = MAX_QUERY_CHARS) {
|
|
4303
|
+
const parts = operatorParts(options);
|
|
4304
|
+
const body = query.trim();
|
|
4305
|
+
const notes = [];
|
|
4306
|
+
const attempt = (withInclude, withLang) => {
|
|
4307
|
+
const budget = bodyBudget(parts, withInclude, withLang, maxChars);
|
|
4308
|
+
if (charCount(body) <= budget)
|
|
4309
|
+
return assemble(body, parts, withInclude, withLang);
|
|
4310
|
+
if (budget >= MIN_BODY_CHARS) {
|
|
4311
|
+
notes.push(`query truncated to ${budget} characters (query length limit)`);
|
|
4312
|
+
return assemble(truncate(body, budget), parts, withInclude, withLang);
|
|
4313
|
+
}
|
|
4314
|
+
return;
|
|
4315
|
+
};
|
|
4316
|
+
let assembled = attempt(true, true);
|
|
4317
|
+
if (assembled === undefined && parts.language) {
|
|
4318
|
+
notes.push("language filter dropped (query length limit)");
|
|
4319
|
+
assembled = attempt(true, false);
|
|
4320
|
+
}
|
|
4321
|
+
if (assembled === undefined && parts.include) {
|
|
4322
|
+
notes.push("includeDomains filter dropped (query length limit)");
|
|
4323
|
+
assembled = attempt(false, false);
|
|
4324
|
+
}
|
|
4325
|
+
if (assembled === undefined) {
|
|
4326
|
+
throw new Error(`DeepSeek search query cannot fit ${maxChars} characters: the blockedDomains -site: ` + `operators alone occupy ${charCount(parts.blocked.join(" "))} characters`);
|
|
4327
|
+
}
|
|
4328
|
+
return { query: assembled, notes };
|
|
4226
4329
|
}
|
|
4227
4330
|
function parsePublished(value) {
|
|
4228
4331
|
if (typeof value !== "string" || !value.trim())
|
|
@@ -4257,7 +4360,14 @@ function processResults(raw, options) {
|
|
|
4257
4360
|
continue;
|
|
4258
4361
|
}
|
|
4259
4362
|
seen.add(url);
|
|
4260
|
-
const
|
|
4363
|
+
const body = optionalText2(item.content);
|
|
4364
|
+
const digest = optionalText2(item.summary) ?? optionalText2(item.snippet) ?? "";
|
|
4365
|
+
const fromPage = body !== undefined && options.contentMaxChars > 0;
|
|
4366
|
+
const rawContent = fromPage && body !== undefined ? body : digest;
|
|
4367
|
+
const contentBudget = fromPage ? options.contentMaxChars : options.maxSummaryChars;
|
|
4368
|
+
const cutContent = truncate(rawContent, contentBudget);
|
|
4369
|
+
const content = cutContent && charCount(rawContent) > contentBudget ? `${cutContent}…` : cutContent;
|
|
4370
|
+
const contentKind = fromPage ? "page" : "digest";
|
|
4261
4371
|
const published = parsePublished(item.date);
|
|
4262
4372
|
const title = optionalText2(item.title);
|
|
4263
4373
|
const rerankScore = typeof rerank === "number" && Number.isFinite(rerank) ? rerank : undefined;
|
|
@@ -4266,7 +4376,7 @@ function processResults(raw, options) {
|
|
|
4266
4376
|
results.push({
|
|
4267
4377
|
url,
|
|
4268
4378
|
...title ? { title } : {},
|
|
4269
|
-
...content ? { content } : {},
|
|
4379
|
+
...content ? { content, contentKind } : {},
|
|
4270
4380
|
...published !== undefined ? { published } : {},
|
|
4271
4381
|
...rerankScore !== undefined ? { rerankScore } : {},
|
|
4272
4382
|
...authorityScore !== undefined ? { authority: authorityScore } : {},
|
|
@@ -4281,7 +4391,9 @@ function optionalText2(value) {
|
|
|
4281
4391
|
const trimmed = value.trim();
|
|
4282
4392
|
return trimmed ? trimmed : undefined;
|
|
4283
4393
|
}
|
|
4394
|
+
var MIN_BODY_CHARS = 8;
|
|
4284
4395
|
var init_search = __esm(() => {
|
|
4396
|
+
init_constants();
|
|
4285
4397
|
init_domains();
|
|
4286
4398
|
init_format2();
|
|
4287
4399
|
init_url();
|
|
@@ -4487,41 +4599,64 @@ var init_transport = __esm(() => {
|
|
|
4487
4599
|
function effectiveTimeout(configuredMs, perCallMs) {
|
|
4488
4600
|
return perCallMs === undefined ? configuredMs : Math.min(configuredMs, perCallMs);
|
|
4489
4601
|
}
|
|
4602
|
+
function effectiveMaxChars(configured, perCall) {
|
|
4603
|
+
if (perCall === undefined || !Number.isFinite(perCall))
|
|
4604
|
+
return configured;
|
|
4605
|
+
return Math.min(MAX_FETCH_CHARS, Math.max(0, Math.trunc(perCall)));
|
|
4606
|
+
}
|
|
4607
|
+
function apiUrl(baseUrl, path) {
|
|
4608
|
+
return `${baseUrl ?? DEFAULT_API_BASE_URL}${path}`;
|
|
4609
|
+
}
|
|
4490
4610
|
function oriQuestionValue(oriQuestion) {
|
|
4491
4611
|
const trimmed = oriQuestion?.trim();
|
|
4492
4612
|
return trimmed ? truncate(trimmed, MAX_ORI_QUESTION_CHARS) : undefined;
|
|
4493
4613
|
}
|
|
4614
|
+
function requestContext(config, apiKey, signal, perCall) {
|
|
4615
|
+
return {
|
|
4616
|
+
signal,
|
|
4617
|
+
auth: { apiKey },
|
|
4618
|
+
retry: config.retry,
|
|
4619
|
+
baseUrl: config.baseUrl,
|
|
4620
|
+
...perCall?.timeoutMs === undefined ? {} : { timeoutMs: perCall.timeoutMs },
|
|
4621
|
+
...perCall?.maxChars === undefined ? {} : { maxChars: perCall.maxChars }
|
|
4622
|
+
};
|
|
4623
|
+
}
|
|
4494
4624
|
async function searchWeb(query, options, request) {
|
|
4495
4625
|
const trimmed = query.trim();
|
|
4496
4626
|
if (!trimmed)
|
|
4497
4627
|
throw new Error("DeepSeek search query must not be empty");
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
const
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
const timeout = effectiveTimeout(options.timeoutMs, request.timeoutMs);
|
|
4628
|
+
const trimmedLength = Array.from(trimmed).length;
|
|
4629
|
+
if (trimmedLength > MAX_QUERY_CHARS) {
|
|
4630
|
+
throw new Error(`DeepSeek search query must be ${MAX_QUERY_CHARS} characters or fewer ` + `(got ${trimmedLength}: ${JSON.stringify(truncate(trimmed, 64))})`);
|
|
4631
|
+
}
|
|
4632
|
+
const composed = composeSearchQuery(trimmed, options);
|
|
4633
|
+
const builtQuery = composed.query;
|
|
4634
|
+
const needContent = request.needContent === true;
|
|
4635
|
+
const timeout = effectiveTimeout(needContent ? options.contentTimeoutMs : options.timeoutMs, request.timeoutMs);
|
|
4507
4636
|
const oriQuestion = oriQuestionValue(request.oriQuestion);
|
|
4508
|
-
const payload = await postJson(
|
|
4637
|
+
const payload = await postJson(apiUrl(request.baseUrl, SEARCH_ENDPOINT_PATH), {
|
|
4509
4638
|
query: builtQuery,
|
|
4510
4639
|
count: options.count,
|
|
4511
|
-
need_content:
|
|
4640
|
+
need_content: needContent,
|
|
4512
4641
|
...oriQuestion === undefined ? {} : { ori_question: oriQuestion },
|
|
4513
4642
|
...options.localization === undefined ? {} : { localization: options.localization }
|
|
4514
4643
|
}, "DeepSeek Search", { ...request, timeoutMs: timeout });
|
|
4515
4644
|
if (!Array.isArray(payload.results)) {
|
|
4516
4645
|
throw new Error("DeepSeek Search returned a response without a results array");
|
|
4517
4646
|
}
|
|
4518
|
-
|
|
4647
|
+
const results = processResults(payload.results, options);
|
|
4648
|
+
return {
|
|
4649
|
+
results,
|
|
4650
|
+
inputQuery: trimmed,
|
|
4651
|
+
effectiveQuery: builtQuery,
|
|
4652
|
+
notes: composed.notes
|
|
4653
|
+
};
|
|
4519
4654
|
}
|
|
4520
4655
|
async function fetchPage(url, options, request) {
|
|
4521
4656
|
const target = parseFetchUrl(url.href);
|
|
4522
4657
|
assertFetchHostPolicy(target, options);
|
|
4523
4658
|
const timeout = effectiveTimeout(options.timeoutMs, request.timeoutMs);
|
|
4524
|
-
const payload = await postJson(
|
|
4659
|
+
const payload = await postJson(apiUrl(request.baseUrl, FETCH_ENDPOINT_PATH), { url: target.href }, "DeepSeek Fetch", {
|
|
4525
4660
|
...request,
|
|
4526
4661
|
timeoutMs: timeout
|
|
4527
4662
|
});
|
|
@@ -4536,7 +4671,7 @@ async function fetchDocument(url, options, request) {
|
|
|
4536
4671
|
if (typeof url !== "string" && !(url instanceof URL))
|
|
4537
4672
|
throw new Error('"url" must be a string or URL');
|
|
4538
4673
|
const page = await fetchPage(url instanceof URL ? url : parseFetchUrl(url), options, request);
|
|
4539
|
-
const formatted = formatFetchedContent(page.title, page.content, options.maxChars);
|
|
4674
|
+
const formatted = formatFetchedContent(page.title, page.content, effectiveMaxChars(options.maxChars, request.maxChars));
|
|
4540
4675
|
return {
|
|
4541
4676
|
url: page.url,
|
|
4542
4677
|
title: page.title,
|
|
@@ -4582,6 +4717,8 @@ function envOptionsReport(env = process.env) {
|
|
|
4582
4717
|
putText(options, "searchLocalization", env);
|
|
4583
4718
|
putNumber(options, "maxSummaryChars", env, warnings);
|
|
4584
4719
|
putNumber(options, "searchTimeoutMs", env, warnings);
|
|
4720
|
+
putNumber(options, "searchContentMaxChars", env, warnings);
|
|
4721
|
+
putNumber(options, "searchContentTimeoutMs", env, warnings);
|
|
4585
4722
|
putNumber(options, "fetchMaxChars", env, warnings);
|
|
4586
4723
|
putDomains(options, "fetchAllowedDomains", env, warnings);
|
|
4587
4724
|
putDomains(options, "fetchBlockedDomains", env, warnings);
|
|
@@ -4591,6 +4728,7 @@ function envOptionsReport(env = process.env) {
|
|
|
4591
4728
|
putFalseOnly(options, "fetchEnabled", env, warnings);
|
|
4592
4729
|
putNumber(options, "maxRetries", env, warnings);
|
|
4593
4730
|
putNumber(options, "retryBaseDelayMs", env, warnings);
|
|
4731
|
+
putText(options, "baseUrl", env);
|
|
4594
4732
|
const known = new Set(OPTION_ENV_VARS);
|
|
4595
4733
|
for (const key of Object.keys(env)) {
|
|
4596
4734
|
if (!key.startsWith("DEEPSEEK_") || key === API_KEY_ENV || known.has(key))
|
|
@@ -4668,6 +4806,8 @@ var init_env = __esm(() => {
|
|
|
4668
4806
|
minAuthority: "DEEPSEEK_SEARCH_MIN_AUTHORITY",
|
|
4669
4807
|
maxSummaryChars: "DEEPSEEK_SEARCH_MAX_SUMMARY_CHARS",
|
|
4670
4808
|
searchTimeoutMs: "DEEPSEEK_SEARCH_TIMEOUT_MS",
|
|
4809
|
+
searchContentMaxChars: "DEEPSEEK_SEARCH_CONTENT_MAX_CHARS",
|
|
4810
|
+
searchContentTimeoutMs: "DEEPSEEK_SEARCH_CONTENT_TIMEOUT_MS",
|
|
4671
4811
|
fetchMaxChars: "DEEPSEEK_FETCH_MAX_CHARS",
|
|
4672
4812
|
fetchTimeoutMs: "DEEPSEEK_FETCH_TIMEOUT_MS",
|
|
4673
4813
|
maxRetries: "DEEPSEEK_MAX_RETRIES",
|
|
@@ -4675,7 +4815,8 @@ var init_env = __esm(() => {
|
|
|
4675
4815
|
};
|
|
4676
4816
|
TEXT_ENV = {
|
|
4677
4817
|
language: "DEEPSEEK_SEARCH_LANGUAGE",
|
|
4678
|
-
searchLocalization: SEARCH_LOCALIZATION_ENV
|
|
4818
|
+
searchLocalization: SEARCH_LOCALIZATION_ENV,
|
|
4819
|
+
baseUrl: BASE_URL_ENV
|
|
4679
4820
|
};
|
|
4680
4821
|
DOMAINS_ENV = {
|
|
4681
4822
|
blockedDomains: "DEEPSEEK_SEARCH_BLOCKED_DOMAINS",
|
|
@@ -4725,15 +4866,16 @@ async function runConfigCommand(query, io, deps = {}) {
|
|
|
4725
4866
|
const client = deps.client ?? createRetrievalClient();
|
|
4726
4867
|
const startedAt = Date.now();
|
|
4727
4868
|
try {
|
|
4728
|
-
const
|
|
4869
|
+
const outcome = await client.searchWeb(query, resolved.search, {
|
|
4729
4870
|
signal: new AbortController().signal,
|
|
4730
4871
|
auth: { apiKey },
|
|
4731
|
-
retry: resolved.retry
|
|
4872
|
+
retry: resolved.retry,
|
|
4873
|
+
baseUrl: resolved.baseUrl
|
|
4732
4874
|
});
|
|
4733
4875
|
const elapsedMs = Date.now() - startedAt;
|
|
4734
|
-
io.out(`search "${query.trim()}" — ${results.length} ${results.length === 1 ? "result" : "results"} in ${elapsedMs} ms`);
|
|
4876
|
+
io.out(`search "${query.trim()}" — ${outcome.results.length} ${outcome.results.length === 1 ? "result" : "results"} in ${elapsedMs} ms`);
|
|
4735
4877
|
io.out("");
|
|
4736
|
-
for (const line of formatSearchResults(results).split(`
|
|
4878
|
+
for (const line of formatSearchResults(outcome.results, outcome).split(`
|
|
4737
4879
|
`))
|
|
4738
4880
|
io.out(line);
|
|
4739
4881
|
return 0;
|
|
@@ -4846,6 +4988,7 @@ var init_rulesContent = __esm(() => {
|
|
|
4846
4988
|
});
|
|
4847
4989
|
|
|
4848
4990
|
// src/installer/hosts/shared.ts
|
|
4991
|
+
import { spawnSync } from "node:child_process";
|
|
4849
4992
|
function planHeader(input, paths, loaded, dialect) {
|
|
4850
4993
|
const notes = [];
|
|
4851
4994
|
const errors = [];
|
|
@@ -4866,24 +5009,26 @@ function planHeader(input, paths, loaded, dialect) {
|
|
|
4866
5009
|
notes.push(competitorSteeringNote(paths.rules, steering));
|
|
4867
5010
|
return { notes, errors, abort: false };
|
|
4868
5011
|
}
|
|
5012
|
+
function probeHostVersion(command, parse4, err) {
|
|
5013
|
+
const result = spawnSync(command, ["--version"], { timeout: 5000, encoding: "utf8", env: process.env, shell: true });
|
|
5014
|
+
const version = result.status === 0 ? parse4(result.stdout ?? "") : undefined;
|
|
5015
|
+
if (version === undefined) {
|
|
5016
|
+
err(`note: ${command} was not found on PATH or printed no version; the version-drift check was skipped`);
|
|
5017
|
+
}
|
|
5018
|
+
return version;
|
|
5019
|
+
}
|
|
4869
5020
|
var init_shared = __esm(() => {
|
|
4870
5021
|
init_detect();
|
|
4871
5022
|
init_formats();
|
|
4872
5023
|
});
|
|
4873
5024
|
|
|
4874
5025
|
// src/installer/hosts/claude-code.ts
|
|
4875
|
-
import { spawnSync } from "node:child_process";
|
|
4876
5026
|
import { existsSync as existsSync9 } from "node:fs";
|
|
4877
5027
|
function valueKind(value) {
|
|
4878
5028
|
return Array.isArray(value) ? "an array" : `a ${typeof value}`;
|
|
4879
5029
|
}
|
|
4880
5030
|
function probeClaudeCodeVersion(err) {
|
|
4881
|
-
|
|
4882
|
-
const version = result.status === 0 ? parseClaudeCodeVersion(result.stdout ?? "") : undefined;
|
|
4883
|
-
if (version === undefined) {
|
|
4884
|
-
err("note: claude was not found on PATH or printed no version; the version-drift check was skipped");
|
|
4885
|
-
}
|
|
4886
|
-
return version;
|
|
5031
|
+
return probeHostVersion("claude", parseClaudeCodeVersion, err);
|
|
4887
5032
|
}
|
|
4888
5033
|
function parseClaudeCodeVersion(output) {
|
|
4889
5034
|
return [...output.matchAll(/\b\d+\.\d+\.\d+\b/g)].map((match) => match[0]).at(-1);
|
|
@@ -5089,14 +5234,8 @@ var init_claude_code = __esm(() => {
|
|
|
5089
5234
|
});
|
|
5090
5235
|
|
|
5091
5236
|
// src/installer/hosts/codex.ts
|
|
5092
|
-
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
5093
5237
|
function probeCodexVersion(err) {
|
|
5094
|
-
|
|
5095
|
-
const version = result.status === 0 ? parseCodexVersion(result.stdout ?? "") : undefined;
|
|
5096
|
-
if (version === undefined) {
|
|
5097
|
-
err("note: codex was not found on PATH or printed no version; the version-drift check was skipped");
|
|
5098
|
-
}
|
|
5099
|
-
return version;
|
|
5238
|
+
return probeHostVersion("codex", parseCodexVersion, err);
|
|
5100
5239
|
}
|
|
5101
5240
|
function parseCodexVersion(output) {
|
|
5102
5241
|
return /\bcodex(?:-cli)?\s+(\d+\.\d+\.\d+)\b/.exec(output)?.[1];
|
|
@@ -5299,8 +5438,8 @@ var init_opencode = __esm(() => {
|
|
|
5299
5438
|
init_value();
|
|
5300
5439
|
init_ownership();
|
|
5301
5440
|
init_paths();
|
|
5441
|
+
init_record();
|
|
5302
5442
|
init_rulesContent();
|
|
5303
|
-
init_state();
|
|
5304
5443
|
init_shared();
|
|
5305
5444
|
OPENCODE_TOOL_IDS = { search: "websearch (provider deepseek)", fetch: "deepseek_fetch" };
|
|
5306
5445
|
});
|
|
@@ -5405,10 +5544,10 @@ function planPiExtension(input, previous) {
|
|
|
5405
5544
|
format: "rules",
|
|
5406
5545
|
block: buildRulesBlock(input.host, rulesLines(RAW_MCP_TOOL_IDS, input.search, input.fetch))
|
|
5407
5546
|
});
|
|
5408
|
-
notes.push(
|
|
5547
|
+
notes.push(rulesUpsertNote(paths.rules));
|
|
5409
5548
|
}
|
|
5410
5549
|
if (forced) {
|
|
5411
|
-
notes.push(
|
|
5550
|
+
notes.push(FORCED_NO_BUILTIN_NOTE);
|
|
5412
5551
|
}
|
|
5413
5552
|
return { host: "pi", scope: input.scope, tiers, edits, notes, errors };
|
|
5414
5553
|
}
|
|
@@ -5515,16 +5654,17 @@ function planPiMcp(input) {
|
|
|
5515
5654
|
format: "rules",
|
|
5516
5655
|
block: buildRulesBlock(input.host, rulesLines(MCP_TOOL_IDS, input.search, input.fetch))
|
|
5517
5656
|
});
|
|
5518
|
-
notes.push(
|
|
5657
|
+
notes.push(rulesUpsertNote(paths.rules));
|
|
5519
5658
|
}
|
|
5520
5659
|
if (input.search === "forced" || input.fetch === "forced") {
|
|
5521
|
-
notes.push(
|
|
5660
|
+
notes.push(FORCED_NO_BUILTIN_NOTE);
|
|
5522
5661
|
}
|
|
5523
5662
|
return { host: "pi", scope: input.scope, tiers, edits, notes, errors };
|
|
5524
5663
|
}
|
|
5525
5664
|
function collectSources(value) {
|
|
5526
5665
|
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
5527
5666
|
}
|
|
5667
|
+
var rulesUpsertNote = (rules) => `Rules block upserted in ${rules}; Pi loads AGENTS.md context files from the working directory and its parents.`, FORCED_NO_BUILTIN_NOTE;
|
|
5528
5668
|
var init_pi = __esm(() => {
|
|
5529
5669
|
init_constants();
|
|
5530
5670
|
init_settings();
|
|
@@ -5534,9 +5674,10 @@ var init_pi = __esm(() => {
|
|
|
5534
5674
|
init_value();
|
|
5535
5675
|
init_ownership();
|
|
5536
5676
|
init_paths();
|
|
5677
|
+
init_record();
|
|
5537
5678
|
init_rulesContent();
|
|
5538
|
-
init_state();
|
|
5539
5679
|
init_shared();
|
|
5680
|
+
FORCED_NO_BUILTIN_NOTE = "Forced tier: Pi 1.3.0 ships no built-in web tool, so the registered tools are the only web path (de-facto). " + FORCED_COST_NOTE;
|
|
5540
5681
|
});
|
|
5541
5682
|
|
|
5542
5683
|
// src/installer/hosts/index.ts
|
|
@@ -5623,7 +5764,7 @@ function withSecretGuard(plan) {
|
|
|
5623
5764
|
reverts: undefined,
|
|
5624
5765
|
errors: [
|
|
5625
5766
|
{
|
|
5626
|
-
message: `${edit2.file}: the existing value at ${op.path.join(".")} contains a credential-looking key ` + `(${offender}). The installer refuses to snapshot secrets into its
|
|
5767
|
+
message: `${edit2.file}: the existing value at ${op.path.join(".")} contains a credential-looking key ` + `(${offender}). The installer refuses to snapshot secrets into its install record. Move the key to ` + `environment indirection ("\${${API_KEY_ENV}}" expansion or env_vars) and re-run.`
|
|
5627
5768
|
}
|
|
5628
5769
|
]
|
|
5629
5770
|
};
|
|
@@ -5680,7 +5821,7 @@ var init_hosts = __esm(() => {
|
|
|
5680
5821
|
init_constants();
|
|
5681
5822
|
init_value();
|
|
5682
5823
|
init_ownership();
|
|
5683
|
-
|
|
5824
|
+
init_record();
|
|
5684
5825
|
init_claude_code();
|
|
5685
5826
|
init_codex();
|
|
5686
5827
|
init_opencode();
|
|
@@ -5758,7 +5899,7 @@ function preview(value) {
|
|
|
5758
5899
|
|
|
5759
5900
|
// src/installer/uninstall.ts
|
|
5760
5901
|
function uninstall(input) {
|
|
5761
|
-
return withOperationLock([
|
|
5902
|
+
return withOperationLock([recordPathFor(input)], () => {
|
|
5762
5903
|
const record = readRecord(input);
|
|
5763
5904
|
if (record === undefined) {
|
|
5764
5905
|
throw new Error(`No ${input.host} install is recorded for the ${input.scope} scope.`);
|
|
@@ -5786,7 +5927,7 @@ function uninstallLocked(input, record) {
|
|
|
5786
5927
|
}
|
|
5787
5928
|
releaseFileOwnership(input, record.files);
|
|
5788
5929
|
deleteRecord(input);
|
|
5789
|
-
pruneEmptyDir(
|
|
5930
|
+
pruneEmptyDir(recordDir(input, input.scope));
|
|
5790
5931
|
lines.push(`removed install record (${input.host}, ${input.scope})`);
|
|
5791
5932
|
return lines;
|
|
5792
5933
|
}
|
|
@@ -5795,8 +5936,8 @@ var init_uninstall = __esm(() => {
|
|
|
5795
5936
|
init_operation_lock();
|
|
5796
5937
|
init_ownership();
|
|
5797
5938
|
init_paths();
|
|
5939
|
+
init_record();
|
|
5798
5940
|
init_revert();
|
|
5799
|
-
init_state();
|
|
5800
5941
|
});
|
|
5801
5942
|
|
|
5802
5943
|
// src/installer/cli.ts
|
|
@@ -5896,7 +6037,7 @@ ${USAGE}`);
|
|
|
5896
6037
|
return 0;
|
|
5897
6038
|
}
|
|
5898
6039
|
if (readingLegacyGlobalRecords({ ...roots, configHome: configHome2 })) {
|
|
5899
|
-
out(`Global install records are being read from the legacy ${
|
|
6040
|
+
out(`Global install records are being read from the legacy ${legacyGlobalRecordDir(roots)} ` + "(written before the Windows config home moved to %APPDATA%).");
|
|
5900
6041
|
}
|
|
5901
6042
|
for (const record of records) {
|
|
5902
6043
|
const by = record.installerVersion ? ` by v${record.installerVersion}` : "";
|
|
@@ -6030,7 +6171,7 @@ var init_cli = __esm(() => {
|
|
|
6030
6171
|
init_config_command();
|
|
6031
6172
|
init_hosts();
|
|
6032
6173
|
init_ownership();
|
|
6033
|
-
|
|
6174
|
+
init_record();
|
|
6034
6175
|
init_uninstall();
|
|
6035
6176
|
HOSTS = Object.keys(HOST_PLANNERS);
|
|
6036
6177
|
HOST_CHOICES = `<${HOSTS.join("|")}>`;
|
|
@@ -6070,17 +6211,6 @@ re-running.`;
|
|
|
6070
6211
|
PATH_CHOICES = ["extension", "mcp"];
|
|
6071
6212
|
});
|
|
6072
6213
|
|
|
6073
|
-
// src/mcp/auth.ts
|
|
6074
|
-
function requireApiKey(env) {
|
|
6075
|
-
const apiKey = apiKeyFromEnv(env);
|
|
6076
|
-
if (!apiKey)
|
|
6077
|
-
throw new Error(MISSING_API_KEY_MESSAGE);
|
|
6078
|
-
return apiKey;
|
|
6079
|
-
}
|
|
6080
|
-
var init_auth2 = __esm(() => {
|
|
6081
|
-
init_auth();
|
|
6082
|
-
});
|
|
6083
|
-
|
|
6084
6214
|
// src/mcp/client.ts
|
|
6085
6215
|
import { CLIENT_INFO_META_KEY } from "@modelcontextprotocol/server";
|
|
6086
6216
|
function clientNameOf(server, ctx) {
|
|
@@ -6108,7 +6238,7 @@ var init_package = __esm(() => {
|
|
|
6108
6238
|
package_default = {
|
|
6109
6239
|
$schema: "https://json.schemastore.org/package.json",
|
|
6110
6240
|
name: "deepseek-web-retrieval",
|
|
6111
|
-
version: "0.
|
|
6241
|
+
version: "0.10.0",
|
|
6112
6242
|
description: "Token-efficient DeepSeek Search and Fetch for coding agents, starting with OpenCode v2.",
|
|
6113
6243
|
type: "module",
|
|
6114
6244
|
license: "MIT",
|
|
@@ -6164,7 +6294,8 @@ var init_package = __esm(() => {
|
|
|
6164
6294
|
prepublishOnly: "bun run check:release",
|
|
6165
6295
|
test: "bun test",
|
|
6166
6296
|
"test:package": "node scripts/test-package.mjs",
|
|
6167
|
-
"test:e2e:keyless": "bun run test:e2e pi pi-extension installer reinit upgrade pkg capability-off",
|
|
6297
|
+
"test:e2e:keyless": "bun run test:e2e pi pi-extension pi-mock opencode-mock installer reinit upgrade pkg capability-off",
|
|
6298
|
+
"test:e2e:billed": "bun run test:e2e pi-billed opencode codex claude-code preferred",
|
|
6168
6299
|
"check:release": "bun run check && bun run test:package && bun run test:e2e:keyless",
|
|
6169
6300
|
"test:e2e": "bun run test/e2e/run.ts",
|
|
6170
6301
|
"docs:matrix": "bun run scripts/render-matrix.ts",
|
|
@@ -6211,11 +6342,14 @@ var init_package = __esm(() => {
|
|
|
6211
6342
|
});
|
|
6212
6343
|
|
|
6213
6344
|
// src/core/descriptions.ts
|
|
6214
|
-
var FETCH_SERVICE_LIMITS = "DeepSeek does not render JavaScript and cannot reach private networks; PDFs, images, and other binary files are unsupported.", FETCH_OUTPUT_TRUNCATED = "Output is truncated to the configured size.", SEARCH_RANKED_FILTERED = "Results are ranked by relevance and source authority, deduplicated by URL, and capped at the configured maximum.", SEARCH_EMPTY_IS_NORMAL = "An empty result list is normal, not an error.", SEARCH_QUERY_OPERATORS = "Use site:/-site: operators in the query to include or exclude domains, and after:/before: with a year or date (e.g. after:2025-06-01) to filter by publish time; the full query must stay within 128 characters.", SEARCH_TOOL_DESCRIPTION, FETCH_TOOL_DESCRIPTION, SEARCH_QUERY_PARAMETER = "Search query, 128 characters or fewer including operators (site:, -site:, quoted phrases, lang:, after:, before:).", SEARCH_ORI_QUESTION_PARAMETER, FETCH_URL_PARAMETER = "Absolute http:// or https:// URL to fetch. Credentials are rejected.";
|
|
6345
|
+
var FETCH_SERVICE_LIMITS = "DeepSeek does not render JavaScript and cannot reach private networks; PDFs, images, and other binary files are unsupported.", FETCH_OUTPUT_TRUNCATED = "Output is truncated to the configured size; the optional maxChars parameter raises or lowers that size for one call.", SEARCH_RANKED_FILTERED = "Results are ranked by relevance and source authority, deduplicated by URL, and capped at the configured maximum.", SEARCH_EMPTY_IS_NORMAL = "An empty result list is normal, not an error.", SEARCH_QUERY_OPERATORS = "Use site:/-site: operators in the query to include or exclude domains, and after:/before: with a year or date (e.g. after:2025-06-01) to filter by publish time; the full query must stay within 128 characters.", SEARCH_TOOL_DESCRIPTION, FETCH_TOOL_DESCRIPTION, SEARCH_QUERY_PARAMETER = "Search query, 128 characters or fewer including operators (site:, -site:, quoted phrases, lang:, after:, before:).", SEARCH_ORI_QUESTION_PARAMETER, FETCH_URL_PARAMETER = "Absolute http:// or https:// URL to fetch. Credentials are rejected.", FETCH_MAX_CHARS_PARAMETER, SEARCH_COUNT_PARAMETER, SEARCH_NEED_CONTENT_PARAMETER;
|
|
6215
6346
|
var init_descriptions = __esm(() => {
|
|
6216
|
-
SEARCH_TOOL_DESCRIPTION = "Search the web with DeepSeek's in-house search engine and return a short, filtered result " + "list (title, URL, summary). " + `${SEARCH_RANKED_FILTERED} ` + `${SEARCH_EMPTY_IS_NORMAL} ` + `${SEARCH_QUERY_OPERATORS} ` + "To read
|
|
6347
|
+
SEARCH_TOOL_DESCRIPTION = "Search the web with DeepSeek's in-house search engine and return a short, filtered result " + "list (title, URL, summary). " + `${SEARCH_RANKED_FILTERED} ` + `${SEARCH_EMPTY_IS_NORMAL} ` + `${SEARCH_QUERY_OPERATORS} ` + "To read one full page in depth, pass a result URL to the deepseek_fetch tool; to carry each result's " + "page body in this response, set need_content to true.";
|
|
6217
6348
|
FETCH_TOOL_DESCRIPTION = "Fetch one web page as clean Markdown through DeepSeek's Fetch API. " + `${FETCH_SERVICE_LIMITS} ` + `${FETCH_OUTPUT_TRUNCATED}`;
|
|
6218
6349
|
SEARCH_ORI_QUESTION_PARAMETER = "The user's original question or task that triggered this search, in the user's own words rather than a " + "reformulated query. Optional; 1-128 characters, and longer values are truncated.";
|
|
6350
|
+
FETCH_MAX_CHARS_PARAMETER = "Optional per-call character budget for this fetch, 0-1,000,000, title included. Overrides the configured cap " + "for this call; a [content truncated] marker is appended when the output is cut.";
|
|
6351
|
+
SEARCH_COUNT_PARAMETER = "Optional number of results for this call, 1-50. Overrides the configured count and result cap for this call; " + "billing is per request, not per result.";
|
|
6352
|
+
SEARCH_NEED_CONTENT_PARAMETER = "Optional; set true to attach each result's page body as Markdown — an excerpt clamped to a configured per-result " + "budget, not a full page — in place of its short summary. Slower per call (DeepSeek fetches the result pages) and " + "priced the same per request; for a full page, call deepseek_fetch.";
|
|
6219
6353
|
});
|
|
6220
6354
|
|
|
6221
6355
|
// src/mcp/server.ts
|
|
@@ -6229,15 +6363,16 @@ function createDeepSeekServer(config, env = process.env, client = retrievalClien
|
|
|
6229
6363
|
description: SEARCH_TOOL_DESCRIPTION,
|
|
6230
6364
|
inputSchema: SEARCH_INPUT,
|
|
6231
6365
|
annotations: { readOnlyHint: true }
|
|
6232
|
-
}, async ({ query, original_question }, ctx) => {
|
|
6366
|
+
}, async ({ query, original_question, count, need_content }, ctx) => {
|
|
6233
6367
|
try {
|
|
6234
|
-
const request = requestContext(config, env, ctx.mcpReq.signal);
|
|
6235
|
-
const
|
|
6368
|
+
const request = requestContext(config, requireApiKey(env), ctx.mcpReq.signal);
|
|
6369
|
+
const outcome = await client.searchWeb(query, withPerCallCount(config.search, count), {
|
|
6236
6370
|
...request,
|
|
6237
|
-
oriQuestion: original_question
|
|
6371
|
+
oriQuestion: original_question,
|
|
6372
|
+
needContent: need_content === true
|
|
6238
6373
|
});
|
|
6239
|
-
return callResult(server, ctx, formatSearchResults(results), {
|
|
6240
|
-
results: results.map(toStructuredResult)
|
|
6374
|
+
return callResult(server, ctx, formatSearchResults(outcome.results, outcome), {
|
|
6375
|
+
results: outcome.results.map(toStructuredResult)
|
|
6241
6376
|
});
|
|
6242
6377
|
} catch (error) {
|
|
6243
6378
|
return toErrorResult(error);
|
|
@@ -6250,9 +6385,9 @@ function createDeepSeekServer(config, env = process.env, client = retrievalClien
|
|
|
6250
6385
|
description: FETCH_TOOL_DESCRIPTION,
|
|
6251
6386
|
inputSchema: FETCH_INPUT,
|
|
6252
6387
|
annotations: { readOnlyHint: true }
|
|
6253
|
-
}, async ({ url }, ctx) => {
|
|
6388
|
+
}, async ({ url, maxChars }, ctx) => {
|
|
6254
6389
|
try {
|
|
6255
|
-
const request = requestContext(config, env, ctx.mcpReq.signal);
|
|
6390
|
+
const request = requestContext(config, requireApiKey(env), ctx.mcpReq.signal, { maxChars });
|
|
6256
6391
|
const page = await client.fetchDocument(url, config.fetch, request);
|
|
6257
6392
|
return callResult(server, ctx, page.content, {
|
|
6258
6393
|
url: page.url,
|
|
@@ -6267,18 +6402,13 @@ function createDeepSeekServer(config, env = process.env, client = retrievalClien
|
|
|
6267
6402
|
}
|
|
6268
6403
|
return server;
|
|
6269
6404
|
}
|
|
6270
|
-
function requestContext(config, env, signal) {
|
|
6271
|
-
return { signal, auth: { apiKey: requireApiKey(env) }, retry: config.retry };
|
|
6272
|
-
}
|
|
6273
6405
|
function callResult(server, ctx, text, structured) {
|
|
6274
6406
|
if (omitStructuredContent(server, ctx))
|
|
6275
6407
|
return { content: [{ type: "text", text }] };
|
|
6276
6408
|
return { content: [{ type: "text", text }], structuredContent: structured };
|
|
6277
6409
|
}
|
|
6278
6410
|
function servePrecheck(config) {
|
|
6279
|
-
|
|
6280
|
-
return;
|
|
6281
|
-
return `Both tools are disabled (${ENABLE_SEARCH_ENV} and ${ENABLE_FETCH_ENV} are false); ` + "at least one must stay enabled.";
|
|
6411
|
+
return bothDisabledPrecheck(config, `${ENABLE_SEARCH_ENV} and ${ENABLE_FETCH_ENV} are false`);
|
|
6282
6412
|
}
|
|
6283
6413
|
function toErrorResult(error) {
|
|
6284
6414
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -6287,18 +6417,22 @@ function toErrorResult(error) {
|
|
|
6287
6417
|
var MCP_SERVER_NAME = "deepseek-web-retrieval", SEARCH_INPUT, FETCH_INPUT;
|
|
6288
6418
|
var init_server = __esm(() => {
|
|
6289
6419
|
init_package();
|
|
6420
|
+
init_auth();
|
|
6290
6421
|
init_client();
|
|
6291
6422
|
init_constants();
|
|
6292
6423
|
init_descriptions();
|
|
6293
6424
|
init_format2();
|
|
6294
|
-
|
|
6425
|
+
init_options();
|
|
6295
6426
|
init_client2();
|
|
6296
6427
|
SEARCH_INPUT = z2.object({
|
|
6297
6428
|
query: z2.string().min(1).describe(SEARCH_QUERY_PARAMETER),
|
|
6298
|
-
original_question: z2.string().optional().describe(SEARCH_ORI_QUESTION_PARAMETER)
|
|
6429
|
+
original_question: z2.string().optional().describe(SEARCH_ORI_QUESTION_PARAMETER),
|
|
6430
|
+
count: z2.number().int().min(1).max(MAX_SEARCH_COUNT).optional().describe(SEARCH_COUNT_PARAMETER),
|
|
6431
|
+
need_content: z2.boolean().optional().describe(SEARCH_NEED_CONTENT_PARAMETER)
|
|
6299
6432
|
});
|
|
6300
6433
|
FETCH_INPUT = z2.object({
|
|
6301
|
-
url: z2.string().min(1).describe(FETCH_URL_PARAMETER)
|
|
6434
|
+
url: z2.string().min(1).describe(FETCH_URL_PARAMETER),
|
|
6435
|
+
maxChars: z2.number().int().min(0).max(MAX_FETCH_CHARS).optional().describe(FETCH_MAX_CHARS_PARAMETER)
|
|
6302
6436
|
});
|
|
6303
6437
|
});
|
|
6304
6438
|
|
|
@@ -6346,7 +6480,6 @@ var init_serve = __esm(() => {
|
|
|
6346
6480
|
init_config_file();
|
|
6347
6481
|
init_config_summary();
|
|
6348
6482
|
init_env();
|
|
6349
|
-
init_auth2();
|
|
6350
6483
|
init_client2();
|
|
6351
6484
|
init_server();
|
|
6352
6485
|
});
|