@tangle-network/browser-agent-driver 0.24.0 → 0.24.2
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/dist/brain/index.d.ts +5 -0
- package/dist/brain/index.d.ts.map +1 -1
- package/dist/brain/index.js +76 -20
- package/dist/brain/index.js.map +1 -1
- package/dist/cli-attach.d.ts +128 -0
- package/dist/cli-attach.d.ts.map +1 -0
- package/dist/cli-attach.js +254 -0
- package/dist/cli-attach.js.map +1 -0
- package/dist/cli-ui.d.ts.map +1 -1
- package/dist/cli-ui.js +10 -0
- package/dist/cli-ui.js.map +1 -1
- package/dist/cli.js +109 -9
- package/dist/cli.js.map +1 -1
- package/dist/drivers/playwright.d.ts +8 -0
- package/dist/drivers/playwright.d.ts.map +1 -1
- package/dist/drivers/playwright.js +117 -7
- package/dist/drivers/playwright.js.map +1 -1
- package/dist/drivers/types.d.ts +5 -0
- package/dist/drivers/types.d.ts.map +1 -1
- package/dist/multi-actor.d.ts.map +1 -1
- package/dist/multi-actor.js +4 -0
- package/dist/multi-actor.js.map +1 -1
- package/dist/run-state.d.ts +4 -0
- package/dist/run-state.d.ts.map +1 -1
- package/dist/run-state.js +14 -2
- package/dist/run-state.js.map +1 -1
- package/dist/runner/parallel-runner.d.ts +4 -0
- package/dist/runner/parallel-runner.d.ts.map +1 -1
- package/dist/runner/parallel-runner.js +1 -0
- package/dist/runner/parallel-runner.js.map +1 -1
- package/dist/runner/runner.d.ts +8 -0
- package/dist/runner/runner.d.ts.map +1 -1
- package/dist/runner/runner.js +52 -24
- package/dist/runner/runner.js.map +1 -1
- package/dist/skills/domain-loader.d.ts +78 -0
- package/dist/skills/domain-loader.d.ts.map +1 -0
- package/dist/skills/domain-loader.js +201 -0
- package/dist/skills/domain-loader.js.map +1 -0
- package/dist/skills/macro-loader.d.ts +95 -0
- package/dist/skills/macro-loader.d.ts.map +1 -0
- package/dist/skills/macro-loader.js +237 -0
- package/dist/skills/macro-loader.js.map +1 -0
- package/dist/supervisor/critic.js +1 -1
- package/dist/supervisor/policy.js +7 -0
- package/dist/supervisor/policy.js.map +1 -1
- package/dist/test-runner.d.ts +6 -0
- package/dist/test-runner.d.ts.map +1 -1
- package/dist/test-runner.js +3 -0
- package/dist/test-runner.js.map +1 -1
- package/dist/types.d.ts +19 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain-skill loader — reads `skills/domain/<host>/SKILL.md` files from
|
|
3
|
+
* the bad repo and exposes them as a `BadExtension`-compatible bundle so
|
|
4
|
+
* the existing `setExtensionRules` path at brain/index.ts:899 picks up the
|
|
5
|
+
* per-domain rules without a second injection site.
|
|
6
|
+
*
|
|
7
|
+
* Inspired by browser-use/browser-harness's `domain-skills/` layout.
|
|
8
|
+
* The differences that matter:
|
|
9
|
+
*
|
|
10
|
+
* 1. Every skill carries YAML frontmatter with `host` + optional
|
|
11
|
+
* `aliases` so one file covers www.amazon.com + smile.amazon.com.
|
|
12
|
+
* 2. The body is plain markdown — no code, no enforcement. It lands
|
|
13
|
+
* verbatim in the system prompt's USER RULES (domain match) section
|
|
14
|
+
* that already exists at brain/index.ts:865-870.
|
|
15
|
+
* 3. Skills under `skills/domain/experimental/<host>/` are loaded into
|
|
16
|
+
* the same registry but tagged so the promotion script can demote
|
|
17
|
+
* those whose bench cases don't produce a measurable win.
|
|
18
|
+
*
|
|
19
|
+
* No YAML parser dep — we handle the tiny subset we support (flat
|
|
20
|
+
* key/value + single-line array aliases) with regex. If a skill needs
|
|
21
|
+
* more expressive metadata, grow the parser with a failing test first.
|
|
22
|
+
*/
|
|
23
|
+
import type { BadExtension } from '../extensions/types.js';
|
|
24
|
+
/** Parsed frontmatter + body split, before we turn it into rules. */
|
|
25
|
+
export interface ParsedDomainSkill {
|
|
26
|
+
/** Primary hostname the skill applies to (e.g. "amazon.com") */
|
|
27
|
+
host: string;
|
|
28
|
+
/** Extra hosts that share the same rules (e.g. ["smile.amazon.com"]) */
|
|
29
|
+
aliases: string[];
|
|
30
|
+
/** Optional human-readable label */
|
|
31
|
+
title?: string;
|
|
32
|
+
/** Markdown body verbatim (no processing — goes straight into the prompt) */
|
|
33
|
+
body: string;
|
|
34
|
+
/** Absolute path to the source file, for logging and promotion flows */
|
|
35
|
+
sourcePath: string;
|
|
36
|
+
/** Whether this skill lives under experimental/ */
|
|
37
|
+
experimental: boolean;
|
|
38
|
+
}
|
|
39
|
+
/** Cap on the body size we'll inject, to keep domain rules from blowing up
|
|
40
|
+
* the system prompt. Bodies longer than this are truncated with a marker
|
|
41
|
+
* and the loader warns. 4 KB is ~1000 tokens — comfortable headroom even
|
|
42
|
+
* across multiple domain matches. */
|
|
43
|
+
export declare const MAX_DOMAIN_BODY_BYTES = 4096;
|
|
44
|
+
/**
|
|
45
|
+
* Parse a single SKILL.md file. The caller has already read the raw text.
|
|
46
|
+
* Throws on missing `host` — that's not a recoverable schema hole, we
|
|
47
|
+
* want the loader to surface it.
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseDomainSkill(raw: string, sourcePath: string, experimental?: boolean): ParsedDomainSkill;
|
|
50
|
+
export interface LoadDomainSkillsOptions {
|
|
51
|
+
/** Root directory containing the domain skill tree. Defaults to the
|
|
52
|
+
* `skills/domain` dir shipped with the bad package. */
|
|
53
|
+
rootDir?: string;
|
|
54
|
+
/** Logger for load errors. Defaults to a stderr warning. */
|
|
55
|
+
onError?: (sourcePath: string, err: unknown) => void;
|
|
56
|
+
}
|
|
57
|
+
export interface LoadDomainSkillsResult {
|
|
58
|
+
skills: ParsedDomainSkill[];
|
|
59
|
+
/** Files that failed to parse. Never throws — a broken file should never
|
|
60
|
+
* bring down the run, but the operator needs to see which file it was. */
|
|
61
|
+
errors: Array<{
|
|
62
|
+
path: string;
|
|
63
|
+
error: string;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
/** Locate the packaged `skills/domain` directory. Works both when running
|
|
67
|
+
* from source (tsx/test) and from the built dist/ layout. */
|
|
68
|
+
export declare function defaultDomainSkillsRoot(): string;
|
|
69
|
+
/** Walk the domain tree and return parsed skills. Missing dir ⇒ empty list,
|
|
70
|
+
* not an error: shipping without any seeded skills is a valid state. */
|
|
71
|
+
export declare function loadDomainSkills(options?: LoadDomainSkillsOptions): Promise<LoadDomainSkillsResult>;
|
|
72
|
+
/** Collapse a list of parsed skills into a BadExtension so they flow through
|
|
73
|
+
* the existing resolveExtensions → setExtensionRules path. Aliases are only
|
|
74
|
+
* emitted as additional keys when they can't already be reached by the
|
|
75
|
+
* matcher's substring comparison — otherwise the same body would be
|
|
76
|
+
* concatenated onto itself at match time. */
|
|
77
|
+
export declare function buildDomainSkillExtension(skills: ParsedDomainSkill[]): BadExtension;
|
|
78
|
+
//# sourceMappingURL=domain-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-loader.d.ts","sourceRoot":"","sources":["../../src/skills/domain-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,wBAAwB,CAAA;AAEvE,qEAAqE;AACrE,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAA;IACZ,wEAAwE;IACxE,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAA;IACZ,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAA;IAClB,mDAAmD;IACnD,YAAY,EAAE,OAAO,CAAA;CACtB;AAED;;;qCAGqC;AACrC,eAAO,MAAM,qBAAqB,OAAO,CAAA;AAEzC;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,UAAQ,GAAG,iBAAiB,CAyBzG;AA8CD,MAAM,WAAW,uBAAuB;IACtC;2DACuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CACrD;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B;8EAC0E;IAC1E,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC/C;AAED;6DAC6D;AAC7D,wBAAgB,uBAAuB,IAAI,MAAM,CAMhD;AAED;wEACwE;AACxE,wBAAsB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAuB7G;AAuBD;;;;6CAI6C;AAC7C,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,YAAY,CAenF"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain-skill loader — reads `skills/domain/<host>/SKILL.md` files from
|
|
3
|
+
* the bad repo and exposes them as a `BadExtension`-compatible bundle so
|
|
4
|
+
* the existing `setExtensionRules` path at brain/index.ts:899 picks up the
|
|
5
|
+
* per-domain rules without a second injection site.
|
|
6
|
+
*
|
|
7
|
+
* Inspired by browser-use/browser-harness's `domain-skills/` layout.
|
|
8
|
+
* The differences that matter:
|
|
9
|
+
*
|
|
10
|
+
* 1. Every skill carries YAML frontmatter with `host` + optional
|
|
11
|
+
* `aliases` so one file covers www.amazon.com + smile.amazon.com.
|
|
12
|
+
* 2. The body is plain markdown — no code, no enforcement. It lands
|
|
13
|
+
* verbatim in the system prompt's USER RULES (domain match) section
|
|
14
|
+
* that already exists at brain/index.ts:865-870.
|
|
15
|
+
* 3. Skills under `skills/domain/experimental/<host>/` are loaded into
|
|
16
|
+
* the same registry but tagged so the promotion script can demote
|
|
17
|
+
* those whose bench cases don't produce a measurable win.
|
|
18
|
+
*
|
|
19
|
+
* No YAML parser dep — we handle the tiny subset we support (flat
|
|
20
|
+
* key/value + single-line array aliases) with regex. If a skill needs
|
|
21
|
+
* more expressive metadata, grow the parser with a failing test first.
|
|
22
|
+
*/
|
|
23
|
+
import * as fs from 'node:fs';
|
|
24
|
+
import * as path from 'node:path';
|
|
25
|
+
import { fileURLToPath } from 'node:url';
|
|
26
|
+
/** Cap on the body size we'll inject, to keep domain rules from blowing up
|
|
27
|
+
* the system prompt. Bodies longer than this are truncated with a marker
|
|
28
|
+
* and the loader warns. 4 KB is ~1000 tokens — comfortable headroom even
|
|
29
|
+
* across multiple domain matches. */
|
|
30
|
+
export const MAX_DOMAIN_BODY_BYTES = 4096;
|
|
31
|
+
/**
|
|
32
|
+
* Parse a single SKILL.md file. The caller has already read the raw text.
|
|
33
|
+
* Throws on missing `host` — that's not a recoverable schema hole, we
|
|
34
|
+
* want the loader to surface it.
|
|
35
|
+
*/
|
|
36
|
+
export function parseDomainSkill(raw, sourcePath, experimental = false) {
|
|
37
|
+
const { frontmatter, body } = splitFrontmatter(raw);
|
|
38
|
+
const meta = parseFrontmatterBlock(frontmatter);
|
|
39
|
+
const host = typeof meta.host === 'string' ? meta.host.trim() : '';
|
|
40
|
+
if (!host) {
|
|
41
|
+
throw new Error(`${sourcePath}: frontmatter missing required "host" field`);
|
|
42
|
+
}
|
|
43
|
+
const aliases = Array.isArray(meta.aliases)
|
|
44
|
+
? meta.aliases.map(String).map((s) => s.trim()).filter(Boolean)
|
|
45
|
+
: [];
|
|
46
|
+
const title = typeof meta.title === 'string' ? meta.title.trim() : undefined;
|
|
47
|
+
let trimmedBody = body.trim();
|
|
48
|
+
if (Buffer.byteLength(trimmedBody, 'utf-8') > MAX_DOMAIN_BODY_BYTES) {
|
|
49
|
+
trimmedBody = trimmedBody.slice(0, MAX_DOMAIN_BODY_BYTES).trimEnd() +
|
|
50
|
+
'\n\n[…truncated — domain skill body exceeded MAX_DOMAIN_BODY_BYTES]';
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
host,
|
|
54
|
+
aliases,
|
|
55
|
+
title,
|
|
56
|
+
body: trimmedBody,
|
|
57
|
+
sourcePath,
|
|
58
|
+
experimental,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Split `---\nfrontmatter\n---\nbody` into its two parts. Files without
|
|
62
|
+
* frontmatter (missing opening `---\n`) are treated as body-only. The
|
|
63
|
+
* strict `---\n` match avoids misinterpreting a body that happens to
|
|
64
|
+
* start with a markdown horizontal rule. */
|
|
65
|
+
function splitFrontmatter(raw) {
|
|
66
|
+
if (!raw.startsWith('---\n') && !raw.startsWith('---\r\n'))
|
|
67
|
+
return { frontmatter: '', body: raw };
|
|
68
|
+
const afterOpenDelimiter = raw.startsWith('---\r\n') ? 5 : 4;
|
|
69
|
+
// Closing delimiter must be on its own line (\n---\n or \n---\r\n or EOF).
|
|
70
|
+
const bodyStart = raw.indexOf('\n---\n', afterOpenDelimiter);
|
|
71
|
+
const altStart = raw.indexOf('\n---\r\n', afterOpenDelimiter);
|
|
72
|
+
const endIndex = altStart !== -1 && (bodyStart === -1 || altStart < bodyStart) ? altStart : bodyStart;
|
|
73
|
+
if (endIndex === -1)
|
|
74
|
+
return { frontmatter: '', body: raw };
|
|
75
|
+
const frontmatter = raw.slice(afterOpenDelimiter, endIndex).trim();
|
|
76
|
+
const bodyOffset = raw.indexOf('\n', endIndex + 4); // skip past the closing --- and its newline
|
|
77
|
+
const body = bodyOffset >= 0 ? raw.slice(bodyOffset + 1) : '';
|
|
78
|
+
return { frontmatter, body };
|
|
79
|
+
}
|
|
80
|
+
/** Tiny YAML subset: flat scalars + single-line [a, b] arrays. Quoted and
|
|
81
|
+
* unquoted strings both work; inline comments (`# …`) are ignored. */
|
|
82
|
+
function parseFrontmatterBlock(block) {
|
|
83
|
+
const out = {};
|
|
84
|
+
if (!block)
|
|
85
|
+
return out;
|
|
86
|
+
for (const line of block.split('\n')) {
|
|
87
|
+
const trimmed = line.trim();
|
|
88
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
89
|
+
continue;
|
|
90
|
+
const match = trimmed.match(/^([A-Za-z0-9_-]+)\s*:\s*(.*)$/);
|
|
91
|
+
if (!match)
|
|
92
|
+
continue;
|
|
93
|
+
const key = match[1];
|
|
94
|
+
let rawVal = match[2].replace(/\s+#.*$/, '').trim();
|
|
95
|
+
if (rawVal.startsWith('[') && rawVal.endsWith(']')) {
|
|
96
|
+
out[key] = rawVal.slice(1, -1)
|
|
97
|
+
.split(',')
|
|
98
|
+
.map((s) => s.trim().replace(/^['"]|['"]$/g, ''))
|
|
99
|
+
.filter(Boolean);
|
|
100
|
+
}
|
|
101
|
+
else if ((rawVal.startsWith('"') && rawVal.endsWith('"')) || (rawVal.startsWith("'") && rawVal.endsWith("'"))) {
|
|
102
|
+
out[key] = rawVal.slice(1, -1);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
out[key] = rawVal;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
/** Locate the packaged `skills/domain` directory. Works both when running
|
|
111
|
+
* from source (tsx/test) and from the built dist/ layout. */
|
|
112
|
+
export function defaultDomainSkillsRoot() {
|
|
113
|
+
const here = fileURLToPath(import.meta.url);
|
|
114
|
+
// src layout: src/skills/domain-loader.ts → <repo>/skills/domain
|
|
115
|
+
// dist layout: dist/skills/domain-loader.js → <repo>/skills/domain
|
|
116
|
+
const packageRoot = path.resolve(path.dirname(here), '..', '..');
|
|
117
|
+
return path.join(packageRoot, 'skills', 'domain');
|
|
118
|
+
}
|
|
119
|
+
/** Walk the domain tree and return parsed skills. Missing dir ⇒ empty list,
|
|
120
|
+
* not an error: shipping without any seeded skills is a valid state. */
|
|
121
|
+
export async function loadDomainSkills(options = {}) {
|
|
122
|
+
const rootDir = options.rootDir ?? defaultDomainSkillsRoot();
|
|
123
|
+
const onError = options.onError ?? ((p, err) => {
|
|
124
|
+
// eslint-disable-next-line no-console
|
|
125
|
+
console.error(`[domain-skill] failed to load ${p}: ${err instanceof Error ? err.message : String(err)}`);
|
|
126
|
+
});
|
|
127
|
+
if (!fs.existsSync(rootDir))
|
|
128
|
+
return { skills: [], errors: [] };
|
|
129
|
+
const skills = [];
|
|
130
|
+
const errors = [];
|
|
131
|
+
for (const candidate of walkSkillMarkdown(rootDir)) {
|
|
132
|
+
try {
|
|
133
|
+
const raw = fs.readFileSync(candidate.path, 'utf-8');
|
|
134
|
+
const parsed = parseDomainSkill(raw, candidate.path, candidate.experimental);
|
|
135
|
+
skills.push(parsed);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
139
|
+
errors.push({ path: candidate.path, error: message });
|
|
140
|
+
onError(candidate.path, err);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return { skills, errors };
|
|
144
|
+
}
|
|
145
|
+
/** Enumerate <root>/<host>/SKILL.md and <root>/experimental/<host>/SKILL.md.
|
|
146
|
+
* Any other files or directory depths are ignored — the layout is strict on
|
|
147
|
+
* purpose so we don't accidentally pick up READMEs or notes. */
|
|
148
|
+
function* walkSkillMarkdown(rootDir) {
|
|
149
|
+
const entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
|
150
|
+
for (const entry of entries) {
|
|
151
|
+
if (!entry.isDirectory())
|
|
152
|
+
continue;
|
|
153
|
+
const entryPath = path.join(rootDir, entry.name);
|
|
154
|
+
if (entry.name === 'experimental') {
|
|
155
|
+
for (const inner of fs.readdirSync(entryPath, { withFileTypes: true })) {
|
|
156
|
+
if (!inner.isDirectory())
|
|
157
|
+
continue;
|
|
158
|
+
const skillPath = path.join(entryPath, inner.name, 'SKILL.md');
|
|
159
|
+
if (fs.existsSync(skillPath))
|
|
160
|
+
yield { path: skillPath, experimental: true };
|
|
161
|
+
}
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const skillPath = path.join(entryPath, 'SKILL.md');
|
|
165
|
+
if (fs.existsSync(skillPath))
|
|
166
|
+
yield { path: skillPath, experimental: false };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/** Collapse a list of parsed skills into a BadExtension so they flow through
|
|
170
|
+
* the existing resolveExtensions → setExtensionRules path. Aliases are only
|
|
171
|
+
* emitted as additional keys when they can't already be reached by the
|
|
172
|
+
* matcher's substring comparison — otherwise the same body would be
|
|
173
|
+
* concatenated onto itself at match time. */
|
|
174
|
+
export function buildDomainSkillExtension(skills) {
|
|
175
|
+
const addRulesForDomain = {};
|
|
176
|
+
for (const skill of skills) {
|
|
177
|
+
const body = skill.body;
|
|
178
|
+
addBody(addRulesForDomain, skill.host, body);
|
|
179
|
+
for (const alias of skill.aliases) {
|
|
180
|
+
// Skip aliases that already match via substring on the primary or any
|
|
181
|
+
// prior alias — registering them would double-emit the body when the
|
|
182
|
+
// brain matcher walks every registered key.
|
|
183
|
+
if (alias.includes(skill.host))
|
|
184
|
+
continue;
|
|
185
|
+
if (skill.aliases.some((other) => other !== alias && alias.includes(other)))
|
|
186
|
+
continue;
|
|
187
|
+
addBody(addRulesForDomain, alias, body);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return { addRulesForDomain };
|
|
191
|
+
}
|
|
192
|
+
function addBody(map, host, body) {
|
|
193
|
+
const existing = map[host];
|
|
194
|
+
if (existing && existing.extraRules) {
|
|
195
|
+
existing.extraRules = `${existing.extraRules}\n\n${body}`;
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
map[host] = { extraRules: body };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=domain-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-loader.js","sourceRoot":"","sources":["../../src/skills/domain-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Macro loader and executor. Reads `skills/macros/*.json` files and validates
|
|
3
|
+
* each into a typed MacroDefinition. A macro is a flat ordered list of
|
|
4
|
+
* existing primitive Actions with string-template interpolation over its
|
|
5
|
+
* declared params.
|
|
6
|
+
*
|
|
7
|
+
* This is the minimum viable mutable tool surface: the agent can add new
|
|
8
|
+
* capability by composing safe primitives into named workflows, without us
|
|
9
|
+
* eval-ing arbitrary code. Raw TS/JS handlers are a later generation's fight.
|
|
10
|
+
*
|
|
11
|
+
* Safety invariants (all enforced at load time, tested):
|
|
12
|
+
* 1. Step types are restricted to SAFE_MACRO_STEP_TYPES. No `macro` steps
|
|
13
|
+
* (flat only). No `navigate` — macros are page-interaction primitives,
|
|
14
|
+
* not navigation helpers (macros get triggered when the agent is
|
|
15
|
+
* already somewhere).
|
|
16
|
+
* 2. Param substitution only applies to string fields. Objects and numbers
|
|
17
|
+
* pass through as-is.
|
|
18
|
+
* 3. Unknown params in steps (e.g. `${oops}`) are a load-time error.
|
|
19
|
+
* 4. Duplicate macro names error at registration.
|
|
20
|
+
*/
|
|
21
|
+
import type { Action } from '../types.js';
|
|
22
|
+
/**
|
|
23
|
+
* A step inside a macro is any safe primitive action. We use Action
|
|
24
|
+
* directly — the loader validates that `step.action` is in SAFE_MACRO_STEP_TYPES
|
|
25
|
+
* at load time. Strings in these actions can carry `${param}` placeholders
|
|
26
|
+
* that are substituted from the caller's args.
|
|
27
|
+
*/
|
|
28
|
+
export type MacroStep = Action;
|
|
29
|
+
/** Primitive actions that are safe inside a macro. Explicitly whitelists —
|
|
30
|
+
* any new Action type needs to be added here deliberately. Note that
|
|
31
|
+
* `navigate` and `complete`/`abort` are intentionally absent: macros
|
|
32
|
+
* compose local interactions, not run-level control flow. */
|
|
33
|
+
/** Maximum steps in a single macro. Bounds the total wall-time any one
|
|
34
|
+
* macro can consume (step count × per-action timeout) to keep the
|
|
35
|
+
* CLI's budget cap meaningful. Raising this requires a measured reason —
|
|
36
|
+
* a longer macro is usually a hint that the work should split into two.
|
|
37
|
+
*/
|
|
38
|
+
export declare const MAX_MACRO_STEPS = 8;
|
|
39
|
+
export declare const SAFE_MACRO_STEP_TYPES: Set<"select" | "fill" | "type" | "click" | "scroll" | "navigate" | "wait" | "complete" | "press" | "hover" | "evaluate" | "runScript" | "extractWithIndex" | "verifyPreview" | "abort" | "clickSequence" | "clickAt" | "typeAt" | "clickLabel" | "typeLabel" | "macro">;
|
|
40
|
+
export interface MacroParamSpec {
|
|
41
|
+
name: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
/** Minimum required; if absent the macro can be invoked without this arg */
|
|
44
|
+
required?: boolean;
|
|
45
|
+
}
|
|
46
|
+
export interface MacroDefinition {
|
|
47
|
+
name: string;
|
|
48
|
+
description: string;
|
|
49
|
+
/** Declared parameters. Empty array if the macro takes no args. */
|
|
50
|
+
params: MacroParamSpec[];
|
|
51
|
+
/** Ordered, flat list of primitive actions */
|
|
52
|
+
steps: MacroStep[];
|
|
53
|
+
/** Where on disk this came from (for diagnostics) */
|
|
54
|
+
sourcePath: string;
|
|
55
|
+
/** If true, macro is in staging and is NOT exposed in the agent's prompt
|
|
56
|
+
* until it passes eval-gated promotion. */
|
|
57
|
+
experimental?: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface MacroRegistry {
|
|
60
|
+
macros: Map<string, MacroDefinition>;
|
|
61
|
+
/** The same list rendered for the LLM's system prompt */
|
|
62
|
+
promptBlock: string;
|
|
63
|
+
}
|
|
64
|
+
/** Exported for tests + promotion script to share the same root resolution.
|
|
65
|
+
* BAD_MACROS_DIR overrides the packaged path — the promotion script uses this
|
|
66
|
+
* to point at a staging directory without mutating the canonical tree. */
|
|
67
|
+
export declare function defaultMacrosRoot(): string;
|
|
68
|
+
export interface LoadMacrosOptions {
|
|
69
|
+
rootDir?: string;
|
|
70
|
+
/** Include experimental/ subdir (for the promotion script only). */
|
|
71
|
+
includeExperimental?: boolean;
|
|
72
|
+
onError?: (p: string, err: unknown) => void;
|
|
73
|
+
}
|
|
74
|
+
export interface LoadMacrosResult {
|
|
75
|
+
macros: MacroDefinition[];
|
|
76
|
+
errors: Array<{
|
|
77
|
+
path: string;
|
|
78
|
+
error: string;
|
|
79
|
+
}>;
|
|
80
|
+
}
|
|
81
|
+
export declare function loadMacros(options?: LoadMacrosOptions): Promise<LoadMacrosResult>;
|
|
82
|
+
export declare function validateMacroDefinition(raw: unknown, sourcePath: string, experimental: boolean): MacroDefinition;
|
|
83
|
+
/** Render the loaded macros into the system-prompt snippet. Called once at
|
|
84
|
+
* runner boot and cached in the brain. Returns empty string if no macros. */
|
|
85
|
+
export declare function renderMacroPromptBlock(macros: MacroDefinition[]): string;
|
|
86
|
+
/** Substitute `${param}` placeholders in string-typed fields of a step,
|
|
87
|
+
* using the provided args. Returns a new object — does not mutate input.
|
|
88
|
+
* Unknown placeholders are collected so the dispatcher can fail fast
|
|
89
|
+
* instead of silently typing a literal `${missing}` into a form. */
|
|
90
|
+
export declare function interpolateStep(step: MacroStep, args: Record<string, string>): {
|
|
91
|
+
step: MacroStep;
|
|
92
|
+
unresolved: string[];
|
|
93
|
+
};
|
|
94
|
+
export declare function buildMacroRegistry(macros: MacroDefinition[]): MacroRegistry;
|
|
95
|
+
//# sourceMappingURL=macro-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"macro-loader.d.ts","sourceRoot":"","sources":["../../src/skills/macro-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAA;AAE9B;;;6DAG6D;AAC7D;;;;GAIG;AACH,eAAO,MAAM,eAAe,IAAI,CAAA;AAEhC,eAAO,MAAM,qBAAqB,yQAchC,CAAA;AAEF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,mEAAmE;IACnE,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,8CAA8C;IAC9C,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAA;IAClB;gDAC4C;IAC5C,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IACpC,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;0EAE0E;AAC1E,wBAAgB,iBAAiB,IAAI,MAAM,CAQ1C;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,oEAAoE;IACpE,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CAC5C;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC/C;AAED,wBAAsB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA6B3F;AAmBD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,eAAe,CAgEhH;AAqBD;6EAC6E;AAC7E,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,CAaxE;AAED;;;oEAGoE;AACpE,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAc3C;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,aAAa,CAO3E"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Macro loader and executor. Reads `skills/macros/*.json` files and validates
|
|
3
|
+
* each into a typed MacroDefinition. A macro is a flat ordered list of
|
|
4
|
+
* existing primitive Actions with string-template interpolation over its
|
|
5
|
+
* declared params.
|
|
6
|
+
*
|
|
7
|
+
* This is the minimum viable mutable tool surface: the agent can add new
|
|
8
|
+
* capability by composing safe primitives into named workflows, without us
|
|
9
|
+
* eval-ing arbitrary code. Raw TS/JS handlers are a later generation's fight.
|
|
10
|
+
*
|
|
11
|
+
* Safety invariants (all enforced at load time, tested):
|
|
12
|
+
* 1. Step types are restricted to SAFE_MACRO_STEP_TYPES. No `macro` steps
|
|
13
|
+
* (flat only). No `navigate` — macros are page-interaction primitives,
|
|
14
|
+
* not navigation helpers (macros get triggered when the agent is
|
|
15
|
+
* already somewhere).
|
|
16
|
+
* 2. Param substitution only applies to string fields. Objects and numbers
|
|
17
|
+
* pass through as-is.
|
|
18
|
+
* 3. Unknown params in steps (e.g. `${oops}`) are a load-time error.
|
|
19
|
+
* 4. Duplicate macro names error at registration.
|
|
20
|
+
*/
|
|
21
|
+
import * as fs from 'node:fs';
|
|
22
|
+
import * as path from 'node:path';
|
|
23
|
+
import { fileURLToPath } from 'node:url';
|
|
24
|
+
/** Primitive actions that are safe inside a macro. Explicitly whitelists —
|
|
25
|
+
* any new Action type needs to be added here deliberately. Note that
|
|
26
|
+
* `navigate` and `complete`/`abort` are intentionally absent: macros
|
|
27
|
+
* compose local interactions, not run-level control flow. */
|
|
28
|
+
/** Maximum steps in a single macro. Bounds the total wall-time any one
|
|
29
|
+
* macro can consume (step count × per-action timeout) to keep the
|
|
30
|
+
* CLI's budget cap meaningful. Raising this requires a measured reason —
|
|
31
|
+
* a longer macro is usually a hint that the work should split into two.
|
|
32
|
+
*/
|
|
33
|
+
export const MAX_MACRO_STEPS = 8;
|
|
34
|
+
export const SAFE_MACRO_STEP_TYPES = new Set([
|
|
35
|
+
'click',
|
|
36
|
+
'type',
|
|
37
|
+
'press',
|
|
38
|
+
'hover',
|
|
39
|
+
'select',
|
|
40
|
+
'scroll',
|
|
41
|
+
'wait',
|
|
42
|
+
'clickAt',
|
|
43
|
+
'typeAt',
|
|
44
|
+
'clickLabel',
|
|
45
|
+
'typeLabel',
|
|
46
|
+
'clickSequence',
|
|
47
|
+
'fill',
|
|
48
|
+
]);
|
|
49
|
+
/** Exported for tests + promotion script to share the same root resolution.
|
|
50
|
+
* BAD_MACROS_DIR overrides the packaged path — the promotion script uses this
|
|
51
|
+
* to point at a staging directory without mutating the canonical tree. */
|
|
52
|
+
export function defaultMacrosRoot() {
|
|
53
|
+
const override = process.env.BAD_MACROS_DIR;
|
|
54
|
+
if (override && override.length > 0)
|
|
55
|
+
return path.resolve(override);
|
|
56
|
+
const here = fileURLToPath(import.meta.url);
|
|
57
|
+
// src/skills/macro-loader.ts → <repo>/skills/macros
|
|
58
|
+
// dist/skills/macro-loader.js → <repo>/skills/macros
|
|
59
|
+
const pkgRoot = path.resolve(path.dirname(here), '..', '..');
|
|
60
|
+
return path.join(pkgRoot, 'skills', 'macros');
|
|
61
|
+
}
|
|
62
|
+
export async function loadMacros(options = {}) {
|
|
63
|
+
const rootDir = options.rootDir ?? defaultMacrosRoot();
|
|
64
|
+
const onError = options.onError ?? ((p, err) => {
|
|
65
|
+
// eslint-disable-next-line no-console
|
|
66
|
+
console.error(`[macro] failed to load ${p}: ${err instanceof Error ? err.message : String(err)}`);
|
|
67
|
+
});
|
|
68
|
+
if (!fs.existsSync(rootDir))
|
|
69
|
+
return { macros: [], errors: [] };
|
|
70
|
+
const candidates = collectMacroFiles(rootDir, options.includeExperimental ?? false);
|
|
71
|
+
const macros = [];
|
|
72
|
+
const errors = [];
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
for (const { file, experimental } of candidates) {
|
|
75
|
+
try {
|
|
76
|
+
const raw = fs.readFileSync(file, 'utf-8');
|
|
77
|
+
const parsed = JSON.parse(raw);
|
|
78
|
+
const macro = validateMacroDefinition(parsed, file, experimental);
|
|
79
|
+
if (seen.has(macro.name)) {
|
|
80
|
+
throw new Error(`Duplicate macro name "${macro.name}" (already loaded from a prior file)`);
|
|
81
|
+
}
|
|
82
|
+
seen.add(macro.name);
|
|
83
|
+
macros.push(macro);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
errors.push({ path: file, error: err instanceof Error ? err.message : String(err) });
|
|
87
|
+
onError(file, err);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { macros, errors };
|
|
91
|
+
}
|
|
92
|
+
function collectMacroFiles(rootDir, includeExperimental) {
|
|
93
|
+
const result = [];
|
|
94
|
+
for (const entry of fs.readdirSync(rootDir, { withFileTypes: true })) {
|
|
95
|
+
const full = path.join(rootDir, entry.name);
|
|
96
|
+
if (entry.isFile() && entry.name.endsWith('.json')) {
|
|
97
|
+
result.push({ file: full, experimental: false });
|
|
98
|
+
}
|
|
99
|
+
else if (entry.isDirectory() && entry.name === 'experimental' && includeExperimental) {
|
|
100
|
+
for (const inner of fs.readdirSync(full, { withFileTypes: true })) {
|
|
101
|
+
if (inner.isFile() && inner.name.endsWith('.json')) {
|
|
102
|
+
result.push({ file: path.join(full, inner.name), experimental: true });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
export function validateMacroDefinition(raw, sourcePath, experimental) {
|
|
110
|
+
if (!raw || typeof raw !== 'object')
|
|
111
|
+
throw new Error('macro must be a JSON object');
|
|
112
|
+
const obj = raw;
|
|
113
|
+
const name = obj.name;
|
|
114
|
+
if (typeof name !== 'string' || !/^[a-z][a-z0-9-]*$/i.test(name)) {
|
|
115
|
+
throw new Error('macro.name must be a non-empty string matching /^[a-z][a-z0-9-]*$/i');
|
|
116
|
+
}
|
|
117
|
+
const description = obj.description;
|
|
118
|
+
if (typeof description !== 'string' || description.length === 0) {
|
|
119
|
+
throw new Error(`macro.description must be a non-empty string (for the agent's prompt)`);
|
|
120
|
+
}
|
|
121
|
+
const rawParams = Array.isArray(obj.params) ? obj.params : [];
|
|
122
|
+
const params = [];
|
|
123
|
+
const paramNames = new Set();
|
|
124
|
+
for (const p of rawParams) {
|
|
125
|
+
if (!p || typeof p !== 'object')
|
|
126
|
+
throw new Error('each macro param must be an object');
|
|
127
|
+
const pn = p.name;
|
|
128
|
+
if (typeof pn !== 'string' || !/^[a-z][a-z0-9_]*$/i.test(pn)) {
|
|
129
|
+
throw new Error(`macro param name must match /^[a-z][a-z0-9_]*$/i, got ${String(pn)}`);
|
|
130
|
+
}
|
|
131
|
+
if (paramNames.has(pn))
|
|
132
|
+
throw new Error(`duplicate macro param: ${pn}`);
|
|
133
|
+
paramNames.add(pn);
|
|
134
|
+
const spec = { name: pn };
|
|
135
|
+
if (typeof p.description === 'string') {
|
|
136
|
+
spec.description = p.description;
|
|
137
|
+
}
|
|
138
|
+
if (typeof p.required === 'boolean') {
|
|
139
|
+
spec.required = p.required;
|
|
140
|
+
}
|
|
141
|
+
params.push(spec);
|
|
142
|
+
}
|
|
143
|
+
const rawSteps = obj.steps;
|
|
144
|
+
if (!Array.isArray(rawSteps) || rawSteps.length === 0) {
|
|
145
|
+
throw new Error('macro.steps must be a non-empty array');
|
|
146
|
+
}
|
|
147
|
+
if (rawSteps.length > MAX_MACRO_STEPS) {
|
|
148
|
+
throw new Error(`macro.steps has ${rawSteps.length} entries, max ${MAX_MACRO_STEPS}. Split the macro.`);
|
|
149
|
+
}
|
|
150
|
+
const steps = [];
|
|
151
|
+
for (let i = 0; i < rawSteps.length; i++) {
|
|
152
|
+
const step = rawSteps[i];
|
|
153
|
+
if (!step || typeof step !== 'object' || Array.isArray(step)) {
|
|
154
|
+
throw new Error(`macro.steps[${i}] must be an object`);
|
|
155
|
+
}
|
|
156
|
+
const action = step.action;
|
|
157
|
+
if (typeof action !== 'string' || !SAFE_MACRO_STEP_TYPES.has(action)) {
|
|
158
|
+
throw new Error(`macro.steps[${i}].action "${String(action)}" is not in the safe-macro whitelist. ` +
|
|
159
|
+
`Allowed: ${[...SAFE_MACRO_STEP_TYPES].join(', ')}`);
|
|
160
|
+
}
|
|
161
|
+
validateStepTemplates(step, paramNames, i);
|
|
162
|
+
steps.push(step);
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
name,
|
|
166
|
+
description,
|
|
167
|
+
params,
|
|
168
|
+
steps,
|
|
169
|
+
sourcePath,
|
|
170
|
+
...(experimental ? { experimental: true } : {}),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/** Walk a step's string fields and ensure every `${foo}` matches a declared
|
|
174
|
+
* param. We don't support nested interpolation, math, or anything fancy —
|
|
175
|
+
* just a literal `${name}` token. */
|
|
176
|
+
function validateStepTemplates(step, paramNames, index) {
|
|
177
|
+
const re = /\$\{([a-z][a-z0-9_]*)\}/gi;
|
|
178
|
+
for (const [key, value] of Object.entries(step)) {
|
|
179
|
+
if (typeof value !== 'string')
|
|
180
|
+
continue;
|
|
181
|
+
let match;
|
|
182
|
+
while ((match = re.exec(value)) !== null) {
|
|
183
|
+
const name = match[1];
|
|
184
|
+
if (!paramNames.has(name)) {
|
|
185
|
+
throw new Error(`macro.steps[${index}].${key} references undeclared param \${${name}} — add it to params[] or remove the template`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/** Render the loaded macros into the system-prompt snippet. Called once at
|
|
191
|
+
* runner boot and cached in the brain. Returns empty string if no macros. */
|
|
192
|
+
export function renderMacroPromptBlock(macros) {
|
|
193
|
+
const available = macros.filter((m) => !m.experimental);
|
|
194
|
+
if (available.length === 0)
|
|
195
|
+
return '';
|
|
196
|
+
const lines = [];
|
|
197
|
+
lines.push('');
|
|
198
|
+
lines.push('USER MACROS (invoke via {"action":"macro","name":"<name>","args":{...}}):');
|
|
199
|
+
for (const macro of available) {
|
|
200
|
+
const paramList = macro.params.length > 0
|
|
201
|
+
? ` args: { ${macro.params.map((p) => `${p.name}${p.required === false ? '?' : ''}: string`).join(', ')} }`
|
|
202
|
+
: '';
|
|
203
|
+
lines.push(`- ${macro.name} — ${macro.description}${paramList}`);
|
|
204
|
+
}
|
|
205
|
+
return lines.join('\n');
|
|
206
|
+
}
|
|
207
|
+
/** Substitute `${param}` placeholders in string-typed fields of a step,
|
|
208
|
+
* using the provided args. Returns a new object — does not mutate input.
|
|
209
|
+
* Unknown placeholders are collected so the dispatcher can fail fast
|
|
210
|
+
* instead of silently typing a literal `${missing}` into a form. */
|
|
211
|
+
export function interpolateStep(step, args) {
|
|
212
|
+
const unresolved = new Set();
|
|
213
|
+
const entries = Object.entries(step).map(([k, v]) => {
|
|
214
|
+
if (typeof v !== 'string')
|
|
215
|
+
return [k, v];
|
|
216
|
+
return [k, v.replace(/\$\{([a-z][a-z0-9_]*)\}/gi, (match, name) => {
|
|
217
|
+
if (Object.prototype.hasOwnProperty.call(args, name))
|
|
218
|
+
return args[name];
|
|
219
|
+
unresolved.add(name);
|
|
220
|
+
return match;
|
|
221
|
+
})];
|
|
222
|
+
});
|
|
223
|
+
return {
|
|
224
|
+
step: Object.fromEntries(entries),
|
|
225
|
+
unresolved: [...unresolved],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
export function buildMacroRegistry(macros) {
|
|
229
|
+
const map = new Map();
|
|
230
|
+
for (const macro of macros)
|
|
231
|
+
map.set(macro.name, macro);
|
|
232
|
+
return {
|
|
233
|
+
macros: map,
|
|
234
|
+
promptBlock: renderMacroPromptBlock(macros),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=macro-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"macro-loader.js","sourceRoot":"","sources":["../../src/skills/macro-loader.ts"],"names":[],"mappings":"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"}
|