@artenx/dshb 0.1.33 → 0.1.34
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/cordis.patch.yml +8 -4
- package/lib/auth/reset-cli.d.ts +15 -0
- package/lib/auth/reset-cli.js +47 -0
- package/lib/auth/reset-cli.js.map +1 -0
- package/package.json +6 -4
- package/lib/auth/startup.d.ts +0 -15
- package/lib/auth/startup.js +0 -68
- package/lib/auth/startup.js.map +0 -1
package/cordis.patch.yml
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# DSHB 的补丁层:仅插入各功能条目。
|
|
2
|
+
#
|
|
3
|
+
# 注意:不要在这里禁用或替换上游 `web-startup`(dsh-web-app)。webserver 行
|
|
4
|
+
# `inject: [webStartup]`,运行时启用/停用插件的补丁重载一旦更换 webStartup
|
|
5
|
+
# 提供者,webServer 会被连带销毁重建,拆断桌面端正在使用的连接(0.1.33 及
|
|
6
|
+
# 之前桌面端"启用即断连、左下角重连横幅"的根因)。上游 web-startup 与历史
|
|
7
|
+
# 替换版功能等价(--host/--port/--trusted-host/--no-open),凭据重置已改为
|
|
8
|
+
# 独立命令 `dshb-auth-reset`(见 src/auth/reset-cli.ts)。
|
|
3
9
|
- insert:
|
|
4
10
|
- id: dshb-auth
|
|
5
11
|
name: "@artenx/dshb/auth"
|
|
6
|
-
- id: dshb-web-startup
|
|
7
|
-
name: "@artenx/dshb/auth/startup"
|
|
8
12
|
- id: dshb-core
|
|
9
13
|
name: "@artenx/dshb/core"
|
|
10
14
|
- id: dshb-router
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/auth/reset-cli.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* 独立的凭据重置 CLI(`dshb-auth-reset`)。
|
|
4
|
+
*
|
|
5
|
+
* 历史上它是 `dsh --profile web auth-reset` 子命令,由 dshb-web-startup 替换
|
|
6
|
+
* 上游 web-startup 实现。该替换会让 webServer 行(inject: [webStartup])在
|
|
7
|
+
* 运行时启用/停用插件的补丁重载中连带重建,拆断桌面端正在使用的连接
|
|
8
|
+
* (0.1.33 及之前桌面端"启用即断连"的根因)。上游 web-startup 与本替换版
|
|
9
|
+
* 功能等价(--host/--port/--trusted-host/--no-open 全部一致),因此 0.1.34
|
|
10
|
+
* 起不再替换,auth-reset 降级为本独立命令。
|
|
11
|
+
*/
|
|
12
|
+
declare function runAuthResetCli(argv: readonly string[]): never;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { runAuthResetCli };
|
|
15
|
+
//# sourceMappingURL=reset-cli.d.ts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { sanitizeUsername, sharedCredentialStore } from "./credentials.js";
|
|
2
|
+
//#region src/auth/reset-cli.ts
|
|
3
|
+
/**
|
|
4
|
+
* 独立的凭据重置 CLI(`dshb-auth-reset`)。
|
|
5
|
+
*
|
|
6
|
+
* 历史上它是 `dsh --profile web auth-reset` 子命令,由 dshb-web-startup 替换
|
|
7
|
+
* 上游 web-startup 实现。该替换会让 webServer 行(inject: [webStartup])在
|
|
8
|
+
* 运行时启用/停用插件的补丁重载中连带重建,拆断桌面端正在使用的连接
|
|
9
|
+
* (0.1.33 及之前桌面端"启用即断连"的根因)。上游 web-startup 与本替换版
|
|
10
|
+
* 功能等价(--host/--port/--trusted-host/--no-open 全部一致),因此 0.1.34
|
|
11
|
+
* 起不再替换,auth-reset 降级为本独立命令。
|
|
12
|
+
*/
|
|
13
|
+
function runAuthResetCli(argv) {
|
|
14
|
+
const store = sharedCredentialStore();
|
|
15
|
+
if (!store.isInitialized()) {
|
|
16
|
+
console.error("dshb-auth-reset: 尚未初始化管理员账号");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
let password;
|
|
20
|
+
let username;
|
|
21
|
+
for (let i = 0; i < argv.length; i += 1) if (argv[i] === "--password") password = argv[++i];
|
|
22
|
+
else if (argv[i] === "--username") username = argv[++i];
|
|
23
|
+
if (password === void 0 && username === void 0) {
|
|
24
|
+
console.error("dshb-auth-reset: 需要 --password <新密码> 和/或 --username <新用户名>");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
if (password !== void 0 && password.length < 8) {
|
|
28
|
+
console.error("dshb-auth-reset: 密码至少 8 个字符");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
if (username !== void 0) {
|
|
32
|
+
username = sanitizeUsername(username);
|
|
33
|
+
if (!username) {
|
|
34
|
+
console.error("dshb-auth-reset: 用户名不能为空");
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (password !== void 0) store.changePassword(username ?? store.username, password);
|
|
39
|
+
else store.changeUsername(username);
|
|
40
|
+
console.log("dshb-auth-reset: 已更新凭据并轮换会话密钥,全部已签发会话作废");
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
if (process.argv[1]?.endsWith("reset-cli.js") === true || process.argv[1]?.endsWith("dshb-auth-reset") === true) runAuthResetCli(process.argv.slice(2));
|
|
44
|
+
//#endregion
|
|
45
|
+
export { runAuthResetCli };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=reset-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reset-cli.js","names":[],"sources":["../../src/auth/reset-cli.ts"],"sourcesContent":["import { sanitizeUsername, sharedCredentialStore } from './credentials.js'\n\n/**\n * 独立的凭据重置 CLI(`dshb-auth-reset`)。\n *\n * 历史上它是 `dsh --profile web auth-reset` 子命令,由 dshb-web-startup 替换\n * 上游 web-startup 实现。该替换会让 webServer 行(inject: [webStartup])在\n * 运行时启用/停用插件的补丁重载中连带重建,拆断桌面端正在使用的连接\n * (0.1.33 及之前桌面端\"启用即断连\"的根因)。上游 web-startup 与本替换版\n * 功能等价(--host/--port/--trusted-host/--no-open 全部一致),因此 0.1.34\n * 起不再替换,auth-reset 降级为本独立命令。\n */\nexport function runAuthResetCli(argv: readonly string[]): never {\n const store = sharedCredentialStore()\n if (!store.isInitialized()) {\n console.error('dshb-auth-reset: 尚未初始化管理员账号')\n process.exit(1)\n }\n let password: string | undefined\n let username: string | undefined\n for (let i = 0; i < argv.length; i += 1) {\n if (argv[i] === '--password') password = argv[++i]\n else if (argv[i] === '--username') username = argv[++i]\n }\n if (password === undefined && username === undefined) {\n console.error('dshb-auth-reset: 需要 --password <新密码> 和/或 --username <新用户名>')\n process.exit(1)\n }\n if (password !== undefined && password.length < 8) {\n console.error('dshb-auth-reset: 密码至少 8 个字符')\n process.exit(1)\n }\n if (username !== undefined) {\n username = sanitizeUsername(username)\n if (!username) {\n console.error('dshb-auth-reset: 用户名不能为空')\n process.exit(1)\n }\n }\n if (password !== undefined) store.changePassword(username ?? store.username!, password)\n else store.changeUsername(username!)\n console.log('dshb-auth-reset: 已更新凭据并轮换会话密钥,全部已签发会话作废')\n process.exit(0)\n}\n\nconst invokedDirectly = process.argv[1]?.endsWith('reset-cli.js') === true\n || process.argv[1]?.endsWith('dshb-auth-reset') === true\nif (invokedDirectly) runAuthResetCli(process.argv.slice(2))\n"],"mappings":";;;;;;;;;;;;AAYA,SAAgB,gBAAgB,MAAgC;CAC9D,MAAM,QAAQ,sBAAsB;CACpC,IAAI,CAAC,MAAM,cAAc,GAAG;EAC1B,QAAQ,MAAM,6BAA6B;EAC3C,QAAQ,KAAK,CAAC;CAChB;CACA,IAAI;CACJ,IAAI;CACJ,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GACpC,IAAI,KAAK,OAAO,cAAc,WAAW,KAAK,EAAE;MAC3C,IAAI,KAAK,OAAO,cAAc,WAAW,KAAK,EAAE;CAEvD,IAAI,aAAa,KAAA,KAAa,aAAa,KAAA,GAAW;EACpD,QAAQ,MAAM,4DAA4D;EAC1E,QAAQ,KAAK,CAAC;CAChB;CACA,IAAI,aAAa,KAAA,KAAa,SAAS,SAAS,GAAG;EACjD,QAAQ,MAAM,6BAA6B;EAC3C,QAAQ,KAAK,CAAC;CAChB;CACA,IAAI,aAAa,KAAA,GAAW;EAC1B,WAAW,iBAAiB,QAAQ;EACpC,IAAI,CAAC,UAAU;GACb,QAAQ,MAAM,0BAA0B;GACxC,QAAQ,KAAK,CAAC;EAChB;CACF;CACA,IAAI,aAAa,KAAA,GAAW,MAAM,eAAe,YAAY,MAAM,UAAW,QAAQ;MACjF,MAAM,eAAe,QAAS;CACnC,QAAQ,IAAI,yCAAyC;CACrD,QAAQ,KAAK,CAAC;AAChB;AAIA,IAFwB,QAAQ,KAAK,EAAE,EAAE,SAAS,cAAc,MAAM,QACjE,QAAQ,KAAK,EAAE,EAAE,SAAS,iBAAiB,MAAM,MACjC,gBAAgB,QAAQ,KAAK,MAAM,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@artenx/dshb",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.34",
|
|
4
4
|
"description": "Routes DSH filesystem, shell, subprocess, and PTY operations to local, SSH, and Docker execution nodes, with a node registry, an authentication gate, a mobile-aware management UI, and per-workspace custom project instructions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": "./lib/index.js",
|
|
10
10
|
"./auth": "./lib/auth/index.js",
|
|
11
|
-
"./auth/startup": "./lib/auth/startup.js",
|
|
12
11
|
"./auth/decorate-webserver": "./lib/auth/decorate-webserver.js",
|
|
13
12
|
"./auth/loopback": "./lib/auth/loopback.js",
|
|
14
13
|
"./auth/loopback-compat": "./lib/auth/loopback-compat.js",
|
|
@@ -83,8 +82,8 @@
|
|
|
83
82
|
"./package.json": "./package.json"
|
|
84
83
|
},
|
|
85
84
|
"files": [
|
|
86
|
-
"
|
|
87
|
-
"
|
|
85
|
+
"cordis.patch.yml",
|
|
86
|
+
"lib"
|
|
88
87
|
],
|
|
89
88
|
"dsh": {
|
|
90
89
|
"bundle": {
|
|
@@ -180,5 +179,8 @@
|
|
|
180
179
|
"homepage": "https://github.com/Artenx/dsh-honeybee#readme",
|
|
181
180
|
"bugs": {
|
|
182
181
|
"url": "https://github.com/Artenx/dsh-honeybee/issues"
|
|
182
|
+
},
|
|
183
|
+
"bin": {
|
|
184
|
+
"dshb-auth-reset": "./lib/auth/reset-cli.js"
|
|
183
185
|
}
|
|
184
186
|
}
|
package/lib/auth/startup.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Context } from "@deepseek-ai/cordis";
|
|
2
|
-
//#region src/auth/startup.d.ts
|
|
3
|
-
declare const name = "web-startup";
|
|
4
|
-
declare const inject: string[];
|
|
5
|
-
declare const WEB_STARTUP_SERVICE = "webStartup";
|
|
6
|
-
interface WebStartupValues {
|
|
7
|
-
openBrowser: boolean;
|
|
8
|
-
host?: string;
|
|
9
|
-
port?: number;
|
|
10
|
-
trustedHosts: string[];
|
|
11
|
-
}
|
|
12
|
-
declare function apply(ctx: Context): void;
|
|
13
|
-
//#endregion
|
|
14
|
-
export { WEB_STARTUP_SERVICE, WebStartupValues, apply, inject, name };
|
|
15
|
-
//# sourceMappingURL=startup.d.ts.map
|
package/lib/auth/startup.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { sanitizeUsername, sharedCredentialStore } from "./credentials.js";
|
|
2
|
-
import { Command } from "commander";
|
|
3
|
-
import { parseCmdline } from "@deepseek-ai/dsh-cmdline";
|
|
4
|
-
//#region src/auth/startup.ts
|
|
5
|
-
const name = "web-startup";
|
|
6
|
-
const inject = ["cmdlineArgs"];
|
|
7
|
-
const WEB_STARTUP_SERVICE = "webStartup";
|
|
8
|
-
function runAuthReset(args) {
|
|
9
|
-
const store = sharedCredentialStore();
|
|
10
|
-
if (!store.isInitialized()) {
|
|
11
|
-
console.error("auth-reset: 尚未初始化管理员账号");
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
14
|
-
let password;
|
|
15
|
-
let username;
|
|
16
|
-
for (let i = 1; i < args.length; i++) if (args[i] === "--password") password = args[++i];
|
|
17
|
-
else if (args[i] === "--username") username = args[++i];
|
|
18
|
-
if (password === void 0 && username === void 0) {
|
|
19
|
-
console.error("auth-reset: 需要 --password <新密码> 和/或 --username <新用户名>");
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
22
|
-
if (password !== void 0 && password.length < 8) {
|
|
23
|
-
console.error("auth-reset: 密码至少 8 个字符");
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
if (username !== void 0) {
|
|
27
|
-
username = sanitizeUsername(username);
|
|
28
|
-
if (!username) {
|
|
29
|
-
console.error("auth-reset: 用户名不能为空");
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
if (password !== void 0) store.changePassword(username ?? store.username, password);
|
|
34
|
-
else store.changeUsername(username);
|
|
35
|
-
console.log("auth-reset: 已更新凭据并轮换会话密钥,全部已签发会话作废");
|
|
36
|
-
process.exit(0);
|
|
37
|
-
}
|
|
38
|
-
function webCommand() {
|
|
39
|
-
return new Command().name("dsh --profile web").description("Serve the DeepSeek Harness UI.").helpOption("-h, --help", "show this help").option("--host <host>", "bind host").option("--no-open", "do not open the UI in the default browser").option("--port <port>", "listen port; pass 0 to let the OS pick a free one").option("--trusted-host <authority...>", "extra authority the /api browser-trust fence accepts (host or host:port; repeatable)").addHelpText("after", `
|
|
40
|
-
Examples:
|
|
41
|
-
dsh --profile web serve on the composed host and port
|
|
42
|
-
dsh --profile web --no-open serve without opening a browser (desktop app ignores this)
|
|
43
|
-
dsh --profile web --port 8080 serve on another port
|
|
44
|
-
dsh --profile web --host 0.0.0.0 serve on all interfaces (DSHB: auth plugin guards access)
|
|
45
|
-
dsh --profile web auth-reset --password <新密码> reset admin credentials and revoke all sessions
|
|
46
|
-
`);
|
|
47
|
-
}
|
|
48
|
-
function apply(ctx) {
|
|
49
|
-
const args = ctx.cmdlineArgs?.get() ?? [];
|
|
50
|
-
if (args[0] === "auth-reset") runAuthReset(args);
|
|
51
|
-
const program = webCommand();
|
|
52
|
-
program.action(() => {
|
|
53
|
-
const options = program.opts();
|
|
54
|
-
if (options.port !== void 0 && !/^\d+$/.test(options.port)) program.error(`error: --port must be a number, got ${JSON.stringify(options.port)}`);
|
|
55
|
-
const values = {
|
|
56
|
-
openBrowser: options.open,
|
|
57
|
-
...options.host !== void 0 && { host: options.host },
|
|
58
|
-
...options.port !== void 0 && { port: Number(options.port) },
|
|
59
|
-
trustedHosts: options.trustedHost ?? []
|
|
60
|
-
};
|
|
61
|
-
ctx.provide(WEB_STARTUP_SERVICE, values);
|
|
62
|
-
});
|
|
63
|
-
parseCmdline(ctx, program);
|
|
64
|
-
}
|
|
65
|
-
//#endregion
|
|
66
|
-
export { WEB_STARTUP_SERVICE, apply, inject, name };
|
|
67
|
-
|
|
68
|
-
//# sourceMappingURL=startup.js.map
|
package/lib/auth/startup.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"startup.js","names":[],"sources":["../../src/auth/startup.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { parseCmdline } from '@deepseek-ai/dsh-cmdline'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { sanitizeUsername, sharedCredentialStore } from './credentials.js'\n\nexport const name = 'web-startup'\n\nexport const inject = ['cmdlineArgs']\n\nexport const WEB_STARTUP_SERVICE = 'webStartup'\n\nexport interface WebStartupValues {\n openBrowser: boolean\n host?: string\n port?: number\n trustedHosts: string[]\n}\n\nfunction runAuthReset(args: readonly string[]): never {\n const store = sharedCredentialStore()\n if (!store.isInitialized()) {\n console.error('auth-reset: 尚未初始化管理员账号')\n process.exit(1)\n }\n let password: string | undefined\n let username: string | undefined\n for (let i = 1; i < args.length; i++) {\n if (args[i] === '--password') password = args[++i]\n else if (args[i] === '--username') username = args[++i]\n }\n if (password === undefined && username === undefined) {\n console.error('auth-reset: 需要 --password <新密码> 和/或 --username <新用户名>')\n process.exit(1)\n }\n if (password !== undefined && password.length < 8) {\n console.error('auth-reset: 密码至少 8 个字符')\n process.exit(1)\n }\n if (username !== undefined) {\n username = sanitizeUsername(username)\n if (!username) {\n console.error('auth-reset: 用户名不能为空')\n process.exit(1)\n }\n }\n if (password !== undefined) store.changePassword(username ?? store.username!, password)\n else store.changeUsername(username!)\n console.log('auth-reset: 已更新凭据并轮换会话密钥,全部已签发会话作废')\n process.exit(0)\n}\n\nfunction webCommand(): Command {\n return new Command()\n .name('dsh --profile web')\n .description('Serve the DeepSeek Harness UI.')\n .helpOption('-h, --help', 'show this help')\n .option('--host <host>', 'bind host')\n .option('--no-open', 'do not open the UI in the default browser')\n .option('--port <port>', 'listen port; pass 0 to let the OS pick a free one')\n .option(\n '--trusted-host <authority...>',\n 'extra authority the /api browser-trust fence accepts (host or host:port; repeatable)',\n )\n .addHelpText(\n 'after',\n `\nExamples:\n dsh --profile web serve on the composed host and port\n dsh --profile web --no-open serve without opening a browser (desktop app ignores this)\n dsh --profile web --port 8080 serve on another port\n dsh --profile web --host 0.0.0.0 serve on all interfaces (DSHB: auth plugin guards access)\n dsh --profile web auth-reset --password <新密码> reset admin credentials and revoke all sessions\n`,\n )\n}\n\nexport function apply(ctx: Context): void {\n const args = ctx.cmdlineArgs?.get() ?? []\n if (args[0] === 'auth-reset') runAuthReset(args)\n const program = webCommand()\n program.action(() => {\n const options = program.opts()\n if (options.port !== undefined && !/^\\d+$/.test(options.port)) {\n program.error(`error: --port must be a number, got ${JSON.stringify(options.port)}`)\n }\n const values: WebStartupValues = {\n openBrowser: options.open,\n ...(options.host !== undefined && { host: options.host }),\n ...(options.port !== undefined && { port: Number(options.port) }),\n trustedHosts: options.trustedHost ?? [],\n }\n ctx.provide(WEB_STARTUP_SERVICE, values)\n })\n parseCmdline(ctx, program)\n}\n"],"mappings":";;;;AAKA,MAAa,OAAO;AAEpB,MAAa,SAAS,CAAC,aAAa;AAEpC,MAAa,sBAAsB;AASnC,SAAS,aAAa,MAAgC;CACpD,MAAM,QAAQ,sBAAsB;CACpC,IAAI,CAAC,MAAM,cAAc,GAAG;EAC1B,QAAQ,MAAM,wBAAwB;EACtC,QAAQ,KAAK,CAAC;CAChB;CACA,IAAI;CACJ,IAAI;CACJ,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAC/B,IAAI,KAAK,OAAO,cAAc,WAAW,KAAK,EAAE;MAC3C,IAAI,KAAK,OAAO,cAAc,WAAW,KAAK,EAAE;CAEvD,IAAI,aAAa,KAAA,KAAa,aAAa,KAAA,GAAW;EACpD,QAAQ,MAAM,uDAAuD;EACrE,QAAQ,KAAK,CAAC;CAChB;CACA,IAAI,aAAa,KAAA,KAAa,SAAS,SAAS,GAAG;EACjD,QAAQ,MAAM,wBAAwB;EACtC,QAAQ,KAAK,CAAC;CAChB;CACA,IAAI,aAAa,KAAA,GAAW;EAC1B,WAAW,iBAAiB,QAAQ;EACpC,IAAI,CAAC,UAAU;GACb,QAAQ,MAAM,qBAAqB;GACnC,QAAQ,KAAK,CAAC;EAChB;CACF;CACA,IAAI,aAAa,KAAA,GAAW,MAAM,eAAe,YAAY,MAAM,UAAW,QAAQ;MACjF,MAAM,eAAe,QAAS;CACnC,QAAQ,IAAI,oCAAoC;CAChD,QAAQ,KAAK,CAAC;AAChB;AAEA,SAAS,aAAsB;CAC7B,OAAO,IAAI,QAAQ,CAAC,CACjB,KAAK,mBAAmB,CAAC,CACzB,YAAY,gCAAgC,CAAC,CAC7C,WAAW,cAAc,gBAAgB,CAAC,CAC1C,OAAO,iBAAiB,WAAW,CAAC,CACpC,OAAO,aAAa,2CAA2C,CAAC,CAChE,OAAO,iBAAiB,mDAAmD,CAAC,CAC5E,OACC,iCACA,sFACF,CAAC,CACA,YACC,SACA;;;;;;;CAQF;AACJ;AAEA,SAAgB,MAAM,KAAoB;CACxC,MAAM,OAAO,IAAI,aAAa,IAAI,KAAK,CAAC;CACxC,IAAI,KAAK,OAAO,cAAc,aAAa,IAAI;CAC/C,MAAM,UAAU,WAAW;CAC3B,QAAQ,aAAa;EACnB,MAAM,UAAU,QAAQ,KAAK;EAC7B,IAAI,QAAQ,SAAS,KAAA,KAAa,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAC1D,QAAQ,MAAM,uCAAuC,KAAK,UAAU,QAAQ,IAAI,GAAG;EAErF,MAAM,SAA2B;GAC/B,aAAa,QAAQ;GACrB,GAAI,QAAQ,SAAS,KAAA,KAAa,EAAE,MAAM,QAAQ,KAAK;GACvD,GAAI,QAAQ,SAAS,KAAA,KAAa,EAAE,MAAM,OAAO,QAAQ,IAAI,EAAE;GAC/D,cAAc,QAAQ,eAAe,CAAC;EACxC;EACA,IAAI,QAAQ,qBAAqB,MAAM;CACzC,CAAC;CACD,aAAa,KAAK,OAAO;AAC3B"}
|