aamp-openclaw-plugin 0.1.19-alpha.0 → 0.1.20
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/bin/aamp-openclaw-plugin.mjs +175 -65
- package/dist/file-store.js +35 -9
- package/dist/file-store.js.map +3 -3
- package/dist/index.js +316 -14989
- package/dist/index.js.map +4 -4
- package/openclaw.plugin.json +1 -0
- package/package.json +5 -3
|
@@ -11,6 +11,36 @@ import { fileURLToPath } from 'node:url'
|
|
|
11
11
|
const PLUGIN_ID = 'aamp-openclaw-plugin'
|
|
12
12
|
const DEFAULT_AAMP_HOST = 'https://meshmail.ai'
|
|
13
13
|
const DEFAULT_CREDENTIALS_FILE = '~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json'
|
|
14
|
+
const CODING_TOOL_ALLOWLIST = [
|
|
15
|
+
'read',
|
|
16
|
+
'write',
|
|
17
|
+
'edit',
|
|
18
|
+
'apply_patch',
|
|
19
|
+
'exec',
|
|
20
|
+
'process',
|
|
21
|
+
'web_search',
|
|
22
|
+
'web_fetch',
|
|
23
|
+
'memory_search',
|
|
24
|
+
'memory_get',
|
|
25
|
+
'sessions_list',
|
|
26
|
+
'sessions_history',
|
|
27
|
+
'sessions_send',
|
|
28
|
+
'sessions_spawn',
|
|
29
|
+
'sessions_yield',
|
|
30
|
+
'subagents',
|
|
31
|
+
'session_status',
|
|
32
|
+
'cron',
|
|
33
|
+
'image',
|
|
34
|
+
'image_generate',
|
|
35
|
+
]
|
|
36
|
+
const AAMP_PLUGIN_TOOL_ALLOWLIST = [
|
|
37
|
+
'aamp_send_result',
|
|
38
|
+
'aamp_send_help',
|
|
39
|
+
'aamp_pending_tasks',
|
|
40
|
+
'aamp_dispatch_task',
|
|
41
|
+
'aamp_check_protocol',
|
|
42
|
+
'aamp_download_attachment',
|
|
43
|
+
]
|
|
14
44
|
|
|
15
45
|
function resolveOpenClawHome() {
|
|
16
46
|
return process.env.OPENCLAW_HOME?.trim() || join(homedir(), '.openclaw')
|
|
@@ -46,12 +76,11 @@ function normalizeBaseUrl(url) {
|
|
|
46
76
|
return `https://${url.replace(/\/$/, '')}`
|
|
47
77
|
}
|
|
48
78
|
|
|
49
|
-
function ensurePluginConfig(config, pluginConfig,
|
|
79
|
+
function ensurePluginConfig(config, pluginConfig, options = {}) {
|
|
50
80
|
const next = config && typeof config === 'object' ? structuredClone(config) : {}
|
|
51
81
|
if (!next.plugins || typeof next.plugins !== 'object') next.plugins = {}
|
|
52
82
|
if (!Array.isArray(next.plugins.allow)) next.plugins.allow = []
|
|
53
83
|
if (!next.plugins.entries || typeof next.plugins.entries !== 'object') next.plugins.entries = {}
|
|
54
|
-
if (!next.plugins.installs || typeof next.plugins.installs !== 'object') next.plugins.installs = {}
|
|
55
84
|
|
|
56
85
|
if (!next.plugins.allow.includes(PLUGIN_ID)) {
|
|
57
86
|
next.plugins.allow.push(PLUGIN_ID)
|
|
@@ -77,17 +106,65 @@ function ensurePluginConfig(config, pluginConfig, installRecord) {
|
|
|
77
106
|
delete next.plugins.entries.aamp
|
|
78
107
|
}
|
|
79
108
|
|
|
80
|
-
|
|
81
|
-
delete next.plugins.installs.aamp
|
|
82
|
-
}
|
|
109
|
+
next.tools = ensureAampToolAllowlist(next.tools, options)
|
|
83
110
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
111
|
+
return next
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function ensureAampToolAllowlist(toolsConfig, options = {}) {
|
|
115
|
+
const next = toolsConfig && typeof toolsConfig === 'object' ? structuredClone(toolsConfig) : {}
|
|
116
|
+
const existingAllow = Array.isArray(next.allow) ? next.allow.filter((value) => typeof value === 'string' && value.trim()) : []
|
|
117
|
+
const includeCodingBaseline = options.includeCodingBaseline === true
|
|
118
|
+
|
|
119
|
+
const mergedAllow = [
|
|
120
|
+
...existingAllow,
|
|
121
|
+
...(includeCodingBaseline ? CODING_TOOL_ALLOWLIST : []),
|
|
122
|
+
...AAMP_PLUGIN_TOOL_ALLOWLIST,
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
next.allow = Array.from(new Set(mergedAllow))
|
|
87
126
|
|
|
88
127
|
return next
|
|
89
128
|
}
|
|
90
129
|
|
|
130
|
+
function planToolPolicyUpdate(toolsConfig, options = {}) {
|
|
131
|
+
const current = toolsConfig && typeof toolsConfig === 'object' ? structuredClone(toolsConfig) : {}
|
|
132
|
+
const existingAllow = Array.isArray(current.allow) ? current.allow.filter((value) => typeof value === 'string' && value.trim()) : []
|
|
133
|
+
const includeCodingBaseline = options.includeCodingBaseline === true
|
|
134
|
+
const missingAampTools = AAMP_PLUGIN_TOOL_ALLOWLIST.filter((tool) => !existingAllow.includes(tool))
|
|
135
|
+
const currentProfile = typeof current.profile === 'string' ? current.profile : undefined
|
|
136
|
+
const missingCodingTools = includeCodingBaseline
|
|
137
|
+
? CODING_TOOL_ALLOWLIST.filter((tool) => !existingAllow.includes(tool))
|
|
138
|
+
: []
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
current,
|
|
142
|
+
missingAampTools,
|
|
143
|
+
missingCodingTools,
|
|
144
|
+
needsAnyChange: missingAampTools.length > 0 || missingCodingTools.length > 0,
|
|
145
|
+
needsNonPluginChange: missingCodingTools.length > 0,
|
|
146
|
+
currentProfile,
|
|
147
|
+
next: ensureAampToolAllowlist(current, { includeCodingBaseline }),
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function currentToolPolicySummary(plan) {
|
|
152
|
+
const lines = []
|
|
153
|
+
if (plan.currentProfile) {
|
|
154
|
+
lines.push(` current tools.profile: ${plan.currentProfile}`)
|
|
155
|
+
} else {
|
|
156
|
+
lines.push(` current tools.profile: (none)`)
|
|
157
|
+
}
|
|
158
|
+
lines.push(` current tools.allow count: ${Array.isArray(plan.current.allow) ? plan.current.allow.length : 0}`)
|
|
159
|
+
if (plan.missingAampTools.length > 0) {
|
|
160
|
+
lines.push(` missing AAMP tools: ${plan.missingAampTools.join(', ')}`)
|
|
161
|
+
}
|
|
162
|
+
if (plan.needsNonPluginChange) {
|
|
163
|
+
lines.push(` additional core tools to add: ${plan.missingCodingTools.join(', ')}`)
|
|
164
|
+
}
|
|
165
|
+
return lines.join('\n')
|
|
166
|
+
}
|
|
167
|
+
|
|
91
168
|
function parseDispatchContextRules(raw) {
|
|
92
169
|
const trimmed = raw.trim()
|
|
93
170
|
if (!trimmed) return undefined
|
|
@@ -130,9 +207,34 @@ function copyIntoDir(src, dest) {
|
|
|
130
207
|
cpSync(src, dest, { recursive: true, force: true })
|
|
131
208
|
}
|
|
132
209
|
|
|
210
|
+
function ensureBuiltArtifacts(packageRoot) {
|
|
211
|
+
const entryFile = join(packageRoot, 'dist', 'index.js')
|
|
212
|
+
if (existsSync(entryFile)) return
|
|
213
|
+
|
|
214
|
+
const result = spawnSync('npm', ['run', 'build'], {
|
|
215
|
+
cwd: packageRoot,
|
|
216
|
+
encoding: 'utf-8',
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
if (result.error) {
|
|
220
|
+
throw new Error(`Failed to build plugin artifacts: ${result.error.message}`)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (result.status !== 0) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
`Failed to build plugin artifacts: ${(result.stderr || result.stdout || `exit code ${result.status}`).trim()}`,
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!existsSync(entryFile)) {
|
|
230
|
+
throw new Error(`Plugin build completed but ${entryFile} is still missing`)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
133
234
|
function installPluginFiles(credentialsFile = DEFAULT_CREDENTIALS_FILE) {
|
|
134
235
|
const extensionDir = resolveExtensionDir()
|
|
135
236
|
const packageRoot = packageRootFromEntry(fileURLToPath(import.meta.url))
|
|
237
|
+
ensureBuiltArtifacts(packageRoot)
|
|
136
238
|
const packageJson = readJsonFile(join(packageRoot, 'package.json'))
|
|
137
239
|
const credentialsPath = expandHome(credentialsFile)
|
|
138
240
|
const existingCredentials = existsSync(credentialsPath)
|
|
@@ -151,12 +253,24 @@ function installPluginFiles(credentialsFile = DEFAULT_CREDENTIALS_FILE) {
|
|
|
151
253
|
|
|
152
254
|
writeJsonFile(join(extensionDir, 'package.json'), packageJson)
|
|
153
255
|
|
|
256
|
+
const dependencyPackages = ['aamp-sdk', 'ws', 'nodemailer']
|
|
257
|
+
const nodeModulesDir = join(extensionDir, 'node_modules')
|
|
258
|
+
mkdirSync(nodeModulesDir, { recursive: true })
|
|
259
|
+
|
|
260
|
+
for (const dep of dependencyPackages) {
|
|
261
|
+
const depRoot = join(packageRoot, 'node_modules', dep)
|
|
262
|
+
if (!existsSync(depRoot)) {
|
|
263
|
+
throw new Error(`Missing dependency directory: ${depRoot}`)
|
|
264
|
+
}
|
|
265
|
+
copyIntoDir(depRoot, join(nodeModulesDir, dep))
|
|
266
|
+
}
|
|
267
|
+
|
|
154
268
|
if (existingCredentials) {
|
|
155
269
|
mkdirSync(dirname(credentialsPath), { recursive: true })
|
|
156
270
|
writeFileSync(credentialsPath, existingCredentials)
|
|
157
271
|
}
|
|
158
272
|
|
|
159
|
-
return
|
|
273
|
+
return extensionDir
|
|
160
274
|
}
|
|
161
275
|
|
|
162
276
|
function restartGateway() {
|
|
@@ -184,28 +298,6 @@ function restartGateway() {
|
|
|
184
298
|
}
|
|
185
299
|
}
|
|
186
300
|
|
|
187
|
-
async function fetchJson(url, init, stepLabel) {
|
|
188
|
-
let res
|
|
189
|
-
try {
|
|
190
|
-
res = await fetch(url, init)
|
|
191
|
-
} catch (error) {
|
|
192
|
-
const message = error instanceof Error ? error.message : String(error)
|
|
193
|
-
throw new Error(`${stepLabel} failed for ${url}: ${message}`)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (!res.ok) {
|
|
197
|
-
const text = await res.text().catch(() => '')
|
|
198
|
-
throw new Error(`${stepLabel} failed (${res.status}) for ${url}: ${text || res.statusText}`)
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
try {
|
|
202
|
-
return await res.json()
|
|
203
|
-
} catch (error) {
|
|
204
|
-
const message = error instanceof Error ? error.message : String(error)
|
|
205
|
-
throw new Error(`${stepLabel} returned invalid JSON for ${url}: ${message}`)
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
301
|
async function ensureMailboxIdentity({ aampHost, slug, credentialsFile }) {
|
|
210
302
|
const resolvedCreds = expandHome(credentialsFile)
|
|
211
303
|
if (existsSync(resolvedCreds)) {
|
|
@@ -213,30 +305,33 @@ async function ensureMailboxIdentity({ aampHost, slug, credentialsFile }) {
|
|
|
213
305
|
}
|
|
214
306
|
|
|
215
307
|
const base = normalizeBaseUrl(aampHost)
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
{
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
308
|
+
const registerRes = await fetch(`${base}/api/nodes/self-register`, {
|
|
309
|
+
method: 'POST',
|
|
310
|
+
headers: { 'Content-Type': 'application/json' },
|
|
311
|
+
body: JSON.stringify({
|
|
312
|
+
slug,
|
|
313
|
+
description: 'OpenClaw AAMP agent node',
|
|
314
|
+
}),
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
if (!registerRes.ok) {
|
|
318
|
+
const text = await registerRes.text().catch(() => '')
|
|
319
|
+
throw new Error(`AAMP self-register failed (${registerRes.status}): ${text || registerRes.statusText}`)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const registerData = await registerRes.json()
|
|
229
323
|
const code = registerData?.registrationCode
|
|
230
324
|
if (!code) {
|
|
231
325
|
throw new Error('AAMP self-register succeeded but no registrationCode was returned')
|
|
232
326
|
}
|
|
233
327
|
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
328
|
+
const credRes = await fetch(`${base}/api/nodes/credentials?code=${encodeURIComponent(code)}`)
|
|
329
|
+
if (!credRes.ok) {
|
|
330
|
+
const text = await credRes.text().catch(() => '')
|
|
331
|
+
throw new Error(`AAMP credential exchange failed (${credRes.status}): ${text || credRes.statusText}`)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const credData = await credRes.json()
|
|
240
335
|
const identity = {
|
|
241
336
|
email: credData?.email,
|
|
242
337
|
jmapToken: credData?.jmap?.token,
|
|
@@ -278,6 +373,7 @@ async function runInit() {
|
|
|
278
373
|
let senderPolicies = previousConfig?.senderPolicies
|
|
279
374
|
let slug = previousSlug
|
|
280
375
|
let reuseExistingConfig = Boolean(previousConfig)
|
|
376
|
+
let includeCodingBaseline = false
|
|
281
377
|
|
|
282
378
|
if (input.isTTY) {
|
|
283
379
|
const rl = createInterface({ input, output })
|
|
@@ -319,6 +415,28 @@ async function runInit() {
|
|
|
319
415
|
senderPolicies = undefined
|
|
320
416
|
}
|
|
321
417
|
}
|
|
418
|
+
|
|
419
|
+
const codingPromptPlan = planToolPolicyUpdate(existing?.tools, { includeCodingBaseline: true })
|
|
420
|
+
const shouldOfferCodingBaseline =
|
|
421
|
+
codingPromptPlan.missingCodingTools.length > 0 &&
|
|
422
|
+
!Array.isArray(existing?.tools?.allow) &&
|
|
423
|
+
!(typeof existing?.tools?.profile === 'string' && existing.tools.profile.trim())
|
|
424
|
+
|
|
425
|
+
if (shouldOfferCodingBaseline) {
|
|
426
|
+
output.write(
|
|
427
|
+
[
|
|
428
|
+
'',
|
|
429
|
+
'Optional tool policy upgrade:',
|
|
430
|
+
' Default init only adds the AAMP plugin tools needed for mailbox-style task receive/reply.',
|
|
431
|
+
' If this agent also needs file/shell/web coding workflows, you can additionally add',
|
|
432
|
+
' the coding baseline tool set now.',
|
|
433
|
+
currentToolPolicySummary(codingPromptPlan),
|
|
434
|
+
'',
|
|
435
|
+
].join('\n'),
|
|
436
|
+
)
|
|
437
|
+
const toolAnswer = await rl.question('Also add coding baseline tools? [y/N]: ')
|
|
438
|
+
includeCodingBaseline = isYes(toolAnswer, false)
|
|
439
|
+
}
|
|
322
440
|
} finally {
|
|
323
441
|
rl.close()
|
|
324
442
|
}
|
|
@@ -340,27 +458,17 @@ async function runInit() {
|
|
|
340
458
|
}
|
|
341
459
|
|
|
342
460
|
output.write('\nInstalling OpenClaw plugin files...\n')
|
|
343
|
-
const
|
|
344
|
-
const nowIso = new Date().toISOString()
|
|
345
|
-
const installRecord = {
|
|
346
|
-
source: 'npm',
|
|
347
|
-
spec: packageJson.name,
|
|
348
|
-
sourcePath: packageRoot,
|
|
349
|
-
installPath: extensionDir,
|
|
350
|
-
version: packageJson.version,
|
|
351
|
-
resolvedName: packageJson.name,
|
|
352
|
-
resolvedVersion: packageJson.version,
|
|
353
|
-
resolvedSpec: `${packageJson.name}@${packageJson.version}`,
|
|
354
|
-
installedAt: nowIso,
|
|
355
|
-
resolvedAt: nowIso,
|
|
356
|
-
}
|
|
461
|
+
const extensionDir = installPluginFiles(previousCredentialsFile)
|
|
357
462
|
|
|
463
|
+
const toolPolicyPlan = planToolPolicyUpdate(existing?.tools, { includeCodingBaseline })
|
|
358
464
|
const next = ensurePluginConfig(existing, {
|
|
359
465
|
aampHost,
|
|
360
466
|
slug,
|
|
361
467
|
credentialsFile: DEFAULT_CREDENTIALS_FILE,
|
|
362
468
|
...(senderPolicies ? { senderPolicies } : {}),
|
|
363
|
-
},
|
|
469
|
+
}, {
|
|
470
|
+
includeCodingBaseline,
|
|
471
|
+
})
|
|
364
472
|
|
|
365
473
|
writeJsonFile(configPath, next)
|
|
366
474
|
|
|
@@ -383,6 +491,8 @@ async function runInit() {
|
|
|
383
491
|
` aampHost: ${aampHost}`,
|
|
384
492
|
` credentialsFile: ${DEFAULT_CREDENTIALS_FILE}`,
|
|
385
493
|
` senderPolicies: ${senderPolicies ? JSON.stringify(senderPolicies) : '(allow all)'}`,
|
|
494
|
+
` tools.allow: ${JSON.stringify(next.tools?.allow ?? [])}`,
|
|
495
|
+
` codingBaselineAdded: ${toolPolicyPlan.missingCodingTools.length > 0 && includeCodingBaseline ? 'yes' : 'no'}`,
|
|
386
496
|
identityResult.created
|
|
387
497
|
? ` mailbox: ${identityResult.email} (registered and saved to ${identityResult.credentialsPath})`
|
|
388
498
|
: ` mailbox: existing credentials reused from ${identityResult.credentialsPath}`,
|
package/dist/file-store.js
CHANGED
|
@@ -1,12 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
// src/file-store.ts
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
function defaultCredentialsPath() {
|
|
6
|
+
return join(homedir(), ".openclaw", "extensions", "aamp-openclaw-plugin", ".credentials.json");
|
|
7
|
+
}
|
|
8
|
+
function loadCachedIdentity(file) {
|
|
9
|
+
const resolved = file ?? defaultCredentialsPath();
|
|
10
|
+
if (!existsSync(resolved))
|
|
11
|
+
return null;
|
|
12
|
+
try {
|
|
13
|
+
const parsed = JSON.parse(readFileSync(resolved, "utf-8"));
|
|
14
|
+
if (!parsed.email || !parsed.jmapToken || !parsed.smtpPassword)
|
|
15
|
+
return null;
|
|
16
|
+
return parsed;
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function saveCachedIdentity(identity, file) {
|
|
22
|
+
const resolved = file ?? defaultCredentialsPath();
|
|
23
|
+
mkdirSync(dirname(resolved), { recursive: true });
|
|
24
|
+
writeFileSync(resolved, JSON.stringify(identity, null, 2), "utf-8");
|
|
25
|
+
}
|
|
26
|
+
function ensureDir(dir) {
|
|
27
|
+
mkdirSync(dir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
function readBinaryFile(path) {
|
|
30
|
+
return readFileSync(path);
|
|
31
|
+
}
|
|
32
|
+
function writeBinaryFile(path, content) {
|
|
33
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
34
|
+
writeFileSync(path, content);
|
|
35
|
+
}
|
|
10
36
|
export {
|
|
11
37
|
defaultCredentialsPath,
|
|
12
38
|
ensureDir,
|
package/dist/file-store.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": [],
|
|
4
|
-
"sourcesContent": [],
|
|
5
|
-
"mappings": "",
|
|
3
|
+
"sources": ["../src/file-store.ts"],
|
|
4
|
+
"sourcesContent": ["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { homedir } from 'node:os'\n\nexport interface Identity {\n email: string\n jmapToken: string\n smtpPassword: string\n}\n\nexport function defaultCredentialsPath(): string {\n return join(homedir(), '.openclaw', 'extensions', 'aamp-openclaw-plugin', '.credentials.json')\n}\n\nexport function loadCachedIdentity(file?: string): Identity | null {\n const resolved = file ?? defaultCredentialsPath()\n if (!existsSync(resolved)) return null\n try {\n const parsed = JSON.parse(readFileSync(resolved, 'utf-8')) as Partial<Identity>\n if (!parsed.email || !parsed.jmapToken || !parsed.smtpPassword) return null\n return parsed as Identity\n } catch {\n return null\n }\n}\n\nexport function saveCachedIdentity(identity: Identity, file?: string): void {\n const resolved = file ?? defaultCredentialsPath()\n mkdirSync(dirname(resolved), { recursive: true })\n writeFileSync(resolved, JSON.stringify(identity, null, 2), 'utf-8')\n}\n\nexport function ensureDir(dir: string): void {\n mkdirSync(dir, { recursive: true })\n}\n\nexport function readBinaryFile(path: string): Buffer {\n return readFileSync(path)\n}\n\nexport function writeBinaryFile(path: string, content: Uint8Array | Buffer): void {\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, content)\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,SAAS,YAAY;AAC9B,SAAS,eAAe;AAQjB,SAAS,yBAAiC;AAC/C,SAAO,KAAK,QAAQ,GAAG,aAAa,cAAc,wBAAwB,mBAAmB;AAC/F;AAEO,SAAS,mBAAmB,MAAgC;AACjE,QAAM,WAAW,QAAQ,uBAAuB;AAChD,MAAI,CAAC,WAAW,QAAQ;AAAG,WAAO;AAClC,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,aAAa,UAAU,OAAO,CAAC;AACzD,QAAI,CAAC,OAAO,SAAS,CAAC,OAAO,aAAa,CAAC,OAAO;AAAc,aAAO;AACvE,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,mBAAmB,UAAoB,MAAqB;AAC1E,QAAM,WAAW,QAAQ,uBAAuB;AAChD,YAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,gBAAc,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;AACpE;AAEO,SAAS,UAAU,KAAmB;AAC3C,YAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC;AAEO,SAAS,eAAe,MAAsB;AACnD,SAAO,aAAa,IAAI;AAC1B;AAEO,SAAS,gBAAgB,MAAc,SAAoC;AAChF,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,gBAAc,MAAM,OAAO;AAC7B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|