ysagc-agent 0.3.0 → 0.5.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/README.md +132 -60
- package/dist/agent.d.ts +17 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +331 -0
- package/dist/agent.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +320 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +58 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +152 -0
- package/dist/config.js.map +1 -0
- package/dist/executor.d.ts +48 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +458 -0
- package/dist/executor.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +37 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +121 -0
- package/dist/logger.js.map +1 -0
- package/dist/path-utils.d.ts +44 -0
- package/dist/path-utils.d.ts.map +1 -0
- package/dist/path-utils.js +206 -0
- package/dist/path-utils.js.map +1 -0
- package/dist/protocol.d.ts +92 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +94 -0
- package/dist/protocol.js.map +1 -0
- package/dist/security.d.ts +32 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +228 -0
- package/dist/security.js.map +1 -0
- package/dist/skills.d.ts +16 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +105 -0
- package/dist/skills.js.map +1 -0
- package/dist/tokens.d.ts +26 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +50 -0
- package/dist/tokens.js.map +1 -0
- package/dist/tools.d.ts +69 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +694 -0
- package/dist/tools.js.map +1 -0
- package/package.json +36 -21
- package/src/config.js +0 -94
- package/src/http-fs.js +0 -178
- package/src/index.js +0 -442
- package/src/logger.js +0 -84
- package/src/methods/browse.js +0 -91
- package/src/methods/dialog.js +0 -84
- package/src/methods/fs.js +0 -674
- package/src/methods/git.js +0 -391
- package/src/methods/project.js +0 -57
- package/src/methods/scaffold.js +0 -131
- package/src/methods/skill.js +0 -472
- package/src/methods/system.js +0 -82
- package/src/methods/terminal.js +0 -234
- package/src/origin.js +0 -38
- package/src/pairing.js +0 -168
- package/src/rpc.js +0 -62
- package/src/sandbox.js +0 -178
package/dist/security.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Security helpers: dangerous-command detection, sensitive-path detection and
|
|
4
|
+
* secret masking. These are intentionally conservative — when in doubt the
|
|
5
|
+
* path/command is treated as unsafe.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.MASK = void 0;
|
|
9
|
+
exports.isDangerCommand = isDangerCommand;
|
|
10
|
+
exports.isSensitivePath = isSensitivePath;
|
|
11
|
+
exports.maskSecrets = maskSecrets;
|
|
12
|
+
/**
|
|
13
|
+
* Rules that flag destructive / privilege-escalating commands. Matching is
|
|
14
|
+
* case-insensitive and uses word boundaries so ordinary words ("removal",
|
|
15
|
+
* "formatting") do not trigger a false positive. Rules are cross-platform:
|
|
16
|
+
* bash, powershell and cmd are all covered.
|
|
17
|
+
*/
|
|
18
|
+
const DANGER_RULES = [
|
|
19
|
+
// rm -rf / rm -fr / rm -fir etc. (any order of r/f flags)
|
|
20
|
+
{
|
|
21
|
+
severity: 'critical',
|
|
22
|
+
reason: 'recursive/force delete (rm -rf, rm -fr)',
|
|
23
|
+
re: /\brm\b[^\n]*\s+-[a-z]*r[a-z]*f[a-z]*\b|\brm\b[^\n]*\s+-[a-z]*f[a-z]*r[a-z]*\b/i,
|
|
24
|
+
},
|
|
25
|
+
// rm -r on the filesystem root or a system-ish path
|
|
26
|
+
{
|
|
27
|
+
severity: 'critical',
|
|
28
|
+
reason: 'recursive delete targeting a system path (/ , ~, /var, /etc, /dev, /usr, /tmp)',
|
|
29
|
+
re: /\brm\b[^\n]*\s+-[a-z]*r[a-z]*\s+(?:\\|~\/|\$HOME|\/tmp|\/var|\/etc|\/dev|\/usr|\/boot|\/home)\b/i,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
severity: 'critical',
|
|
33
|
+
reason: 'privilege escalation (sudo)',
|
|
34
|
+
re: /\bsudo\b/i,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
severity: 'critical',
|
|
38
|
+
reason: 'disk format',
|
|
39
|
+
re: /\bformat\b/i,
|
|
40
|
+
},
|
|
41
|
+
// Windows: del /f /q /s (force/quiet/subdirs)
|
|
42
|
+
{
|
|
43
|
+
severity: 'critical',
|
|
44
|
+
reason: 'forced recursive delete (del /f, del /s, del /q)',
|
|
45
|
+
re: /\bdel\b[^\n]*\s+\/[a-z]*[fsq][a-z]*\b/i,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
severity: 'critical',
|
|
49
|
+
reason: 'system shutdown',
|
|
50
|
+
re: /\bshutdown\b/i,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
severity: 'critical',
|
|
54
|
+
reason: 'system reboot',
|
|
55
|
+
re: /\breboot\b/i,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
severity: 'danger',
|
|
59
|
+
reason: 'git hard reset (git reset --hard)',
|
|
60
|
+
re: /\bgit\b[^\n]*\breset\b[^\n]*--hard\b/i,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
severity: 'danger',
|
|
64
|
+
reason: 'git clean (git clean -fdx)',
|
|
65
|
+
re: /\bgit\b[^\n]*\bclean\b[^\n]*-(?:fd?x?|xfd?|dxf|fx)\b/i,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
severity: 'danger',
|
|
69
|
+
reason: 'find ... -delete',
|
|
70
|
+
re: /\bfind\b[^\n]*\s-delete\b/i,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
severity: 'critical',
|
|
74
|
+
reason: 'Remove-Item -Recurse -Force',
|
|
75
|
+
re: /\bRemove-Item\b[^\n]*-Recurse\b[^\n]*-Force\b/i,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
severity: 'critical',
|
|
79
|
+
reason: 'move /y to a drive root',
|
|
80
|
+
re: /\bmove\b[^\n]*\/y\b[^\n]*[a-z]:\\/i,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
severity: 'critical',
|
|
84
|
+
reason: 'diskpart',
|
|
85
|
+
re: /\bdiskpart\b/i,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
severity: 'critical',
|
|
89
|
+
reason: 'registry delete',
|
|
90
|
+
re: /\breg\b[^\n]*\bdelete\b/i,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
severity: 'danger',
|
|
94
|
+
reason: 'chmod -R 777 /',
|
|
95
|
+
re: /\bchmod\b[^\n]*\s+-R\b[^\n]*777\b[^\n]*(?:\\|~|\$HOME|\/etc|\/var|\/dev|\/usr|\/home)\b/i,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
severity: 'danger',
|
|
99
|
+
reason: 'mkfs (filesystem creation)',
|
|
100
|
+
re: /\bmkfs\b/i,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
severity: 'critical',
|
|
104
|
+
reason: 'dd writing to a block device',
|
|
105
|
+
re: /\bdd\b[^\n]*\bof=\/dev\/\S+/i,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
severity: 'critical',
|
|
109
|
+
reason: 'redirection to a block device',
|
|
110
|
+
re: />>?\s*\/dev\/(?:sda|sdb|sd\w|nvme\w*|mmcblk\w*|disk\w*)\b/i,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
severity: 'danger',
|
|
114
|
+
reason: 'force-kill all processes',
|
|
115
|
+
re: /\b(?:killall|pkill)\b[^\n]*\s+-9\b|\bkill\b[^\n]*\s+-9\b[^\n]*-?\d+\b/i,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
severity: 'danger',
|
|
119
|
+
reason: 'fork bomb',
|
|
120
|
+
re: /:\(\)\s*\{\s*:\|:&\s*\};:/i,
|
|
121
|
+
},
|
|
122
|
+
// Truncating the filesystem root or a system file.
|
|
123
|
+
{
|
|
124
|
+
severity: 'critical',
|
|
125
|
+
reason: 'truncate a system path',
|
|
126
|
+
re: />>?\s*(?:\/etc\/(?:passwd|shadow|hosts)|\/var\/log\b|\/dev\/null\b)/i,
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
/**
|
|
130
|
+
* Assess whether a shell command is too dangerous to run automatically.
|
|
131
|
+
* Cross-platform (bash / powershell / cmd). Returns a severity and reason.
|
|
132
|
+
*/
|
|
133
|
+
function isDangerCommand(command) {
|
|
134
|
+
if (typeof command !== 'string' || command.trim() === '') {
|
|
135
|
+
return { danger: false, severity: 'none', reason: 'empty command' };
|
|
136
|
+
}
|
|
137
|
+
const trimmedTrimmed = command.trim();
|
|
138
|
+
for (const rule of DANGER_RULES) {
|
|
139
|
+
if (rule.re.test(trimmedTrimmed)) {
|
|
140
|
+
return {
|
|
141
|
+
danger: rule.severity === 'critical' || rule.severity === 'danger',
|
|
142
|
+
severity: rule.severity,
|
|
143
|
+
reason: rule.reason,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return { danger: false, severity: 'none', reason: 'allowed' };
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* High-value / credential paths that are blocked regardless of authorisation
|
|
151
|
+
* mode. Matched against the lower-cased, slash-normalised path so both POSIX
|
|
152
|
+
* and Windows spellings are covered.
|
|
153
|
+
*/
|
|
154
|
+
const SENSITIVE_PATTERN = [
|
|
155
|
+
// System config / credential stores
|
|
156
|
+
/(^|\/)(etc\/passwd|etc\/shadow|etc\/sudoers|etc\/hosts|etc\/group|etc\/fstab)$/,
|
|
157
|
+
/(^|\/)(etc\/ssh|etc\/ssl\/private|etc\/ssl\/certs|etc\/pkcs11)/,
|
|
158
|
+
/(^|\/)(dev\/(sda|sdb|sd[a-z]|nvme[0-9n]+|mmcblk[0-9]+|disk[a-z0-9]+|mem|kmem|initctl|loop[0-9]+|tty))$/,
|
|
159
|
+
/(^|\/)(proc|sys|boot|run|sbin|bin|usr\/lib|usr\/share|lib|lib64|var\/log|var\/lib|var\/run)\b/,
|
|
160
|
+
/(^|\/)(system\/library|library\/keychains|library\/preferences|applications)\b/,
|
|
161
|
+
// Windows system directories
|
|
162
|
+
/^([a-z]:)?\/(windows|winnt|program files|programdata|users\/(?:default|public)\/appdata)/,
|
|
163
|
+
/\/windows\/(system32|syswow64|system|security|systemroot|boot)\b/,
|
|
164
|
+
// Private keys and credential files
|
|
165
|
+
/(^|\/)(id_rsa|id_dsa|id_ecdsa|id_ed25519)(\.pub)?$/,
|
|
166
|
+
/(^|\/)\.(ssh|gnupg|aws|azure|kube|docker-npm|config)\b/,
|
|
167
|
+
/(^|\/)\.env(\.|$)/i,
|
|
168
|
+
/(^|\/)\.npmrc$/,
|
|
169
|
+
/(^|\/)(\.netrc|\.pgpass|\.htpasswd|\.htaccess|\.git-credentials|\.boto|\.s3cfg)$/,
|
|
170
|
+
/(^|\/)(credentials|credentials\.json|secrets?\.json|service-account[^/]*\.json|google-credentials[^/]*\.json|application_default_credentials[^/]*\.json)$/,
|
|
171
|
+
/(^|\/)(keystore|\.keystore|\.jks|\.p12|\.pfx|\.pem|\.key|cert\.pem|server\.pem)$/,
|
|
172
|
+
/(^|\/)(\.npmrc|\.yarnrc|\.pypirc|\.gemrc|\.dockerconfigjson)$/,
|
|
173
|
+
// Files that typically bundle tokens/config keys
|
|
174
|
+
/(^|\/)(config\.json|settings\.json|secrets\.yml|secrets\.yaml|\.env\.(local|prod|production|dev|development|test))$/,
|
|
175
|
+
];
|
|
176
|
+
/**
|
|
177
|
+
* Whether a (canonical absolute) path should never be read/written by a tool.
|
|
178
|
+
* These are blocked no matter what permission mode is in effect.
|
|
179
|
+
*/
|
|
180
|
+
function isSensitivePath(p) {
|
|
181
|
+
if (typeof p !== 'string' || p === '')
|
|
182
|
+
return false;
|
|
183
|
+
const norm = p.replace(/\\/g, '/').toLowerCase();
|
|
184
|
+
for (const re of SENSITIVE_PATTERN) {
|
|
185
|
+
if (re.test(norm))
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
/** Emitted value when any secret has been redacted. */
|
|
191
|
+
exports.MASK = '***';
|
|
192
|
+
/**
|
|
193
|
+
* Redact secrets from a string: provider keys, bearer tokens, JWTs, private
|
|
194
|
+
* keys, and `key=value` / `key: value` pairs for obvious secret names. Used
|
|
195
|
+
* before logging or returning tool output so internal keys never leak.
|
|
196
|
+
*/
|
|
197
|
+
function maskSecrets(text) {
|
|
198
|
+
if (typeof text !== 'string' || text === '')
|
|
199
|
+
return text;
|
|
200
|
+
let out = text;
|
|
201
|
+
// Named secret pairs: password=..., token=..., api_key=..., authorization: ...
|
|
202
|
+
out = out.replace(/(password|passwd|secret|token|api[_-]?key|access[_-]?key|auth(?:orization)?|session|refresh[_-]?token|private[_-]?key|cookie|client[_-]?secret)\s*(?:[:=]\s*|["']?\s+)(["']?)([^\s,;'"&]+)\2/gi, (_m, _key, _q, value) => {
|
|
203
|
+
// Never redact a placeholder already present.
|
|
204
|
+
if (value === exports.MASK)
|
|
205
|
+
return _m;
|
|
206
|
+
return `${_q}${exports.MASK}${_q}`;
|
|
207
|
+
});
|
|
208
|
+
// Bearer / Basic auth headers.
|
|
209
|
+
out = out.replace(/\b(Bearer|Basic)\s+[A-Za-z0-9+/=_-]{8,}/gi, `$1 ${exports.MASK}`);
|
|
210
|
+
// OpenAI / Anthropic style sk- keys.
|
|
211
|
+
out = out.replace(/\bsk-[A-Za-z0-9_-]{16,}\b/g, `sk-${exports.MASK}`);
|
|
212
|
+
// Stripe live/test keys.
|
|
213
|
+
out = out.replace(/\bsk_(?:live|test)_[A-Za-z0-9]{16,}\b/g, `sk_${exports.MASK}`);
|
|
214
|
+
// GitHub tokens.
|
|
215
|
+
out = out.replace(/\bgh[pousr]_[A-Za-z0-9]{16,}\b/g, `gh${exports.MASK}`.replace('***', exports.MASK));
|
|
216
|
+
// Slack tokens.
|
|
217
|
+
out = out.replace(/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, `xox${exports.MASK}`.replace('***', exports.MASK));
|
|
218
|
+
// Google API keys.
|
|
219
|
+
out = out.replace(/\bAIza[0-9A-Za-z_-]{30,}\b/g, `AIza${exports.MASK}`);
|
|
220
|
+
// AWS access key ID.
|
|
221
|
+
out = out.replace(/\b(AKIA|ASIA)[0-9A-Z]{16}\b/g, `$1${exports.MASK}`);
|
|
222
|
+
// JWT.
|
|
223
|
+
out = out.replace(/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g, `eyJ${exports.MASK}`);
|
|
224
|
+
// PEM / OpenSSH private-key blocks.
|
|
225
|
+
out = out.replace(/-----BEGIN ([A-Z ]*)?PRIVATE KEY-----[\s\S]*?-----END ([A-Z ]*)?PRIVATE KEY-----/g, '-----BEGIN PRIVATE KEY-----\n' + exports.MASK + '\n-----END PRIVATE KEY-----');
|
|
226
|
+
return out;
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"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"}
|
package/dist/skills.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** List available skill names in `<root>/.dsh/skills`. */
|
|
2
|
+
export declare function listSkills(root: string): string[];
|
|
3
|
+
/** Return the content of a single skill, or undefined when it does not exist
|
|
4
|
+
* (or the name is unsafe / the read fails). */
|
|
5
|
+
export declare function loadSkill(root: string, name: string): string | undefined;
|
|
6
|
+
export interface SkillRecord {
|
|
7
|
+
loaded: boolean;
|
|
8
|
+
name?: string;
|
|
9
|
+
content?: string;
|
|
10
|
+
available: string[];
|
|
11
|
+
note?: string;
|
|
12
|
+
}
|
|
13
|
+
/** Load a skill by name into a structured record, or return a "not found" /
|
|
14
|
+
* "no skills configured" record. */
|
|
15
|
+
export declare function loadSkillRecord(root: string, name?: string): SkillRecord;
|
|
16
|
+
//# sourceMappingURL=skills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"AAoBA,0DAA0D;AAC1D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAWjD;AAED;+CAC+C;AAC/C,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWxE;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;oCACoC;AACpC,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAkBxE"}
|
package/dist/skills.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.listSkills = listSkills;
|
|
37
|
+
exports.loadSkill = loadSkill;
|
|
38
|
+
exports.loadSkillRecord = loadSkillRecord;
|
|
39
|
+
const fs = __importStar(require("node:fs"));
|
|
40
|
+
const path = __importStar(require("node:path"));
|
|
41
|
+
/**
|
|
42
|
+
* A lightweight, file-backed skills registry. Skills are Markdown files that
|
|
43
|
+
* live under `<workspace-root>/.dsh/skills/<name>.md`. The registry never
|
|
44
|
+
* throws: when the directory is absent or a skill is missing it simply reports
|
|
45
|
+
* "none found" so the agent stays non-fatal.
|
|
46
|
+
*/
|
|
47
|
+
const SKILLS_SUBDIR = path.join('.dsh', 'skills');
|
|
48
|
+
function skillsDir(root) {
|
|
49
|
+
return path.join(root, SKILLS_SUBDIR);
|
|
50
|
+
}
|
|
51
|
+
function isValidName(name) {
|
|
52
|
+
return typeof name === 'string' && /^[A-Za-z0-9._-]+$/.test(name);
|
|
53
|
+
}
|
|
54
|
+
/** List available skill names in `<root>/.dsh/skills`. */
|
|
55
|
+
function listSkills(root) {
|
|
56
|
+
const dir = skillsDir(root);
|
|
57
|
+
try {
|
|
58
|
+
const names = fs.readdirSync(dir);
|
|
59
|
+
return names
|
|
60
|
+
.filter((n) => n.endsWith('.md'))
|
|
61
|
+
.map((n) => n.slice(0, -3))
|
|
62
|
+
.sort();
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Return the content of a single skill, or undefined when it does not exist
|
|
69
|
+
* (or the name is unsafe / the read fails). */
|
|
70
|
+
function loadSkill(root, name) {
|
|
71
|
+
if (!isValidName(name))
|
|
72
|
+
return undefined;
|
|
73
|
+
const file = path.join(skillsDir(root), `${name}.md`);
|
|
74
|
+
try {
|
|
75
|
+
// Guard against reading a directory.
|
|
76
|
+
const stat = fs.statSync(file);
|
|
77
|
+
if (!stat.isFile())
|
|
78
|
+
return undefined;
|
|
79
|
+
return fs.readFileSync(file, 'utf8');
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** Load a skill by name into a structured record, or return a "not found" /
|
|
86
|
+
* "no skills configured" record. */
|
|
87
|
+
function loadSkillRecord(root, name) {
|
|
88
|
+
const available = listSkills(root);
|
|
89
|
+
if (!name || name.trim() === '') {
|
|
90
|
+
return {
|
|
91
|
+
loaded: false,
|
|
92
|
+
available,
|
|
93
|
+
note: 'No skill name supplied.',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const content = loadSkill(root, name);
|
|
97
|
+
if (content === undefined) {
|
|
98
|
+
const note = available.length === 0
|
|
99
|
+
? `No skills are configured in ${skillsDir(root)}.`
|
|
100
|
+
: `Skill "${name}" not found. Available: ${available.join(', ')}.`;
|
|
101
|
+
return { loaded: false, name, available, note };
|
|
102
|
+
}
|
|
103
|
+
return { loaded: true, name, content, available };
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,gCAWC;AAID,8BAWC;AAYD,0CAkBC;AA7ED,4CAA8B;AAC9B,gDAAkC;AAElC;;;;;GAKG;AAEH,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAElD,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpE,CAAC;AAED,0DAA0D;AAC1D,SAAgB,UAAU,CAAC,IAAY;IACrC,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,KAAK;aACT,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;aAChC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;aAC1B,IAAI,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;+CAC+C;AAC/C,SAAgB,SAAS,CAAC,IAAY,EAAE,IAAY;IAClD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,KAAK,CAAC,CAAC;IACtD,IAAI,CAAC;QACH,qCAAqC;QACrC,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;YAAE,OAAO,SAAS,CAAC;QACrC,OAAO,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAUD;oCACoC;AACpC,SAAgB,eAAe,CAAC,IAAY,EAAE,IAAa;IACzD,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAChC,OAAO;YACL,MAAM,EAAE,KAAK;YACb,SAAS;YACT,IAAI,EAAE,yBAAyB;SAChC,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACtC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,GACR,SAAS,CAAC,MAAM,KAAK,CAAC;YACpB,CAAC,CAAC,+BAA+B,SAAS,CAAC,IAAI,CAAC,GAAG;YACnD,CAAC,CAAC,UAAU,IAAI,2BAA2B,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QACvE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAClD,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AACpD,CAAC"}
|
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight token accounting. This agent never calls an LLM directly, so the
|
|
3
|
+
* platform supplies exact usage when it can; otherwise we fall back to a cheap
|
|
4
|
+
* characters-per-token estimate so every turn still reports an approximate
|
|
5
|
+
* request/response token count.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Estimate the number of tokens in a piece of plain text. A very cheap,
|
|
9
|
+
* deterministic approximation: `ceil(chars / 4)` with a floor of 1 for
|
|
10
|
+
* non-empty text. Not an exact BPE count, but monotonic and stable enough for
|
|
11
|
+
* reporting purposes.
|
|
12
|
+
*/
|
|
13
|
+
export declare function countTextTokens(text: string): number;
|
|
14
|
+
/** Serialise an arbitrary value (deep) and count its estimated tokens. */
|
|
15
|
+
export declare function estimateTokens(value: unknown): number;
|
|
16
|
+
export interface TokenUsage {
|
|
17
|
+
/** Approximate tokens consumed by the request / tool arguments. */
|
|
18
|
+
inputTokens: number;
|
|
19
|
+
/** Approximate tokens emitted by the response / tool result. */
|
|
20
|
+
outputTokens: number;
|
|
21
|
+
/** inputTokens + outputTokens. */
|
|
22
|
+
totalTokens: number;
|
|
23
|
+
}
|
|
24
|
+
/** Build a token usage report from a request payload and its response. */
|
|
25
|
+
export declare function computeUsage(input: unknown, output: unknown): TokenUsage;
|
|
26
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMpD;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAUrD;AAED,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,YAAY,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,UAAU,CAIxE"}
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Lightweight token accounting. This agent never calls an LLM directly, so the
|
|
4
|
+
* platform supplies exact usage when it can; otherwise we fall back to a cheap
|
|
5
|
+
* characters-per-token estimate so every turn still reports an approximate
|
|
6
|
+
* request/response token count.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.countTextTokens = countTextTokens;
|
|
10
|
+
exports.estimateTokens = estimateTokens;
|
|
11
|
+
exports.computeUsage = computeUsage;
|
|
12
|
+
/** Rough characters-per-token heuristic (English-ish text). */
|
|
13
|
+
const CHARS_PER_TOKEN = 4;
|
|
14
|
+
/**
|
|
15
|
+
* Estimate the number of tokens in a piece of plain text. A very cheap,
|
|
16
|
+
* deterministic approximation: `ceil(chars / 4)` with a floor of 1 for
|
|
17
|
+
* non-empty text. Not an exact BPE count, but monotonic and stable enough for
|
|
18
|
+
* reporting purposes.
|
|
19
|
+
*/
|
|
20
|
+
function countTextTokens(text) {
|
|
21
|
+
if (typeof text !== 'string' || text === '')
|
|
22
|
+
return 0;
|
|
23
|
+
// Strip whitespace runs and count remaining characters in 4-char blocks.
|
|
24
|
+
const squeezed = text.replace(/\s+/g, ' ').trim();
|
|
25
|
+
if (squeezed === '')
|
|
26
|
+
return 0;
|
|
27
|
+
return Math.max(1, Math.ceil(squeezed.length / CHARS_PER_TOKEN));
|
|
28
|
+
}
|
|
29
|
+
/** Serialise an arbitrary value (deep) and count its estimated tokens. */
|
|
30
|
+
function estimateTokens(value) {
|
|
31
|
+
if (value === undefined || value === null)
|
|
32
|
+
return 0;
|
|
33
|
+
if (typeof value === 'string')
|
|
34
|
+
return countTextTokens(value);
|
|
35
|
+
let text;
|
|
36
|
+
try {
|
|
37
|
+
text = JSON.stringify(value);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
text = String(value);
|
|
41
|
+
}
|
|
42
|
+
return countTextTokens(text ?? '');
|
|
43
|
+
}
|
|
44
|
+
/** Build a token usage report from a request payload and its response. */
|
|
45
|
+
function computeUsage(input, output) {
|
|
46
|
+
const inputTokens = estimateTokens(input);
|
|
47
|
+
const outputTokens = estimateTokens(output);
|
|
48
|
+
return { inputTokens, outputTokens, totalTokens: inputTokens + outputTokens };
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAWH,0CAMC;AAGD,wCAUC;AAYD,oCAIC;AA5CD,+DAA+D;AAC/D,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B;;;;;GAKG;AACH,SAAgB,eAAe,CAAC,IAAY;IAC1C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,CAAC,CAAC;IACtD,yEAAyE;IACzE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAClD,IAAI,QAAQ,KAAK,EAAE;QAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,eAAe,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,0EAA0E;AAC1E,SAAgB,cAAc,CAAC,KAAc;IAC3C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,CAAC,CAAC;IACpD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;IAC7D,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IACD,OAAO,eAAe,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;AACrC,CAAC;AAWD,0EAA0E;AAC1E,SAAgB,YAAY,CAAC,KAAc,EAAE,MAAe;IAC1D,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,YAAY,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAC5C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,GAAG,YAAY,EAAE,CAAC;AAChF,CAAC"}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { PermissionMode, PermissionPreset } from './config';
|
|
2
|
+
import type { Logger } from './logger';
|
|
3
|
+
/** Thrown when a tool invocation is invalid or unsafe. */
|
|
4
|
+
export declare class ToolError extends Error {
|
|
5
|
+
readonly code: string;
|
|
6
|
+
constructor(code: string, message: string);
|
|
7
|
+
}
|
|
8
|
+
export interface ToolContext {
|
|
9
|
+
/** Canonical authorised root; all file operations stay inside it. */
|
|
10
|
+
root: string;
|
|
11
|
+
mode: PermissionMode;
|
|
12
|
+
/** Sandbox-mode × approval preset for the guarded gate. */
|
|
13
|
+
permissionPreset?: PermissionPreset;
|
|
14
|
+
webEnabled: boolean;
|
|
15
|
+
timeoutMs: number;
|
|
16
|
+
maxBytes: number;
|
|
17
|
+
logger: Logger;
|
|
18
|
+
}
|
|
19
|
+
export interface ToolOutcome {
|
|
20
|
+
ok: boolean;
|
|
21
|
+
result?: unknown;
|
|
22
|
+
error?: string;
|
|
23
|
+
/** Set when the guarded gate requires platform approval before running. */
|
|
24
|
+
needsApproval?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface ToolResult extends ToolOutcome {
|
|
27
|
+
durationMs: number;
|
|
28
|
+
/** Set when the result came from the idempotency cache. */
|
|
29
|
+
cached?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export type ToolExecutor = (ctx: ToolContext, args: Record<string, unknown>, signal?: AbortSignal) => Promise<ToolOutcome>;
|
|
32
|
+
export interface PreCheckOutcome {
|
|
33
|
+
ok: boolean;
|
|
34
|
+
error?: string;
|
|
35
|
+
/** Set when a workspace-write gate would prompt the platform for approval. */
|
|
36
|
+
needsApproval?: boolean;
|
|
37
|
+
/** Human-readable reason for the decision. */
|
|
38
|
+
reason?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface PostCheckOutcome {
|
|
41
|
+
ok: boolean;
|
|
42
|
+
error?: string;
|
|
43
|
+
reason?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The guarded pre-execution hook. Runs before every tool:
|
|
47
|
+
* 1. enforces the permission preset (read-only blocks writes/commands);
|
|
48
|
+
* 2. flags dangerous commands (workspace-write asks for approval, most
|
|
49
|
+
* permissive mode only blocks critical commands, read-only blocks all);
|
|
50
|
+
* 3. canonicalises and validates every path argument (inside-root + sensitive
|
|
51
|
+
* path check).
|
|
52
|
+
* Returns `{ ok: true }` when execution may proceed, otherwise a structured
|
|
53
|
+
* block with an error and optional `needsApproval`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function preExecute(ctx: ToolContext, tool: string, args: Record<string, unknown>): PreCheckOutcome;
|
|
56
|
+
/**
|
|
57
|
+
* The guarded post-execution hook. Runs after every tool: masks secrets in the
|
|
58
|
+
* result and records the reason the invocation ended up here.
|
|
59
|
+
*/
|
|
60
|
+
export declare function postExecute(_ctx: ToolContext, tool: string, result: ToolResult): PostCheckOutcome;
|
|
61
|
+
export interface ToolRegistry {
|
|
62
|
+
readonly ctx: ToolContext;
|
|
63
|
+
readonly tools: readonly string[];
|
|
64
|
+
has(name: string): boolean;
|
|
65
|
+
dispatch(name: string, args: Record<string, unknown>, signal?: AbortSignal): Promise<ToolResult>;
|
|
66
|
+
}
|
|
67
|
+
/** Build a tool registry bound to a single execution context. */
|
|
68
|
+
export declare function createToolRegistry(ctx: ToolContext): ToolRegistry;
|
|
69
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,0DAA0D;AAC1D,qBAAa,SAAU,SAAQ,KAAK;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C;AAED,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,cAAc,CAAC;IACrB,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,UAAW,SAAQ,WAAW;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,MAAM,YAAY,GAAG,CACzB,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC,WAAW,CAAC,CAAC;AAqH1B,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAoDD;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,eAAe,CA6DjB;AAgBD;;;GAGG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,UAAU,GACjB,gBAAgB,CAWlB;AAmWD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAClG;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,WAAW,GAAG,YAAY,CA6DjE"}
|