huaweicloud-devkit 1.1.0-next.2 → 1.1.0-next.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -1
- package/README.zh-CN.md +63 -1
- package/package.json +3 -1
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +46 -21
- package/plugins/huaweicloud-core/openclaw.plugin.json +1 -1
- package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +136 -24
- package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +15 -14
- package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +5 -1
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/hc-activity.md +27 -0
- package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +11 -11
- package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +6 -5
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +596 -93
- package/plugins/huaweicloud-core/skills/huawei-sandbox/references/framework-commands.md +0 -1
- package/plugins/huaweicloud-core/skills/huawei-sandbox/references/nginx-templates.md +8 -4
- package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +19 -12
- package/plugins/huaweicloud-core/skills/huawei-voucher/SKILL.md +52 -0
- package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +26 -26
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +12 -6
- package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +7 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +7 -7
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +2 -0
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +8 -0
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +30 -1
- package/plugins/huaweicloud-core/src/auth/service.mjs +33 -0
- package/plugins/huaweicloud-core/src/detect-framework.mjs +25 -3
- package/plugins/huaweicloud-core/src/hcloud-cli.mjs +67 -5
- package/plugins/huaweicloud-core/src/mcp-server.mjs +23 -0
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +11 -3
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +436 -21
- package/plugins/huaweicloud-core/src/search-market.mjs +1 -0
- package/plugins/huaweicloud-core/src/setup-cli.mjs +605 -50
- package/plugins/huaweicloud-core/src/tools.mjs +193 -18
- package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +29 -8
|
@@ -6,7 +6,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
6
6
|
|
|
7
7
|
import { evaluateArtifacts, evaluateCommandRisk, evaluateDeployPlan } from './risk-rule-engine.mjs';
|
|
8
8
|
import { classifyTextCommand, redactSecrets } from './safety-policy.mjs';
|
|
9
|
-
import { planHcloudCommand, runHcloud } from './hcloud-cli.mjs';
|
|
9
|
+
import { planHcloudCommand, runHcloud, consumeApprovalToken } from './hcloud-cli.mjs';
|
|
10
10
|
import { searchMarketplace } from './search-market.mjs';
|
|
11
11
|
import { getServiceIcon } from './icon-library.mjs';
|
|
12
12
|
import { detectFramework } from './detect-framework.mjs';
|
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
closeSession,
|
|
17
17
|
uploadFileWithSession,
|
|
18
18
|
uploadProjectWithSession,
|
|
19
|
+
deployNginx,
|
|
20
|
+
deployCheck,
|
|
19
21
|
getCurrentWorkspaceId,
|
|
20
22
|
setWorkspaceId,
|
|
21
23
|
} from './sandbox/session-manager.mjs';
|
|
@@ -27,6 +29,7 @@ import {
|
|
|
27
29
|
hdkitVoucherStatus,
|
|
28
30
|
hdkitVoucherClaim,
|
|
29
31
|
} from './sandbox/hdkitservice-api.mjs';
|
|
32
|
+
import { getCredentials } from './sandbox/hwlink-api.mjs';
|
|
30
33
|
import { getAuthStatus, syncAuth } from './auth/service.mjs';
|
|
31
34
|
import {
|
|
32
35
|
readGlobalCredentials,
|
|
@@ -45,6 +48,10 @@ function codeartsSkillsDir() {
|
|
|
45
48
|
const home = homedir();
|
|
46
49
|
return join(home, '.codeartsdoer', 'skills');
|
|
47
50
|
}
|
|
51
|
+
function codeartsWorkSkillsDir() {
|
|
52
|
+
const home = homedir();
|
|
53
|
+
return join(home, '.codeartswork', 'skills');
|
|
54
|
+
}
|
|
48
55
|
function workbuddySkillsDir() {
|
|
49
56
|
const home = homedir();
|
|
50
57
|
return join(home, '.workbuddy', 'skills');
|
|
@@ -99,6 +106,10 @@ function hermesSkillsDir() {
|
|
|
99
106
|
return join(home, '.hermes', 'skills');
|
|
100
107
|
}
|
|
101
108
|
|
|
109
|
+
function codexDesktopSkillsDir() {
|
|
110
|
+
return join(homedir(), '.agents', 'skills');
|
|
111
|
+
}
|
|
112
|
+
|
|
102
113
|
export function listSkillDirs(root) {
|
|
103
114
|
if (!existsSync(root)) return [];
|
|
104
115
|
try {
|
|
@@ -123,10 +134,12 @@ function resolveSkillsRoot() {
|
|
|
123
134
|
SKILLS_ROOT_DEV,
|
|
124
135
|
dshSkillsDir(),
|
|
125
136
|
codeartsSkillsDir(),
|
|
137
|
+
codeartsWorkSkillsDir(),
|
|
126
138
|
opencodeSkillsDir(),
|
|
127
139
|
workbuddySkillsDir(),
|
|
128
140
|
officeaceSkillsRoot(),
|
|
129
141
|
hermesSkillsDir(),
|
|
142
|
+
codexDesktopSkillsDir(),
|
|
130
143
|
]) || SKILLS_ROOT_DEV
|
|
131
144
|
);
|
|
132
145
|
}
|
|
@@ -211,16 +224,16 @@ export const TOOL_DEFINITIONS = [
|
|
|
211
224
|
'Run a write-capable hcloud command only after the exact command has been shown and explicitly approved by the user.',
|
|
212
225
|
inputSchema: {
|
|
213
226
|
type: 'object',
|
|
214
|
-
required: ['args', '
|
|
227
|
+
required: ['args', 'approvalToken', 'approvedByUser'],
|
|
215
228
|
properties: {
|
|
216
229
|
args: {
|
|
217
230
|
type: 'array',
|
|
218
231
|
items: { type: 'string' },
|
|
219
232
|
description: 'hcloud arguments, excluding the hcloud executable.',
|
|
220
233
|
},
|
|
221
|
-
|
|
234
|
+
approvalToken: {
|
|
222
235
|
type: 'string',
|
|
223
|
-
description: 'The
|
|
236
|
+
description: 'The approvalToken returned by huaweicloud_plan_cli_command.',
|
|
224
237
|
},
|
|
225
238
|
approvedByUser: {
|
|
226
239
|
type: 'boolean',
|
|
@@ -446,7 +459,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
446
459
|
target: {
|
|
447
460
|
type: 'string',
|
|
448
461
|
description:
|
|
449
|
-
'Agent target to check: opencode, codex, codex-desktop, codearts, workbuddy, dsh, officeace, hermes, openclaw, atomcode, or all (default).',
|
|
462
|
+
'Agent target to check: opencode, codex, codex-desktop, codearts, codearts-work, workbuddy, dsh, officeace, hermes, openclaw, atomcode, or all (default).',
|
|
450
463
|
},
|
|
451
464
|
},
|
|
452
465
|
},
|
|
@@ -461,7 +474,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
461
474
|
target: {
|
|
462
475
|
type: 'string',
|
|
463
476
|
description:
|
|
464
|
-
'Agent target to report after sync: opencode, codex, codex-desktop, codearts, workbuddy, dsh, officeace, hermes, or all (default).',
|
|
477
|
+
'Agent target to report after sync: opencode, codex, codex-desktop, codearts, codearts-work, workbuddy, dsh, officeace, hermes, or all (default).',
|
|
465
478
|
},
|
|
466
479
|
},
|
|
467
480
|
},
|
|
@@ -549,7 +562,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
549
562
|
'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
|
|
550
563
|
},
|
|
551
564
|
username: { type: 'string', description: 'Login username (default: root)' },
|
|
552
|
-
timeout_ms: { type: 'number', description: '
|
|
565
|
+
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 60000)' },
|
|
553
566
|
},
|
|
554
567
|
},
|
|
555
568
|
},
|
|
@@ -586,6 +599,72 @@ export const TOOL_DEFINITIONS = [
|
|
|
586
599
|
},
|
|
587
600
|
},
|
|
588
601
|
},
|
|
602
|
+
{
|
|
603
|
+
name: 'huaweicloud_sandbox_deploy_nginx',
|
|
604
|
+
description:
|
|
605
|
+
'Deploy an nginx configuration on the sandbox and reload. Takes nginxType, port, project, outputDir from framework detection and writes the correct template (SPA try_files, SSR reverse proxy, or static). Also fixes directory traverse permissions on the project path. Use this instead of manually constructing nginx config — it handles permissions, template selection, and reload in one call.',
|
|
606
|
+
inputSchema: {
|
|
607
|
+
type: 'object',
|
|
608
|
+
required: ['nginx_type', 'port', 'project', 'output_dir'],
|
|
609
|
+
properties: {
|
|
610
|
+
nginx_type: {
|
|
611
|
+
type: 'string',
|
|
612
|
+
description:
|
|
613
|
+
'Nginx config type from framework detection: spa (try_files fallback for SPA/SSG/cross-platform), proxy (reverse proxy for SSR), or static (plain root for Hugo/Hexo).',
|
|
614
|
+
enum: ['spa', 'proxy', 'static'],
|
|
615
|
+
},
|
|
616
|
+
port: { type: 'number', description: 'Listen port (from framework detection).' },
|
|
617
|
+
project: { type: 'string', description: 'Project directory name under /workspace, e.g. movie-ticket.' },
|
|
618
|
+
output_dir: {
|
|
619
|
+
type: 'string',
|
|
620
|
+
description: 'Build output directory relative to /workspace/<project>, e.g. dist/build/h5.',
|
|
621
|
+
},
|
|
622
|
+
node_port: { type: 'number', description: 'Node.js app port for SSR (required when nginx_type=proxy).' },
|
|
623
|
+
public_port: { type: 'number', description: 'Public listen port for SSR proxy (optional, defaults to port).' },
|
|
624
|
+
config_name: {
|
|
625
|
+
type: 'string',
|
|
626
|
+
description:
|
|
627
|
+
'Config file name (without .conf suffix). Defaults to the project name, ensuring each project gets its own config. Override with distinct names (e.g. admin, docs) for sub-app deployments.',
|
|
628
|
+
},
|
|
629
|
+
workspace_id: {
|
|
630
|
+
type: 'string',
|
|
631
|
+
description:
|
|
632
|
+
'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
|
|
633
|
+
},
|
|
634
|
+
username: { type: 'string', description: 'Login username (default: root)' },
|
|
635
|
+
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 30000)' },
|
|
636
|
+
},
|
|
637
|
+
},
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
name: 'huaweicloud_sandbox_deploy_check',
|
|
641
|
+
description:
|
|
642
|
+
'Run a deployment completeness check on the sandbox. Verifies nginx is serving, output directory exists, DevBridge tunnel is active and accessible, and QR code exists (cross-platform). Returns a score and nextStep to fix any missing items. Call this at the end of a deployment workflow to confirm everything is working before reporting success.',
|
|
643
|
+
inputSchema: {
|
|
644
|
+
type: 'object',
|
|
645
|
+
required: ['port', 'project', 'output_dir'],
|
|
646
|
+
properties: {
|
|
647
|
+
port: { type: 'number', description: 'App listen port (from framework detection).' },
|
|
648
|
+
project: { type: 'string', description: 'Project directory name under /workspace.' },
|
|
649
|
+
output_dir: {
|
|
650
|
+
type: 'string',
|
|
651
|
+
description: 'Build output directory relative to /workspace/<project>, e.g. dist/build/h5.',
|
|
652
|
+
},
|
|
653
|
+
framework_type: {
|
|
654
|
+
type: 'string',
|
|
655
|
+
description: 'Framework type from detect_framework. Set to cross-platform for QR code check.',
|
|
656
|
+
enum: ['spa', 'ssr', 'ssg', 'cross-platform', 'monorepo', 'static'],
|
|
657
|
+
},
|
|
658
|
+
workspace_id: {
|
|
659
|
+
type: 'string',
|
|
660
|
+
description:
|
|
661
|
+
'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
|
|
662
|
+
},
|
|
663
|
+
username: { type: 'string', description: 'Login username (default: root)' },
|
|
664
|
+
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 30000)' },
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
},
|
|
589
668
|
{
|
|
590
669
|
name: 'huaweicloud_sandbox_check_user',
|
|
591
670
|
description:
|
|
@@ -648,21 +727,27 @@ export const TOOL_DEFINITIONS = [
|
|
|
648
727
|
},
|
|
649
728
|
{
|
|
650
729
|
name: 'huaweicloud_voucher_status',
|
|
651
|
-
description: '
|
|
730
|
+
description: '查询代金券领取状态。',
|
|
652
731
|
inputSchema: {
|
|
653
732
|
type: 'object',
|
|
654
733
|
properties: {
|
|
655
|
-
domain_id: {
|
|
734
|
+
domain_id: {
|
|
735
|
+
type: 'string',
|
|
736
|
+
description: 'Optional. Leave empty in production — account is resolved from IAM automatically.',
|
|
737
|
+
},
|
|
656
738
|
},
|
|
657
739
|
},
|
|
658
740
|
},
|
|
659
741
|
{
|
|
660
742
|
name: 'huaweicloud_voucher_claim',
|
|
661
|
-
description: '
|
|
743
|
+
description: '领取代金券(一人一次)。重复领取会返回已领取。',
|
|
662
744
|
inputSchema: {
|
|
663
745
|
type: 'object',
|
|
664
746
|
properties: {
|
|
665
|
-
domain_id: {
|
|
747
|
+
domain_id: {
|
|
748
|
+
type: 'string',
|
|
749
|
+
description: 'Optional. Leave empty in production — account is resolved from IAM automatically.',
|
|
750
|
+
},
|
|
666
751
|
},
|
|
667
752
|
},
|
|
668
753
|
},
|
|
@@ -818,6 +903,59 @@ export async function callTool(name, args = {}) {
|
|
|
818
903
|
},
|
|
819
904
|
);
|
|
820
905
|
}
|
|
906
|
+
case 'huaweicloud_sandbox_deploy_nginx': {
|
|
907
|
+
if (!args.nginx_type || !args.port || !args.project || !args.output_dir) {
|
|
908
|
+
throw new Error('nginx_type, port, project, and output_dir are required.');
|
|
909
|
+
}
|
|
910
|
+
const sandboxWsId7 = args.workspace_id || getCurrentWorkspaceId();
|
|
911
|
+
if (!sandboxWsId7) {
|
|
912
|
+
throw new Error(
|
|
913
|
+
'workspace_id is required. No sandbox connected — call huaweicloud_sandbox_connect first, ' +
|
|
914
|
+
'or set HW_WORKSPACE_ID environment variable before starting the agent.',
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
const sandboxUser7 = args.username || 'root';
|
|
918
|
+
const sandboxTimeout7 = args.timeout_ms || 60000;
|
|
919
|
+
return await deployNginx(
|
|
920
|
+
sandboxWsId7,
|
|
921
|
+
{
|
|
922
|
+
nginxType: args.nginx_type,
|
|
923
|
+
port: args.port,
|
|
924
|
+
project: args.project,
|
|
925
|
+
outputDir: args.output_dir,
|
|
926
|
+
nodePort: args.node_port,
|
|
927
|
+
publicPort: args.public_port,
|
|
928
|
+
configName: args.config_name,
|
|
929
|
+
},
|
|
930
|
+
sandboxUser7,
|
|
931
|
+
sandboxTimeout7,
|
|
932
|
+
);
|
|
933
|
+
}
|
|
934
|
+
case 'huaweicloud_sandbox_deploy_check': {
|
|
935
|
+
if (!args.port || !args.project || !args.output_dir) {
|
|
936
|
+
throw new Error('port, project, and output_dir are required.');
|
|
937
|
+
}
|
|
938
|
+
const sandboxWsId8 = args.workspace_id || getCurrentWorkspaceId();
|
|
939
|
+
if (!sandboxWsId8) {
|
|
940
|
+
throw new Error(
|
|
941
|
+
'workspace_id is required. No sandbox connected — call huaweicloud_sandbox_connect first, ' +
|
|
942
|
+
'or set HW_WORKSPACE_ID environment variable before starting the agent.',
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
const sandboxUser8 = args.username || 'root';
|
|
946
|
+
const sandboxTimeout8 = args.timeout_ms || 30000;
|
|
947
|
+
return await deployCheck(
|
|
948
|
+
sandboxWsId8,
|
|
949
|
+
{
|
|
950
|
+
port: args.port,
|
|
951
|
+
project: args.project,
|
|
952
|
+
outputDir: args.output_dir,
|
|
953
|
+
frameworkType: args.framework_type,
|
|
954
|
+
},
|
|
955
|
+
sandboxUser8,
|
|
956
|
+
sandboxTimeout8,
|
|
957
|
+
);
|
|
958
|
+
}
|
|
821
959
|
case 'huaweicloud_sandbox_check_user':
|
|
822
960
|
return await hdkitCheckUser();
|
|
823
961
|
case 'huaweicloud_sandbox_sign_agreement':
|
|
@@ -827,11 +965,38 @@ export async function callTool(name, args = {}) {
|
|
|
827
965
|
const devStageId = connectResult?.dev_stage_id || connectResult?.devStageId;
|
|
828
966
|
if (devStageId) {
|
|
829
967
|
setWorkspaceId(devStageId);
|
|
968
|
+
try {
|
|
969
|
+
await execOneShot(devStageId, 'devbridge delete-all 2>/dev/null || true', 'root', 15000);
|
|
970
|
+
} catch {}
|
|
830
971
|
}
|
|
831
972
|
return connectResult;
|
|
832
973
|
}
|
|
833
|
-
case 'huaweicloud_sandbox_credentials':
|
|
834
|
-
|
|
974
|
+
case 'huaweicloud_sandbox_credentials': {
|
|
975
|
+
const devStageId = args.dev_stage_id || getCurrentWorkspaceId();
|
|
976
|
+
const credResult = await hdkitCredentials(args.session_id, devStageId, args.enable_sts !== false);
|
|
977
|
+
const sandboxWsIdCred = args.dev_stage_id || getCurrentWorkspaceId();
|
|
978
|
+
if (sandboxWsIdCred) {
|
|
979
|
+
try {
|
|
980
|
+
const { ak, sk, securitytoken } = getCredentials();
|
|
981
|
+
const credsScript = [
|
|
982
|
+
`export HW_ACCESS_KEY='${ak}'`,
|
|
983
|
+
`export HW_SECRET_KEY='${sk}'`,
|
|
984
|
+
securitytoken ? `export HW_SECURITY_TOKEN='${securitytoken}'` : '',
|
|
985
|
+
securitytoken ? `export X_HW_SECURITY_TOKEN='${securitytoken}'` : '',
|
|
986
|
+
]
|
|
987
|
+
.filter(Boolean)
|
|
988
|
+
.join('\n');
|
|
989
|
+
const credsFile = '/tmp/hw_creds.sh';
|
|
990
|
+
await execOneShot(
|
|
991
|
+
sandboxWsIdCred,
|
|
992
|
+
`cat > ${credsFile} << 'HWCREDS_EOF'\n${credsScript}\nHWCREDS_EOF\nchmod 600 ${credsFile}`,
|
|
993
|
+
'root',
|
|
994
|
+
15000,
|
|
995
|
+
);
|
|
996
|
+
} catch {}
|
|
997
|
+
}
|
|
998
|
+
return credResult;
|
|
999
|
+
}
|
|
835
1000
|
case 'huaweicloud_voucher_status':
|
|
836
1001
|
return await hdkitVoucherStatus(args.domain_id);
|
|
837
1002
|
case 'huaweicloud_voucher_claim':
|
|
@@ -1066,12 +1231,17 @@ async function runApprovedCommand(args = {}) {
|
|
|
1066
1231
|
if (args.approvedByUser !== true) {
|
|
1067
1232
|
throw new Error('approvedByUser must be true after explicit user approval for this exact command.');
|
|
1068
1233
|
}
|
|
1069
|
-
const
|
|
1070
|
-
const
|
|
1071
|
-
if (
|
|
1072
|
-
throw new Error('
|
|
1234
|
+
const token = String(args.approvalToken || '');
|
|
1235
|
+
const storedArgs = consumeApprovalToken(token);
|
|
1236
|
+
if (!storedArgs || storedArgs.length === 0) {
|
|
1237
|
+
throw new Error('Invalid or expired approval token. Please re-plan the command.');
|
|
1238
|
+
}
|
|
1239
|
+
const providedArgs = Array.isArray(args.args) ? args.args.map(String) : [];
|
|
1240
|
+
if (JSON.stringify(storedArgs) !== JSON.stringify(providedArgs)) {
|
|
1241
|
+
throw new Error('Provided args do not match the approved plan. Use the exact args from the plan.');
|
|
1073
1242
|
}
|
|
1074
|
-
const
|
|
1243
|
+
const strictPlan = planHcloudCommand(providedArgs, { allowWrites: false });
|
|
1244
|
+
const result = await runHcloud(providedArgs, {
|
|
1075
1245
|
allowWrites: true,
|
|
1076
1246
|
timeoutMs: args.timeoutMs,
|
|
1077
1247
|
maxRetries: args.maxRetries,
|
|
@@ -1184,6 +1354,11 @@ function serviceCatalog(intent = '') {
|
|
|
1184
1354
|
skills: ['huawei-dds-dcs'],
|
|
1185
1355
|
services: ['DDS', 'DCS'],
|
|
1186
1356
|
},
|
|
1357
|
+
{
|
|
1358
|
+
keywords: ['voucher', 'coupon', 'incentive', 'credit', '领券', '代金券', '优惠券', '激励金', '领取'],
|
|
1359
|
+
skills: ['huawei-voucher'],
|
|
1360
|
+
services: ['Incentive Voucher'],
|
|
1361
|
+
},
|
|
1187
1362
|
];
|
|
1188
1363
|
const matched = [];
|
|
1189
1364
|
const tokens = new Set(it.split(/[\s,./-]+/).filter((t) => t.length > 0));
|
|
@@ -38,15 +38,36 @@ function closeQuietly(ws) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function escapeRegExp(text) {
|
|
42
|
-
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
41
|
function stripEchoedLine(text, line, options = {}) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
if (text.startsWith(line)) {
|
|
43
|
+
text = text.slice(line.length);
|
|
44
|
+
text = text.replace(/^\r?\n/, '');
|
|
45
|
+
if (!options.allowAttached) return text;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (const nl of ['\r\n', '\n']) {
|
|
49
|
+
const idx = text.indexOf(nl + line);
|
|
50
|
+
if (idx !== -1) {
|
|
51
|
+
const before = text.slice(0, idx + nl.length);
|
|
52
|
+
const after = text.slice(idx + nl.length + line.length);
|
|
53
|
+
text = before + after.replace(/^\r?\n/, '');
|
|
54
|
+
if (!options.allowAttached) return text;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (options.allowAttached) {
|
|
60
|
+
for (let trail = 2; trail >= 0; trail--) {
|
|
61
|
+
for (const nl of ['\r\n', '\n']) {
|
|
62
|
+
const suffix = nl.repeat(trail);
|
|
63
|
+
if (text.endsWith(line + suffix)) {
|
|
64
|
+
return text.slice(0, text.length - line.length - suffix.length);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return text;
|
|
50
71
|
}
|
|
51
72
|
|
|
52
73
|
function buildMarkers(nonce = randomBytes(8).toString('hex')) {
|