kimiflare 0.54.1 → 0.55.1
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/dist/index.js +101 -32
- package/dist/index.js.map +1 -1
- package/dist/sdk/index.d.ts +4 -0
- package/dist/sdk/index.js +58 -24
- package/dist/sdk/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -126,6 +126,7 @@ async function loadConfig() {
|
|
|
126
126
|
const envCostAttribution = readBooleanEnv("KIMI_COST_ATTRIBUTION");
|
|
127
127
|
const envFilePicker = readBooleanEnv("KIMIFLARE_FILE_PICKER");
|
|
128
128
|
const envCloudMode = readBooleanEnv("KIMIFLARE_CLOUD");
|
|
129
|
+
const envShell = process.env.KIMIFLARE_SHELL;
|
|
129
130
|
if (envCloudMode) {
|
|
130
131
|
return {
|
|
131
132
|
accountId: "",
|
|
@@ -139,7 +140,7 @@ async function loadConfig() {
|
|
|
139
140
|
cacheStablePrompts,
|
|
140
141
|
compiledContext,
|
|
141
142
|
imageHistoryTurns: Number.isNaN(imageHistoryTurns) ? void 0 : imageHistoryTurns,
|
|
142
|
-
memoryEnabled: envMemoryEnabled,
|
|
143
|
+
memoryEnabled: envMemoryEnabled ?? true,
|
|
143
144
|
memoryDbPath: envMemoryDbPath,
|
|
144
145
|
memoryMaxAgeDays: envMemoryMaxAgeDays,
|
|
145
146
|
memoryMaxEntries: envMemoryMaxEntries,
|
|
@@ -147,7 +148,8 @@ async function loadConfig() {
|
|
|
147
148
|
plumbingModel: envPlumbingModel,
|
|
148
149
|
codeMode: envCodeMode,
|
|
149
150
|
costAttribution: envCostAttribution ?? false,
|
|
150
|
-
filePicker: envFilePicker ?? true
|
|
151
|
+
filePicker: envFilePicker ?? true,
|
|
152
|
+
shell: envShell
|
|
151
153
|
};
|
|
152
154
|
}
|
|
153
155
|
if (envAccount && envToken) {
|
|
@@ -176,7 +178,8 @@ async function loadConfig() {
|
|
|
176
178
|
memoryExtractionModel: envMemoryExtractionModel,
|
|
177
179
|
codeMode: envCodeMode ?? true,
|
|
178
180
|
costAttribution: envCostAttribution ?? true,
|
|
179
|
-
filePicker: envFilePicker ?? true
|
|
181
|
+
filePicker: envFilePicker ?? true,
|
|
182
|
+
shell: envShell
|
|
180
183
|
};
|
|
181
184
|
}
|
|
182
185
|
try {
|
|
@@ -196,7 +199,7 @@ async function loadConfig() {
|
|
|
196
199
|
cacheStablePrompts: parsed.cacheStablePrompts ?? cacheStablePrompts,
|
|
197
200
|
compiledContext: parsed.compiledContext ?? compiledContext,
|
|
198
201
|
imageHistoryTurns: Number.isNaN(imageHistoryTurns) ? parsed.imageHistoryTurns : imageHistoryTurns,
|
|
199
|
-
memoryEnabled: envMemoryEnabled ?? parsed.memoryEnabled,
|
|
202
|
+
memoryEnabled: envMemoryEnabled ?? parsed.memoryEnabled ?? true,
|
|
200
203
|
memoryDbPath: envMemoryDbPath ?? parsed.memoryDbPath,
|
|
201
204
|
memoryMaxAgeDays: envMemoryMaxAgeDays ?? parsed.memoryMaxAgeDays,
|
|
202
205
|
memoryMaxEntries: envMemoryMaxEntries ?? parsed.memoryMaxEntries,
|
|
@@ -205,7 +208,8 @@ async function loadConfig() {
|
|
|
205
208
|
codeMode: envCodeMode ?? parsed.codeMode,
|
|
206
209
|
costAttribution: envCostAttribution ?? parsed.costAttribution ?? false,
|
|
207
210
|
filePicker: envFilePicker ?? parsed.filePicker ?? true,
|
|
208
|
-
theme: parsed.theme
|
|
211
|
+
theme: parsed.theme,
|
|
212
|
+
shell: envShell ?? parsed.shell
|
|
209
213
|
};
|
|
210
214
|
}
|
|
211
215
|
if (parsed.accountId && parsed.apiToken) {
|
|
@@ -237,7 +241,8 @@ async function loadConfig() {
|
|
|
237
241
|
costAttribution: envCostAttribution ?? parsed.costAttribution ?? true,
|
|
238
242
|
filePicker: envFilePicker ?? parsed.filePicker ?? true,
|
|
239
243
|
cloudMode: envCloudMode ?? parsed.cloudMode,
|
|
240
|
-
theme: parsed.theme
|
|
244
|
+
theme: parsed.theme,
|
|
245
|
+
shell: envShell ?? parsed.shell
|
|
241
246
|
};
|
|
242
247
|
}
|
|
243
248
|
} catch {
|
|
@@ -1581,8 +1586,8 @@ var require_node_gyp_build = __commonJS({
|
|
|
1581
1586
|
var abi = process.versions.modules;
|
|
1582
1587
|
var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
|
|
1583
1588
|
var arch = process.env.npm_config_arch || os2.arch();
|
|
1584
|
-
var
|
|
1585
|
-
var libc = process.env.LIBC || (isAlpine(
|
|
1589
|
+
var platform5 = process.env.npm_config_platform || os2.platform();
|
|
1590
|
+
var libc = process.env.LIBC || (isAlpine(platform5) ? "musl" : "glibc");
|
|
1586
1591
|
var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars.arm_version) || "";
|
|
1587
1592
|
var uv = (process.versions.uv || "").split(".")[0];
|
|
1588
1593
|
module.exports = load;
|
|
@@ -1607,7 +1612,7 @@ var require_node_gyp_build = __commonJS({
|
|
|
1607
1612
|
var nearby = resolve3(path.dirname(process.execPath));
|
|
1608
1613
|
if (nearby) return nearby;
|
|
1609
1614
|
var target = [
|
|
1610
|
-
"platform=" +
|
|
1615
|
+
"platform=" + platform5,
|
|
1611
1616
|
"arch=" + arch,
|
|
1612
1617
|
"runtime=" + runtime,
|
|
1613
1618
|
"abi=" + abi,
|
|
@@ -1622,7 +1627,7 @@ var require_node_gyp_build = __commonJS({
|
|
|
1622
1627
|
throw new Error("No native build was found for " + target + "\n loaded from: " + dir + "\n");
|
|
1623
1628
|
function resolve3(dir2) {
|
|
1624
1629
|
var tuples = readdirSync(path.join(dir2, "prebuilds")).map(parseTuple);
|
|
1625
|
-
var tuple = tuples.filter(matchTuple(
|
|
1630
|
+
var tuple = tuples.filter(matchTuple(platform5, arch)).sort(compareTuples)[0];
|
|
1626
1631
|
if (!tuple) return;
|
|
1627
1632
|
var prebuilds = path.join(dir2, "prebuilds", tuple.name);
|
|
1628
1633
|
var parsed = readdirSync(prebuilds).map(parseTags);
|
|
@@ -1648,17 +1653,17 @@ var require_node_gyp_build = __commonJS({
|
|
|
1648
1653
|
function parseTuple(name) {
|
|
1649
1654
|
var arr = name.split("-");
|
|
1650
1655
|
if (arr.length !== 2) return;
|
|
1651
|
-
var
|
|
1656
|
+
var platform6 = arr[0];
|
|
1652
1657
|
var architectures = arr[1].split("+");
|
|
1653
|
-
if (!
|
|
1658
|
+
if (!platform6) return;
|
|
1654
1659
|
if (!architectures.length) return;
|
|
1655
1660
|
if (!architectures.every(Boolean)) return;
|
|
1656
|
-
return { name, platform:
|
|
1661
|
+
return { name, platform: platform6, architectures };
|
|
1657
1662
|
}
|
|
1658
|
-
function matchTuple(
|
|
1663
|
+
function matchTuple(platform6, arch2) {
|
|
1659
1664
|
return function(tuple) {
|
|
1660
1665
|
if (tuple == null) return false;
|
|
1661
|
-
if (tuple.platform !==
|
|
1666
|
+
if (tuple.platform !== platform6) return false;
|
|
1662
1667
|
return tuple.architectures.includes(arch2);
|
|
1663
1668
|
};
|
|
1664
1669
|
}
|
|
@@ -1726,8 +1731,8 @@ var require_node_gyp_build = __commonJS({
|
|
|
1726
1731
|
if (process.env.ELECTRON_RUN_AS_NODE) return true;
|
|
1727
1732
|
return typeof window !== "undefined" && window.process && window.process.type === "renderer";
|
|
1728
1733
|
}
|
|
1729
|
-
function isAlpine(
|
|
1730
|
-
return
|
|
1734
|
+
function isAlpine(platform6) {
|
|
1735
|
+
return platform6 === "linux" && fs.existsSync("/etc/alpine-release");
|
|
1731
1736
|
}
|
|
1732
1737
|
load.parseTags = parseTags;
|
|
1733
1738
|
load.matchTags = matchTags;
|
|
@@ -2836,7 +2841,7 @@ ${sandboxResult.output}` : sandboxResult.output;
|
|
|
2836
2841
|
const result = await opts2.executor.run(
|
|
2837
2842
|
{ id: tc.id, name: tc.function.name, arguments: tc.function.arguments },
|
|
2838
2843
|
opts2.callbacks.askPermission,
|
|
2839
|
-
{ cwd: opts2.cwd, signal: opts2.signal, onTasks: opts2.callbacks.onTasks, coauthor: opts2.coauthor, memoryManager: opts2.memoryManager, sessionId: opts2.sessionId, githubToken: opts2.githubToken },
|
|
2844
|
+
{ cwd: opts2.cwd, signal: opts2.signal, onTasks: opts2.callbacks.onTasks, coauthor: opts2.coauthor, memoryManager: opts2.memoryManager, sessionId: opts2.sessionId, githubToken: opts2.githubToken, shell: opts2.shell },
|
|
2840
2845
|
opts2.onFileChange
|
|
2841
2846
|
);
|
|
2842
2847
|
let content2 = result.content;
|
|
@@ -3473,8 +3478,36 @@ var init_edit = __esm({
|
|
|
3473
3478
|
|
|
3474
3479
|
// src/tools/bash.ts
|
|
3475
3480
|
import { spawn } from "child_process";
|
|
3476
|
-
import { tmpdir } from "os";
|
|
3481
|
+
import { tmpdir, platform as platform2 } from "os";
|
|
3477
3482
|
import { join as join8 } from "path";
|
|
3483
|
+
function getShellCommand(override) {
|
|
3484
|
+
const raw = override?.trim();
|
|
3485
|
+
if (raw && raw !== "auto") {
|
|
3486
|
+
const lower = raw.toLowerCase();
|
|
3487
|
+
if (lower === "bash") {
|
|
3488
|
+
return { shell: "bash", args: ["-lc"], isPosix: true };
|
|
3489
|
+
}
|
|
3490
|
+
if (lower === "cmd") {
|
|
3491
|
+
return { shell: process.env.COMSPEC || "cmd.exe", args: ["/c"], isPosix: false };
|
|
3492
|
+
}
|
|
3493
|
+
if (lower === "powershell") {
|
|
3494
|
+
return { shell: "powershell", args: ["-Command"], isPosix: false };
|
|
3495
|
+
}
|
|
3496
|
+
const base = lower.replace(/\\/g, "/").split("/").pop() || "";
|
|
3497
|
+
if (base.includes("cmd")) {
|
|
3498
|
+
return { shell: raw, args: ["/c"], isPosix: false };
|
|
3499
|
+
}
|
|
3500
|
+
if (base.includes("powershell") || base.includes("pwsh")) {
|
|
3501
|
+
return { shell: raw, args: ["-Command"], isPosix: false };
|
|
3502
|
+
}
|
|
3503
|
+
return { shell: raw, args: ["-lc"], isPosix: true };
|
|
3504
|
+
}
|
|
3505
|
+
const isWindows = platform2() === "win32";
|
|
3506
|
+
if (isWindows) {
|
|
3507
|
+
return { shell: process.env.COMSPEC || "cmd.exe", args: ["/c"], isPosix: false };
|
|
3508
|
+
}
|
|
3509
|
+
return { shell: "bash", args: ["-lc"], isPosix: true };
|
|
3510
|
+
}
|
|
3478
3511
|
function formatBashTitle(raw) {
|
|
3479
3512
|
let cmd = (raw ?? "").trim();
|
|
3480
3513
|
const m = cmd.match(/^cd\s+([^\s&;]+)\s*(?:&&|;)\s*(.*)$/);
|
|
@@ -3512,10 +3545,11 @@ function injectCoauthor(command, coauthor) {
|
|
|
3512
3545
|
}
|
|
3513
3546
|
function runBash(args, ctx) {
|
|
3514
3547
|
const timeout = Math.min(Math.max(1e3, args.timeout_ms ?? DEFAULT_TIMEOUT), MAX_TIMEOUT);
|
|
3515
|
-
const
|
|
3548
|
+
const { shell, args: shellArgs, isPosix } = getShellCommand(ctx.shell);
|
|
3549
|
+
const command = isPosix ? injectCoauthor(args.command, ctx.coauthor) : args.command;
|
|
3516
3550
|
return new Promise((resolve3, reject) => {
|
|
3517
|
-
logger.debug("bash:spawn", { command: args.command.slice(0, 200), cwd: ctx.cwd });
|
|
3518
|
-
const child = spawn(
|
|
3551
|
+
logger.debug("bash:spawn", { command: args.command.slice(0, 200), cwd: ctx.cwd, shell });
|
|
3552
|
+
const child = spawn(shell, [...shellArgs, command], {
|
|
3519
3553
|
cwd: ctx.cwd,
|
|
3520
3554
|
env: {
|
|
3521
3555
|
...process.env,
|
|
@@ -3583,7 +3617,7 @@ var init_bash = __esm({
|
|
|
3583
3617
|
MAX_TIMEOUT = 6e5;
|
|
3584
3618
|
bashTool = {
|
|
3585
3619
|
name: "bash",
|
|
3586
|
-
description: "Run a shell command
|
|
3620
|
+
description: "Run a shell command. On Unix the default shell is bash; on Windows it falls back to cmd.exe. Prompts the user for permission before executing. stdout and stderr are captured and combined. Large outputs are reduced to a compact summary by default; use expand_artifact to retrieve the full log.",
|
|
3587
3621
|
parameters: {
|
|
3588
3622
|
type: "object",
|
|
3589
3623
|
properties: {
|
|
@@ -8832,7 +8866,7 @@ async function createAgentSession(opts2) {
|
|
|
8832
8866
|
const tools = opts2.tools ?? ALL_TOOLS;
|
|
8833
8867
|
const executor = new ToolExecutor(tools);
|
|
8834
8868
|
let memoryManager = null;
|
|
8835
|
-
const memoryEnabled = opts2.memoryEnabled ?? config.memoryEnabled ??
|
|
8869
|
+
const memoryEnabled = opts2.memoryEnabled ?? config.memoryEnabled ?? true;
|
|
8836
8870
|
if (memoryEnabled) {
|
|
8837
8871
|
const dbPath = config.memoryDbPath ?? join19(homedir12(), ".local", "share", "kimiflare", "memory.db");
|
|
8838
8872
|
memoryManager = new MemoryManager({
|
|
@@ -11633,7 +11667,7 @@ var init_text_input = __esm({
|
|
|
11633
11667
|
// src/ui/permission.tsx
|
|
11634
11668
|
import { useState as useState4, useCallback } from "react";
|
|
11635
11669
|
import { Box as Box8, Text as Text9, useInput as useInput2 } from "ink";
|
|
11636
|
-
import { platform as
|
|
11670
|
+
import { platform as platform3 } from "os";
|
|
11637
11671
|
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
11638
11672
|
function formatSelection(label, shortcut) {
|
|
11639
11673
|
return `${label} [${MOD_KEY}+${shortcut}]`;
|
|
@@ -11796,7 +11830,7 @@ var init_permission = __esm({
|
|
|
11796
11830
|
{ value: "allow_session", label: "Allow for this session", key: 2 },
|
|
11797
11831
|
{ value: "deny", label: "Something else", key: 3 }
|
|
11798
11832
|
];
|
|
11799
|
-
MOD_KEY =
|
|
11833
|
+
MOD_KEY = platform3() === "darwin" ? "\u2325" : "Alt";
|
|
11800
11834
|
}
|
|
11801
11835
|
});
|
|
11802
11836
|
|
|
@@ -12211,8 +12245,8 @@ import { exec } from "child_process";
|
|
|
12211
12245
|
import { promisify as promisify2 } from "util";
|
|
12212
12246
|
import { Fragment, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
12213
12247
|
function openBrowser(url) {
|
|
12214
|
-
const
|
|
12215
|
-
const cmd =
|
|
12248
|
+
const platform5 = process.platform;
|
|
12249
|
+
const cmd = platform5 === "darwin" ? `open "${url}"` : platform5 === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
|
|
12216
12250
|
exec(cmd, (err) => {
|
|
12217
12251
|
if (err) {
|
|
12218
12252
|
}
|
|
@@ -13732,6 +13766,7 @@ var init_builtins = __esm({
|
|
|
13732
13766
|
{ name: "remote", argHint: "<prompt>", description: "Run a remote session on Cloudflare", source: "builtin" },
|
|
13733
13767
|
{ name: "update", description: "Check for updates", source: "builtin" },
|
|
13734
13768
|
{ name: "hello", description: "Send a voice note to the creator", source: "builtin" },
|
|
13769
|
+
{ name: "shell", argHint: "[auto|bash|cmd|powershell|<path>]", description: "Show or set shell for bash tool", source: "builtin" },
|
|
13735
13770
|
{ name: "logout", description: "Clear stored credentials", source: "builtin" },
|
|
13736
13771
|
{ name: "exit", description: "Exit kimiflare", source: "builtin" },
|
|
13737
13772
|
{ name: "quit", description: "Alias for /exit", source: "builtin" }
|
|
@@ -14743,7 +14778,8 @@ function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
|
|
|
14743
14778
|
const [pendingEnabled, setPendingEnabled] = useState11(true);
|
|
14744
14779
|
const runInstall = (command) => {
|
|
14745
14780
|
setInstallState({ status: "running", output: "Installing..." });
|
|
14746
|
-
const
|
|
14781
|
+
const { shell, args } = getShellCommand();
|
|
14782
|
+
const child = spawn3(shell, [...args, command], {
|
|
14747
14783
|
env: process.env
|
|
14748
14784
|
});
|
|
14749
14785
|
let stdout = "";
|
|
@@ -15139,6 +15175,7 @@ var init_lsp_wizard = __esm({
|
|
|
15139
15175
|
"src/ui/lsp-wizard.tsx"() {
|
|
15140
15176
|
"use strict";
|
|
15141
15177
|
init_theme_context();
|
|
15178
|
+
init_bash();
|
|
15142
15179
|
init_text_input();
|
|
15143
15180
|
PRESETS = [
|
|
15144
15181
|
{
|
|
@@ -16702,7 +16739,7 @@ import { join as join27 } from "path";
|
|
|
16702
16739
|
import { unlink as unlink4 } from "fs/promises";
|
|
16703
16740
|
import { execSync as execSync2 } from "child_process";
|
|
16704
16741
|
import { spawn as spawn4 } from "child_process";
|
|
16705
|
-
import { platform as
|
|
16742
|
+
import { platform as platform4 } from "os";
|
|
16706
16743
|
import fg4 from "fast-glob";
|
|
16707
16744
|
import { readFileSync as readFileSync3 } from "fs";
|
|
16708
16745
|
import { jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
@@ -16845,7 +16882,7 @@ function gatewayUsageLookupFromConfig(cfg, meta) {
|
|
|
16845
16882
|
};
|
|
16846
16883
|
}
|
|
16847
16884
|
function openBrowser2(url) {
|
|
16848
|
-
const cmd =
|
|
16885
|
+
const cmd = platform4() === "darwin" ? "open" : platform4() === "win32" ? "start" : "xdg-open";
|
|
16849
16886
|
const child = spawn4(cmd, [url], { detached: true, stdio: "ignore" });
|
|
16850
16887
|
child.unref();
|
|
16851
16888
|
}
|
|
@@ -18105,6 +18142,7 @@ ${wcagWarnings.join("\n")}` }
|
|
|
18105
18142
|
cloudMode: cfg.cloudMode,
|
|
18106
18143
|
cloudToken: cloudToken ?? initialCloudToken,
|
|
18107
18144
|
cloudDeviceId: cloudDeviceId ?? initialCloudDeviceId,
|
|
18145
|
+
shell: cfg.shell,
|
|
18108
18146
|
onIterationEnd,
|
|
18109
18147
|
onFileChange: (path, content) => {
|
|
18110
18148
|
if (content) {
|
|
@@ -18574,6 +18612,36 @@ ${wcagWarnings.join("\n")}` }
|
|
|
18574
18612
|
});
|
|
18575
18613
|
return true;
|
|
18576
18614
|
}
|
|
18615
|
+
if (c === "/shell") {
|
|
18616
|
+
if (!cfg) return true;
|
|
18617
|
+
const valid = ["auto", "bash", "cmd", "powershell"];
|
|
18618
|
+
if (arg === "auto" || arg === "bash" || arg === "cmd" || arg === "powershell") {
|
|
18619
|
+
const next = { ...cfg, shell: arg === "auto" ? void 0 : arg };
|
|
18620
|
+
setCfg(next);
|
|
18621
|
+
void saveConfig(next).catch(() => {
|
|
18622
|
+
});
|
|
18623
|
+
setEvents((e) => [...e, { kind: "info", key: mkKey(), text: `shell set to ${arg}` }]);
|
|
18624
|
+
return true;
|
|
18625
|
+
}
|
|
18626
|
+
if (arg) {
|
|
18627
|
+
const next = { ...cfg, shell: arg };
|
|
18628
|
+
setCfg(next);
|
|
18629
|
+
void saveConfig(next).catch(() => {
|
|
18630
|
+
});
|
|
18631
|
+
setEvents((e) => [...e, { kind: "info", key: mkKey(), text: `shell set to ${arg}` }]);
|
|
18632
|
+
return true;
|
|
18633
|
+
}
|
|
18634
|
+
const detected = getShellCommand(cfg.shell);
|
|
18635
|
+
setEvents((e) => [
|
|
18636
|
+
...e,
|
|
18637
|
+
{
|
|
18638
|
+
kind: "info",
|
|
18639
|
+
key: mkKey(),
|
|
18640
|
+
text: `shell: ${cfg.shell ?? "auto"} (${detected.shell} ${detected.args.join(" ")})`
|
|
18641
|
+
}
|
|
18642
|
+
]);
|
|
18643
|
+
return true;
|
|
18644
|
+
}
|
|
18577
18645
|
if (c === "/model") {
|
|
18578
18646
|
setEvents((e) => [
|
|
18579
18647
|
...e,
|
|
@@ -20317,6 +20385,7 @@ var init_app = __esm({
|
|
|
20317
20385
|
init_artifact_compaction();
|
|
20318
20386
|
init_session_state();
|
|
20319
20387
|
init_executor();
|
|
20388
|
+
init_bash();
|
|
20320
20389
|
init_manager3();
|
|
20321
20390
|
init_manager2();
|
|
20322
20391
|
init_lsp();
|
|
@@ -20602,7 +20671,7 @@ async function main() {
|
|
|
20602
20671
|
cloudToken = cloudCreds.accessToken;
|
|
20603
20672
|
cloudDeviceId = cloudCreds.deviceId;
|
|
20604
20673
|
cfg = {
|
|
20605
|
-
...cfg ?? { accountId: "", apiToken: "", model: DEFAULT_MODEL },
|
|
20674
|
+
...cfg ?? { accountId: "", apiToken: "", model: DEFAULT_MODEL, memoryEnabled: true },
|
|
20606
20675
|
cloudMode: true
|
|
20607
20676
|
};
|
|
20608
20677
|
}
|