opencode-snippets 1.4.2 → 1.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 +21 -5
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +157 -0
- package/dist/index.js.map +1 -0
- package/dist/src/arg-parser.d.ts +16 -0
- package/dist/src/arg-parser.d.ts.map +1 -0
- package/dist/src/arg-parser.js +94 -0
- package/dist/src/arg-parser.js.map +1 -0
- package/dist/src/commands.d.ts +30 -0
- package/dist/src/commands.d.ts.map +1 -0
- package/dist/src/commands.js +315 -0
- package/dist/src/commands.js.map +1 -0
- package/dist/src/config.d.ts +41 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +150 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/constants.d.ts +35 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +40 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/expander.d.ts +36 -0
- package/dist/src/expander.d.ts.map +1 -0
- package/dist/src/expander.js +187 -0
- package/dist/src/expander.js.map +1 -0
- package/dist/src/loader.d.ts +46 -0
- package/dist/src/loader.d.ts.map +1 -0
- package/dist/src/loader.js +224 -0
- package/dist/src/loader.js.map +1 -0
- package/dist/src/logger.d.ts +15 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +100 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/notification.d.ts +11 -0
- package/dist/src/notification.d.ts.map +1 -0
- package/dist/src/notification.js +26 -0
- package/dist/src/notification.js.map +1 -0
- package/dist/src/shell.d.ts +18 -0
- package/dist/src/shell.d.ts.map +1 -0
- package/dist/src/shell.js +30 -0
- package/dist/src/shell.js.map +1 -0
- package/dist/src/types.d.ts +65 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +10 -6
- package/skill/snippets/SKILL.md +148 -0
- package/index.ts +0 -81
- package/src/arg-parser.test.ts +0 -177
- package/src/arg-parser.ts +0 -87
- package/src/commands.test.ts +0 -188
- package/src/commands.ts +0 -414
- package/src/constants.ts +0 -32
- package/src/expander.test.ts +0 -846
- package/src/expander.ts +0 -225
- package/src/loader.test.ts +0 -352
- package/src/loader.ts +0 -268
- package/src/logger.test.ts +0 -136
- package/src/logger.ts +0 -121
- package/src/notification.ts +0 -30
- package/src/shell.ts +0 -50
- package/src/types.ts +0 -71
package/README.md
CHANGED
|
@@ -281,18 +281,34 @@ Be extremely concise. No explanations unless asked.
|
|
|
281
281
|
|
|
282
282
|
## Configuration
|
|
283
283
|
|
|
284
|
-
|
|
284
|
+
The plugin can be configured via `config.jsonc` files:
|
|
285
285
|
|
|
286
|
-
|
|
286
|
+
- **Global**: `~/.config/opencode/snippet/config.jsonc`
|
|
287
|
+
- **Project**: `.opencode/snippet/config.jsonc` (overrides global settings)
|
|
287
288
|
|
|
288
|
-
|
|
289
|
-
|
|
289
|
+
A default config file is created automatically on first run.
|
|
290
|
+
|
|
291
|
+
### Full Configuration Example
|
|
292
|
+
|
|
293
|
+
```jsonc
|
|
294
|
+
{
|
|
295
|
+
"$schema": "https://raw.githubusercontent.com/JosXa/opencode-snippets/main/schema/config.schema.json",
|
|
296
|
+
"logging": {
|
|
297
|
+
"debug": false // Enable debug logging (logs: ~/.config/opencode/logs/snippets/daily/)
|
|
298
|
+
},
|
|
299
|
+
"installSkill": true // Auto-install SKILL.md to ~/.config/opencode/skill/snippets/
|
|
300
|
+
}
|
|
290
301
|
```
|
|
291
302
|
|
|
292
|
-
|
|
303
|
+
All boolean settings accept: `true`, `false`, `"enabled"`, `"disabled"`
|
|
304
|
+
|
|
305
|
+
### Debug Logging
|
|
306
|
+
|
|
307
|
+
Logs are written to `~/.config/opencode/logs/snippets/daily/` when enabled.
|
|
293
308
|
|
|
294
309
|
## Behavior Notes
|
|
295
310
|
|
|
311
|
+
- Snippets expand everywhere: regular chat, question responses, skills, and slash commands
|
|
296
312
|
- Snippets are loaded once at plugin startup
|
|
297
313
|
- Hashtag matching is **case-insensitive** (`#Hello` = `#hello`)
|
|
298
314
|
- Unknown hashtags are left unchanged
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
|
+
/**
|
|
3
|
+
* Snippets Plugin for OpenCode
|
|
4
|
+
*
|
|
5
|
+
* Expands hashtag-based shortcuts in user messages into predefined text snippets.
|
|
6
|
+
* Also provides /snippet command for managing snippets.
|
|
7
|
+
*
|
|
8
|
+
* @see https://github.com/JosXa/opencode-snippets for full documentation
|
|
9
|
+
*/
|
|
10
|
+
export declare const SnippetsPlugin: Plugin;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA8ClD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,EAAE,MAoH5B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { createCommandExecuteHandler } from "./src/commands.js";
|
|
5
|
+
import { loadConfig } from "./src/config.js";
|
|
6
|
+
import { assembleMessage, expandHashtags } from "./src/expander.js";
|
|
7
|
+
import { loadSnippets } from "./src/loader.js";
|
|
8
|
+
import { logger } from "./src/logger.js";
|
|
9
|
+
import { executeShellCommands } from "./src/shell.js";
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
const PLUGIN_ROOT = join(__dirname, "..");
|
|
13
|
+
const SKILL_DIR = join(PLUGIN_ROOT, "skill");
|
|
14
|
+
// Install skill to global config directory
|
|
15
|
+
async function installSkillToGlobal() {
|
|
16
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
17
|
+
const globalSkillDir = join(home, ".config", "opencode", "skill", "snippets");
|
|
18
|
+
const globalSkillPath = join(globalSkillDir, "SKILL.md");
|
|
19
|
+
const sourceSkillPath = join(SKILL_DIR, "snippets", "SKILL.md");
|
|
20
|
+
try {
|
|
21
|
+
const sourceFile = Bun.file(sourceSkillPath);
|
|
22
|
+
if (!(await sourceFile.exists())) {
|
|
23
|
+
logger.debug("Source skill not found", { path: sourceSkillPath });
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// Check if already installed with same content
|
|
27
|
+
const globalFile = Bun.file(globalSkillPath);
|
|
28
|
+
if (await globalFile.exists()) {
|
|
29
|
+
const existing = await globalFile.text();
|
|
30
|
+
const source = await sourceFile.text();
|
|
31
|
+
if (existing === source) {
|
|
32
|
+
logger.debug("Skill already installed", { path: globalSkillPath });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
mkdirSync(globalSkillDir, { recursive: true });
|
|
37
|
+
await Bun.write(globalSkillPath, sourceFile);
|
|
38
|
+
logger.debug("Installed snippets skill", { path: globalSkillPath });
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
logger.debug("Failed to install skill", { error: String(err) });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Snippets Plugin for OpenCode
|
|
46
|
+
*
|
|
47
|
+
* Expands hashtag-based shortcuts in user messages into predefined text snippets.
|
|
48
|
+
* Also provides /snippet command for managing snippets.
|
|
49
|
+
*
|
|
50
|
+
* @see https://github.com/JosXa/opencode-snippets for full documentation
|
|
51
|
+
*/
|
|
52
|
+
export const SnippetsPlugin = async (ctx) => {
|
|
53
|
+
// Load configuration (global + project-local override)
|
|
54
|
+
const config = loadConfig(ctx.directory);
|
|
55
|
+
// Apply config settings
|
|
56
|
+
logger.debugEnabled = config.logging.debug;
|
|
57
|
+
// Install skill to global config so OpenCode discovers it (if enabled)
|
|
58
|
+
if (config.installSkill) {
|
|
59
|
+
await installSkillToGlobal();
|
|
60
|
+
}
|
|
61
|
+
// Load all snippets at startup (global + project directory)
|
|
62
|
+
const startupStart = performance.now();
|
|
63
|
+
const snippets = await loadSnippets(ctx.directory);
|
|
64
|
+
const startupTime = performance.now() - startupStart;
|
|
65
|
+
logger.debug("Plugin startup complete", {
|
|
66
|
+
startupTimeMs: startupTime.toFixed(2),
|
|
67
|
+
snippetCount: snippets.size,
|
|
68
|
+
installSkill: config.installSkill,
|
|
69
|
+
debugLogging: config.logging.debug,
|
|
70
|
+
});
|
|
71
|
+
// Create command handler
|
|
72
|
+
const commandHandler = createCommandExecuteHandler(ctx.client, snippets, ctx.directory);
|
|
73
|
+
/**
|
|
74
|
+
* Processes text parts for snippet expansion and shell command execution
|
|
75
|
+
*/
|
|
76
|
+
const processTextParts = async (parts) => {
|
|
77
|
+
const messageStart = performance.now();
|
|
78
|
+
let expandTimeTotal = 0;
|
|
79
|
+
let shellTimeTotal = 0;
|
|
80
|
+
let processedParts = 0;
|
|
81
|
+
for (const part of parts) {
|
|
82
|
+
if (part.type === "text" && part.text) {
|
|
83
|
+
// 1. Expand hashtags recursively with loop detection
|
|
84
|
+
const expandStart = performance.now();
|
|
85
|
+
const expansionResult = expandHashtags(part.text, snippets);
|
|
86
|
+
part.text = assembleMessage(expansionResult);
|
|
87
|
+
const expandTime = performance.now() - expandStart;
|
|
88
|
+
expandTimeTotal += expandTime;
|
|
89
|
+
// 2. Execute shell commands: !`command`
|
|
90
|
+
const shellStart = performance.now();
|
|
91
|
+
part.text = await executeShellCommands(part.text, ctx);
|
|
92
|
+
const shellTime = performance.now() - shellStart;
|
|
93
|
+
shellTimeTotal += shellTime;
|
|
94
|
+
processedParts += 1;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const totalTime = performance.now() - messageStart;
|
|
98
|
+
if (processedParts > 0) {
|
|
99
|
+
logger.debug("Text parts processing complete", {
|
|
100
|
+
totalTimeMs: totalTime.toFixed(2),
|
|
101
|
+
snippetExpandTimeMs: expandTimeTotal.toFixed(2),
|
|
102
|
+
shellTimeMs: shellTimeTotal.toFixed(2),
|
|
103
|
+
processedParts,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
return {
|
|
108
|
+
// Register /snippet command
|
|
109
|
+
config: async (opencodeConfig) => {
|
|
110
|
+
opencodeConfig.command ??= {};
|
|
111
|
+
opencodeConfig.command.snippet = {
|
|
112
|
+
template: "",
|
|
113
|
+
description: "Manage text snippets (add, delete, list, help)",
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
// Handle /snippet command execution
|
|
117
|
+
"command.execute.before": commandHandler,
|
|
118
|
+
"chat.message": async (_input, output) => {
|
|
119
|
+
// Only process user messages, never assistant messages
|
|
120
|
+
if (output.message.role !== "user")
|
|
121
|
+
return;
|
|
122
|
+
// Skip processing if any part is marked as ignored (e.g., command output)
|
|
123
|
+
if (output.parts.some((part) => "ignored" in part && part.ignored))
|
|
124
|
+
return;
|
|
125
|
+
await processTextParts(output.parts);
|
|
126
|
+
},
|
|
127
|
+
// Process all messages including question tool responses
|
|
128
|
+
"experimental.chat.messages.transform": async (_input, output) => {
|
|
129
|
+
for (const message of output.messages) {
|
|
130
|
+
// Only process user messages
|
|
131
|
+
if (message.info.role === "user") {
|
|
132
|
+
// Skip processing if any part is marked as ignored (e.g., command output)
|
|
133
|
+
if (message.parts.some((part) => "ignored" in part && part.ignored))
|
|
134
|
+
continue;
|
|
135
|
+
await processTextParts(message.parts);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
// Process skill tool output to expand snippets in skill content
|
|
140
|
+
"tool.execute.after": async (input, output) => {
|
|
141
|
+
// Only process the skill tool
|
|
142
|
+
if (input.tool !== "skill")
|
|
143
|
+
return;
|
|
144
|
+
// The skill tool returns markdown content in its output
|
|
145
|
+
// Expand hashtags in the skill content
|
|
146
|
+
if (typeof output.output === "string" && output.output.trim()) {
|
|
147
|
+
const expansionResult = expandHashtags(output.output, snippets);
|
|
148
|
+
output.output = assembleMessage(expansionResult);
|
|
149
|
+
logger.debug("Skill content expanded", {
|
|
150
|
+
tool: input.tool,
|
|
151
|
+
callID: input.callID,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell-like argument parser that handles quoted strings correctly.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - Space-separated arguments
|
|
6
|
+
* - Double-quoted strings (preserves spaces, allows single quotes inside)
|
|
7
|
+
* - Single-quoted strings (preserves spaces, allows double quotes inside)
|
|
8
|
+
* - --key=value syntax with quoted values
|
|
9
|
+
* - Multiline content inside quotes
|
|
10
|
+
* - Backslash escapes for quotes inside quoted strings
|
|
11
|
+
*
|
|
12
|
+
* @param input - The raw argument string to parse
|
|
13
|
+
* @returns Array of parsed arguments with quotes stripped
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseCommandArgs(input: string): string[];
|
|
16
|
+
//# sourceMappingURL=arg-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arg-parser.d.ts","sourceRoot":"","sources":["../../src/arg-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAwExD"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell-like argument parser that handles quoted strings correctly.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - Space-separated arguments
|
|
6
|
+
* - Double-quoted strings (preserves spaces, allows single quotes inside)
|
|
7
|
+
* - Single-quoted strings (preserves spaces, allows double quotes inside)
|
|
8
|
+
* - --key=value syntax with quoted values
|
|
9
|
+
* - Multiline content inside quotes
|
|
10
|
+
* - Backslash escapes for quotes inside quoted strings
|
|
11
|
+
*
|
|
12
|
+
* @param input - The raw argument string to parse
|
|
13
|
+
* @returns Array of parsed arguments with quotes stripped
|
|
14
|
+
*/
|
|
15
|
+
export function parseCommandArgs(input) {
|
|
16
|
+
const args = [];
|
|
17
|
+
let current = "";
|
|
18
|
+
let state = "normal";
|
|
19
|
+
let hasQuotedContent = false; // Track if we've entered a quoted section
|
|
20
|
+
let i = 0;
|
|
21
|
+
while (i < input.length) {
|
|
22
|
+
const char = input[i];
|
|
23
|
+
if (state === "normal") {
|
|
24
|
+
if (char === " " || char === "\t") {
|
|
25
|
+
// Whitespace in normal mode: finish current token
|
|
26
|
+
if (current.length > 0 || hasQuotedContent) {
|
|
27
|
+
args.push(current);
|
|
28
|
+
current = "";
|
|
29
|
+
hasQuotedContent = false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else if (char === '"') {
|
|
33
|
+
// Enter double-quote mode
|
|
34
|
+
state = "double";
|
|
35
|
+
hasQuotedContent = true;
|
|
36
|
+
}
|
|
37
|
+
else if (char === "'") {
|
|
38
|
+
// Enter single-quote mode
|
|
39
|
+
state = "single";
|
|
40
|
+
hasQuotedContent = true;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
current += char;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else if (state === "double") {
|
|
47
|
+
if (char === "\\") {
|
|
48
|
+
// Check for escape sequences
|
|
49
|
+
const next = input[i + 1];
|
|
50
|
+
if (next === '"' || next === "\\") {
|
|
51
|
+
current += next;
|
|
52
|
+
i++; // Skip the escaped character
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
current += char;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else if (char === '"') {
|
|
59
|
+
// Exit double-quote mode
|
|
60
|
+
state = "normal";
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
current += char;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else if (state === "single") {
|
|
67
|
+
if (char === "\\") {
|
|
68
|
+
// Check for escape sequences
|
|
69
|
+
const next = input[i + 1];
|
|
70
|
+
if (next === "'" || next === "\\") {
|
|
71
|
+
current += next;
|
|
72
|
+
i++; // Skip the escaped character
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
current += char;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (char === "'") {
|
|
79
|
+
// Exit single-quote mode
|
|
80
|
+
state = "normal";
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
current += char;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
i++;
|
|
87
|
+
}
|
|
88
|
+
// Handle any remaining content (including unclosed quotes or empty quoted strings)
|
|
89
|
+
if (current.length > 0 || hasQuotedContent) {
|
|
90
|
+
args.push(current);
|
|
91
|
+
}
|
|
92
|
+
return args;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=arg-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arg-parser.js","sourceRoot":"","sources":["../../src/arg-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { OpencodeClient, SnippetRegistry } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Parsed options from the add command arguments
|
|
4
|
+
*/
|
|
5
|
+
export interface AddOptions {
|
|
6
|
+
aliases: string[];
|
|
7
|
+
description: string | undefined;
|
|
8
|
+
isProject: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parses option arguments for the add command.
|
|
12
|
+
*
|
|
13
|
+
* Supports all variations per PR #13 requirements:
|
|
14
|
+
* - --alias=a,b, --alias a,b, --aliases=a,b, --aliases a,b
|
|
15
|
+
* - --desc=x, --desc x, --description=x, --description x
|
|
16
|
+
* - --project flag
|
|
17
|
+
*
|
|
18
|
+
* @param args - Array of parsed arguments (after name and content extraction)
|
|
19
|
+
* @returns Parsed options object
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseAddOptions(args: string[]): AddOptions;
|
|
22
|
+
/**
|
|
23
|
+
* Creates the command execute handler for the snippets command
|
|
24
|
+
*/
|
|
25
|
+
export declare function createCommandExecuteHandler(client: OpencodeClient, snippets: SnippetRegistry, projectDir?: string): (input: {
|
|
26
|
+
command: string;
|
|
27
|
+
sessionID: string;
|
|
28
|
+
arguments: string;
|
|
29
|
+
}) => Promise<void>;
|
|
30
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/commands.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAclE;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CA8D1D;AAYD;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,eAAe,EACzB,UAAU,CAAC,EAAE,MAAM,IAEL,OAAO;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,mBAsD/E"}
|