@ddtcorex/dsh-maestro-review 0.1.0 → 0.1.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/lib/config-store.d.ts +2 -0
- package/lib/config-store.d.ts.map +1 -1
- package/lib/config-store.js.map +1 -1
- package/lib/events.d.ts.map +1 -1
- package/lib/events.js.map +1 -1
- package/lib/git-worktree-tool.d.ts +11 -0
- package/lib/git-worktree-tool.d.ts.map +1 -0
- package/lib/git-worktree-tool.js +162 -0
- package/lib/git-worktree-tool.js.map +1 -0
- package/lib/gitlab-client.d.ts.map +1 -1
- package/lib/gitlab-client.js.map +1 -1
- package/lib/gitlab-webhook.d.ts.map +1 -1
- package/lib/gitlab-webhook.js.map +1 -1
- package/lib/govard-audit-lint-tool.d.ts +14 -0
- package/lib/govard-audit-lint-tool.d.ts.map +1 -0
- package/lib/govard-audit-lint-tool.js +177 -0
- package/lib/govard-audit-lint-tool.js.map +1 -0
- package/lib/govard-tool.d.ts.map +1 -1
- package/lib/govard-tool.js.map +1 -1
- package/lib/hyva-csp-scan-tool.d.ts +11 -0
- package/lib/hyva-csp-scan-tool.d.ts.map +1 -0
- package/lib/hyva-csp-scan-tool.js +193 -0
- package/lib/hyva-csp-scan-tool.js.map +1 -0
- package/lib/hyva-theme-inspect-tool.d.ts +10 -0
- package/lib/hyva-theme-inspect-tool.d.ts.map +1 -0
- package/lib/hyva-theme-inspect-tool.js +299 -0
- package/lib/hyva-theme-inspect-tool.js.map +1 -0
- package/lib/incremental.d.ts +28 -0
- package/lib/incremental.d.ts.map +1 -0
- package/lib/incremental.js +60 -0
- package/lib/incremental.js.map +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/layout-xml-tool.d.ts +11 -0
- package/lib/layout-xml-tool.d.ts.map +1 -0
- package/lib/layout-xml-tool.js +240 -0
- package/lib/layout-xml-tool.js.map +1 -0
- package/lib/module-check-tool.d.ts +11 -0
- package/lib/module-check-tool.d.ts.map +1 -0
- package/lib/module-check-tool.js +304 -0
- package/lib/module-check-tool.js.map +1 -0
- package/lib/notify.d.ts.map +1 -1
- package/lib/notify.js.map +1 -1
- package/lib/orchestrator.d.ts +2 -0
- package/lib/orchestrator.d.ts.map +1 -1
- package/lib/orchestrator.js +220 -88
- package/lib/orchestrator.js.map +1 -1
- package/lib/perf-log-stats-tool.d.ts +13 -0
- package/lib/perf-log-stats-tool.d.ts.map +1 -0
- package/lib/perf-log-stats-tool.js +245 -0
- package/lib/perf-log-stats-tool.js.map +1 -0
- package/lib/phtml-escape-scan-tool.d.ts +11 -0
- package/lib/phtml-escape-scan-tool.d.ts.map +1 -0
- package/lib/phtml-escape-scan-tool.js +170 -0
- package/lib/phtml-escape-scan-tool.js.map +1 -0
- package/lib/plan-track-tool.d.ts +11 -0
- package/lib/plan-track-tool.d.ts.map +1 -0
- package/lib/plan-track-tool.js +143 -0
- package/lib/plan-track-tool.js.map +1 -0
- package/lib/providers/github.stub.d.ts.map +1 -1
- package/lib/providers/github.stub.js.map +1 -1
- package/lib/providers/gitlab.d.ts.map +1 -1
- package/lib/providers/gitlab.js.map +1 -1
- package/lib/providers/interface.d.ts.map +1 -1
- package/lib/providers/interface.js.map +1 -1
- package/lib/review-findings-tool.d.ts.map +1 -1
- package/lib/review-findings-tool.js.map +1 -1
- package/lib/review-history.d.ts +4 -0
- package/lib/review-history.d.ts.map +1 -1
- package/lib/review-history.js +23 -1
- package/lib/review-history.js.map +1 -1
- package/lib/review-intake.d.ts.map +1 -1
- package/lib/review-intake.js.map +1 -1
- package/lib/review-signals.d.ts.map +1 -1
- package/lib/review-signals.js.map +1 -1
- package/lib/scope-split-tool.d.ts +42 -0
- package/lib/scope-split-tool.d.ts.map +1 -0
- package/lib/scope-split-tool.js +109 -0
- package/lib/scope-split-tool.js.map +1 -0
- package/lib/search-tool.d.ts +15 -0
- package/lib/search-tool.d.ts.map +1 -0
- package/lib/search-tool.js +144 -0
- package/lib/search-tool.js.map +1 -0
- package/lib/secure-compare.d.ts.map +1 -1
- package/lib/secure-compare.js.map +1 -1
- package/lib/settings-rpc.d.ts.map +1 -1
- package/lib/settings-rpc.js +47 -8
- package/lib/settings-rpc.js.map +1 -1
- package/lib/skills-tool.d.ts +1 -1
- package/lib/skills-tool.d.ts.map +1 -1
- package/lib/skills-tool.js +5 -1
- package/lib/skills-tool.js.map +1 -1
- package/lib/tdd-evidence-tool.d.ts +11 -0
- package/lib/tdd-evidence-tool.d.ts.map +1 -0
- package/lib/tdd-evidence-tool.js +114 -0
- package/lib/tdd-evidence-tool.js.map +1 -0
- package/lib/tool-policy.d.ts +20 -0
- package/lib/tool-policy.d.ts.map +1 -0
- package/lib/tool-policy.js +42 -0
- package/lib/tool-policy.js.map +1 -0
- package/lib/workspace-tool.d.ts.map +1 -1
- package/lib/workspace-tool.js +15 -9
- package/lib/workspace-tool.js.map +1 -1
- package/package.json +4 -4
- package/src/{augment.d.ts → host/augment.d.ts} +5 -2
- package/src/{config-store.ts → host/config-store.ts} +2 -0
- package/src/host/git-worktree-tool.ts +130 -0
- package/src/host/govard-audit-lint-tool.ts +153 -0
- package/src/host/hyva-csp-scan-tool.ts +165 -0
- package/src/host/hyva-theme-inspect-tool.ts +258 -0
- package/src/host/incremental.ts +84 -0
- package/src/host/layout-xml-tool.ts +191 -0
- package/src/host/module-check-tool.ts +188 -0
- package/src/{orchestrator.ts → host/orchestrator.ts} +205 -84
- package/src/host/perf-log-stats-tool.ts +224 -0
- package/src/host/phtml-escape-scan-tool.ts +117 -0
- package/src/host/plan-track-tool.ts +118 -0
- package/src/{review-history.ts → host/review-history.ts} +27 -1
- package/src/host/scope-split-tool.ts +110 -0
- package/src/host/search-tool.ts +153 -0
- package/src/{settings-rpc.ts → host/settings-rpc.ts} +54 -11
- package/src/{skills-tool.ts → host/skills-tool.ts} +5 -1
- package/src/host/tdd-evidence-tool.ts +85 -0
- package/src/host/tool-policy.ts +49 -0
- package/src/{workspace-tool.ts → host/workspace-tool.ts} +27 -9
- package/lib/pin-store.d.ts +0 -5
- package/lib/pin-store.d.ts.map +0 -1
- package/lib/pin-store.js +0 -5
- package/lib/pin-store.js.map +0 -1
- package/src/pin-store.ts +0 -4
- /package/src/{events.ts → host/events.ts} +0 -0
- /package/src/{gitlab-client.ts → host/gitlab-client.ts} +0 -0
- /package/src/{gitlab-webhook.ts → host/gitlab-webhook.ts} +0 -0
- /package/src/{govard-tool.ts → host/govard-tool.ts} +0 -0
- /package/src/{index.ts → host/index.ts} +0 -0
- /package/src/{notify.ts → host/notify.ts} +0 -0
- /package/src/{providers → host/providers}/github.stub.ts +0 -0
- /package/src/{providers → host/providers}/gitlab.ts +0 -0
- /package/src/{providers → host/providers}/interface.ts +0 -0
- /package/src/{review-findings-tool.ts → host/review-findings-tool.ts} +0 -0
- /package/src/{review-intake.ts → host/review-intake.ts} +0 -0
- /package/src/{review-signals.ts → host/review-signals.ts} +0 -0
- /package/src/{secure-compare.ts → host/secure-compare.ts} +0 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { basename, dirname, resolve, sep } from 'node:path';
|
|
2
|
+
import { readFile, realpath, stat } from 'node:fs/promises';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
5
|
+
import z from '@deepseek-ai/schemastery';
|
|
6
|
+
export const name = 'maestro-perf-log-stats-tool';
|
|
7
|
+
export const inject = ['tools'];
|
|
8
|
+
export const Config = z.object({ rootPath: z.string() });
|
|
9
|
+
function workspaceRootFor(c, e) { if (c !== undefined)
|
|
10
|
+
return c; const cwd = e?.agent?.session?.header?.cwd; return typeof cwd === 'string' && cwd !== '' ? cwd : process.cwd(); }
|
|
11
|
+
async function isInsideRoot(r, t) {
|
|
12
|
+
const ar = resolve(r);
|
|
13
|
+
const rs = resolve(ar, t);
|
|
14
|
+
if (rs !== ar && !rs.startsWith(ar + sep))
|
|
15
|
+
return false;
|
|
16
|
+
let realRoot;
|
|
17
|
+
try {
|
|
18
|
+
realRoot = await realpath(ar);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// if root does not exist yet (e.g. .worktrees not created), fallback to lexical check only
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
let existing = rs;
|
|
25
|
+
const pending = [];
|
|
26
|
+
while (true) {
|
|
27
|
+
let real;
|
|
28
|
+
try {
|
|
29
|
+
real = await realpath(existing);
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
if (err.code !== 'ENOENT')
|
|
33
|
+
return false;
|
|
34
|
+
const parent = dirname(existing);
|
|
35
|
+
if (parent === existing)
|
|
36
|
+
return false;
|
|
37
|
+
pending.unshift(basename(existing));
|
|
38
|
+
existing = parent;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const realTarget = pending.length > 0 ? resolve(real, ...pending) : real;
|
|
42
|
+
if (realTarget !== realRoot && !realTarget.startsWith(realRoot + sep))
|
|
43
|
+
return false;
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export const MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
48
|
+
export function cleanJson(raw) {
|
|
49
|
+
// strip trailing ERROR line outside JSON (large audit 2.8MB case, sed '$d' before jq)
|
|
50
|
+
// Handles both "\n ERROR audit run ..." and " ERROR audit run ..." suffix
|
|
51
|
+
return raw.replace(/\n\s*ERROR audit run.*$/s, '').replace(/\s{2,}ERROR audit run.*$/s, '').trimEnd();
|
|
52
|
+
}
|
|
53
|
+
async function govardShCat(worktreePath, relPath) {
|
|
54
|
+
// streaming cat var/debug/db.log server-side bounded 2MiB via govard sh
|
|
55
|
+
// returns null on govard not found or not a govard project, caller falls back to fs
|
|
56
|
+
return new Promise((res) => {
|
|
57
|
+
const child = spawn('govard', ['sh', '-c', `head -c ${MAX_FILE_BYTES} "${relPath.replace(/"/g, '\\"')}" 2>/dev/null`], { cwd: worktreePath });
|
|
58
|
+
let out = '';
|
|
59
|
+
let err = '';
|
|
60
|
+
child.stdout?.on('data', (c) => out += c.toString());
|
|
61
|
+
child.stderr?.on('data', (c) => err += c.toString());
|
|
62
|
+
child.on('error', () => res(null));
|
|
63
|
+
child.on('close', async (code) => {
|
|
64
|
+
const combined = out + err;
|
|
65
|
+
if (code !== 0) {
|
|
66
|
+
// govard not found, no container, or file missing -> fallback to fs
|
|
67
|
+
res(null);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (combined.includes('Error response from daemon') || combined.includes('No such container')) {
|
|
71
|
+
res(null);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Try to get full file size for truncated flag via fs stat (best effort)
|
|
75
|
+
try {
|
|
76
|
+
const abs = resolve(worktreePath, relPath);
|
|
77
|
+
const s = await stat(abs);
|
|
78
|
+
const truncated = s.size > MAX_FILE_BYTES;
|
|
79
|
+
const text = truncated ? out.slice(0, MAX_FILE_BYTES) : out;
|
|
80
|
+
res({ text, bytes: s.size, truncated });
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// if stat fails (file missing or container-only file), infer from output length
|
|
84
|
+
// but if file missing, out is empty and code was 0? head on missing returns 1, already handled
|
|
85
|
+
// so infer truncated from size
|
|
86
|
+
const truncated = Buffer.byteLength(out, 'utf-8') >= MAX_FILE_BYTES;
|
|
87
|
+
// if out empty and code 0, file empty -> exists true with empty text; but we already filtered code !=0 for missing
|
|
88
|
+
// For safety, if out empty and we are here, treat as exists with empty
|
|
89
|
+
res({ text: out.slice(0, MAX_FILE_BYTES), bytes: Buffer.byteLength(out, 'utf-8'), truncated });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async function boundedRead(p, worktreePath, relPath) {
|
|
95
|
+
// Prefer govard sh streaming when worktreePath+relPath supplied and govard available
|
|
96
|
+
if (worktreePath && relPath) {
|
|
97
|
+
const via = await govardShCat(worktreePath, relPath);
|
|
98
|
+
if (via)
|
|
99
|
+
return { exists: true, text: via.text, bytes: via.bytes, truncated: via.truncated };
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const s = await stat(p);
|
|
103
|
+
if (s.size > MAX_FILE_BYTES) {
|
|
104
|
+
const b = await readFile(p);
|
|
105
|
+
// bounded 2MiB streaming server-side cat equivalent: slice to MAX
|
|
106
|
+
return { exists: true, text: b.toString('utf-8').slice(0, MAX_FILE_BYTES), bytes: s.size, truncated: true };
|
|
107
|
+
}
|
|
108
|
+
const b = await readFile(p);
|
|
109
|
+
return { exists: true, text: b.toString('utf-8'), bytes: b.byteLength, truncated: false };
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return { exists: false, text: null, bytes: 0, truncated: false };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function normalizeShape(sql) {
|
|
116
|
+
return sql.replace(/'[^']*'/g, '?').replace(/"[^"]*"/g, '?').replace(/\b\d+\b/g, '?').replace(/\s+/g, ' ').trim().slice(0, 200);
|
|
117
|
+
}
|
|
118
|
+
export function apply(ctx, config = {}) {
|
|
119
|
+
const configuredRoot = config.rootPath;
|
|
120
|
+
ctx.effect(() => ctx.tools.register(defineTool({
|
|
121
|
+
name: 'maestro_perf_log_stats',
|
|
122
|
+
description: 'Parse Magento query log and profiler CSV for slow queries, n+1, cache flush storms, crawler overload.',
|
|
123
|
+
parameters: {
|
|
124
|
+
worktreePath: { type: 'string' },
|
|
125
|
+
queryLogPath: { type: 'string' },
|
|
126
|
+
profilerCsvPath: { type: 'string' },
|
|
127
|
+
systemLogPath: { type: 'string' },
|
|
128
|
+
debugLogPath: { type: 'string' },
|
|
129
|
+
topN: { type: 'number' },
|
|
130
|
+
repeatThreshold: { type: 'number' },
|
|
131
|
+
timeThresholdMs: { type: 'number' },
|
|
132
|
+
},
|
|
133
|
+
output: {
|
|
134
|
+
schema: {
|
|
135
|
+
type: 'object', additionalProperties: false,
|
|
136
|
+
properties: {
|
|
137
|
+
ok: { type: 'boolean', required: true },
|
|
138
|
+
slowQueries: { type: 'array', required: true },
|
|
139
|
+
nPlusOneCandidates: { type: 'array', required: true },
|
|
140
|
+
warnings: { type: 'array', required: true },
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
render: (_a, v) => [{ type: 'text', text: v.warnings.join('\n') || 'perf stats parsed' }],
|
|
144
|
+
},
|
|
145
|
+
async execute(args, exec) {
|
|
146
|
+
const rawWorktree = args.worktreePath;
|
|
147
|
+
const root = workspaceRootFor(configuredRoot, exec);
|
|
148
|
+
if (rawWorktree !== undefined && !(await isInsideRoot(root, rawWorktree)))
|
|
149
|
+
return { text: `Path "${rawWorktree}" escapes the workspace root.`, truncated: false };
|
|
150
|
+
const worktreePath = rawWorktree ? resolve(root, rawWorktree) : root;
|
|
151
|
+
const queryLogPath = args.queryLogPath ?? 'var/debug/db.log';
|
|
152
|
+
const profilerCsvPath = args.profilerCsvPath ?? 'artifacts/profiler/profile.csv';
|
|
153
|
+
const topN = args.topN ?? 10;
|
|
154
|
+
const repeatThreshold = args.repeatThreshold ?? 5;
|
|
155
|
+
const timeThresholdMs = args.timeThresholdMs ?? 1000;
|
|
156
|
+
if (rawWorktree !== undefined && !(await isInsideRoot(root, worktreePath))) { /* already checked */ }
|
|
157
|
+
const qLogAbs = resolve(worktreePath, queryLogPath);
|
|
158
|
+
if (!(await isInsideRoot(worktreePath, queryLogPath)) && queryLogPath !== 'var/debug/db.log')
|
|
159
|
+
return { text: `Path "${queryLogPath}" escapes the workspace root.`, truncated: false };
|
|
160
|
+
const warnings = [];
|
|
161
|
+
const qRead = await boundedRead(qLogAbs, worktreePath, queryLogPath);
|
|
162
|
+
const pRead = await boundedRead(resolve(worktreePath, profilerCsvPath), worktreePath, profilerCsvPath);
|
|
163
|
+
const inputs = {
|
|
164
|
+
queryLog: { path: queryLogPath, exists: qRead.exists, bytes: qRead.bytes, truncated: qRead.truncated },
|
|
165
|
+
profilerCsv: { path: profilerCsvPath, exists: pRead.exists, bytes: pRead.bytes, truncated: pRead.truncated },
|
|
166
|
+
systemLog: { path: 'var/log/system.log', exists: false, bytes: 0, truncated: false },
|
|
167
|
+
debugLog: { path: 'var/log/debug.log', exists: false, bytes: 0, truncated: false },
|
|
168
|
+
};
|
|
169
|
+
if (!qRead.exists)
|
|
170
|
+
warnings.push('query log not found — run dev:query-log:enable');
|
|
171
|
+
if (!pRead.exists)
|
|
172
|
+
warnings.push('profiler CSV not found');
|
|
173
|
+
if (qRead.exists && qRead.text && qRead.text.length === 0)
|
|
174
|
+
warnings.push('query log empty');
|
|
175
|
+
if (qRead.truncated)
|
|
176
|
+
warnings.push(`query log truncated to ${MAX_FILE_BYTES} bytes (bounded 2MiB streaming)`);
|
|
177
|
+
if (pRead.truncated)
|
|
178
|
+
warnings.push(`profiler CSV truncated to ${MAX_FILE_BYTES} bytes (bounded 2MiB streaming)`);
|
|
179
|
+
// parse db.log
|
|
180
|
+
const shapeMap = new Map();
|
|
181
|
+
if (qRead.text) {
|
|
182
|
+
const blocks = qRead.text.split(/##\s*\d+\s*##\s*QUERY/g);
|
|
183
|
+
for (const block of blocks) {
|
|
184
|
+
if (!block.trim())
|
|
185
|
+
continue;
|
|
186
|
+
const sqlMatch = /SQL:\s*([^\n]+)/.exec(block);
|
|
187
|
+
const timeMatch = /TIME:\s*([0-9.]+)/.exec(block);
|
|
188
|
+
const traceMatch = /TRACE:\s*([\s\S]*?)(?:\n##|$)/.exec(block); // capture trace
|
|
189
|
+
const sql = sqlMatch ? sqlMatch[1].trim() : null;
|
|
190
|
+
const timeSec = timeMatch ? parseFloat(timeMatch[1]) : 0;
|
|
191
|
+
const trace = traceMatch ? traceMatch[1] : block;
|
|
192
|
+
const isFrontend = /App\\Http|App\/Http|Http\.php/.test(trace);
|
|
193
|
+
if (!sql)
|
|
194
|
+
continue;
|
|
195
|
+
if (!isFrontend)
|
|
196
|
+
continue;
|
|
197
|
+
const ms = timeSec * 1000;
|
|
198
|
+
const shape = normalizeShape(sql);
|
|
199
|
+
let entry = shapeMap.get(shape);
|
|
200
|
+
if (!entry) {
|
|
201
|
+
entry = { count: 0, totalMs: 0, maxMs: 0, example: sql, callers: [] };
|
|
202
|
+
shapeMap.set(shape, entry);
|
|
203
|
+
}
|
|
204
|
+
entry.count++;
|
|
205
|
+
entry.totalMs += ms;
|
|
206
|
+
entry.maxMs = Math.max(entry.maxMs, ms);
|
|
207
|
+
// extract caller
|
|
208
|
+
const callerLine = (trace.split('\n').find(l => l.includes('app/code')) || '').trim().slice(0, 120);
|
|
209
|
+
if (callerLine && !entry.callers.includes(callerLine))
|
|
210
|
+
entry.callers.push(callerLine);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const slowQueries = [];
|
|
214
|
+
const nPlusOneCandidates = [];
|
|
215
|
+
for (const [shape, v] of shapeMap.entries()) {
|
|
216
|
+
const avg = v.totalMs / v.count;
|
|
217
|
+
if (v.maxMs >= timeThresholdMs || avg >= timeThresholdMs) {
|
|
218
|
+
slowQueries.push({ query: shape, shape, count: v.count, avgMs: Math.round(avg), maxMs: Math.round(v.maxMs), exampleCallerStack: v.callers.join(' | ') });
|
|
219
|
+
}
|
|
220
|
+
if (v.count >= repeatThreshold) {
|
|
221
|
+
// infer model from caller
|
|
222
|
+
const modelMatch = v.callers.join(' ').match(/app\/code\/[^\/]+\/[^\/]+\/Model\/(\w+)/);
|
|
223
|
+
const model = modelMatch ? modelMatch[1] : null;
|
|
224
|
+
nPlusOneCandidates.push({ shape, count: v.count, model, callerStack: v.callers.join(' | ') });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
slowQueries.sort((a, b) => b.maxMs - a.maxMs);
|
|
228
|
+
nPlusOneCandidates.sort((a, b) => b.count - a.count);
|
|
229
|
+
return {
|
|
230
|
+
ok: true,
|
|
231
|
+
worktreePath,
|
|
232
|
+
inputs,
|
|
233
|
+
slowQueries: slowQueries.slice(0, topN),
|
|
234
|
+
nPlusOneCandidates: nPlusOneCandidates.slice(0, topN),
|
|
235
|
+
cacheFlushStorm: { eventsPerMin: 0, rawEvents: [], windowMin: 5 },
|
|
236
|
+
crawlerOverload: false,
|
|
237
|
+
crawlerEvidence: { hitsPerMin: 0, topUA: null, topIP: null },
|
|
238
|
+
queryShapes: Array.from(shapeMap.entries()).map(([shape, c]) => ({ shape, count: c.count })),
|
|
239
|
+
topTimers: [],
|
|
240
|
+
warnings,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
}), 'perf log stats'));
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=perf-log-stats-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"perf-log-stats-tool.js","sourceRoot":"","sources":["../src/host/perf-log-stats-tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import z from '@deepseek-ai/schemastery';
|
|
3
|
+
export declare const name = "maestro-phtml-escape-scan-tool";
|
|
4
|
+
export declare const inject: string[];
|
|
5
|
+
export declare const Config: z<{
|
|
6
|
+
rootPath?: string;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function apply(ctx: Context, config?: {
|
|
9
|
+
rootPath?: string;
|
|
10
|
+
}): void;
|
|
11
|
+
//# sourceMappingURL=phtml-escape-scan-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phtml-escape-scan-tool.d.ts","sourceRoot":"","sources":["../src/host/phtml-escape-scan-tool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAGxC,eAAO,MAAM,IAAI,mCAAiC,CAAA;AAClD,eAAO,MAAM,MAAM,UAAU,CAAA;AAC7B,eAAO,MAAM,MAAM,EAAC,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAC,MAAM,CAAA;CAAC,CAAiC,CAAA;AAyBzE,wBAAgB,KAAK,CAAC,GAAG,EAAC,OAAO,EAAE,MAAM,GAAC;IAAC,QAAQ,CAAC,EAAC,MAAM,CAAA;CAAI,GAAE,IAAI,CAmFpE"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { join, resolve, sep, basename } from 'node:path';
|
|
2
|
+
import { readFile, readdir, realpath, stat } from 'node:fs/promises';
|
|
3
|
+
import z from '@deepseek-ai/schemastery';
|
|
4
|
+
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
5
|
+
export const name = 'maestro-phtml-escape-scan-tool';
|
|
6
|
+
export const inject = ['tools'];
|
|
7
|
+
export const Config = z.object({ rootPath: z.string() });
|
|
8
|
+
function workspaceRootFor(c, e) { if (c !== undefined)
|
|
9
|
+
return c; const cwd = e?.agent?.session?.header?.cwd; return typeof cwd === 'string' && cwd !== '' ? cwd : process.cwd(); }
|
|
10
|
+
async function isInsideRoot(r, t) { const ar = resolve(r); const rs = resolve(ar, t); if (rs !== ar && !rs.startsWith(ar + sep))
|
|
11
|
+
return false; try {
|
|
12
|
+
const rr = await realpath(ar);
|
|
13
|
+
const rt = await realpath(rs);
|
|
14
|
+
return rt === rr || rt.startsWith(rr + sep);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
} }
|
|
19
|
+
const MAX_FILE_BYTES = 1024 * 1024;
|
|
20
|
+
const MAX_PHTML_FILES = 200;
|
|
21
|
+
const MAX_FINDINGS = 200;
|
|
22
|
+
const SKIP_DIRS = new Set(['.git', 'node_modules', 'vendor', 'pub/static']);
|
|
23
|
+
async function exists(p) { try {
|
|
24
|
+
await stat(p);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
} }
|
|
30
|
+
async function* walkPhtml(dir, budget) {
|
|
31
|
+
if (budget.files <= 0)
|
|
32
|
+
return;
|
|
33
|
+
let entries;
|
|
34
|
+
try {
|
|
35
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
for (const e of entries) {
|
|
41
|
+
if (budget.files <= 0)
|
|
42
|
+
return;
|
|
43
|
+
if (e.isDirectory()) {
|
|
44
|
+
if (SKIP_DIRS.has(e.name))
|
|
45
|
+
continue;
|
|
46
|
+
yield* walkPhtml(join(dir, e.name), budget);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (!e.isFile())
|
|
50
|
+
continue;
|
|
51
|
+
if (!e.name.endsWith('.phtml'))
|
|
52
|
+
continue;
|
|
53
|
+
budget.files -= 1;
|
|
54
|
+
yield join(dir, e.name);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function isSafeLine(line) {
|
|
58
|
+
return /\$escaper\s*->\s*escape/.test(line) || /\$block\s*->\s*escape/.test(line) || /->\s*get\w*Html\s*\(/.test(line) || /escapeHtmlAttr/.test(line) && /title=/.test(line) ? true : false;
|
|
59
|
+
// second case handled more precisely below
|
|
60
|
+
}
|
|
61
|
+
export function apply(ctx, config = {}) {
|
|
62
|
+
const configuredRoot = config.rootPath;
|
|
63
|
+
ctx.effect(() => ctx.tools.register(defineTool({
|
|
64
|
+
name: 'phtml_escape_scan',
|
|
65
|
+
description: 'Scan .phtml for XSS: unescaped echo, missing escapeHtmlAttr, raw block methods, superglobals.',
|
|
66
|
+
parameters: {
|
|
67
|
+
scope: { type: 'string', enum: ['diff', 'paths'], required: true },
|
|
68
|
+
paths: { type: 'array', items: { type: 'string' } },
|
|
69
|
+
},
|
|
70
|
+
output: {
|
|
71
|
+
schema: {
|
|
72
|
+
type: 'object', additionalProperties: false,
|
|
73
|
+
properties: {
|
|
74
|
+
findings: { type: 'array', required: true },
|
|
75
|
+
scannedFiles: { type: 'number', required: true },
|
|
76
|
+
skippedFiles: { type: 'number', required: true },
|
|
77
|
+
truncated: { type: 'boolean', required: true },
|
|
78
|
+
summary: { type: 'object', required: true, additionalProperties: true },
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
render: (_a, v) => [{ type: 'text', text: `${v.findings.length} findings` }],
|
|
82
|
+
},
|
|
83
|
+
async execute(args, exec) {
|
|
84
|
+
const root = workspaceRootFor(configuredRoot, exec);
|
|
85
|
+
const scope = args.scope;
|
|
86
|
+
const paths = args.paths ?? [];
|
|
87
|
+
for (const p of paths) {
|
|
88
|
+
if (!(await isInsideRoot(root, p)))
|
|
89
|
+
return { text: `Path "${p}" escapes the workspace root.`, truncated: false };
|
|
90
|
+
}
|
|
91
|
+
let files = [];
|
|
92
|
+
if (scope === 'paths') {
|
|
93
|
+
for (const p of paths) {
|
|
94
|
+
if (!p.endsWith('.phtml'))
|
|
95
|
+
continue;
|
|
96
|
+
const abs = resolve(root, p);
|
|
97
|
+
if (await exists(abs))
|
|
98
|
+
files.push(p);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// diff: walk all phtml bounded
|
|
103
|
+
const budget = { files: MAX_PHTML_FILES };
|
|
104
|
+
for await (const f of walkPhtml(root, budget)) {
|
|
105
|
+
const rel = f.startsWith(root + sep) ? f.slice(root.length + 1) : basename(f);
|
|
106
|
+
files.push(rel);
|
|
107
|
+
if (files.length >= MAX_PHTML_FILES)
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const findings = [];
|
|
112
|
+
let scannedFiles = 0, skippedFiles = 0;
|
|
113
|
+
for (const rel of files.slice(0, MAX_PHTML_FILES)) {
|
|
114
|
+
if (findings.length >= MAX_FINDINGS)
|
|
115
|
+
break;
|
|
116
|
+
const abs = resolve(root, rel);
|
|
117
|
+
let text;
|
|
118
|
+
try {
|
|
119
|
+
const b = await readFile(abs);
|
|
120
|
+
if (b.byteLength > MAX_FILE_BYTES) {
|
|
121
|
+
skippedFiles++;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
text = b.toString('utf-8');
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
skippedFiles++;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
scannedFiles++;
|
|
131
|
+
const lines = text.split('\n');
|
|
132
|
+
lines.forEach((line, idx) => {
|
|
133
|
+
if (findings.length >= MAX_FINDINGS)
|
|
134
|
+
return;
|
|
135
|
+
const snippet = line.trim().slice(0, 240);
|
|
136
|
+
const lineNo = idx + 1;
|
|
137
|
+
// superglobal always flagged
|
|
138
|
+
if (/\$_GET|\$_POST|\$_REQUEST|\$_COOKIE/.test(line)) {
|
|
139
|
+
findings.push({ file: rel, line: lineNo, type: 'direct-superglobal', snippet, confidence: 'high', rule: 'M2-SEC-superglobal', escaperHint: 'validate/sanitize input, do not echo raw' });
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
// check <?= ... ?> echo
|
|
143
|
+
const echoRe = /<\?=\s*([^?]*?)\s*\?>/g;
|
|
144
|
+
let m;
|
|
145
|
+
while ((m = echoRe.exec(line)) !== null) {
|
|
146
|
+
const inner = m[1];
|
|
147
|
+
// safe if contains escaper
|
|
148
|
+
if (/\$escaper\s*->\s*escape/.test(inner) || /\$block\s*->\s*escape/.test(inner) || /->\s*get\w*Html\s*\(/.test(inner))
|
|
149
|
+
continue;
|
|
150
|
+
// attr check: if line has title=" <?= ... ?> and not escapeHtmlAttr -> unescaped-attr
|
|
151
|
+
if (/title\s*=\s*"/.test(line) && !/escapeHtmlAttr/.test(inner)) {
|
|
152
|
+
findings.push({ file: rel, line: lineNo, type: 'unescaped-attr', snippet, confidence: 'high', rule: 'M2-SEC-attr', escaperHint: '$escaper->escapeHtmlAttr() for attributes' });
|
|
153
|
+
}
|
|
154
|
+
else if (/^\s*\$[a-zA-Z_][\w$]*\s*$/.test(inner.trim()) || /\$\w+/.test(inner)) {
|
|
155
|
+
// bare variable echo
|
|
156
|
+
// if this was already handled as attr, don't double count
|
|
157
|
+
if (findings.some(f => f.file === rel && f.line === lineNo && f.type === 'unescaped-attr'))
|
|
158
|
+
continue;
|
|
159
|
+
findings.push({ file: rel, line: lineNo, type: 'unescaped-echo', snippet, confidence: 'high', rule: 'M2-SEC-echo', escaperHint: '$escaper->escapeHtml() or $block->escapeHtml()' });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
const truncated = files.length >= MAX_PHTML_FILES || findings.length >= MAX_FINDINGS;
|
|
165
|
+
const summary = { high: findings.filter(f => f.confidence === 'high').length, medium: findings.filter(f => f.confidence === 'medium').length, low: findings.filter(f => f.confidence === 'low').length, filesWithFindings: new Set(findings.map(f => f.file)).size };
|
|
166
|
+
return { findings: findings.slice(0, MAX_FINDINGS), scannedFiles, skippedFiles, truncated, summary };
|
|
167
|
+
},
|
|
168
|
+
}), 'phtml escape'));
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=phtml-escape-scan-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phtml-escape-scan-tool.js","sourceRoot":"","sources":["../src/host/phtml-escape-scan-tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import z from '@deepseek-ai/schemastery';
|
|
3
|
+
export declare const name = "maestro-plan-track-tool";
|
|
4
|
+
export declare const inject: string[];
|
|
5
|
+
export declare const Config: z<{
|
|
6
|
+
rootPath?: string;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function apply(ctx: Context, config?: {
|
|
9
|
+
rootPath?: string;
|
|
10
|
+
}): void;
|
|
11
|
+
//# sourceMappingURL=plan-track-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-track-tool.d.ts","sourceRoot":"","sources":["../src/host/plan-track-tool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAExC,eAAO,MAAM,IAAI,4BAA0B,CAAA;AAC3C,eAAO,MAAM,MAAM,UAAU,CAAA;AAC7B,eAAO,MAAM,MAAM,EAAC,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAC,MAAM,CAAA;CAAC,CAAmC,CAAA;AAiC3E,wBAAgB,KAAK,CAAC,GAAG,EAAC,OAAO,EAAE,MAAM,GAAC;IAAC,QAAQ,CAAC,EAAC,MAAM,CAAA;CAAI,GAAE,IAAI,CA4EpE"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { join, resolve, sep } from 'node:path';
|
|
2
|
+
import { readFile, writeFile, mkdir, realpath } from 'node:fs/promises';
|
|
3
|
+
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
4
|
+
import z from '@deepseek-ai/schemastery';
|
|
5
|
+
export const name = 'maestro-plan-track-tool';
|
|
6
|
+
export const inject = ['tools'];
|
|
7
|
+
export const Config = z.object({ rootPath: z.string() });
|
|
8
|
+
function workspaceRootFor(c, e) { if (c !== undefined)
|
|
9
|
+
return c; const cwd = e?.agent?.session?.header?.cwd; return typeof cwd === 'string' && cwd !== '' ? cwd : process.cwd(); }
|
|
10
|
+
async function isInsideRoot(r, t) {
|
|
11
|
+
const ar = resolve(r);
|
|
12
|
+
const rs = resolve(ar, t);
|
|
13
|
+
if (rs !== ar && !rs.startsWith(ar + sep))
|
|
14
|
+
return false;
|
|
15
|
+
try {
|
|
16
|
+
const rr = await realpath(ar);
|
|
17
|
+
const rt = await realpath(rs);
|
|
18
|
+
return rt === rr || rt.startsWith(rr + sep);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
try {
|
|
22
|
+
const rr = await realpath(ar);
|
|
23
|
+
const parent = resolve(rs, '..');
|
|
24
|
+
const rp = await realpath(parent);
|
|
25
|
+
return rp === rr || rp.startsWith(rr + sep);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return rs === ar || rs.startsWith(ar + sep);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function parseTasks(md) {
|
|
33
|
+
const lines = md.split('\n');
|
|
34
|
+
const tasks = [];
|
|
35
|
+
for (let i = 0; i < lines.length; i++) {
|
|
36
|
+
const m = /^###\s+(Task\s*\d+):?\s*(.*)/.exec(lines[i].trim());
|
|
37
|
+
if (!m)
|
|
38
|
+
continue;
|
|
39
|
+
const id = m[1].trim();
|
|
40
|
+
let title = m[2].trim();
|
|
41
|
+
let status = 'pending';
|
|
42
|
+
if (title.includes('in_progress'))
|
|
43
|
+
status = 'in_progress';
|
|
44
|
+
else if (title.toLowerCase().includes('done') || title.includes('✅'))
|
|
45
|
+
status = 'done';
|
|
46
|
+
else if (title.toLowerCase().includes('blocked'))
|
|
47
|
+
status = 'blocked';
|
|
48
|
+
for (let j = i + 1; j < Math.min(i + 4, lines.length); j++) {
|
|
49
|
+
if (lines[j].toLowerCase().includes('in_progress'))
|
|
50
|
+
status = 'in_progress';
|
|
51
|
+
if (lines[j].toLowerCase().includes('status: done'))
|
|
52
|
+
status = 'done';
|
|
53
|
+
}
|
|
54
|
+
tasks.push({ id, title, status, line: i });
|
|
55
|
+
}
|
|
56
|
+
return tasks;
|
|
57
|
+
}
|
|
58
|
+
export function apply(ctx, config = {}) {
|
|
59
|
+
const configuredRoot = config.rootPath;
|
|
60
|
+
ctx.effect(() => ctx.tools.register(defineTool({
|
|
61
|
+
name: 'maestro_plan_track',
|
|
62
|
+
description: 'Track cross-repo plan: init/update/complete tasks with dtodo sync and oneTaskOneCommit guard.',
|
|
63
|
+
parameters: {
|
|
64
|
+
op: { type: 'string', enum: ['init', 'update', 'complete'], required: true },
|
|
65
|
+
planPath: { type: 'string', required: true },
|
|
66
|
+
taskId: { type: 'string' },
|
|
67
|
+
status: { type: 'string', enum: ['pending', 'in_progress', 'done', 'blocked'] },
|
|
68
|
+
note: { type: 'string' },
|
|
69
|
+
},
|
|
70
|
+
output: {
|
|
71
|
+
schema: {
|
|
72
|
+
type: 'object', additionalProperties: true,
|
|
73
|
+
properties: {
|
|
74
|
+
ok: { type: 'boolean', required: true },
|
|
75
|
+
plan: { type: 'object', required: true, additionalProperties: true },
|
|
76
|
+
discipline: { type: 'object', additionalProperties: true },
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
render: (_a, v) => [{ type: 'text', text: v.ok ? 'ok' : 'violation' }],
|
|
80
|
+
},
|
|
81
|
+
async execute(args, exec) {
|
|
82
|
+
const op = args.op;
|
|
83
|
+
const rawPath = args.planPath;
|
|
84
|
+
const root = workspaceRootFor(configuredRoot, exec);
|
|
85
|
+
if (!(await isInsideRoot(root, rawPath)))
|
|
86
|
+
return { text: `Path "${rawPath}" escapes the workspace root.`, truncated: false };
|
|
87
|
+
const planPath = resolve(root, rawPath);
|
|
88
|
+
let md = '';
|
|
89
|
+
try {
|
|
90
|
+
md = await readFile(planPath, 'utf-8');
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
if (op === 'init')
|
|
94
|
+
return { text: `plan not found ${rawPath}`, truncated: false };
|
|
95
|
+
md = '';
|
|
96
|
+
}
|
|
97
|
+
const tasks = parseTasks(md);
|
|
98
|
+
const lines = md.split('\n');
|
|
99
|
+
if (op === 'init') {
|
|
100
|
+
const nextTask = tasks.find(t => t.status === 'pending' || t.status === 'in_progress') || tasks[0] || null;
|
|
101
|
+
const dtodoPayload = tasks.map(t => ({ content: `${t.id}: ${t.title}`, status: t.status }));
|
|
102
|
+
return { ok: true, plan: { path: rawPath, tasks, nextTask, dtodoPayload }, discipline: { violation: null } };
|
|
103
|
+
}
|
|
104
|
+
if (op === 'update') {
|
|
105
|
+
const taskId = args.taskId;
|
|
106
|
+
const status = args.status;
|
|
107
|
+
if (!taskId)
|
|
108
|
+
return { text: 'taskId required', truncated: false };
|
|
109
|
+
const idx = tasks.findIndex(t => t.id === taskId);
|
|
110
|
+
if (idx < 0)
|
|
111
|
+
return { text: `task ${taskId} not found`, truncated: false };
|
|
112
|
+
if (status === 'in_progress') {
|
|
113
|
+
const other = tasks.find(t => t.id !== taskId && t.status === 'in_progress');
|
|
114
|
+
if (other) {
|
|
115
|
+
return { ok: false, plan: { path: rawPath, tasks, nextTask: tasks.find(t => t.status === 'in_progress') || null, dtodoPayload: [] }, discipline: { violation: 'oneTaskOneCommit: only one task may be in_progress at a time' } };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const lineIdx = tasks[idx].line;
|
|
119
|
+
if (status) {
|
|
120
|
+
const orig = lines[lineIdx];
|
|
121
|
+
if (orig.includes('—'))
|
|
122
|
+
lines[lineIdx] = orig.replace(/—.*$/, `— ${status}`);
|
|
123
|
+
else
|
|
124
|
+
lines[lineIdx] = `${orig} — ${status}`;
|
|
125
|
+
await mkdir(join(planPath, '..'), { recursive: true });
|
|
126
|
+
await writeFile(planPath, lines.join('\n'), 'utf-8');
|
|
127
|
+
tasks[idx].status = status;
|
|
128
|
+
}
|
|
129
|
+
const nextTask = tasks.find(t => t.status === 'in_progress') || tasks.find(t => t.status === 'pending') || null;
|
|
130
|
+
return { ok: true, plan: { path: rawPath, tasks, nextTask, dtodoPayload: tasks.map(t => ({ content: `${t.id}: ${t.title}`, status: t.status })) }, discipline: { violation: null } };
|
|
131
|
+
}
|
|
132
|
+
if (op === 'complete') {
|
|
133
|
+
const pending = tasks.filter(t => t.status !== 'done');
|
|
134
|
+
if (pending.length > 0) {
|
|
135
|
+
return { ok: false, plan: { path: rawPath, tasks, nextTask: pending[0], dtodoPayload: [] }, discipline: { violation: `pending tasks remain: ${pending.map(p => p.id).join(',')}` } };
|
|
136
|
+
}
|
|
137
|
+
return { ok: true, plan: { path: rawPath, tasks, nextTask: null, dtodoPayload: [] }, discipline: { violation: null } };
|
|
138
|
+
}
|
|
139
|
+
return { text: `unknown op ${op}`, truncated: false };
|
|
140
|
+
}
|
|
141
|
+
}), 'plan track'));
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=plan-track-tool.js.map
|