mstro-app 0.4.17 → 0.4.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +148 -75
- package/dist/server/cli/headless/claude-invoker-process.d.ts +1 -1
- package/dist/server/cli/headless/claude-invoker-process.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker-process.js +4 -10
- package/dist/server/cli/headless/claude-invoker-process.js.map +1 -1
- package/dist/server/cli/headless/claude-invoker.js +1 -1
- package/dist/server/cli/headless/claude-invoker.js.map +1 -1
- package/dist/server/cli/headless/mcp-config.d.ts +7 -2
- package/dist/server/cli/headless/mcp-config.d.ts.map +1 -1
- package/dist/server/cli/headless/mcp-config.js +28 -4
- package/dist/server/cli/headless/mcp-config.js.map +1 -1
- package/dist/server/cli/headless/runner.d.ts.map +1 -1
- package/dist/server/cli/headless/runner.js +0 -1
- package/dist/server/cli/headless/runner.js.map +1 -1
- package/dist/server/cli/headless/types.d.ts +1 -4
- package/dist/server/cli/headless/types.d.ts.map +1 -1
- package/dist/server/cli/improvisation-retry.d.ts +1 -1
- package/dist/server/cli/improvisation-retry.d.ts.map +1 -1
- package/dist/server/cli/improvisation-retry.js +1 -2
- package/dist/server/cli/improvisation-retry.js.map +1 -1
- package/dist/server/cli/improvisation-session-manager.d.ts +0 -1
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.js +44 -9
- package/dist/server/cli/improvisation-session-manager.js.map +1 -1
- package/dist/server/index.js +17 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/bouncer-haiku.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-haiku.js +10 -5
- package/dist/server/mcp/bouncer-haiku.js.map +1 -1
- package/dist/server/mcp/bouncer-integration.d.ts +3 -1
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-integration.js +16 -5
- package/dist/server/mcp/bouncer-integration.js.map +1 -1
- package/dist/server/mcp/server.js +3 -1
- package/dist/server/mcp/server.js.map +1 -1
- package/dist/server/services/plan/composer.d.ts +1 -1
- package/dist/server/services/plan/composer.d.ts.map +1 -1
- package/dist/server/services/plan/composer.js +2 -3
- package/dist/server/services/plan/composer.js.map +1 -1
- package/dist/server/services/plan/executor.d.ts +0 -3
- package/dist/server/services/plan/executor.d.ts.map +1 -1
- package/dist/server/services/plan/executor.js +1 -8
- package/dist/server/services/plan/executor.js.map +1 -1
- package/dist/server/services/plan/review-gate.d.ts.map +1 -1
- package/dist/server/services/plan/review-gate.js +19 -2
- package/dist/server/services/plan/review-gate.js.map +1 -1
- package/dist/server/services/plan/state-reconciler.d.ts +6 -0
- package/dist/server/services/plan/state-reconciler.d.ts.map +1 -1
- package/dist/server/services/plan/state-reconciler.js +68 -1
- package/dist/server/services/plan/state-reconciler.js.map +1 -1
- package/dist/server/services/platform.d.ts.map +1 -1
- package/dist/server/services/platform.js +18 -6
- package/dist/server/services/platform.js.map +1 -1
- package/dist/server/services/terminal/pty-manager.d.ts +2 -4
- package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
- package/dist/server/services/terminal/pty-manager.js +5 -28
- package/dist/server/services/terminal/pty-manager.js.map +1 -1
- package/dist/server/services/terminal/pty-utils.d.ts +2 -13
- package/dist/server/services/terminal/pty-utils.d.ts.map +1 -1
- package/dist/server/services/terminal/pty-utils.js +2 -74
- package/dist/server/services/terminal/pty-utils.js.map +1 -1
- package/dist/server/services/websocket/autocomplete.d.ts +1 -1
- package/dist/server/services/websocket/autocomplete.d.ts.map +1 -1
- package/dist/server/services/websocket/autocomplete.js +37 -24
- package/dist/server/services/websocket/autocomplete.js.map +1 -1
- package/dist/server/services/websocket/file-explorer-handlers.d.ts +2 -2
- package/dist/server/services/websocket/file-explorer-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/file-explorer-handlers.js +11 -4
- package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -1
- package/dist/server/services/websocket/handler.d.ts.map +1 -1
- package/dist/server/services/websocket/handler.js +6 -1
- package/dist/server/services/websocket/handler.js.map +1 -1
- package/dist/server/services/websocket/plan-board-handlers.d.ts +5 -5
- package/dist/server/services/websocket/plan-board-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-board-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-execution-handlers.d.ts +6 -6
- package/dist/server/services/websocket/plan-execution-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-execution-handlers.js +1 -4
- package/dist/server/services/websocket/plan-execution-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-handlers.d.ts +1 -1
- package/dist/server/services/websocket/plan-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-helpers.d.ts +1 -1
- package/dist/server/services/websocket/plan-helpers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-helpers.js.map +1 -1
- package/dist/server/services/websocket/plan-issue-handlers.d.ts +4 -4
- package/dist/server/services/websocket/plan-issue-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-issue-handlers.js +10 -0
- package/dist/server/services/websocket/plan-issue-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-sprint-handlers.d.ts +3 -3
- package/dist/server/services/websocket/plan-sprint-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-sprint-handlers.js.map +1 -1
- package/dist/server/services/websocket/quality-handlers.d.ts +1 -1
- package/dist/server/services/websocket/quality-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-handlers.js +9 -5
- package/dist/server/services/websocket/quality-handlers.js.map +1 -1
- package/dist/server/services/websocket/quality-review-agent.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-review-agent.js +7 -4
- package/dist/server/services/websocket/quality-review-agent.js.map +1 -1
- package/dist/server/services/websocket/session-handlers.d.ts +1 -1
- package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/session-handlers.js +5 -2
- package/dist/server/services/websocket/session-handlers.js.map +1 -1
- package/dist/server/services/websocket/terminal-handlers.d.ts +1 -1
- package/dist/server/services/websocket/terminal-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/terminal-handlers.js +9 -21
- package/dist/server/services/websocket/terminal-handlers.js.map +1 -1
- package/dist/server/services/websocket/types.d.ts +2 -2
- package/dist/server/services/websocket/types.d.ts.map +1 -1
- package/dist/server/utils/port.d.ts +0 -11
- package/dist/server/utils/port.d.ts.map +1 -1
- package/dist/server/utils/port.js +0 -31
- package/dist/server/utils/port.js.map +1 -1
- package/package.json +1 -2
- package/server/cli/headless/claude-invoker-process.ts +5 -12
- package/server/cli/headless/claude-invoker.ts +1 -1
- package/server/cli/headless/mcp-config.ts +31 -4
- package/server/cli/headless/runner.ts +0 -1
- package/server/cli/headless/types.ts +1 -4
- package/server/cli/improvisation-retry.ts +0 -2
- package/server/cli/improvisation-session-manager.ts +45 -10
- package/server/index.ts +16 -2
- package/server/mcp/bouncer-haiku.ts +11 -5
- package/server/mcp/bouncer-integration.ts +14 -5
- package/server/mcp/server.ts +3 -1
- package/server/services/plan/composer.ts +1 -3
- package/server/services/plan/executor.ts +1 -9
- package/server/services/plan/review-gate.ts +13 -2
- package/server/services/plan/state-reconciler.ts +70 -1
- package/server/services/platform.ts +17 -6
- package/server/services/terminal/pty-manager.ts +6 -33
- package/server/services/terminal/pty-utils.ts +2 -80
- package/server/services/websocket/autocomplete.ts +48 -26
- package/server/services/websocket/file-explorer-handlers.ts +14 -7
- package/server/services/websocket/handler.ts +8 -2
- package/server/services/websocket/plan-board-handlers.ts +5 -5
- package/server/services/websocket/plan-execution-handlers.ts +7 -10
- package/server/services/websocket/plan-handlers.ts +1 -1
- package/server/services/websocket/plan-helpers.ts +1 -1
- package/server/services/websocket/plan-issue-handlers.ts +14 -4
- package/server/services/websocket/plan-sprint-handlers.ts +3 -3
- package/server/services/websocket/quality-handlers.ts +9 -5
- package/server/services/websocket/quality-review-agent.ts +7 -4
- package/server/services/websocket/session-handlers.ts +8 -3
- package/server/services/websocket/terminal-handlers.ts +10 -24
- package/server/services/websocket/types.ts +2 -2
- package/server/utils/port.ts +0 -41
- package/dist/server/mcp/bouncer-sandbox.d.ts +0 -60
- package/dist/server/mcp/bouncer-sandbox.d.ts.map +0 -1
- package/dist/server/mcp/bouncer-sandbox.js +0 -182
- package/dist/server/mcp/bouncer-sandbox.js.map +0 -1
- package/dist/server/services/credentials.d.ts +0 -39
- package/dist/server/services/credentials.d.ts.map +0 -1
- package/dist/server/services/credentials.js +0 -110
- package/dist/server/services/credentials.js.map +0 -1
- package/dist/server/services/sandbox-utils.d.ts +0 -8
- package/dist/server/services/sandbox-utils.d.ts.map +0 -1
- package/dist/server/services/sandbox-utils.js +0 -75
- package/dist/server/services/sandbox-utils.js.map +0 -1
- package/server/mcp/bouncer-sandbox.ts +0 -214
- package/server/services/credentials.ts +0 -134
- package/server/services/sandbox-utils.ts +0 -82
|
@@ -24,7 +24,7 @@ export function handlePlanMessage(
|
|
|
24
24
|
msg: WebSocketMessage,
|
|
25
25
|
_tabId: string,
|
|
26
26
|
workingDir: string,
|
|
27
|
-
permission?: '
|
|
27
|
+
permission?: 'view',
|
|
28
28
|
): void {
|
|
29
29
|
const handlers: Record<string, () => void> = {
|
|
30
30
|
planInit: () => handlePlanInit(ctx, ws, workingDir),
|
|
@@ -23,7 +23,7 @@ export function resolvePlanPath(workingDir: string, relativePath: string): strin
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/** Guard for write operations — returns true if denied. */
|
|
26
|
-
export function denyIfViewOnly(ctx: HandlerContext, ws: WSContext, permission?: '
|
|
26
|
+
export function denyIfViewOnly(ctx: HandlerContext, ws: WSContext, permission?: 'view'): boolean {
|
|
27
27
|
if (permission === 'view') {
|
|
28
28
|
ctx.send(ws, { type: 'planError', data: { error: 'Permission denied' } });
|
|
29
29
|
return true;
|
|
@@ -5,6 +5,7 @@ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from '
|
|
|
5
5
|
import { basename, join } from 'node:path';
|
|
6
6
|
import { replaceFrontMatterField } from '../plan/front-matter.js';
|
|
7
7
|
import { defaultPmDir, getNextId, parseBoardDirectory, parsePlanDirectory, parseSingleIssue, parseSingleMilestone, parseSingleSprint, planDirExists, resolvePmDir } from '../plan/parser.js';
|
|
8
|
+
import { tryCompleteParentEpic } from '../plan/state-reconciler.js';
|
|
8
9
|
import type { HandlerContext } from './handler-context.js';
|
|
9
10
|
import { buildIssueMarkdown, denyIfViewOnly, formatYamlValue, getWatcher, resolvePlanPath, scaffoldPmDirectory } from './plan-helpers.js';
|
|
10
11
|
import type { WebSocketMessage, WSContext } from './types.js';
|
|
@@ -108,7 +109,7 @@ function resolveBacklogContext(pmDir: string, workingDir: string, boardId?: stri
|
|
|
108
109
|
|
|
109
110
|
export function handleCreateIssue(
|
|
110
111
|
ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage,
|
|
111
|
-
workingDir: string, permission?: '
|
|
112
|
+
workingDir: string, permission?: 'view',
|
|
112
113
|
): void {
|
|
113
114
|
if (denyIfViewOnly(ctx, ws, permission)) return;
|
|
114
115
|
|
|
@@ -135,7 +136,7 @@ export function handleCreateIssue(
|
|
|
135
136
|
|
|
136
137
|
export function handleUpdateIssue(
|
|
137
138
|
ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage,
|
|
138
|
-
workingDir: string, permission?: '
|
|
139
|
+
workingDir: string, permission?: 'view',
|
|
139
140
|
): void {
|
|
140
141
|
if (denyIfViewOnly(ctx, ws, permission)) return;
|
|
141
142
|
|
|
@@ -166,11 +167,20 @@ export function handleUpdateIssue(
|
|
|
166
167
|
|
|
167
168
|
const issue = parseSingleIssue(workingDir, path);
|
|
168
169
|
ctx.broadcastToAll({ type: 'planIssueUpdated', data: issue });
|
|
170
|
+
|
|
171
|
+
// Auto-complete parent epic if all its children are now done
|
|
172
|
+
if (issue) {
|
|
173
|
+
const epicPath = tryCompleteParentEpic(workingDir, issue);
|
|
174
|
+
if (epicPath) {
|
|
175
|
+
const epic = parseSingleIssue(workingDir, epicPath);
|
|
176
|
+
if (epic) ctx.broadcastToAll({ type: 'planIssueUpdated', data: epic });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
169
179
|
}
|
|
170
180
|
|
|
171
181
|
export function handleDeleteIssue(
|
|
172
182
|
ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage,
|
|
173
|
-
workingDir: string, permission?: '
|
|
183
|
+
workingDir: string, permission?: 'view',
|
|
174
184
|
): void {
|
|
175
185
|
if (denyIfViewOnly(ctx, ws, permission)) return;
|
|
176
186
|
|
|
@@ -192,7 +202,7 @@ export function handleDeleteIssue(
|
|
|
192
202
|
|
|
193
203
|
export function handleScaffold(
|
|
194
204
|
ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage,
|
|
195
|
-
workingDir: string, permission?: '
|
|
205
|
+
workingDir: string, permission?: 'view',
|
|
196
206
|
): void {
|
|
197
207
|
if (denyIfViewOnly(ctx, ws, permission)) return;
|
|
198
208
|
|
|
@@ -60,7 +60,7 @@ function assignIssuesToSprint(workingDir: string, issues: Issue[], issueIds: str
|
|
|
60
60
|
|
|
61
61
|
export function handleCreateSprint(
|
|
62
62
|
ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage,
|
|
63
|
-
workingDir: string, permission?: '
|
|
63
|
+
workingDir: string, permission?: 'view',
|
|
64
64
|
): void {
|
|
65
65
|
if (denyIfViewOnly(ctx, ws, permission)) return;
|
|
66
66
|
|
|
@@ -116,7 +116,7 @@ function updateFileField(filePath: string, field: string, value: string): void {
|
|
|
116
116
|
|
|
117
117
|
export function handleActivateSprint(
|
|
118
118
|
ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage,
|
|
119
|
-
workingDir: string, permission?: '
|
|
119
|
+
workingDir: string, permission?: 'view',
|
|
120
120
|
): void {
|
|
121
121
|
if (denyIfViewOnly(ctx, ws, permission)) return;
|
|
122
122
|
|
|
@@ -162,7 +162,7 @@ export function handleActivateSprint(
|
|
|
162
162
|
|
|
163
163
|
export function handleCompleteSprint(
|
|
164
164
|
ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage,
|
|
165
|
-
workingDir: string, permission?: '
|
|
165
|
+
workingDir: string, permission?: 'view',
|
|
166
166
|
): void {
|
|
167
167
|
if (denyIfViewOnly(ctx, ws, permission)) return;
|
|
168
168
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - quality-fix-agent.ts — AI fix prompt, progress tracking, handler
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { join } from 'node:path';
|
|
13
|
+
import { join, resolve } from 'node:path';
|
|
14
14
|
import { validatePathWithinWorkingDir } from '../pathUtils.js';
|
|
15
15
|
import type { HandlerContext } from './handler-context.js';
|
|
16
16
|
import type { FindingForFix } from './quality-fix-agent.js';
|
|
@@ -36,8 +36,12 @@ function getPersistence(workingDir: string): QualityPersistence {
|
|
|
36
36
|
|
|
37
37
|
function resolvePath(workingDir: string, dirPath?: string): string {
|
|
38
38
|
if (!dirPath || dirPath === '.' || dirPath === './') return workingDir;
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const resolved = dirPath.startsWith('/') ? dirPath : join(workingDir, dirPath);
|
|
40
|
+
// Ensure path is within working directory even for non-sandboxed users
|
|
41
|
+
const normalizedResolved = resolve(resolved);
|
|
42
|
+
const normalizedWorkingDir = resolve(workingDir);
|
|
43
|
+
if (!normalizedResolved.startsWith(normalizedWorkingDir)) return workingDir;
|
|
44
|
+
return normalizedResolved;
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
/**
|
|
@@ -68,9 +72,9 @@ export function handleQualityMessage(
|
|
|
68
72
|
msg: WebSocketMessage,
|
|
69
73
|
_tabId: string,
|
|
70
74
|
workingDir: string,
|
|
71
|
-
permission?: '
|
|
75
|
+
permission?: 'view',
|
|
72
76
|
): void {
|
|
73
|
-
const isSandboxed = permission
|
|
77
|
+
const isSandboxed = !!permission;
|
|
74
78
|
const sendPathError = (path: string, error: string) => {
|
|
75
79
|
ctx.send(ws, { type: 'qualityError', data: { path, error } });
|
|
76
80
|
};
|
|
@@ -571,8 +571,11 @@ function persistReviewResults(
|
|
|
571
571
|
): import('./quality-service.js').QualityResults | null {
|
|
572
572
|
const persistence = getPersistence(workingDir);
|
|
573
573
|
const existingReport = persistence.loadReport(reportPath);
|
|
574
|
+
// CodeReviewFinding is structurally compatible with QualityFinding (category is a narrower union)
|
|
575
|
+
const findings = reviewResult.findings as import('./quality-types.js').QualityFinding[];
|
|
576
|
+
const findingsRecord = reviewResult.findings as unknown[] as Record<string, unknown>[];
|
|
574
577
|
if (!existingReport) {
|
|
575
|
-
persistence.saveCodeReview(reportPath,
|
|
578
|
+
persistence.saveCodeReview(reportPath, findingsRecord, reviewResult.summary);
|
|
576
579
|
return null;
|
|
577
580
|
}
|
|
578
581
|
|
|
@@ -582,17 +585,17 @@ function persistReviewResults(
|
|
|
582
585
|
...existingReport,
|
|
583
586
|
overall: reviewResult.score,
|
|
584
587
|
grade: reviewResult.grade,
|
|
585
|
-
codeReview:
|
|
588
|
+
codeReview: findings,
|
|
586
589
|
scoreRationale: reviewResult.scoreRationale ?? undefined,
|
|
587
590
|
};
|
|
588
591
|
} else {
|
|
589
592
|
updatedResults = recomputeWithAiReview(existingReport, reviewResult.findings);
|
|
590
|
-
updatedResults = { ...updatedResults, codeReview:
|
|
593
|
+
updatedResults = { ...updatedResults, codeReview: findings };
|
|
591
594
|
}
|
|
592
595
|
|
|
593
596
|
persistence.saveReport(reportPath, updatedResults);
|
|
594
597
|
persistence.appendHistory(updatedResults, reportPath);
|
|
595
|
-
persistence.saveCodeReview(reportPath,
|
|
598
|
+
persistence.saveCodeReview(reportPath, findingsRecord, reviewResult.summary);
|
|
596
599
|
return updatedResults;
|
|
597
600
|
}
|
|
598
601
|
|
|
@@ -178,15 +178,20 @@ function mergePreUploadedAttachments(ctx: HandlerContext, tabId: string, inlineA
|
|
|
178
178
|
return merged;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
const WRITE_OPS = new Set(['execute', 'cancel', 'new', 'approve', 'reject']);
|
|
182
|
+
|
|
183
|
+
export function handleSessionMessage(ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage, tabId: string, permission?: 'view'): void {
|
|
184
|
+
if (permission === 'view' && WRITE_OPS.has(msg.type)) {
|
|
185
|
+
throw new Error('View-only users cannot perform write operations');
|
|
186
|
+
}
|
|
187
|
+
|
|
182
188
|
switch (msg.type) {
|
|
183
189
|
case 'execute': {
|
|
184
190
|
if (!msg.data?.prompt) throw new Error('Prompt is required');
|
|
185
191
|
const session = requireSession(ctx, ws, tabId);
|
|
186
|
-
const sandboxed = permission === 'control' || permission === 'view';
|
|
187
192
|
const worktreeDir = ctx.gitDirectories.get(tabId);
|
|
188
193
|
const attachments = mergePreUploadedAttachments(ctx, tabId, msg.data.attachments);
|
|
189
|
-
session.executePrompt(msg.data.prompt, attachments, {
|
|
194
|
+
session.executePrompt(msg.data.prompt, attachments, { workingDir: worktreeDir });
|
|
190
195
|
break;
|
|
191
196
|
}
|
|
192
197
|
case 'cancel': {
|
|
@@ -7,11 +7,11 @@ import { getPTYManager } from '../terminal/pty-manager.js';
|
|
|
7
7
|
import type { HandlerContext } from './handler-context.js';
|
|
8
8
|
import type { WebSocketMessage, WSContext } from './types.js';
|
|
9
9
|
|
|
10
|
-
export function handleTerminalMessage(ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage, tabId: string, workingDir: string
|
|
10
|
+
export async function handleTerminalMessage(ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage, tabId: string, workingDir: string): Promise<void> {
|
|
11
11
|
const termId = msg.terminalId || tabId;
|
|
12
12
|
switch (msg.type) {
|
|
13
13
|
case 'terminalInit':
|
|
14
|
-
handleTerminalInit(ctx, ws, termId, workingDir, msg.data?.shell, msg.data?.cols, msg.data?.rows
|
|
14
|
+
await handleTerminalInit(ctx, ws, termId, workingDir, msg.data?.shell, msg.data?.cols, msg.data?.rows);
|
|
15
15
|
break;
|
|
16
16
|
case 'terminalReconnect':
|
|
17
17
|
handleTerminalReconnect(ctx, ws, termId);
|
|
@@ -31,7 +31,7 @@ export function handleTerminalMessage(ctx: HandlerContext, ws: WSContext, msg: W
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
function handleTerminalInit(
|
|
34
|
+
async function handleTerminalInit(
|
|
35
35
|
ctx: HandlerContext,
|
|
36
36
|
ws: WSContext,
|
|
37
37
|
terminalId: string,
|
|
@@ -39,8 +39,7 @@ function handleTerminalInit(
|
|
|
39
39
|
requestedShell?: string,
|
|
40
40
|
cols?: number,
|
|
41
41
|
rows?: number,
|
|
42
|
-
|
|
43
|
-
): void {
|
|
42
|
+
): Promise<void> {
|
|
44
43
|
const ptyManager = getPTYManager();
|
|
45
44
|
|
|
46
45
|
if (!ptyManager.isPtyAvailable()) {
|
|
@@ -59,13 +58,12 @@ function handleTerminalInit(
|
|
|
59
58
|
setupTerminalBroadcastListeners(ctx, terminalId);
|
|
60
59
|
|
|
61
60
|
try {
|
|
62
|
-
const { shell, cwd, isReconnect, platform } = ptyManager.create(
|
|
61
|
+
const { shell, cwd, isReconnect, platform } = await ptyManager.create(
|
|
63
62
|
terminalId,
|
|
64
63
|
workingDir,
|
|
65
64
|
cols || 80,
|
|
66
65
|
rows || 24,
|
|
67
66
|
requestedShell,
|
|
68
|
-
{ sandboxed: permission === 'control' || permission === 'view' }
|
|
69
67
|
);
|
|
70
68
|
|
|
71
69
|
if (!isReconnect) {
|
|
@@ -96,23 +94,11 @@ function handleTerminalInit(
|
|
|
96
94
|
} catch (error: unknown) {
|
|
97
95
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
98
96
|
console.error(`[WebSocketImproviseHandler] Failed to create terminal:`, error);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
data: {
|
|
105
|
-
error: 'SANDBOX_UNAVAILABLE',
|
|
106
|
-
message: 'Terminal requires bubblewrap (bwrap) to be installed on the host machine for shared app sessions. Ask the app owner to install it.',
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
} else {
|
|
110
|
-
ctx.send(ws, {
|
|
111
|
-
type: 'terminalError',
|
|
112
|
-
terminalId,
|
|
113
|
-
data: { error: errorMsg || 'Failed to create terminal' }
|
|
114
|
-
});
|
|
115
|
-
}
|
|
97
|
+
ctx.send(ws, {
|
|
98
|
+
type: 'terminalError',
|
|
99
|
+
terminalId,
|
|
100
|
+
data: { error: errorMsg || 'Failed to create terminal' }
|
|
101
|
+
});
|
|
116
102
|
removeTerminalSubscriber(ctx, terminalId, ws);
|
|
117
103
|
}
|
|
118
104
|
}
|
|
@@ -158,8 +158,8 @@ export interface WebSocketMessage {
|
|
|
158
158
|
terminalId?: string;
|
|
159
159
|
// biome-ignore lint/suspicious/noExplicitAny: message envelope carries heterogeneous payloads
|
|
160
160
|
data?: any;
|
|
161
|
-
/** Injected by server relay for
|
|
162
|
-
_permission?: '
|
|
161
|
+
/** Injected by server relay for view-only shared users */
|
|
162
|
+
_permission?: 'view';
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
export interface WebSocketResponse {
|
package/server/utils/port.ts
CHANGED
|
@@ -59,44 +59,3 @@ export async function findAvailablePort(startPort: number, maxTries: number = 20
|
|
|
59
59
|
}
|
|
60
60
|
throw new Error(`No available ports found between ${startPort} and ${startPort + maxTries}`)
|
|
61
61
|
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Find an available port pair for frontend and backend
|
|
65
|
-
* Frontend = EVEN port (3000, 3002, 3004...)
|
|
66
|
-
* Backend = ODD port (3001, 3003, 3005...)
|
|
67
|
-
*
|
|
68
|
-
* Checks all candidate ports in parallel for fast detection.
|
|
69
|
-
*/
|
|
70
|
-
export async function findAvailablePortPair(startPort: number = 3000, maxPairs: number = 20): Promise<{ frontend: number; backend: number }> {
|
|
71
|
-
// Ensure startPort is even
|
|
72
|
-
const basePort = startPort % 2 === 0 ? startPort : startPort + 1
|
|
73
|
-
|
|
74
|
-
// Generate all candidate pairs
|
|
75
|
-
const pairs: { frontend: number; backend: number }[] = []
|
|
76
|
-
for (let i = 0; i < maxPairs; i++) {
|
|
77
|
-
pairs.push({
|
|
78
|
-
frontend: basePort + (i * 2), // 3000, 3002, 3004...
|
|
79
|
-
backend: basePort + (i * 2) + 1 // 3001, 3003, 3005...
|
|
80
|
-
})
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Check all ports in parallel (both frontend and backend ports)
|
|
84
|
-
const allPorts = pairs.flatMap(p => [p.frontend, p.backend])
|
|
85
|
-
const results = await Promise.all(
|
|
86
|
-
allPorts.map(async (port) => ({ port, available: await isPortAvailable(port) }))
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
// Build a set of available ports for O(1) lookup
|
|
90
|
-
const availablePorts = new Set(
|
|
91
|
-
results.filter(r => r.available).map(r => r.port)
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
// Find first pair where both ports are available
|
|
95
|
-
for (const pair of pairs) {
|
|
96
|
-
if (availablePorts.has(pair.frontend) && availablePorts.has(pair.backend)) {
|
|
97
|
-
return pair
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
throw new Error(`No available port pairs found starting from ${startPort}`)
|
|
102
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
export interface SandboxExecResult {
|
|
2
|
-
/** The sandboxed command that was actually run */
|
|
3
|
-
wrappedCommand: string;
|
|
4
|
-
/** Whether sandbox-runtime is available on this platform */
|
|
5
|
-
sandboxAvailable: boolean;
|
|
6
|
-
/** Whether the sandbox contained the operation (no violations) */
|
|
7
|
-
contained: boolean;
|
|
8
|
-
/** List of violation descriptions if any escaped the sandbox */
|
|
9
|
-
violations: string[];
|
|
10
|
-
}
|
|
11
|
-
export interface CanaryCheckResult {
|
|
12
|
-
/** Whether the canary file still exists (should be true if sandbox contained the write) */
|
|
13
|
-
canaryIntact: boolean;
|
|
14
|
-
/** Whether a file was written outside the sandbox (should be false) */
|
|
15
|
-
escapeDetected: boolean;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Test harness that wraps command execution in sandbox-runtime.
|
|
19
|
-
* Provides canary files and violation tracking to verify containment.
|
|
20
|
-
*/
|
|
21
|
-
export declare class BouncerSandboxHarness {
|
|
22
|
-
private sandboxManager;
|
|
23
|
-
private sandboxAvailable;
|
|
24
|
-
private tempDir;
|
|
25
|
-
private canaryDir;
|
|
26
|
-
constructor();
|
|
27
|
-
/**
|
|
28
|
-
* Initialize the sandbox. Falls back gracefully if bwrap/sandbox-exec not available.
|
|
29
|
-
*/
|
|
30
|
-
initialize(): Promise<{
|
|
31
|
-
available: boolean;
|
|
32
|
-
reason?: string;
|
|
33
|
-
}>;
|
|
34
|
-
/**
|
|
35
|
-
* Execute a command inside the sandbox. Returns containment results.
|
|
36
|
-
* If sandbox is not available, validates the bouncer decision only (no actual execution).
|
|
37
|
-
*/
|
|
38
|
-
executeInSandbox(command: string): Promise<SandboxExecResult>;
|
|
39
|
-
/**
|
|
40
|
-
* Place a canary file and return a checker to verify containment.
|
|
41
|
-
* If a sandboxed command can delete or modify the canary, containment failed.
|
|
42
|
-
*/
|
|
43
|
-
placeCanary(name: string): {
|
|
44
|
-
path: string;
|
|
45
|
-
check: () => CanaryCheckResult;
|
|
46
|
-
};
|
|
47
|
-
/**
|
|
48
|
-
* Get the temp directory where sandboxed commands can write.
|
|
49
|
-
*/
|
|
50
|
-
getSandboxWriteDir(): string;
|
|
51
|
-
/**
|
|
52
|
-
* Whether the sandbox is actually available and initialized.
|
|
53
|
-
*/
|
|
54
|
-
isAvailable(): boolean;
|
|
55
|
-
/**
|
|
56
|
-
* Clean up temp dirs and reset sandbox state.
|
|
57
|
-
*/
|
|
58
|
-
cleanup(): Promise<void>;
|
|
59
|
-
}
|
|
60
|
-
//# sourceMappingURL=bouncer-sandbox.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bouncer-sandbox.d.ts","sourceRoot":"","sources":["../../../server/mcp/bouncer-sandbox.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,cAAc,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kEAAkE;IAClE,SAAS,EAAE,OAAO,CAAC;IACnB,gEAAgE;IAChE,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,2FAA2F;IAC3F,YAAY,EAAE,OAAO,CAAC;IACtB,uEAAuE;IACvE,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;GAGG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,cAAc,CAA0F;IAChH,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;;IAQ1B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAiDpE;;;OAGG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAmDnE;;;OAGG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,iBAAiB,CAAA;KAAE;IAc3E;;OAEG;IACH,kBAAkB,IAAI,MAAM;IAI5B;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAc/B"}
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
-
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
-
/**
|
|
4
|
-
* Sandbox Harness for Bouncer Testing
|
|
5
|
-
*
|
|
6
|
-
* Wraps command execution in Anthropic's sandbox-runtime (bubblewrap on Linux,
|
|
7
|
-
* sandbox-exec on macOS) to safely test what happens when the bouncer FAILS —
|
|
8
|
-
* i.e., when a malicious tool call gets through.
|
|
9
|
-
*
|
|
10
|
-
* Usage in tests:
|
|
11
|
-
* const harness = new BouncerSandboxHarness();
|
|
12
|
-
* await harness.initialize();
|
|
13
|
-
* const result = await harness.executeInSandbox('rm -rf /tmp/test-canary');
|
|
14
|
-
* expect(result.violations).toContain(...)
|
|
15
|
-
* await harness.cleanup();
|
|
16
|
-
*/
|
|
17
|
-
import { execSync } from 'node:child_process';
|
|
18
|
-
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
19
|
-
import { tmpdir } from 'node:os';
|
|
20
|
-
import { join } from 'node:path';
|
|
21
|
-
/**
|
|
22
|
-
* Test harness that wraps command execution in sandbox-runtime.
|
|
23
|
-
* Provides canary files and violation tracking to verify containment.
|
|
24
|
-
*/
|
|
25
|
-
export class BouncerSandboxHarness {
|
|
26
|
-
sandboxManager = null;
|
|
27
|
-
sandboxAvailable = false;
|
|
28
|
-
tempDir;
|
|
29
|
-
canaryDir;
|
|
30
|
-
constructor() {
|
|
31
|
-
this.tempDir = mkdtempSync(join(tmpdir(), 'bouncer-sandbox-'));
|
|
32
|
-
this.canaryDir = join(this.tempDir, 'canaries');
|
|
33
|
-
mkdirSync(this.canaryDir, { recursive: true });
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Initialize the sandbox. Falls back gracefully if bwrap/sandbox-exec not available.
|
|
37
|
-
*/
|
|
38
|
-
async initialize() {
|
|
39
|
-
try {
|
|
40
|
-
const { SandboxManager } = await import('@anthropic-ai/sandbox-runtime');
|
|
41
|
-
if (!SandboxManager.isSupportedPlatform()) {
|
|
42
|
-
return { available: false, reason: 'Platform not supported by sandbox-runtime' };
|
|
43
|
-
}
|
|
44
|
-
const deps = SandboxManager.checkDependencies();
|
|
45
|
-
if (deps.errors.length > 0) {
|
|
46
|
-
return {
|
|
47
|
-
available: false,
|
|
48
|
-
reason: `Missing dependencies: ${deps.errors.join(', ')}`,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
await SandboxManager.initialize({
|
|
52
|
-
network: {
|
|
53
|
-
allowedDomains: [], // Block ALL network access
|
|
54
|
-
deniedDomains: ['*'],
|
|
55
|
-
},
|
|
56
|
-
filesystem: {
|
|
57
|
-
denyRead: [
|
|
58
|
-
'/home/*/.ssh',
|
|
59
|
-
'/home/*/.aws',
|
|
60
|
-
'/home/*/.gnupg',
|
|
61
|
-
'/etc/shadow',
|
|
62
|
-
'/etc/passwd',
|
|
63
|
-
],
|
|
64
|
-
allowWrite: [this.tempDir], // Only allow writes to our temp dir
|
|
65
|
-
denyWrite: [
|
|
66
|
-
'/',
|
|
67
|
-
'/home',
|
|
68
|
-
'/etc',
|
|
69
|
-
'/usr',
|
|
70
|
-
'/var',
|
|
71
|
-
],
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
this.sandboxManager = SandboxManager;
|
|
75
|
-
this.sandboxAvailable = true;
|
|
76
|
-
return { available: true };
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
80
|
-
return { available: false, reason: `Failed to initialize sandbox: ${msg}` };
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Execute a command inside the sandbox. Returns containment results.
|
|
85
|
-
* If sandbox is not available, validates the bouncer decision only (no actual execution).
|
|
86
|
-
*/
|
|
87
|
-
async executeInSandbox(command) {
|
|
88
|
-
if (!this.sandboxAvailable || !this.sandboxManager) {
|
|
89
|
-
return {
|
|
90
|
-
wrappedCommand: command,
|
|
91
|
-
sandboxAvailable: false,
|
|
92
|
-
contained: true,
|
|
93
|
-
violations: ['Sandbox not available — decision-only testing mode'],
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
const violations = [];
|
|
97
|
-
try {
|
|
98
|
-
const wrappedCommand = await this.sandboxManager.wrapWithSandbox(command);
|
|
99
|
-
// Execute the wrapped command and capture violations
|
|
100
|
-
try {
|
|
101
|
-
execSync(wrappedCommand, {
|
|
102
|
-
timeout: 5000,
|
|
103
|
-
stdio: 'pipe',
|
|
104
|
-
cwd: this.tempDir,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
catch {
|
|
108
|
-
// Command failure inside sandbox is expected for malicious ops
|
|
109
|
-
}
|
|
110
|
-
// Check violation store
|
|
111
|
-
const stderr = this.sandboxManager.annotateStderrWithSandboxFailures(command, '');
|
|
112
|
-
if (stderr) {
|
|
113
|
-
violations.push(stderr);
|
|
114
|
-
}
|
|
115
|
-
this.sandboxManager.cleanupAfterCommand();
|
|
116
|
-
return {
|
|
117
|
-
wrappedCommand,
|
|
118
|
-
sandboxAvailable: true,
|
|
119
|
-
contained: violations.length === 0,
|
|
120
|
-
violations,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
catch (error) {
|
|
124
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
125
|
-
violations.push(`Sandbox execution error: ${msg}`);
|
|
126
|
-
return {
|
|
127
|
-
wrappedCommand: command,
|
|
128
|
-
sandboxAvailable: true,
|
|
129
|
-
contained: true, // Error means the command didn't execute
|
|
130
|
-
violations,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Place a canary file and return a checker to verify containment.
|
|
136
|
-
* If a sandboxed command can delete or modify the canary, containment failed.
|
|
137
|
-
*/
|
|
138
|
-
placeCanary(name) {
|
|
139
|
-
const canaryPath = join(this.canaryDir, name);
|
|
140
|
-
const escapePath = join(this.canaryDir, `${name}.escaped`);
|
|
141
|
-
writeFileSync(canaryPath, `canary-${Date.now()}`, 'utf-8');
|
|
142
|
-
return {
|
|
143
|
-
path: canaryPath,
|
|
144
|
-
check: () => ({
|
|
145
|
-
canaryIntact: existsSync(canaryPath),
|
|
146
|
-
escapeDetected: existsSync(escapePath),
|
|
147
|
-
}),
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Get the temp directory where sandboxed commands can write.
|
|
152
|
-
*/
|
|
153
|
-
getSandboxWriteDir() {
|
|
154
|
-
return this.tempDir;
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Whether the sandbox is actually available and initialized.
|
|
158
|
-
*/
|
|
159
|
-
isAvailable() {
|
|
160
|
-
return this.sandboxAvailable;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Clean up temp dirs and reset sandbox state.
|
|
164
|
-
*/
|
|
165
|
-
async cleanup() {
|
|
166
|
-
try {
|
|
167
|
-
if (this.sandboxManager) {
|
|
168
|
-
await this.sandboxManager.reset();
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
catch {
|
|
172
|
-
// Ignore cleanup errors
|
|
173
|
-
}
|
|
174
|
-
try {
|
|
175
|
-
rmSync(this.tempDir, { recursive: true, force: true });
|
|
176
|
-
}
|
|
177
|
-
catch {
|
|
178
|
-
// Ignore cleanup errors
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
//# sourceMappingURL=bouncer-sandbox.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bouncer-sandbox.js","sourceRoot":"","sources":["../../../server/mcp/bouncer-sandbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
export interface Credentials {
|
|
2
|
-
token: string;
|
|
3
|
-
userId: string;
|
|
4
|
-
email: string;
|
|
5
|
-
name?: string;
|
|
6
|
-
deviceId?: string;
|
|
7
|
-
clientId: string;
|
|
8
|
-
createdAt: string;
|
|
9
|
-
lastRefreshedAt?: string;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Get stored credentials, or null if not logged in
|
|
13
|
-
*/
|
|
14
|
-
export declare function getCredentials(): Credentials | null;
|
|
15
|
-
/**
|
|
16
|
-
* Save credentials after successful login
|
|
17
|
-
*/
|
|
18
|
-
export declare function saveCredentials(credentials: Credentials): void;
|
|
19
|
-
/**
|
|
20
|
-
* Update the token (used during refresh)
|
|
21
|
-
*/
|
|
22
|
-
export declare function updateToken(newToken: string): void;
|
|
23
|
-
/**
|
|
24
|
-
* Delete credentials (logout)
|
|
25
|
-
*/
|
|
26
|
-
export declare function deleteCredentials(): boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Check if user is logged in
|
|
29
|
-
*/
|
|
30
|
-
export declare function isLoggedIn(): boolean;
|
|
31
|
-
/**
|
|
32
|
-
* Get the credentials file path (for display)
|
|
33
|
-
*/
|
|
34
|
-
export declare function getCredentialsPath(): string;
|
|
35
|
-
/**
|
|
36
|
-
* Get the mstro directory path
|
|
37
|
-
*/
|
|
38
|
-
export declare function getMstroDir(): string;
|
|
39
|
-
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../../server/services/credentials.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAWD;;GAEG;AACH,wBAAgB,cAAc,IAAI,WAAW,GAAG,IAAI,CAoBnD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAK9D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CASlD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAY3C;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC"}
|